1 /* This file is part of Metaproxy.
2 Copyright (C) 2005-2013 Index Data
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "filter_http_client.hpp"
24 #include <metaproxy/util.hpp>
25 #include "router_chain.hpp"
26 #include <metaproxy/package.hpp>
28 #include <boost/regex.hpp>
29 #include <boost/lexical_cast.hpp>
31 #define BOOST_AUTO_TEST_MAIN
32 #define BOOST_TEST_DYN_LINK
34 #include <boost/test/auto_unit_test.hpp>
36 using namespace boost::unit_test;
37 namespace mp = metaproxy_1;
39 class FilterHeaderRewrite: public mp::filter::Base {
41 void process(mp::Package & package) const {
42 Z_GDU *gdu = package.request().get();
43 //map of request/response vars
44 std::map<std::string, std::string> vars;
46 if (gdu && gdu->which == Z_GDU_HTTP_Request)
48 Z_HTTP_Request *hreq = gdu->u.HTTP_Request;
50 //rewrite the request line
52 if (strstr(hreq->path, "http://") == hreq->path)
54 std::cout << "Path in the method line is absolute, "
55 "possibly a proxy request\n";
60 path += z_HTTP_header_lookup(hreq->headers, "Host");
63 std::cout << "Proxy request URL is " << path << std::endl;
65 search_replace(vars, path, req_uri_rx, req_uri_pat);
66 std::cout << "Resp request URL is " << npath << std::endl;
68 hreq->path = odr_strdup(o, npath.c_str());
69 std::cout << ">> Request headers" << std::endl;
71 for (Z_HTTP_Header *header = hreq->headers;
73 header = header->next)
75 std::cout << header->name << ": " << header->value << std::endl;
76 std::string out = search_replace(vars,
77 std::string(header->value),
78 req_uri_rx, req_uri_pat);
80 header->value = odr_strdup(o, out.c_str());
82 package.request() = gdu;
85 gdu = package.response().get();
86 if (gdu && gdu->which == Z_GDU_HTTP_Response)
88 Z_HTTP_Response *hr = gdu->u.HTTP_Response;
89 std::cout << "Response " << hr->code;
90 std::cout << "<< Respose headers" << std::endl;
93 for (Z_HTTP_Header *header = hr->headers;
95 header = header->next)
97 std::cout << header->name << ": " << header->value << std::endl;
98 std::string out = search_replace(vars,
99 std::string(header->value),
100 resp_uri_rx, resp_uri_pat);
102 header->value = odr_strdup(o, out.c_str());
104 package.response() = gdu;
108 void configure(const xmlNode* ptr, bool test_only, const char *path) {};
110 const std::string search_replace(
111 std::map<std::string, std::string> & vars,
112 const std::string txt,
113 const std::string & uri_re,
114 const std::string & uri_pat) const
116 //exec regex against value
117 boost::regex re(uri_re);
119 std::string::const_iterator start, end;
123 while (regex_search(start, end, what, re)) //find next full match
126 for (i = 1; i < what.size(); ++i)
128 //check if the group is named
129 std::map<int, std::string>::const_iterator it
130 = groups_by_num.find(i);
131 if (it != groups_by_num.end())
133 std::string name = it->second;
134 if (!what[i].str().empty())
135 vars[name] = what[i];
139 //prepare replacement string
140 std::string rvalue = sub_vars(uri_pat, vars);
142 std::string rhvalue = what.prefix().str()
143 + rvalue + what.suffix().str();
144 std::cout << "! Rewritten '"+what.str(0)+"' to '"+rvalue+"'\n";
146 start = what[0].second; //move search forward
151 static void parse_groups(const std::string & str,
152 std::map<int, std::string> & groups_bynum,
153 std::map<std::string, int> & groups_byname)
157 for (int i = 0; i < str.size(); ++i)
159 if (!esc && str[i] == '\\')
164 if (!esc && str[i] == '(') //group starts
167 if (i+1 < str.size() && str[i+1] == '?') //group with attrs
170 if (i+1 < str.size() && str[i+1] == ':') //non-capturing
172 if (gnum > 0) gnum--;
176 if (i+1 < str.size() && str[i+1] == 'P') //optional, python
178 if (i+1 < str.size() && str[i+1] == '<') //named
183 while (++i < str.size())
185 if (str[i] == '>') { term = true; break; }
186 if (!isalnum(str[i]))
187 throw mp::filter::FilterException
188 ("Only alphanumeric chars allowed, found "
192 + boost::lexical_cast<std::string>(i));
196 throw mp::filter::FilterException
197 ("Unterminated group name '" + gname
198 + " in '" + str +"'");
199 groups_bynum[gnum] = gname;
200 groups_byname[gname] = gnum;
201 std::cout << "Found named group '" << gname
202 << "' at $" << gnum << std::endl;
210 static std::string sub_vars (const std::string & in,
211 const std::map<std::string, std::string> & vars)
215 for (int i = 0; i < in.size(); ++i)
217 if (!esc && in[i] == '\\')
222 if (!esc && in[i] == '$') //var
224 if (i+1 < in.size() && in[i+1] == '{') //ref prefix
229 while (++i < in.size())
231 if (in[i] == '}') { term = true; break; }
234 if (!term) throw mp::filter::FilterException
235 ("Unterminated var ref in '"+in+"' at "
236 + boost::lexical_cast<std::string>(i));
237 std::map<std::string, std::string>::const_iterator it
239 if (it != vars.end())
246 throw mp::filter::FilterException
247 ("Malformed or trimmed var ref in '"
248 +in+"' at "+boost::lexical_cast<std::string>(i));
260 const std::string & req_uri_rx,
261 const std::string & req_uri_pat,
262 const std::string & resp_uri_rx,
263 const std::string & resp_uri_pat)
265 this->req_uri_rx = req_uri_rx;
266 this->req_uri_pat = req_uri_pat;
268 parse_groups(req_uri_rx, groups_by_num, groups_by_name);
269 this->resp_uri_rx = resp_uri_rx;
270 this->resp_uri_pat = resp_uri_pat;
274 std::map<std::string, std::string> vars;
275 std::string req_uri_rx;
276 std::string resp_uri_rx;
277 std::string req_uri_pat;
278 std::string resp_uri_pat;
279 std::map<int, std::string> groups_by_num;
280 std::map<std::string, int> groups_by_name;
285 BOOST_AUTO_TEST_CASE( test_filter_rewrite_1 )
289 FilterHeaderRewrite fhr;
296 BOOST_AUTO_TEST_CASE( test_filter_rewrite_2 )
300 mp::RouterChain router;
302 FilterHeaderRewrite fhr;
304 "((?<proto>http\\:\\/\\/s?)(?<pxhost>[^\\/?#]+)\\/(?<pxpath>[^\\/]+)"
305 "(?<target>.+))|(proxyhost)",
306 "${proto}${target}${whatever}",
307 //rewrite connection close
311 mp::filter::HTTPClient hc;
316 // create an http request
320 Z_GDU *gdu_req = z_get_HTTP_Request_uri(odr,
321 "http://proxyhost/proxypath/localhost:80/~jakub/targetsite.php", 0, 1);
323 pack.request() = gdu_req;
326 pack.router(router).move();
328 //analyze the response
329 Z_GDU *gdu_res = pack.response().get();
330 BOOST_CHECK(gdu_res);
331 BOOST_CHECK_EQUAL(gdu_res->which, Z_GDU_HTTP_Response);
333 Z_HTTP_Response *hres = gdu_res->u.HTTP_Response;
337 catch (std::exception & e) {
338 std::cout << e.what();
346 * c-file-style: "Stroustrup"
347 * indent-tabs-mode: nil
349 * vim: shiftwidth=4 tabstop=8 expandtab