Refactor content parse to Content::parse
[metaproxy-moved-to-github.git] / src / filter_http_rewrite.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2013 Index Data
3
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
7 version.
8
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
12 for more details.
13
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
17 */
18
19 #include "config.hpp"
20 #include <metaproxy/filter.hpp>
21 #include <metaproxy/package.hpp>
22 #include <metaproxy/util.hpp>
23 #include "filter_http_rewrite.hpp"
24 #include "html_parser.hpp"
25
26 #include <yaz/zgdu.h>
27 #include <yaz/log.h>
28
29 #include <stack>
30 #include <boost/regex.hpp>
31 #include <boost/lexical_cast.hpp>
32 #include <boost/algorithm/string.hpp>
33
34 #include <map>
35
36 namespace mp = metaproxy_1;
37 namespace yf = mp::filter;
38
39 namespace metaproxy_1 {
40     namespace filter {
41         class HttpRewrite::Replace {
42         public:
43             bool start_anchor;
44             boost::regex re;
45             boost::smatch what;
46             std::string recipe;
47             std::map<int, std::string> group_index;
48             std::string sub_vars(
49                 const std::map<std::string, std::string> & vars) const;
50             void parse_groups(std::string pattern);
51         };
52
53         class HttpRewrite::Rule {
54         public:
55             std::list<Replace> replace_list;
56             bool test_patterns(
57                 std::map<std::string, std::string> &vars,
58                 std::string &txt, bool anchor);
59         };
60         class HttpRewrite::Within {
61         public:
62             std::string header;
63             std::string attr;
64             std::string tag;
65             bool reqline;
66             RulePtr rule;
67         };
68
69         class HttpRewrite::Content {
70         public:
71             std::string type;
72             boost::regex content_re;
73             std::list<Within> within_list;
74             void configure(const xmlNode *ptr,
75                            std::map<std::string, RulePtr > &rules);
76             void quoted_literal(std::string &content,
77                                 std::map<std::string, std::string> &vars) const;
78             void parse(int verbose, std::string &content,
79                        std::map<std::string, std::string> & vars) const;
80         };
81         class HttpRewrite::Phase {
82         public:
83             Phase();
84             int m_verbose;
85             std::list<Content> content_list;
86             void rewrite_reqline(mp::odr & o, Z_HTTP_Request *hreq,
87                 std::map<std::string, std::string> & vars) const;
88             void rewrite_headers(mp::odr & o, Z_HTTP_Header *headers,
89                 std::map<std::string, std::string> & vars) const;
90             void rewrite_body(mp::odr & o,
91                               const char *content_type,
92                               char **content_buf, int *content_len,
93                               std::map<std::string, std::string> & vars) const;
94         };
95         class HttpRewrite::Event : public HTMLParserEvent {
96             void openTagStart(const char *tag, int tag_len);
97             void anyTagEnd(const char *tag, int tag_len, int close_it);
98             void attribute(const char *tag, int tag_len,
99                            const char *attr, int attr_len,
100                            const char *value, int val_len,
101                            const char *sep);
102             void closeTag(const char *tag, int tag_len);
103             void text(const char *value, int len);
104             const Content *m_content;
105             WRBUF m_w;
106             std::stack<std::list<Within>::const_iterator> s_within;
107             std::map<std::string, std::string> &m_vars;
108         public:
109             Event(const Content *p, std::map<std::string, std::string> &vars);
110             ~Event();
111             const char *result();
112         };
113     }
114 }
115
116 yf::HttpRewrite::HttpRewrite() :
117     req_phase(new Phase), res_phase(new Phase)
118 {
119 }
120
121 yf::HttpRewrite::~HttpRewrite()
122 {
123 }
124
125 void yf::HttpRewrite::process(mp::Package & package) const
126 {
127     yaz_log(YLOG_LOG, "HttpRewrite begins....");
128     Z_GDU *gdu = package.request().get();
129     //map of request/response vars
130     std::map<std::string, std::string> vars;
131     //we have an http req
132     if (gdu && gdu->which == Z_GDU_HTTP_Request)
133     {
134         Z_HTTP_Request *hreq = gdu->u.HTTP_Request;
135         mp::odr o;
136         req_phase->rewrite_reqline(o, hreq, vars);
137         yaz_log(YLOG_LOG, ">> Request headers");
138         req_phase->rewrite_headers(o, hreq->headers, vars);
139         req_phase->rewrite_body(o,
140                                 z_HTTP_header_lookup(hreq->headers,
141                                                      "Content-Type"),
142                                 &hreq->content_buf, &hreq->content_len,
143                                 vars);
144         package.request() = gdu;
145     }
146     package.move();
147     gdu = package.response().get();
148     if (gdu && gdu->which == Z_GDU_HTTP_Response)
149     {
150         Z_HTTP_Response *hres = gdu->u.HTTP_Response;
151         yaz_log(YLOG_LOG, "Response code %d", hres->code);
152         mp::odr o;
153         yaz_log(YLOG_LOG, "<< Respose headers");
154         res_phase->rewrite_headers(o, hres->headers, vars);
155         res_phase->rewrite_body(o,
156                                 z_HTTP_header_lookup(hres->headers,
157                                                      "Content-Type"),
158                                 &hres->content_buf, &hres->content_len,
159                                 vars);
160         package.response() = gdu;
161     }
162 }
163
164 void yf::HttpRewrite::Phase::rewrite_reqline (mp::odr & o,
165         Z_HTTP_Request *hreq,
166         std::map<std::string, std::string> & vars) const
167 {
168     //rewrite the request line
169     std::string path;
170     if (strstr(hreq->path, "http://") == hreq->path)
171     {
172         yaz_log(YLOG_LOG, "Path in the method line is absolute, "
173             "possibly a proxy request");
174         path += hreq->path;
175     }
176     else
177     {
178         //TODO what about proto
179         const char *host = z_HTTP_header_lookup(hreq->headers, "Host");
180         if (!host)
181             return;
182
183         path += "http://";
184         path += host;
185         path += hreq->path;
186     }
187
188     std::list<Content>::const_iterator cit = content_list.begin();
189     for (; cit != content_list.end(); cit++)
190         if (cit->type == "headers")
191             break;
192
193     if (cit == content_list.end())
194         return;
195
196     std::list<Within>::const_iterator it = cit->within_list.begin();
197     for (; it != cit->within_list.end(); it++)
198         if (it->reqline)
199         {
200             yaz_log(YLOG_LOG, "Proxy request URL is %s", path.c_str());
201             if (it->rule->test_patterns(vars, path, true))
202             {
203                 yaz_log(YLOG_LOG, "Rewritten request URL is %s", path.c_str());
204                 hreq->path = odr_strdup(o, path.c_str());
205             }
206         }
207 }
208
209 void yf::HttpRewrite::Phase::rewrite_headers(mp::odr & o,
210         Z_HTTP_Header *headers,
211         std::map<std::string, std::string> & vars) const
212 {
213     std::list<Content>::const_iterator cit = content_list.begin();
214     for (; cit != content_list.end(); cit++)
215         if (cit->type == "headers")
216             break;
217
218     if (cit == content_list.end())
219         return;
220
221     for (Z_HTTP_Header *header = headers; header; header = header->next)
222     {
223         std::list<Within>::const_iterator it = cit->within_list.begin();
224         for (; it != cit->within_list.end(); it++)
225         {
226             if (it->header.length() > 0 &&
227                 yaz_strcasecmp(it->header.c_str(), header->name) == 0)
228             {
229                 std::string sheader(header->name);
230                 sheader += ": ";
231                 sheader += header->value;
232
233                 if (it->rule->test_patterns(vars, sheader, true))
234                 {
235                     size_t pos = sheader.find(": ");
236                     if (pos == std::string::npos)
237                     {
238                         yaz_log(YLOG_LOG, "Header malformed during rewrite, ignoring");
239                         continue;
240                     }
241                     header->name = odr_strdup(
242                         o, sheader.substr(0, pos).c_str());
243                     header->value = odr_strdup(
244                         o, sheader.substr(pos + 2, std::string::npos).c_str());
245                 }
246             }
247         }
248     }
249 }
250
251 void yf::HttpRewrite::Phase::rewrite_body(
252     mp::odr &o,
253     const char *content_type,
254     char **content_buf,
255     int *content_len,
256     std::map<std::string, std::string> & vars) const
257 {
258     std::list<Content>::const_iterator cit = content_list.begin();
259     for (; cit != content_list.end(); cit++)
260     {
261         yaz_log(YLOG_LOG, "rewrite_body: content_type=%s type=%s",
262                 content_type, cit->type.c_str());
263         if (cit->type != "headers"
264             && regex_match(content_type, cit->content_re))
265             break;
266     }
267     if (cit == content_list.end())
268         return;
269
270     if (*content_buf)
271     {
272         int i;
273         for (i = 0; i < *content_len; i++)
274             if ((*content_buf)[i] == 0)
275                 return;  // binary content. skip
276
277         std::string content(*content_buf, *content_len);
278         cit->parse(m_verbose, content, vars);
279         *content_buf = odr_strdup(o, content.c_str());
280         *content_len = strlen(*content_buf);
281     }
282 }
283
284 yf::HttpRewrite::Event::Event(const Content *p,
285                               std::map<std::string, std::string> & vars
286     ) : m_content(p), m_vars(vars)
287 {
288     m_w = wrbuf_alloc();
289 }
290
291 yf::HttpRewrite::Event::~Event()
292 {
293     wrbuf_destroy(m_w);
294 }
295
296 const char *yf::HttpRewrite::Event::result()
297 {
298     return wrbuf_cstr(m_w);
299 }
300
301 void yf::HttpRewrite::Event::openTagStart(const char *tag, int tag_len)
302 {
303     wrbuf_putc(m_w, '<');
304     wrbuf_write(m_w, tag, tag_len);
305
306     std::string t(tag, tag_len);
307     std::list<Within>::const_iterator it = m_content->within_list.begin();
308     for (; it != m_content->within_list.end(); it++)
309     {
310         if (it->tag.length() > 0 && yaz_strcasecmp(it->tag.c_str(),
311                                                    t.c_str()) == 0)
312         {
313             std::vector<std::string> attr;
314             boost::split(attr, it->attr, boost::is_any_of(","));
315             size_t i;
316             for (i = 0; i < attr.size(); i++)
317             {
318                 if (attr[i].compare("#text") == 0)
319                 {
320                     s_within.push(it);
321                     return;
322                 }
323             }
324         }
325     }
326 }
327
328 void yf::HttpRewrite::Event::anyTagEnd(const char *tag, int tag_len,
329                                        int close_it)
330 {
331     if (close_it)
332     {
333         if (!s_within.empty())
334         {
335             std::list<Within>::const_iterator it = s_within.top();
336             std::string t(tag, tag_len);
337             if (yaz_strcasecmp(it->tag.c_str(), t.c_str()) == 0)
338                 s_within.pop();
339         }
340     }
341     if (close_it)
342         wrbuf_putc(m_w, '/');
343     wrbuf_putc(m_w, '>');
344 }
345
346 void yf::HttpRewrite::Event::attribute(const char *tag, int tag_len,
347                                        const char *attr, int attr_len,
348                                        const char *value, int val_len,
349                                        const char *sep)
350 {
351     std::list<Within>::const_iterator it = m_content->within_list.begin();
352     bool subst = false;
353
354     for (; it != m_content->within_list.end(); it++)
355     {
356         std::string t(tag, tag_len);
357         if (it->tag.length() == 0 ||
358             yaz_strcasecmp(it->tag.c_str(), t.c_str()) == 0)
359         {
360             std::string a(attr, attr_len);
361             std::vector<std::string> attr;
362             boost::split(attr, it->attr, boost::is_any_of(","));
363             size_t i;
364             for (i = 0; i < attr.size(); i++)
365             {
366                 if (attr[i].compare("#text") &&
367                     yaz_strcasecmp(attr[i].c_str(), a.c_str()) == 0)
368                     subst = true;
369             }
370         }
371         if (subst)
372             break;
373     }
374
375     wrbuf_putc(m_w, ' ');
376     wrbuf_write(m_w, attr, attr_len);
377     if (value)
378     {
379         wrbuf_puts(m_w, "=");
380         wrbuf_puts(m_w, sep);
381
382         std::string output;
383         if (subst)
384         {
385             std::string s(value, val_len);
386             it->rule->test_patterns(m_vars, s, true);
387             wrbuf_puts(m_w, s.c_str());
388         }
389         else
390             wrbuf_write(m_w, value, val_len);
391         wrbuf_puts(m_w, sep);
392     }
393 }
394
395 void yf::HttpRewrite::Event::closeTag(const char *tag, int tag_len)
396 {
397     if (!s_within.empty())
398     {
399         std::list<Within>::const_iterator it = s_within.top();
400         std::string t(tag, tag_len);
401         if (yaz_strcasecmp(it->tag.c_str(), t.c_str()) == 0)
402             s_within.pop();
403     }
404     wrbuf_puts(m_w, "</");
405     wrbuf_write(m_w, tag, tag_len);
406 }
407
408 void yf::HttpRewrite::Event::text(const char *value, int len)
409 {
410     std::list<Within>::const_iterator it = m_content->within_list.end();
411     if (!s_within.empty())
412         it = s_within.top();
413     if (it != m_content->within_list.end())
414     {
415         std::string s(value, len);
416         it->rule->test_patterns(m_vars, s, false);
417         wrbuf_puts(m_w, s.c_str());
418     }
419     else
420         wrbuf_write(m_w, value, len);
421 }
422
423 bool yf::HttpRewrite::Rule::test_patterns(
424     std::map<std::string, std::string> & vars,
425     std::string & txt, bool anchor)
426 {
427     bool replaces = false;
428     bool first = anchor;
429     std::string out;
430     std::string::const_iterator start, end;
431     start = txt.begin();
432     end = txt.end();
433     while (1)
434     {
435         std::list<Replace>::iterator bit = replace_list.end();
436         {
437             std::string::const_iterator best_pos = txt.end();
438             std::list<Replace>::iterator it = replace_list.begin();
439             for (; it != replace_list.end(); it++)
440             {
441                 if (it->start_anchor && !first)
442                     continue;
443                 if (regex_search(start, end, it->what, it->re))
444                 {
445                     if (it->what[0].first < best_pos)
446                     {
447                         best_pos = it->what[0].first;
448                         bit = it;
449                     }
450                 }
451             }
452             if (bit == replace_list.end())
453                 break;
454         }
455         first = false;
456         replaces = true;
457         size_t i;
458         for (i = 1; i < bit->what.size(); ++i)
459         {
460             //check if the group is named
461             std::map<int, std::string>::const_iterator git
462                 = bit->group_index.find(i);
463             if (git != bit->group_index.end())
464             {   //it is
465                 vars[git->second] = bit->what[i];
466             }
467
468         }
469         //prepare replacement string
470         std::string rvalue = bit->sub_vars(vars);
471         yaz_log(YLOG_LOG, "! Rewritten '%s' to '%s'",
472                 bit->what.str(0).c_str(), rvalue.c_str());
473         out.append(start, bit->what[0].first);
474         out.append(rvalue);
475         start = bit->what[0].second; //move search forward
476     }
477     out.append(start, end);
478     txt = out;
479     return replaces;
480 }
481
482 void yf::HttpRewrite::Replace::parse_groups(std::string pattern)
483 {
484     int gnum = 0;
485     bool esc = false;
486     const std::string &str = pattern;
487     std::string res;
488     start_anchor = str[0] == '^';
489     yaz_log(YLOG_LOG, "Parsing groups from '%s'", str.c_str());
490     for (size_t i = 0; i < str.size(); ++i)
491     {
492         res += str[i];
493         if (!esc && str[i] == '\\')
494         {
495             esc = true;
496             continue;
497         }
498         if (!esc && str[i] == '(') //group starts
499         {
500             gnum++;
501             if (i+1 < str.size() && str[i+1] == '?') //group with attrs
502             {
503                 i++;
504                 if (i+1 < str.size() && str[i+1] == ':') //non-capturing
505                 {
506                     if (gnum > 0) gnum--;
507                     res += str[i];
508                     i++;
509                     res += str[i];
510                     continue;
511                 }
512                 if (i+1 < str.size() && str[i+1] == 'P') //optional, python
513                     i++;
514                 if (i+1 < str.size() && str[i+1] == '<') //named
515                 {
516                     i++;
517                     std::string gname;
518                     bool term = false;
519                     while (++i < str.size())
520                     {
521                         if (str[i] == '>') { term = true; break; }
522                         if (!isalnum(str[i]))
523                             throw mp::filter::FilterException
524                                 ("Only alphanumeric chars allowed, found "
525                                  " in '"
526                                  + str
527                                  + "' at "
528                                  + boost::lexical_cast<std::string>(i));
529                         gname += str[i];
530                     }
531                     if (!term)
532                         throw mp::filter::FilterException
533                             ("Unterminated group name '" + gname
534                              + " in '" + str +"'");
535                     group_index[gnum] = gname;
536                     yaz_log(YLOG_LOG, "Found named group '%s' at $%d",
537                             gname.c_str(), gnum);
538                 }
539             }
540         }
541         esc = false;
542     }
543     re = res;
544 }
545
546 std::string yf::HttpRewrite::Replace::sub_vars(
547     const std::map<std::string, std::string> & vars) const
548 {
549     std::string out;
550     bool esc = false;
551     const std::string & in = recipe;
552     for (size_t i = 0; i < in.size(); ++i)
553     {
554         if (!esc && in[i] == '\\')
555         {
556             esc = true;
557             continue;
558         }
559         if (!esc && in[i] == '$') //var
560         {
561             if (i+1 < in.size() && in[i+1] == '{') //ref prefix
562             {
563                 ++i;
564                 std::string name;
565                 bool term = false;
566                 while (++i < in.size())
567                 {
568                     if (in[i] == '}') { term = true; break; }
569                     name += in[i];
570                 }
571                 if (!term) throw mp::filter::FilterException
572                     ("Unterminated var ref in '"+in+"' at "
573                      + boost::lexical_cast<std::string>(i));
574                 std::map<std::string, std::string>::const_iterator it
575                     = vars.find(name);
576                 if (it != vars.end())
577                 {
578                     out += it->second;
579                 }
580             }
581             else
582             {
583                 throw mp::filter::FilterException
584                     ("Malformed or trimmed var ref in '"
585                      +in+"' at "+boost::lexical_cast<std::string>(i));
586             }
587             continue;
588         }
589         //passthru
590         out += in[i];
591         esc = false;
592     }
593     return out;
594 }
595
596 yf::HttpRewrite::Phase::Phase() : m_verbose(0)
597 {
598 }
599
600 void yf::HttpRewrite::Content::parse(
601     int verbose,
602     std::string &content,
603     std::map<std::string, std::string> &vars) const
604 {
605     if (type == "html")
606     {
607         HTMLParser parser;
608         Event ev(this, vars);
609
610         parser.set_verbose(verbose);
611
612         parser.parse(ev, content.c_str());
613         content = ev.result();
614     }
615     if (type == "quoted-literal")
616     {
617         quoted_literal(content, vars);
618     }
619 }
620
621 void yf::HttpRewrite::Content::quoted_literal(
622     std::string &content,
623     std::map<std::string, std::string> &vars) const
624 {
625     std::string res;
626     const char *cp = content.c_str();
627     const char *cp0 = cp;
628     while (*cp)
629     {
630         if (*cp == '"' || *cp == '\'')
631         {
632             int m = *cp;
633             cp++;
634             res.append(cp0, cp - cp0);
635             cp0 = cp;
636             while (*cp)
637             {
638                 if (cp[-1] != '\\' && *cp == m)
639                     break;
640                 if (*cp == '\n')
641                     break;
642                 cp++;
643             }
644             if (!*cp)
645                 break;
646             std::list<Within>::const_iterator it = within_list.begin();
647             std::string s(cp0, cp - cp0);
648             if (it != within_list.end())
649                 it->rule->test_patterns(vars, s, true);
650             cp0 = cp;
651             res.append(s);
652         }
653         else if (*cp == '/' && cp[1] == '/')
654         {
655             while (cp[1] && cp[1] != '\n')
656                 cp++;
657         }
658         cp++;
659     }
660     res.append(cp0, cp - cp0);
661     content = res;
662 }
663
664 void yf::HttpRewrite::Content::configure(
665     const xmlNode *ptr, std::map<std::string, RulePtr > &rules)
666 {
667     for (; ptr; ptr = ptr->next)
668     {
669         if (ptr->type != XML_ELEMENT_NODE)
670             continue;
671         if (!strcmp((const char *) ptr->name, "within"))
672         {
673             static const char *names[6] =
674                 { "header", "attr", "tag", "rule", "reqline", 0 };
675             std::string values[5];
676             mp::xml::parse_attr(ptr, names, values);
677             Within w;
678             w.header = values[0];
679             w.attr = values[1];
680             w.tag = values[2];
681             std::map<std::string,RulePtr>::const_iterator it =
682                 rules.find(values[3]);
683             if (it == rules.end())
684                 throw mp::filter::FilterException
685                     ("Reference to non-existing rule '" + values[3] +
686                      "' in http_rewrite filter");
687             w.rule = it->second;
688             w.reqline = values[4] == "1";
689             within_list.push_back(w);
690         }
691     }
692 }
693
694 void yf::HttpRewrite::configure_phase(const xmlNode *ptr, Phase &phase)
695 {
696     static const char *names[2] = { "verbose", 0 };
697     std::string values[1];
698     values[0] = "0";
699     mp::xml::parse_attr(ptr, names, values);
700
701     phase.m_verbose = atoi(values[0].c_str());
702
703     std::map<std::string, RulePtr > rules;
704     for (ptr = ptr->children; ptr; ptr = ptr->next)
705     {
706         if (ptr->type != XML_ELEMENT_NODE)
707             continue;
708         else if (!strcmp((const char *) ptr->name, "rule"))
709         {
710             static const char *names[2] = { "name", 0 };
711             std::string values[1];
712             values[0] = "default";
713             mp::xml::parse_attr(ptr, names, values);
714
715             RulePtr rule(new Rule);
716             for (xmlNode *p = ptr->children; p; p = p->next)
717             {
718                 if (p->type != XML_ELEMENT_NODE)
719                     continue;
720                 if (!strcmp((const char *) p->name, "rewrite"))
721                 {
722                     Replace replace;
723                     std::string from;
724                     const struct _xmlAttr *attr;
725                     for (attr = p->properties; attr; attr = attr->next)
726                     {
727                         if (!strcmp((const char *) attr->name,  "from"))
728                             from = mp::xml::get_text(attr->children);
729                         else if (!strcmp((const char *) attr->name,  "to"))
730                             replace.recipe = mp::xml::get_text(attr->children);
731                         else
732                             throw mp::filter::FilterException
733                                 ("Bad attribute "
734                                  + std::string((const char *) attr->name)
735                                  + " in rewrite section of http_rewrite");
736                     }
737                     yaz_log(YLOG_LOG, "Found rewrite rule from '%s' to '%s'",
738                             from.c_str(), replace.recipe.c_str());
739                     if (!from.empty())
740                     {
741                         replace.parse_groups(from);
742                         rule->replace_list.push_back(replace);
743                     }
744                 }
745                 else
746                     throw mp::filter::FilterException
747                         ("Bad element "
748                          + std::string((const char *) p->name)
749                          + " in http_rewrite filter");
750             }
751             rules[values[0]] = rule;
752         }
753         else if (!strcmp((const char *) ptr->name, "content"))
754         {
755             static const char *names[3] =
756                 { "type", "mime", 0 };
757             std::string values[2];
758             mp::xml::parse_attr(ptr, names, values);
759             if (values[0].empty())
760             {
761                     throw mp::filter::FilterException
762                         ("Missing attribute, type for for element "
763                          + std::string((const char *) ptr->name)
764                          + " in http_rewrite filter");
765             }
766             Content c;
767
768             c.type = values[0];
769             // if (!values[1].empty())
770                 c.content_re = values[1];
771             c.configure(ptr->children, rules);
772             phase.content_list.push_back(c);
773         }
774         else
775         {
776             throw mp::filter::FilterException
777                 ("Bad element "
778                  + std::string((const char *) ptr->name)
779                  + " in http_rewrite filter");
780         }
781     }
782 }
783
784 void yf::HttpRewrite::configure(const xmlNode * ptr, bool test_only,
785         const char *path)
786 {
787     for (ptr = ptr->children; ptr; ptr = ptr->next)
788     {
789         if (ptr->type != XML_ELEMENT_NODE)
790             continue;
791         else if (!strcmp((const char *) ptr->name, "request"))
792         {
793             configure_phase(ptr, *req_phase);
794         }
795         else if (!strcmp((const char *) ptr->name, "response"))
796         {
797             configure_phase(ptr, *res_phase);
798         }
799         else
800         {
801             throw mp::filter::FilterException
802                 ("Bad element "
803                  + std::string((const char *) ptr->name)
804                  + " in http_rewrite1 filter");
805         }
806     }
807 }
808
809 static mp::filter::Base* filter_creator()
810 {
811     return new mp::filter::HttpRewrite;
812 }
813
814 extern "C" {
815     struct metaproxy_1_filter_struct metaproxy_1_filter_http_rewrite = {
816         0,
817         "http_rewrite",
818         filter_creator
819     };
820 }
821
822
823 /*
824  * Local variables:
825  * c-basic-offset: 4
826  * c-file-style: "Stroustrup"
827  * indent-tabs-mode: nil
828  * End:
829  * vim: shiftwidth=4 tabstop=8 expandtab
830  */
831