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 std::cout << "<< Respose headers" << std::endl;
89 Z_HTTP_Response *hr = gdu->u.HTTP_Response;
92 for (Z_HTTP_Header *header = hr->headers;
94 header = header->next)
96 std::cout << header->name << ": " << header->value << std::endl;
97 std::string out = search_replace(vars,
98 std::string(header->value),
99 resp_uri_rx, resp_uri_pat);
101 header->value = odr_strdup(o, out.c_str());
103 package.response() = gdu;
107 void configure(const xmlNode* ptr, bool test_only, const char *path) {};
109 const std::string search_replace(
110 std::map<std::string, std::string> & vars,
111 const std::string txt,
112 const std::string & uri_re,
113 const std::string & uri_pat) const
115 //exec regex against value
116 boost::regex re(uri_re);
118 std::string::const_iterator start, end;
122 while (regex_search(start, end, what, re)) //find next full match
125 for (i = 1; i < what.size(); ++i)
127 //check if the group is named
128 std::map<int, std::string>::const_iterator it
129 = groups_by_num.find(i);
130 if (it != groups_by_num.end())
132 std::string name = it->second;
133 vars[name] = what[i];
137 //prepare replacement string
138 std::string rvalue = sub_vars(uri_pat, vars);
140 std::string rhvalue = what.prefix().str()
141 + rvalue + what.suffix().str();
142 std::cout << "! Rewritten '"+what.str(0)+"' to '"+rvalue+"'\n";
144 start = what[0].second; //move search forward
149 static void parse_groups(const std::string & str,
150 std::map<int, std::string> & groups_bynum,
151 std::map<std::string, int> & groups_byname)
155 for (int i = 0; i < str.size(); ++i)
157 if (!esc && str[i] == '\\')
162 if (!esc && str[i] == '(') //group starts
165 if (i+1 < str.size() && str[i+1] == '?') //group with attrs
168 if (i+1 < str.size() && str[i+1] == ':') //non-capturing
170 if (gnum > 0) gnum--;
174 if (i+1 < str.size() && str[i+1] == 'P') //optional, python
176 if (i+1 < str.size() && str[i+1] == '<') //named
181 while (++i < str.size())
183 if (str[i] == '>') { term = true; break; }
184 if (!isalnum(str[i]))
185 throw mp::filter::FilterException
186 ("Only alphanumeric chars allowed, found "
190 + boost::lexical_cast<std::string>(i));
194 throw mp::filter::FilterException
195 ("Unterminated group name '" + gname
196 + " in '" + str +"'");
197 groups_bynum[gnum] = gname;
198 groups_byname[gname] = gnum;
199 std::cout << "Found named group '" << gname
200 << "' at $" << gnum << std::endl;
208 static std::string sub_vars (const std::string & in,
209 const std::map<std::string, std::string> & vars)
213 for (int i = 0; i < in.size(); ++i)
215 if (!esc && in[i] == '\\')
220 if (!esc && in[i] == '$') //var
222 if (i+1 < in.size() && in[i+1] == '{') //ref prefix
227 while (++i < in.size())
229 if (in[i] == '}') { term = true; break; }
232 if (!term) throw mp::filter::FilterException
233 ("Unterminated var ref in '"+in+"' at "
234 + boost::lexical_cast<std::string>(i));
235 std::map<std::string, std::string>::const_iterator it
237 if (it != vars.end())
242 throw mp::filter::FilterException
243 ("Malformed or trimmed var ref in '"
244 +in+"' at "+boost::lexical_cast<std::string>(i));
256 const std::string & req_uri_rx,
257 const std::string & req_uri_pat,
258 const std::string & resp_uri_rx,
259 const std::string & resp_uri_pat)
261 this->req_uri_rx = req_uri_rx;
262 this->req_uri_pat = req_uri_pat;
264 parse_groups(req_uri_rx, groups_by_num, groups_by_name);
265 this->resp_uri_rx = resp_uri_rx;
266 this->resp_uri_pat = resp_uri_pat;
270 std::map<std::string, std::string> vars;
271 std::string req_uri_rx;
272 std::string resp_uri_rx;
273 std::string req_uri_pat;
274 std::string resp_uri_pat;
275 std::map<int, std::string> groups_by_num;
276 std::map<std::string, int> groups_by_name;
281 BOOST_AUTO_TEST_CASE( test_filter_rewrite_1 )
285 FilterHeaderRewrite fhr;
292 BOOST_AUTO_TEST_CASE( test_filter_rewrite_2 )
296 mp::RouterChain router;
298 FilterHeaderRewrite fhr;
300 "(?:http\\:\\/\\/s?)?(?<host>[A-Za-z.]+):(?<port>\\d+)",
301 "http://${host}:${port}/somepath",
302 //rewrite connection close
306 mp::filter::HTTPClient hc;
311 // create an http request
315 Z_GDU *gdu_req = z_get_HTTP_Request_uri(odr,
316 "http://localhost:80/~jakub/targetsite.php", 0, 1);
318 pack.request() = gdu_req;
321 pack.router(router).move();
323 //analyze the response
324 Z_GDU *gdu_res = pack.response().get();
325 BOOST_CHECK(gdu_res);
326 BOOST_CHECK_EQUAL(gdu_res->which, Z_GDU_HTTP_Response);
328 Z_HTTP_Response *hres = gdu_res->u.HTTP_Response;
332 catch (std::exception & e) {
333 std::cout << e.what();
341 * c-file-style: "Stroustrup"
342 * indent-tabs-mode: nil
344 * vim: shiftwidth=4 tabstop=8 expandtab