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