]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy.c
Fix #32367 support for ProxyPass /foo !
[apache] / modules / proxy / mod_proxy.c
1 #define FIX_15207
2 /* Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define CORE_PRIVATE
18
19 #include "mod_proxy.h"
20 #include "mod_core.h"
21 #include "apr_optional.h"
22 #include "scoreboard.h"
23 #include "mod_status.h"
24
25 #if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
26 #include "mod_ssl.h"
27 #else
28 APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
29 APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
30 #endif
31
32 #ifndef MAX
33 #define MAX(x,y) ((x) >= (y) ? (x) : (y))
34 #endif
35
36 /*
37  * A Web proxy module. Stages:
38  *
39  *  translate_name: set filename to proxy:<URL>
40  *  map_to_storage: run proxy_walk (rather than directory_walk/file_walk)
41  *                  can't trust directory_walk/file_walk since these are
42  *                  not in our filesystem.  Prevents mod_http from serving
43  *                  the TRACE request we will set aside to handle later.
44  *  type_checker:   set type to PROXY_MAGIC_TYPE if filename begins proxy:
45  *  fix_ups:        convert the URL stored in the filename to the
46  *                  canonical form.
47  *  handler:        handle proxy requests
48  */
49
50 /* -------------------------------------------------------------- */
51 /* Translate the URL into a 'filename' */
52
53 #ifdef FIX_15207
54 /* XXX: EBCDIC safe? --nd */
55 #define x2c(x) (((x >= '0') && (x <= '9'))         \
56                    ? (x - '0')                     \
57                    : (((x >= 'a') && (x <= 'f'))   \
58                        ? (10 + x - 'a')            \
59                        : ((x >= 'A') && (x <='F')) \
60                            ? (10 + x - 'A')        \
61                            : 0                     \
62                      )                             \
63                )
64
65 static unsigned char hex2c(const char* p) {
66   const char c1 = p[1];
67   const char c2 = p[1] ? p[2]: '\0';
68   int i1 = c1 ? x2c(c1) : 0;
69   int i2 = c2 ? x2c(c2) : 0;
70   unsigned char ret = (i1 << 4) | i2;
71
72   return ret;
73 }
74 #endif
75
76 #define PROXY_COPY_CONF_PARAMS(w, c) \
77     do {                             \
78         (w)->timeout              = (c)->timeout;               \
79         (w)->timeout_set          = (c)->timeout_set;           \
80         (w)->recv_buffer_size     = (c)->recv_buffer_size;      \
81         (w)->recv_buffer_size_set = (c)->recv_buffer_size_set;  \
82         (w)->io_buffer_size       = (c)->io_buffer_size;        \
83         (w)->io_buffer_size_set   = (c)->io_buffer_size_set;    \
84     } while (0)
85
86 static const char *set_worker_param(apr_pool_t *p,
87                                     proxy_worker *worker,
88                                     const char *key,
89                                     const char *val)
90 {
91
92     int ival;
93     if (!strcasecmp(key, "loadfactor")) {
94         /* Normalized load factor. Used with BalancerMamber,
95          * it is a number between 1 and 100.
96          */
97         worker->lbfactor = atoi(val);
98         if (worker->lbfactor < 1 || worker->lbfactor > 100)
99             return "LoadFactor must be number between 1..100";
100     }
101     else if (!strcasecmp(key, "retry")) {
102         /* If set it will give the retry timeout for the worker
103          * The default value is 60 seconds, meaning that if
104          * in error state, it will be retried after that timeout.
105          */
106         ival = atoi(val);
107         if (ival < 1)
108             return "Retry must be at least one second";
109         worker->retry = apr_time_from_sec(ival);
110     }
111     else if (!strcasecmp(key, "ttl")) {
112         /* Time in seconds that will destroy all the connections
113          * that exced the smax 
114          */
115         ival = atoi(val);
116         if (ival < 1)
117             return "TTL must be at least one second";
118         worker->ttl = apr_time_from_sec(ival);
119     }
120     else if (!strcasecmp(key, "min")) {
121         /* Initial number of connections to remote
122          */
123         ival = atoi(val);
124         if (ival < 0)
125             return "Min must be a positive number";
126         worker->min = ival;
127     }
128     else if (!strcasecmp(key, "max")) {
129         /* Maximum number of connections to remote
130          */
131         ival = atoi(val);
132         if (ival < 0)
133             return "Max must be a positive number";
134         worker->hmax = ival;
135     }
136     /* XXX: More inteligent naming needed */
137     else if (!strcasecmp(key, "smax")) {
138         /* Maximum number of connections to remote that
139          * will not be destroyed
140          */
141         ival = atoi(val);
142         if (ival < 0)
143             return "Smax must be a positive number";
144         worker->smax = ival;
145     }
146     else if (!strcasecmp(key, "acquire")) {
147         /* Acquire timeout in milliseconds.
148          * If set this will be the maximum time to
149          * wait for a free connection.
150          */
151         ival = atoi(val);
152         if (ival < 1)
153             return "Acquire must be at least one mili second";
154         worker->acquire = apr_time_make(0, ival * 1000);
155         worker->acquire_set = 1;
156     }
157     else if (!strcasecmp(key, "timeout")) {
158         /* Connection timeout in seconds.
159          * Defaults to server timeout.
160          */
161         ival = atoi(val);
162         if (ival < 1)
163             return "Timeout must be at least one second";
164         worker->timeout = apr_time_from_sec(ival);
165         worker->timeout_set = 1;
166     }
167     else if (!strcasecmp(key, "iobuffersize")) {
168         long s = atol(val);
169         worker->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
170         worker->io_buffer_size_set = 1;
171     }
172     else if (!strcasecmp(key, "receivebuffersize")) {
173         ival = atoi(val);
174         if (ival < 512 && ival != 0) {
175             return "ReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
176         }
177         worker->recv_buffer_size = ival;
178         worker->recv_buffer_size_set = 1;
179     }
180     else if (!strcasecmp(key, "keepalive")) {
181         if (!strcasecmp(val, "on"))
182             worker->keepalive = 1;
183         else if (!strcasecmp(val, "off"))
184             worker->keepalive = 0;
185         else
186             return "KeepAlive must be On|Off";
187         worker->keepalive_set = 1;
188     }    
189     else if (!strcasecmp(key, "route")) {
190         /* Worker route.
191          */
192         if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
193             return "Route length must be < 64 characters";
194         worker->route = apr_pstrdup(p, val);
195     }
196     else if (!strcasecmp(key, "redirect")) {
197         /* Worker redirection route.
198          */
199         if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
200             return "Redirect length must be < 64 characters";
201         worker->redirect = apr_pstrdup(p, val);
202     }
203     else {
204         return "unknown Worker parameter";
205     }
206     return NULL;
207 }
208
209 static const char *set_balancer_param(apr_pool_t *p,
210                                       proxy_balancer *balancer,
211                                       const char *key,
212                                       const char *val)
213 {
214
215     int ival;
216     if (!strcasecmp(key, "stickysession")) {
217         /* Balancer sticky session name.
218          * Set to something like JSESSIONID or
219          * PHPSESSIONID, etc..,
220          */
221         balancer->sticky = apr_pstrdup(p, val);
222     }
223     else if (!strcasecmp(key, "nofailover")) {
224         /* If set to 'on' the session will break
225          * if the worker is in error state or
226          * disabled.
227          */
228         if (!strcasecmp(val, "on"))
229             balancer->sticky_force = 1;
230         else if (!strcasecmp(val, "off"))
231             balancer->sticky_force = 0;
232         else
233             return "failover must be On|Off";
234     }
235     else if (!strcasecmp(key, "timeout")) {
236         /* Balancer timeout in seconds.
237          * If set this will be the maximum time to
238          * wait for a free worker.
239          * Default is not to wait.
240          */
241         ival = atoi(val);
242         if (ival < 1)
243             return "timeout must be at least one second";
244         balancer->timeout = apr_time_from_sec(ival);
245     }
246     else if (!strcasecmp(key, "maxattempts")) {
247         /* Maximum number of failover attempts before
248          * giving up.
249          */
250         ival = atoi(val);
251         if (ival < 0)
252             return "maximum number of attempts must be a positive number";
253         balancer->max_attempts = ival;
254         balancer->max_attempts_set = 1;
255     }
256     else if (!strcasecmp(key, "lbmethod")) {
257         /* Which LB scheduler method */
258         if (!strcasecmp(val, "traffic"))
259             balancer->lbmethod = lbmethod_traffic;
260         else if (!strcasecmp(val, "requests"))
261             balancer->lbmethod = lbmethod_requests;
262         else
263             return "lbmethod must be Traffic|Requests";
264     }
265     else {
266         return "unknown Balancer parameter";
267     }
268     return NULL;
269 }
270
271 static int alias_match(const char *uri, const char *alias_fakename)
272 {
273     const char *end_fakename = alias_fakename + strlen(alias_fakename);
274     const char *aliasp = alias_fakename, *urip = uri;
275     const char *end_uri = uri + strlen(uri);
276     unsigned char uric, aliasc;
277
278     while (aliasp < end_fakename && urip < end_uri) {
279         if (*aliasp == '/') {
280             /* any number of '/' in the alias matches any number in
281              * the supplied URI, but there must be at least one...
282              */
283             if (*urip != '/')
284                 return 0;
285
286             while (*aliasp == '/')
287                 ++aliasp;
288             while (*urip == '/')
289                 ++urip;
290         }
291         else {
292 #ifndef FIX_15207
293             /* Other characters are compared literally */
294             if (*urip++ != *aliasp++)
295                 return 0;
296 #else
297             /* Other characters are canonicalised and compared literally */
298             if (*urip == '%') {
299                 uric = hex2c(urip);
300                 urip += 3;
301             } else {
302                 uric = (unsigned char)*urip++;
303             }
304             if (*aliasp == '%') {
305                 aliasc = hex2c(aliasp);
306                 aliasp += 3;
307             } else {
308                 aliasc = (unsigned char)*aliasp++;
309             }
310             if (uric != aliasc) {
311                 return 0;
312             }
313 #endif
314         }
315     }
316
317     /* fixup badly encoded stuff (e.g. % as last character) */
318     if (aliasp > end_fakename) {
319         aliasp = end_fakename;
320     }
321     if (urip > end_uri) {
322         urip = end_uri;
323     }
324
325    /* We reach the end of the uri before the end of "alias_fakename"
326     * for example uri is "/" and alias_fakename "/examples"
327     */
328    if (urip == end_uri && aliasp!=end_fakename) {
329        return 0;
330    }
331
332     /* Check last alias path component matched all the way */
333     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
334         return 0;
335
336     /* Return number of characters from URI which matched (may be
337      * greater than length of alias, since we may have matched
338      * doubled slashes)
339      */
340
341     return urip - uri;
342 }
343
344 /* Detect if an absoluteURI should be proxied or not.  Note that we
345  * have to do this during this phase because later phases are
346  * "short-circuiting"... i.e. translate_names will end when the first
347  * module returns OK.  So for example, if the request is something like:
348  *
349  * GET http://othervhost/cgi-bin/printenv HTTP/1.0
350  *
351  * mod_alias will notice the /cgi-bin part and ScriptAlias it and
352  * short-circuit the proxy... just because of the ordering in the
353  * configuration file.
354  */
355 static int proxy_detect(request_rec *r)
356 {
357     void *sconf = r->server->module_config;
358     proxy_server_conf *conf =
359         (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
360 #ifdef FIX_15207
361     int i, len;
362     struct proxy_alias *ent = (struct proxy_alias *)conf->aliases->elts;
363 #endif
364
365     /* Ick... msvc (perhaps others) promotes ternary short results to int */
366
367     if (conf->req && r->parsed_uri.scheme) {
368         /* but it might be something vhosted */
369         if (!(r->parsed_uri.hostname
370               && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))
371               && ap_matches_request_vhost(r, r->parsed_uri.hostname,
372                                           (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port 
373                                                        : ap_default_port(r))))) {
374             r->proxyreq = PROXYREQ_PROXY;
375             r->uri = r->unparsed_uri;
376             r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
377             r->handler = "proxy-server";
378         }
379     }
380     /* We need special treatment for CONNECT proxying: it has no scheme part */
381     else if (conf->req && r->method_number == M_CONNECT
382              && r->parsed_uri.hostname
383              && r->parsed_uri.port_str) {
384         r->proxyreq = PROXYREQ_PROXY;
385         r->uri = r->unparsed_uri;
386         r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
387         r->handler = "proxy-server";
388 #ifdef FIX_15207
389     } else {
390         /* test for a ProxyPass */
391         for (i = 0; i < conf->aliases->nelts; i++) {
392             len = alias_match(r->unparsed_uri, ent[i].fake);
393             if (len > 0) {
394                 if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
395                     return DECLINED;
396                 }
397                 r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
398                                           r->unparsed_uri + len, NULL);
399                 r->handler = "proxy-server";
400                 r->proxyreq = PROXYREQ_REVERSE;
401                 r->uri = r->unparsed_uri;
402                 break;
403             }
404         }
405 #endif
406     }
407     return DECLINED;
408 }
409
410 static int proxy_trans(request_rec *r)
411 {
412 #ifndef FIX_15207
413     void *sconf = r->server->module_config;
414     proxy_server_conf *conf =
415     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
416     int i, len;
417     struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
418 #endif
419
420     if (r->proxyreq) {
421         /* someone has already set up the proxy, it was possibly ourselves
422          * in proxy_detect
423          */
424         return OK;
425     }
426
427 #ifndef FIX_15207
428     /* XXX: since r->uri has been manipulated already we're not really
429      * compliant with RFC1945 at this point.  But this probably isn't
430      * an issue because this is a hybrid proxy/origin server.
431      */
432
433     for (i = 0; i < conf->aliases->nelts; i++) {
434         len = alias_match(r->uri, ent[i].fake);
435
436        if (len > 0) {
437            if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
438                return DECLINED;
439            }
440
441            r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
442                                      r->uri + len, NULL);
443            r->handler = "proxy-server";
444            r->proxyreq = PROXYREQ_REVERSE;
445            return OK;
446        }
447     }
448 #endif
449     return DECLINED;
450 }
451
452 static int proxy_walk(request_rec *r)
453 {
454     proxy_server_conf *sconf = ap_get_module_config(r->server->module_config,
455                                                     &proxy_module);
456     ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
457     ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts;
458     ap_conf_vector_t *entry_config;
459     proxy_dir_conf *entry_proxy;
460     int num_sec = sconf->sec_proxy->nelts;
461     /* XXX: shouldn't we use URI here?  Canonicalize it first?
462      * Pass over "proxy:" prefix 
463      */
464     const char *proxyname = r->filename + 6;
465     int j;
466
467     for (j = 0; j < num_sec; ++j) 
468     {
469         entry_config = sec_proxy[j];
470         entry_proxy = ap_get_module_config(entry_config, &proxy_module);
471
472         /* XXX: What about case insensitive matching ???
473          * Compare regex, fnmatch or string as appropriate
474          * If the entry doesn't relate, then continue 
475          */
476         if (entry_proxy->r 
477               ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0)
478               : (entry_proxy->p_is_fnmatch
479                    ? apr_fnmatch(entry_proxy->p, proxyname, 0)
480                    : strncmp(proxyname, entry_proxy->p, 
481                                         strlen(entry_proxy->p)))) {
482             continue;
483         }
484         per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults,
485                                                              entry_config);
486     }
487
488     r->per_dir_config = per_dir_defaults;
489
490     return OK;
491 }
492
493 static int proxy_map_location(request_rec *r)
494 {
495     int access_status;
496
497     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
498         return DECLINED;
499
500     /* Don't let the core or mod_http map_to_storage hooks handle this,
501      * We don't need directory/file_walk, and we want to TRACE on our own.
502      */
503     if ((access_status = proxy_walk(r))) {
504         ap_die(access_status, r);
505         return access_status;
506     }
507
508     return OK;
509 }
510 #ifndef FIX_15207
511 /* -------------------------------------------------------------- */
512 /* Fixup the filename */
513
514 /*
515  * Canonicalise the URL
516  */
517 static int proxy_fixup(request_rec *r)
518 {
519     char *url, *p;
520     int access_status;
521
522     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
523         return DECLINED;
524
525 #ifdef FIX_15207
526 /* We definitely shouldn't canonicalize a proxy_pass.
527  * But should we really canonicalize a STD_PROXY??? -- Fahree
528  */
529     if (r->proxyreq == PROXYREQ_REVERSE) {
530         return OK;
531     }
532 #endif
533
534     /* XXX: Shouldn't we try this before we run the proxy_walk? */
535     url = &r->filename[6];
536
537     /* canonicalise each specific scheme */
538     if ((access_status = proxy_run_canon_handler(r, url))) {
539         return access_status;
540     }
541
542     p = strchr(url, ':');
543     if (p == NULL || p == url)
544         return HTTP_BAD_REQUEST;
545
546     return OK;      /* otherwise; we've done the best we can */
547 }
548 #endif
549 /* Send a redirection if the request contains a hostname which is not */
550 /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
551 /* servers like Netscape's allow this and access hosts from the local */
552 /* domain in this case. I think it is better to redirect to a FQDN, since */
553 /* these will later be found in the bookmarks files. */
554 /* The "ProxyDomain" directive determines what domain will be appended */
555 static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
556 {
557     char *nuri;
558     const char *ref;
559
560     /* We only want to worry about GETs */
561     if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
562         return DECLINED;
563
564     /* If host does contain a dot already, or it is "localhost", decline */
565     if (strchr(r->parsed_uri.hostname, '.') != NULL
566      || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
567         return DECLINED;    /* host name has a dot already */
568
569     ref = apr_table_get(r->headers_in, "Referer");
570
571     /* Reassemble the request, but insert the domain after the host name */
572     /* Note that the domain name always starts with a dot */
573     r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname,
574                                          domain, NULL);
575     nuri = apr_uri_unparse(r->pool,
576                            &r->parsed_uri,
577                            APR_URI_UNP_REVEALPASSWORD);
578
579     apr_table_set(r->headers_out, "Location", nuri);
580     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
581                   "Domain missing: %s sent to %s%s%s", r->uri,
582                   apr_uri_unparse(r->pool, &r->parsed_uri,
583                                   APR_URI_UNP_OMITUSERINFO),
584                   ref ? " from " : "", ref ? ref : "");
585
586     return HTTP_MOVED_PERMANENTLY;
587 }
588
589 /* -------------------------------------------------------------- */
590 /* Invoke handler */
591
592 static int proxy_handler(request_rec *r)
593 {
594     char *url, *scheme, *p;
595     const char *p2;
596     void *sconf = r->server->module_config;
597     proxy_server_conf *conf = (proxy_server_conf *)
598         ap_get_module_config(sconf, &proxy_module);
599     apr_array_header_t *proxies = conf->proxies;
600     struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
601     int i, rc, access_status;
602     int direct_connect = 0;
603     const char *str;
604     long maxfwd;
605     proxy_balancer *balancer = NULL;
606     proxy_worker *worker = NULL;
607     int attempts = 0, max_attempts = 0;
608
609     /* is this for us? */
610     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
611         return DECLINED;
612
613     /* handle max-forwards / OPTIONS / TRACE */
614     if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) {
615         maxfwd = strtol(str, NULL, 10);
616         if (maxfwd < 1) {
617             switch (r->method_number) {
618             case M_TRACE: {
619                 int access_status;
620                 r->proxyreq = PROXYREQ_NONE;
621                 if ((access_status = ap_send_http_trace(r)))
622                     ap_die(access_status, r);
623                 else
624                     ap_finalize_request_protocol(r);
625                 return OK;
626             }
627             case M_OPTIONS: {
628                 int access_status;
629                 r->proxyreq = PROXYREQ_NONE;
630                 if ((access_status = ap_send_http_options(r)))
631                     ap_die(access_status, r);
632                 else
633                     ap_finalize_request_protocol(r);
634                 return OK;
635             }
636             default: {
637                 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
638                                      "Max-Forwards has reached zero - proxy loop?");
639             }
640             }
641         }
642         maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0;
643     }
644     else {
645         /* set configured max-forwards */
646         maxfwd = conf->maxfwd;
647     }
648     apr_table_set(r->headers_in, "Max-Forwards", 
649                   apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0));
650
651     do {
652         url = r->filename + 6;
653         p = strchr(url, ':');
654         if (p == NULL) {
655             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
656                           "proxy_handler no URL in %s", r->filename);
657             return HTTP_BAD_REQUEST;
658         }
659
660         /* If the host doesn't have a domain name, add one and redirect. */
661         if (conf->domain != NULL) {
662             rc = proxy_needsdomain(r, url, conf->domain);
663             if (ap_is_HTTP_REDIRECT(rc))
664                 return HTTP_MOVED_PERMANENTLY;
665         }
666
667         *p = '\0';
668         scheme = apr_pstrdup(r->pool, url);
669         *p = ':';
670
671         /* Check URI's destination host against NoProxy hosts */
672         /* Bypass ProxyRemote server lookup if configured as NoProxy */
673         /* we only know how to handle communication to a proxy via http */
674         /*if (strcasecmp(scheme, "http") == 0) */
675         {
676             int ii;
677             struct dirconn_entry *list = (struct dirconn_entry *)
678                                                 conf->dirconn->elts;
679
680             for (direct_connect = ii = 0; ii < conf->dirconn->nelts &&
681                                                !direct_connect; ii++) {
682                 direct_connect = list[ii].matcher(&list[ii], r);
683             }
684 #if DEBUGGING
685             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
686                       (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
687                       r->uri);
688 #endif
689         }
690     
691         /* Try to obtain the most suitable worker */
692         access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
693         if (access_status != OK)
694             return access_status;
695         if (balancer && balancer->max_attempts_set && !max_attempts)
696             max_attempts = balancer->max_attempts;
697         /* firstly, try a proxy, unless a NoProxy directive is active */
698         if (!direct_connect) {
699             for (i = 0; i < proxies->nelts; i++) {
700                 p2 = ap_strchr_c(ents[i].scheme, ':');  /* is it a partial URL? */
701                 if (strcmp(ents[i].scheme, "*") == 0 ||
702                     (ents[i].use_regex && ap_regexec(ents[i].regexp, url,
703                                                      0,NULL, 0)) ||
704                     (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
705                     (p2 != NULL &&
706                     strncasecmp(url, ents[i].scheme,
707                                 strlen(ents[i].scheme)) == 0)) {
708
709                     /* handle the scheme */
710                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
711                                  "Trying to run scheme_handler against proxy");
712                     access_status = proxy_run_scheme_handler(r, worker,
713                                                              conf, url,
714                                                              ents[i].hostname,
715                                                              ents[i].port);
716
717                     /* an error or success */
718                     if (access_status != DECLINED &&
719                         access_status != HTTP_BAD_GATEWAY) {
720                         goto cleanup;
721                     }
722                     /* we failed to talk to the upstream proxy */
723                 }
724             }
725         }
726
727         /* otherwise, try it direct */
728         /* N.B. what if we're behind a firewall, where we must use a proxy or
729         * give up??
730         */
731
732         /* handle the scheme */
733         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
734                      "Running scheme %s handler (attempt %d)",
735                      scheme, attempts);
736         if ((access_status = proxy_run_scheme_handler(r, worker, conf,
737                                                       url, NULL, 0)) == OK)
738             break;
739         
740     } while (!PROXY_WORKER_IS_USABLE(worker) && 
741              max_attempts > attempts++);
742
743     if (DECLINED == access_status) {
744         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
745                     "proxy: No protocol handler was valid for the URL %s. "
746                     "If you are using a DSO version of mod_proxy, make sure "
747                     "the proxy submodules are included in the configuration "
748                     "using LoadModule.", r->uri);
749         access_status = HTTP_FORBIDDEN;
750         goto cleanup;
751     }
752 cleanup:
753     if (balancer) {
754         int post_status = proxy_run_post_request(worker, balancer, r, conf);
755         if (post_status == DECLINED) {
756             post_status = OK; /* no post_request handler available */
757             /* TODO: reclycle direct worker */
758         }
759     }
760     return access_status;
761 }
762
763 /* -------------------------------------------------------------- */
764 /* Setup configurable data */
765
766 static void * create_proxy_config(apr_pool_t *p, server_rec *s)
767 {
768     proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
769
770     ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *));
771     ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote));
772     ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
773     ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
774     ps->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias));
775     ps->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias));
776     ps->cookie_path_str = apr_strmatch_precompile(p, "path=", 0);
777     ps->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0);
778     ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry));
779     ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry));
780     ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int));
781     ps->workers = apr_array_make(p, 10, sizeof(proxy_worker));
782     ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer));
783     ps->forward = NULL;
784     ps->reverse = NULL;
785     ps->domain = NULL;
786     ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
787     ps->viaopt_set = 0; /* 0 means default */
788     ps->req = 0;
789     ps->req_set = 0;
790     ps->recv_buffer_size = 0; /* this default was left unset for some reason */
791     ps->recv_buffer_size_set = 0;
792     ps->io_buffer_size = AP_IOBUFSIZE;
793     ps->io_buffer_size_set = 0;
794     ps->maxfwd = DEFAULT_MAX_FORWARDS;
795     ps->maxfwd_set = 0;
796     ps->error_override = 0; 
797     ps->error_override_set = 0; 
798     ps->preserve_host_set = 0;
799     ps->preserve_host = 0;    
800     ps->timeout = 0;
801     ps->timeout_set = 0;
802     ps->badopt = bad_error;
803     ps->badopt_set = 0;
804     ps->pool = p;
805     return ps;
806 }
807
808 static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
809 {
810     proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
811     proxy_server_conf *base = (proxy_server_conf *) basev;
812     proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
813
814     ps->proxies = apr_array_append(p, base->proxies, overrides->proxies);
815     ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
816     ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
817     ps->raliases = apr_array_append(p, base->raliases, overrides->raliases);
818     ps->cookie_paths
819         = apr_array_append(p, base->cookie_paths, overrides->cookie_paths);
820     ps->cookie_domains
821         = apr_array_append(p, base->cookie_domains, overrides->cookie_domains);
822     ps->cookie_path_str = base->cookie_path_str;
823     ps->cookie_domain_str = base->cookie_domain_str;
824     ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
825     ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
826     ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
827     ps->workers = apr_array_append(p, base->workers, overrides->workers);
828     ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
829     ps->forward = overrides->forward ? overrides->forward : base->forward;
830     ps->reverse = overrides->reverse ? overrides->reverse : base->reverse;
831
832     ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
833     ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
834     ps->req = (overrides->req_set == 0) ? base->req : overrides->req;
835     ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size;
836     ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size;
837     ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd;
838     ps->error_override = (overrides->error_override_set == 0) ? base->error_override : overrides->error_override;
839     ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host;
840     ps->timeout= (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
841     ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
842     ps->proxy_status = (overrides->proxy_status_set == 0) ? base->proxy_status : overrides->proxy_status;
843     ps->pool = p;
844     return ps;
845 }
846
847 static void *create_proxy_dir_config(apr_pool_t *p, char *dummy)
848 {
849     proxy_dir_conf *new =
850         (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
851
852     /* Filled in by proxysection, when applicable */
853
854     return (void *) new;
855 }
856
857 static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
858 {
859     proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
860     proxy_dir_conf *add = (proxy_dir_conf *) addv;
861
862     new->p = add->p;
863     new->p_is_fnmatch = add->p_is_fnmatch;
864     new->r = add->r;
865     return new;
866 }
867
868
869 static const char *
870     add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex)
871 {
872     server_rec *s = cmd->server;
873     proxy_server_conf *conf =
874     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
875     struct proxy_remote *new;
876     char *p, *q;
877     char *r, *f, *scheme;
878     regex_t *reg = NULL;
879     int port;
880
881     r = apr_pstrdup(cmd->pool, r1);
882     scheme = apr_pstrdup(cmd->pool, r1);
883     f = apr_pstrdup(cmd->pool, f1);
884     p = strchr(r, ':');
885     if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') {
886         if (regex)
887             return "ProxyRemoteMatch: Bad syntax for a remote proxy server";
888         else
889             return "ProxyRemote: Bad syntax for a remote proxy server";
890     }
891     else {
892         scheme[p-r] = 0;
893     }
894     q = strchr(p + 3, ':');
895     if (q != NULL) {
896         if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
897             if (regex)
898                 return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)";
899             else
900                 return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
901         }
902         *q = '\0';
903     }
904     else
905         port = -1;
906     *p = '\0';
907     if (regex) {
908         reg = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
909         if (!reg)
910             return "Regular expression for ProxyRemoteMatch could not be compiled.";
911     }
912     else
913         if (strchr(f, ':') == NULL)
914             ap_str_tolower(f);      /* lowercase scheme */
915     ap_str_tolower(p + 3);      /* lowercase hostname */
916
917     if (port == -1) {
918         port = apr_uri_port_of_scheme(scheme);
919     }
920
921     new = apr_array_push(conf->proxies);
922     new->scheme = f;
923     new->protocol = r;
924     new->hostname = p + 3;
925     new->port = port;
926     new->regexp = reg;
927     new->use_regex = regex;
928     return NULL;
929 }
930
931 static const char *
932     add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
933 {
934     return add_proxy(cmd, dummy, f1, r1, 0);
935 }
936
937 static const char *
938     add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
939 {
940     return add_proxy(cmd, dummy, f1, r1, 1);
941 }
942
943 static const char *
944     add_pass(cmd_parms *cmd, void *dummy, const char *arg)
945 {
946     server_rec *s = cmd->server;
947     proxy_server_conf *conf =
948     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
949     struct proxy_alias *new;
950     char *f = cmd->path;
951     char *r = NULL;
952     char *word;
953     apr_table_t *params = apr_table_make(cmd->pool, 5);
954     const apr_array_header_t *arr;
955     const apr_table_entry_t *elts;
956     int i;
957     
958     while (*arg) {
959         word = ap_getword_conf(cmd->pool, &arg);
960         if (!f)
961             f = word;
962         else if (!r)
963             r = word;
964         else {
965             char *val = strchr(word, '=');
966             if (!val) {
967                 if (cmd->path)
968                     return "Invalid ProxyPass parameter.  Parameter must be "
969                            "in the form 'key=value'";
970                 else
971                     return "ProxyPass can not have a path when defined in a location"; 
972             }
973             else
974                 *val++ = '\0';
975             apr_table_setn(params, word, val);
976         }
977     };
978
979     if (r == NULL)
980         return "ProxyPass needs a path when not defined in a location";
981
982     new = apr_array_push(conf->aliases);
983     new->fake = apr_pstrdup(cmd->pool, f);
984     new->real = apr_pstrdup(cmd->pool, r);
985     if (r[0] == '!' && r[1] == '\0')
986         return NULL;
987     
988     arr = apr_table_elts(params);
989     elts = (const apr_table_entry_t *)arr->elts;
990     /* Distinguish the balancer from woker */
991     if (strncasecmp(r, "balancer:", 9) == 0) {
992         proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
993         if (!balancer) {
994             const char *err = ap_proxy_add_balancer(&balancer,
995                                                     cmd->pool,
996                                                     conf, r);
997             if (err)
998                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
999         }        
1000         for (i = 0; i < arr->nelts; i++) {
1001             const char *err = set_balancer_param(cmd->pool, balancer, elts[i].key,
1002                                                  elts[i].val);
1003             if (err)
1004                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1005         }
1006     }
1007     else {
1008         proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, conf, r);
1009         if (!worker) {
1010             const char *err = ap_proxy_add_worker(&worker, cmd->pool, conf, r);
1011             if (err)
1012                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1013         }
1014         PROXY_COPY_CONF_PARAMS(worker, conf);
1015
1016         for (i = 0; i < arr->nelts; i++) {
1017             const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
1018                                                elts[i].val);
1019             if (err)
1020                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1021         }
1022     }
1023     return NULL;
1024 }
1025
1026 static const char *
1027     add_pass_reverse(cmd_parms *cmd, void *dummy, const char *f, const char *r)
1028 {
1029     server_rec *s = cmd->server;
1030     proxy_server_conf *conf;
1031     struct proxy_alias *new;
1032
1033     conf = (proxy_server_conf *)ap_get_module_config(s->module_config, 
1034                                                      &proxy_module);
1035     if (r!=NULL && cmd->path == NULL ) {
1036         new = apr_array_push(conf->raliases);
1037         new->fake = f;
1038         new->real = r;
1039     } else if (r==NULL && cmd->path != NULL) {
1040         new = apr_array_push(conf->raliases);
1041         new->fake = cmd->path;
1042         new->real = f;
1043     } else {
1044         if ( r == NULL)
1045             return "ProxyPassReverse needs a path when not defined in a location";
1046         else 
1047             return "ProxyPassReverse can not have a path when defined in a location";
1048     }
1049
1050     return NULL;
1051 }
1052 static const char*
1053     cookie_path(cmd_parms *cmd, void *dummy, const char *f, const char *r)
1054 {
1055     server_rec *s = cmd->server;
1056     proxy_server_conf *conf;
1057     struct proxy_alias *new;
1058
1059     conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
1060                                                      &proxy_module);
1061     new = apr_array_push(conf->cookie_paths);
1062     new->fake = f;
1063     new->real = r;
1064
1065     return NULL;
1066 }
1067 static const char*
1068     cookie_domain(cmd_parms *cmd, void *dummy, const char *f, const char *r)
1069 {
1070     server_rec *s = cmd->server;
1071     proxy_server_conf *conf;
1072     struct proxy_alias *new;
1073
1074     conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
1075                                                      &proxy_module);
1076     new = apr_array_push(conf->cookie_domains);
1077     new->fake = f;
1078     new->real = r;
1079
1080     return NULL;
1081 }
1082
1083 static const char *
1084     set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg)
1085 {
1086     server_rec *s = parms->server;
1087     proxy_server_conf *conf =
1088     ap_get_module_config(s->module_config, &proxy_module);
1089     struct noproxy_entry *new;
1090     struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
1091     struct apr_sockaddr_t *addr;
1092     int found = 0;
1093     int i;
1094
1095     /* Don't duplicate entries */
1096     for (i = 0; i < conf->noproxies->nelts; i++) {
1097         if (apr_strnatcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */
1098             found = 1;
1099         }
1100     }
1101
1102     if (!found) {
1103         new = apr_array_push(conf->noproxies);
1104         new->name = arg;
1105         if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) {
1106             new->addr = addr;
1107         }
1108         else {
1109             new->addr = NULL;
1110         }
1111     }
1112     return NULL;
1113 }
1114
1115 /*
1116  * Set the ports CONNECT can use
1117  */
1118 static const char *
1119     set_allowed_ports(cmd_parms *parms, void *dummy, const char *arg)
1120 {
1121     server_rec *s = parms->server;
1122     proxy_server_conf *conf =
1123         ap_get_module_config(s->module_config, &proxy_module);
1124     int *New;
1125
1126     if (!apr_isdigit(arg[0]))
1127         return "AllowCONNECT: port number must be numeric";
1128
1129     New = apr_array_push(conf->allowed_connect_ports);
1130     *New = atoi(arg);
1131     return NULL;
1132 }
1133
1134 /* Similar to set_proxy_exclude(), but defining directly connected hosts,
1135  * which should never be accessed via the configured ProxyRemote servers
1136  */
1137 static const char *
1138     set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg)
1139 {
1140     server_rec *s = parms->server;
1141     proxy_server_conf *conf =
1142     ap_get_module_config(s->module_config, &proxy_module);
1143     struct dirconn_entry *New;
1144     struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
1145     int found = 0;
1146     int i;
1147
1148     /* Don't duplicate entries */
1149     for (i = 0; i < conf->dirconn->nelts; i++) {
1150         if (strcasecmp(arg, list[i].name) == 0)
1151             found = 1;
1152     }
1153
1154     if (!found) {
1155         New = apr_array_push(conf->dirconn);
1156         New->name = apr_pstrdup(parms->pool, arg);
1157         New->hostaddr = NULL;
1158
1159     if (ap_proxy_is_ipaddr(New, parms->pool)) {
1160 #if DEBUGGING
1161         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1162                      "Parsed addr %s", inet_ntoa(New->addr));
1163         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1164                      "Parsed mask %s", inet_ntoa(New->mask));
1165 #endif
1166     }
1167     else if (ap_proxy_is_domainname(New, parms->pool)) {
1168         ap_str_tolower(New->name);
1169 #if DEBUGGING
1170         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1171                      "Parsed domain %s", New->name);
1172 #endif
1173         }
1174         else if (ap_proxy_is_hostname(New, parms->pool)) {
1175             ap_str_tolower(New->name);
1176 #if DEBUGGING
1177             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1178                          "Parsed host %s", New->name);
1179 #endif
1180         }
1181         else {
1182             ap_proxy_is_word(New, parms->pool);
1183 #if DEBUGGING
1184             fprintf(stderr, "Parsed word %s\n", New->name);
1185 #endif
1186         }
1187     }
1188     return NULL;
1189 }
1190
1191 static const char *
1192     set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg)
1193 {
1194     proxy_server_conf *psf =
1195     ap_get_module_config(parms->server->module_config, &proxy_module);
1196
1197     if (arg[0] != '.')
1198         return "ProxyDomain: domain name must start with a dot.";
1199
1200     psf->domain = arg;
1201     return NULL;
1202 }
1203
1204 static const char *
1205     set_proxy_req(cmd_parms *parms, void *dummy, int flag)
1206 {
1207     proxy_server_conf *psf =
1208     ap_get_module_config(parms->server->module_config, &proxy_module);
1209
1210     psf->req = flag;
1211     psf->req_set = 1;
1212
1213     if (flag && !psf->forward) {
1214         psf->forward = ap_proxy_create_worker(parms->pool);
1215         psf->forward->name     = "proxy:forward";
1216         psf->forward->hostname = "*";
1217         psf->forward->scheme   = "*";
1218     }
1219     return NULL;
1220 }
1221
1222 static const char *
1223     set_proxy_error_override(cmd_parms *parms, void *dummy, int flag)
1224 {
1225     proxy_server_conf *psf =
1226     ap_get_module_config(parms->server->module_config, &proxy_module);
1227
1228     psf->error_override = flag;
1229     psf->error_override_set = 1;
1230     return NULL;
1231 }
1232 static const char *
1233     set_preserve_host(cmd_parms *parms, void *dummy, int flag)
1234 {
1235     proxy_server_conf *psf =
1236     ap_get_module_config(parms->server->module_config, &proxy_module);
1237
1238     psf->preserve_host = flag;
1239     psf->preserve_host_set = 1;
1240     return NULL;
1241 }
1242
1243 static const char *
1244     set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1245 {
1246     proxy_server_conf *psf =
1247     ap_get_module_config(parms->server->module_config, &proxy_module);
1248     int s = atoi(arg);
1249     if (s < 512 && s != 0) {
1250         return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
1251     }
1252
1253     psf->recv_buffer_size = s;
1254     psf->recv_buffer_size_set = 1;
1255     return NULL;
1256 }
1257
1258 static const char *
1259     set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1260 {
1261     proxy_server_conf *psf =
1262     ap_get_module_config(parms->server->module_config, &proxy_module);
1263     long s = atol(arg);
1264
1265     psf->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
1266     psf->io_buffer_size_set = 1;
1267     return NULL;
1268 }
1269
1270 static const char *
1271     set_max_forwards(cmd_parms *parms, void *dummy, const char *arg)
1272 {
1273     proxy_server_conf *psf =
1274     ap_get_module_config(parms->server->module_config, &proxy_module);
1275     long s = atol(arg);
1276     if (s < 0) {
1277         return "ProxyMaxForwards must be greater or equal to zero..";
1278     }
1279
1280     psf->maxfwd = s;
1281     psf->maxfwd_set = 1;
1282     return NULL;
1283 }
1284 static const char*
1285     set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg)
1286 {
1287     proxy_server_conf *psf =
1288     ap_get_module_config(parms->server->module_config, &proxy_module);
1289     int timeout;
1290
1291     timeout=atoi(arg);
1292     if (timeout<1) {
1293         return "Proxy Timeout must be at least 1 second.";
1294     }
1295     psf->timeout_set=1;
1296     psf->timeout=apr_time_from_sec(timeout);
1297
1298     return NULL;    
1299 }
1300
1301 static const char*
1302     set_via_opt(cmd_parms *parms, void *dummy, const char *arg)
1303 {
1304     proxy_server_conf *psf =
1305     ap_get_module_config(parms->server->module_config, &proxy_module);
1306
1307     if (strcasecmp(arg, "Off") == 0)
1308         psf->viaopt = via_off;
1309     else if (strcasecmp(arg, "On") == 0)
1310         psf->viaopt = via_on;
1311     else if (strcasecmp(arg, "Block") == 0)
1312         psf->viaopt = via_block;
1313     else if (strcasecmp(arg, "Full") == 0)
1314         psf->viaopt = via_full;
1315     else {
1316         return "ProxyVia must be one of: "
1317             "off | on | full | block";
1318     }
1319
1320     psf->viaopt_set = 1;
1321     return NULL;    
1322 }
1323
1324 static const char*
1325     set_bad_opt(cmd_parms *parms, void *dummy, const char *arg)
1326 {
1327     proxy_server_conf *psf =
1328     ap_get_module_config(parms->server->module_config, &proxy_module);
1329
1330     if (strcasecmp(arg, "IsError") == 0)
1331         psf->badopt = bad_error;
1332     else if (strcasecmp(arg, "Ignore") == 0)
1333         psf->badopt = bad_ignore;
1334     else if (strcasecmp(arg, "StartBody") == 0)
1335         psf->badopt = bad_body;
1336     else {
1337         return "ProxyBadHeader must be one of: "
1338             "IsError | Ignore | StartBody";
1339     }
1340
1341     psf->badopt_set = 1;
1342     return NULL;    
1343 }
1344
1345 static const char*
1346     set_status_opt(cmd_parms *parms, void *dummy, const char *arg)
1347 {
1348     proxy_server_conf *psf =
1349     ap_get_module_config(parms->server->module_config, &proxy_module);
1350
1351     if (strcasecmp(arg, "Off") == 0)
1352         psf->proxy_status = status_off;
1353     else if (strcasecmp(arg, "On") == 0)
1354         psf->proxy_status = status_on;
1355     else if (strcasecmp(arg, "Full") == 0)
1356         psf->proxy_status = status_full;
1357     else {
1358         return "ProxyStatus must be one of: "
1359             "off | on | block";
1360     }
1361
1362     psf->proxy_status_set = 1;
1363     return NULL;    
1364 }
1365
1366 static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
1367 {
1368     server_rec *s = cmd->server;
1369     proxy_server_conf *conf =
1370     ap_get_module_config(s->module_config, &proxy_module);
1371     proxy_balancer *balancer;
1372     proxy_worker *worker;
1373     char *path = cmd->path;
1374     char *name = NULL;
1375     char *word;
1376     apr_table_t *params = apr_table_make(cmd->pool, 5);
1377     const apr_array_header_t *arr;
1378     const apr_table_entry_t *elts;
1379     int i;
1380     
1381     if (cmd->path)
1382         path = apr_pstrdup(cmd->pool, cmd->path);
1383     while (*arg) {
1384         word = ap_getword_conf(cmd->pool, &arg);
1385         if (!path)
1386             path = word;
1387         else if (!name)
1388             name = word;
1389         else {
1390             char *val = strchr(word, '=');
1391             if (!val)
1392                 if (cmd->path)
1393                     return "BalancerMember can not have a balancer name when defined in a location";
1394                 else
1395                     return "Invalid BalancerMember parameter. Parameter must "
1396                            "be in the form 'key=value'";
1397             else
1398                 *val++ = '\0';
1399             apr_table_setn(params, word, val);
1400         }
1401     }
1402     if (!path)
1403         return "BalancerMember must define balancer name when outside <Proxy > section";
1404     if (!name)
1405         return "BalancerMember must define remote proxy server";
1406     
1407     ap_str_tolower(path);   /* lowercase scheme://hostname */
1408     ap_str_tolower(name);   /* lowercase scheme://hostname */
1409
1410     /* Try to find existing worker */
1411     worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
1412     if (!worker) {
1413         const char *err;
1414         if ((err = ap_proxy_add_worker(&worker, cmd->pool, conf, name)) != NULL)
1415             return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); 
1416     }
1417     PROXY_COPY_CONF_PARAMS(worker, conf);
1418     
1419     arr = apr_table_elts(params);
1420     elts = (const apr_table_entry_t *)arr->elts;
1421     for (i = 0; i < arr->nelts; i++) {
1422         const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
1423                                            elts[i].val);
1424         if (err)
1425             return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
1426     }
1427     /* Try to find the balancer */
1428     balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, path); 
1429     if (!balancer) {
1430         const char *err = ap_proxy_add_balancer(&balancer,
1431                                                 cmd->pool,
1432                                                 conf, path);
1433         if (err)
1434             return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
1435     }
1436     /* Add the worker to the load balancer */
1437     ap_proxy_add_worker_to_balancer(cmd->pool, balancer, worker);
1438     return NULL;
1439 }
1440
1441 static const char *
1442     set_proxy_param(cmd_parms *cmd, void *dummy, const char *arg)
1443 {
1444     server_rec *s = cmd->server;
1445     proxy_server_conf *conf =
1446     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
1447     char *name = NULL;
1448     char *word, *val;
1449     proxy_balancer *balancer = NULL;
1450     proxy_worker *worker = NULL;
1451     const char *err;
1452
1453     if (cmd->directive->parent &&
1454         strncasecmp(cmd->directive->parent->directive,
1455                     "<Proxy", 6) == 0) {
1456         const char *pargs = cmd->directive->parent->args;
1457         /* Directive inside <Proxy section
1458          * Parent directive arg is the worker/balancer name.
1459          */
1460         name = ap_getword_conf(cmd->temp_pool, &pargs);
1461         if ((word = ap_strchr(name, '>')))
1462             *word = '\0';
1463     }
1464     else {
1465         /* Standard set directive with worker/balancer
1466          * name as first param.
1467          */
1468         name = ap_getword_conf(cmd->temp_pool, &arg);
1469     }
1470  
1471     if (strncasecmp(name, "balancer:", 9) == 0) {
1472         balancer = ap_proxy_get_balancer(cmd->pool, conf, name);
1473         if (!balancer) {
1474             return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
1475                                name, "' Balancer.", NULL);
1476         }        
1477     }
1478     else {
1479         worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
1480         if (!worker) {
1481             return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
1482                                name, "' Worker.", NULL);
1483         }
1484     }
1485
1486     while (*arg) {
1487         word = ap_getword_conf(cmd->pool, &arg);
1488         val = strchr(word, '=');
1489         if (!val) {
1490             return "Invalid ProxySet parameter. Parameter must be "
1491                    "in the form 'key=value'";
1492         }
1493         else
1494             *val++ = '\0';
1495         if (worker)
1496             err = set_worker_param(cmd->pool, worker, word, val);
1497         else
1498             err = set_balancer_param(cmd->pool, balancer, word, val);
1499
1500         if (err)
1501             return apr_pstrcat(cmd->temp_pool, "ProxySet ", err, " ", word, " ", name, NULL);
1502     }
1503
1504     return NULL;
1505 }
1506
1507 static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config)
1508 {
1509     proxy_server_conf *sconf = ap_get_module_config(s->module_config,
1510                                                     &proxy_module);
1511     void **new_space = (void **)apr_array_push(sconf->sec_proxy);
1512     
1513     *new_space = dir_config;
1514 }
1515
1516 static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
1517 {
1518     const char *errmsg;
1519     const char *endp = ap_strrchr_c(arg, '>');
1520     int old_overrides = cmd->override;
1521     char *old_path = cmd->path;
1522     proxy_dir_conf *conf;
1523     ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
1524     regex_t *r = NULL;
1525     const command_rec *thiscmd = cmd->cmd;
1526
1527     const char *err = ap_check_cmd_context(cmd,
1528                                            NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
1529     if (err != NULL) {
1530         return err;
1531     }
1532
1533     if (endp == NULL) {
1534         return apr_pstrcat(cmd->pool, cmd->cmd->name,
1535                            "> directive missing closing '>'", NULL);
1536     }
1537
1538     arg=apr_pstrndup(cmd->pool, arg, endp-arg);
1539
1540     if (!arg) {
1541         if (thiscmd->cmd_data)
1542             return "<ProxyMatch > block must specify a path";
1543         else
1544             return "<Proxy > block must specify a path";
1545     }
1546
1547     cmd->path = ap_getword_conf(cmd->pool, &arg);
1548     cmd->override = OR_ALL|ACCESS_CONF;
1549
1550     if (!strncasecmp(cmd->path, "proxy:", 6))
1551         cmd->path += 6;
1552
1553     /* XXX Ignore case?  What if we proxy a case-insensitive server?!? 
1554      * While we are at it, shouldn't we also canonicalize the entire
1555      * scheme?  See proxy_fixup()
1556      */
1557     if (thiscmd->cmd_data) { /* <ProxyMatch> */
1558         r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
1559         if (!r) {
1560             return "Regex could not be compiled";
1561         }
1562     }
1563     else if (!strcmp(cmd->path, "~")) {
1564         cmd->path = ap_getword_conf(cmd->pool, &arg);
1565         if (!cmd->path)
1566             return "<Proxy ~ > block must specify a path";
1567         if (strncasecmp(cmd->path, "proxy:", 6))
1568             cmd->path += 6;
1569         r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
1570         if (!r) {
1571             return "Regex could not be compiled";
1572         }
1573     }
1574
1575     /* initialize our config and fetch it */
1576     conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
1577                                  &proxy_module, cmd->pool);
1578
1579     errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
1580     if (errmsg != NULL)
1581         return errmsg;
1582
1583     conf->r = r;
1584     conf->p = cmd->path;
1585     conf->p_is_fnmatch = apr_fnmatch_test(conf->p);
1586
1587     ap_add_per_proxy_conf(cmd->server, new_dir_conf);
1588
1589     if (*arg != '\0') {
1590         return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
1591                            "> arguments not (yet) supported.", NULL);
1592     }
1593
1594     cmd->path = old_path;
1595     cmd->override = old_overrides;
1596
1597     return NULL;
1598 }
1599
1600 static const command_rec proxy_cmds[] =
1601 {
1602     AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF, 
1603     "Container for directives affecting resources located in the proxied "
1604     "location"),
1605     AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF,
1606     "Container for directives affecting resources located in the proxied "
1607     "location, in regular expression syntax"),
1608     AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF,
1609      "on if the true proxy requests should be accepted"),
1610     AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF,
1611      "a scheme, partial URL or '*' and a proxy server"),
1612     AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
1613      "a regex pattern and a proxy server"),
1614     AP_INIT_RAW_ARGS("ProxyPass", add_pass, NULL, RSRC_CONF|ACCESS_CONF,
1615      "a virtual path and a URL"),
1616     AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
1617      "a virtual path and a URL for reverse proxy behaviour"),
1618     AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL,
1619        RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
1620     AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL,
1621        RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
1622     AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
1623      "A list of names, hosts or domains to which the proxy will not connect"),
1624     AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF,
1625      "Receive buffer size for outgoing HTTP and FTP connections in bytes"),
1626     AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF,
1627      "IO buffer size for outgoing HTTP and FTP connections in bytes"),
1628     AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF,
1629      "The maximum number of proxies a request may be forwarded through."),
1630     AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF,
1631      "A list of domains, hosts, or subnets to which the proxy will connect directly"),
1632     AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF,
1633      "The default intranet domain name (in absence of a domain in the URL)"),
1634     AP_INIT_ITERATE("AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF,
1635      "A list of ports which CONNECT may connect to"),
1636     AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF,
1637      "Configure Via: proxy header header to one of: on | off | block | full"),
1638     AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF,
1639      "use our error handling pages instead of the servers' we are proxying"),
1640     AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF,
1641      "on if we should preserve host header while proxying"),
1642     AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF,
1643      "Set the timeout (in seconds) for a proxied connection. "
1644      "This overrides the server timeout"),
1645     AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF,
1646      "How to handle bad header line in response: IsError | Ignore | StartBody"),
1647     AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF,
1648      "A balancer name and scheme with list of params"), 
1649     AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF,
1650      "Configure Status: proxy status to one of: on | off | full"),
1651     AP_INIT_RAW_ARGS("ProxySet", set_proxy_param, NULL, RSRC_CONF|ACCESS_CONF,
1652      "A balancer or worker name with list of params"),
1653     {NULL}
1654 };
1655
1656 static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL;
1657 static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL;
1658
1659 PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
1660 {
1661     /* 
1662      * if c == NULL just check if the optional function was imported
1663      * else run the optional function so ssl filters are inserted
1664      */
1665     if (proxy_ssl_enable) {
1666         return c ? proxy_ssl_enable(c) : 1;
1667     }
1668
1669     return 0;
1670 }
1671
1672 PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
1673 {
1674     if (proxy_ssl_disable) {
1675         return proxy_ssl_disable(c);
1676     }
1677
1678     return 0;
1679 }
1680
1681 static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1682                              apr_pool_t *ptemp, server_rec *s)
1683 {
1684
1685     proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
1686     proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
1687
1688     return OK;
1689 }
1690
1691 /*
1692  *  proxy Extension to mod_status
1693  */
1694 static int proxy_status_hook(request_rec *r, int flags)
1695 {
1696     int i, n;
1697     void *sconf = r->server->module_config;
1698     proxy_server_conf *conf = (proxy_server_conf *)
1699         ap_get_module_config(sconf, &proxy_module);
1700     proxy_balancer *balancer = NULL;
1701     proxy_worker *worker = NULL;
1702
1703     if (flags & AP_STATUS_SHORT || conf->balancers->nelts == 0 ||
1704         conf->proxy_status == status_off)
1705         return OK;
1706
1707     balancer = (proxy_balancer *)conf->balancers->elts;
1708     for (i = 0; i < conf->balancers->nelts; i++) {
1709         ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r);
1710         ap_rvputs(r, balancer->name, "</h1>\n\n", NULL);
1711         ap_rputs("\n\n<table border=\"0\"><tr>"
1712                  "<th>SSes</th><th>Timeout</th><th>Method</th>"
1713                  "</tr>\n<tr>", r);                
1714         ap_rvputs(r, "<td>", balancer->sticky, NULL);
1715         ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
1716                    apr_time_sec(balancer->timeout));
1717         ap_rprintf(r, "<td>%s</td>\n",
1718                    balancer->lbmethod == lbmethod_requests ? "Requests" :
1719                    balancer->lbmethod == lbmethod_traffic ? "Traffic" :
1720                    "Unknown");
1721         ap_rputs("</table>\n", r);
1722         ap_rputs("\n\n<table border=\"0\"><tr>"
1723                  "<th>Sch</th><th>Host</th><th>Stat</th>"
1724                  "<th>Route</th><th>Redir</th>"
1725                  "<th>F</th><th>Acc</th><th>Wr</th><th>Rd</th>"
1726                  "</tr>\n", r);
1727
1728         worker = (proxy_worker *)balancer->workers->elts;
1729         for (n = 0; n < balancer->workers->nelts; n++) {
1730             char fbuf[50];
1731             ap_rvputs(r, "<tr>\n<td>", worker->scheme, "</td>", NULL);
1732             ap_rvputs(r, "<td>", worker->hostname, "</td><td>", NULL);
1733             if (worker->s->status & PROXY_WORKER_DISABLED)
1734                 ap_rputs("Dis", r);
1735             else if (worker->s->status & PROXY_WORKER_IN_ERROR)
1736                 ap_rputs("Err", r);
1737             else if (worker->s->status & PROXY_WORKER_INITIALIZED)
1738                 ap_rputs("Ok", r);
1739             else
1740                 ap_rputs("-", r);
1741             ap_rvputs(r, "</td><td>", worker->s->route, NULL);
1742             ap_rvputs(r, "</td><td>", worker->s->redirect, NULL);
1743             ap_rprintf(r, "</td><td>%d</td>", worker->s->lbfactor);
1744             ap_rprintf(r, "<td>%d</td><td>", (int)(worker->s->elected));
1745             ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r);
1746             ap_rputs("</td><td>", r);
1747             ap_rputs(apr_strfsize(worker->s->read, fbuf), r);
1748             ap_rputs("</td>\n", r);
1749
1750             /* TODO: Add the rest of dynamic worker data */
1751             ap_rputs("</tr>\n", r);
1752
1753             ++worker;
1754         }
1755         ap_rputs("</table>\n", r);
1756         ++balancer;
1757     }
1758     ap_rputs("<hr /><table>\n"
1759              "<tr><th>SSes</th><td>Sticky session name</td></tr>\n"
1760              "<tr><th>Timeout</th><td>Balancer Timeout</td></tr>\n"
1761              "<tr><th>Sch</th><td>Connection scheme</td></tr>\n"
1762              "<tr><th>Host</th><td>Backend Hostname</td></tr>\n"
1763              "<tr><th>Stat</th><td>Worker status</td></tr>\n"
1764              "<tr><th>Route</th><td>Session Route</td></tr>\n"
1765              "<tr><th>Redir</th><td>Session Route Redirection</td></tr>\n"
1766              "<tr><th>F</th><td>Load Balancer Factor in %</td></tr>\n"
1767              "<tr><th>Acc</th><td>Number of requests</td></tr>\n"
1768              "<tr><th>Wr</th><td>Number of bytes transferred</td></tr>\n"
1769              "<tr><th>Rd</th><td>Number of bytes read</td></tr>\n"
1770              "</table>", r);
1771
1772     return OK;
1773 }
1774
1775 static void child_init(apr_pool_t *p, server_rec *s)
1776 {
1777     proxy_worker *reverse = NULL;
1778     
1779     while (s) {
1780         void *sconf = s->module_config;
1781         proxy_server_conf *conf;
1782         proxy_worker *worker;
1783         int i;
1784
1785         conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
1786         /* Initialize worker's shared scoreboard data */ 
1787         worker = (proxy_worker *)conf->workers->elts;
1788         for (i = 0; i < conf->workers->nelts; i++) {
1789             ap_proxy_initialize_worker_share(conf, worker, s);
1790             ap_proxy_initialize_worker(worker, s);
1791             worker++;
1792         }
1793         /* Initialize forward worker if defined */
1794         if (conf->forward) {
1795             ap_proxy_initialize_worker_share(conf, conf->forward, s);
1796             ap_proxy_initialize_worker(conf->forward, s);
1797             /* Do not disable worker in case of errors */
1798             conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS;
1799             /* Disable address cache for generic forward worker */ 
1800             conf->forward->is_address_reusable = 0;
1801         }
1802         if (!reverse) {
1803             reverse = ap_proxy_create_worker(p);
1804             reverse->name     = "proxy:reverse";
1805             reverse->hostname = "*";
1806             reverse->scheme   = "*";
1807             ap_proxy_initialize_worker_share(conf, reverse, s);
1808             ap_proxy_initialize_worker(reverse, s);
1809             /* Do not disable worker in case of errors */
1810             reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS;
1811             /* Disable address cache for generic reverse worker */ 
1812             reverse->is_address_reusable = 0;
1813         }
1814         conf->reverse = reverse;
1815         s = s->next;
1816     }
1817 }
1818
1819 /*
1820  * This routine is called before the server processes the configuration
1821  * files.  There is no return value.
1822  */
1823 static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
1824                             apr_pool_t *ptemp)
1825 {
1826     APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL,
1827                       APR_HOOK_MIDDLE);
1828     /* Reset workers count on gracefull restart */ 
1829     proxy_lb_workers = 0;
1830     return OK;
1831 }
1832
1833 static void register_hooks(apr_pool_t *p)
1834 {
1835     /* fixup before mod_rewrite, so that the proxied url will not
1836      * escaped accidentally by our fixup.
1837      */
1838 #ifndef FIX_15207
1839     static const char * const aszSucc[]={ "mod_rewrite.c", NULL };
1840 #endif
1841     /* Only the mpm_winnt has child init hook handler.
1842      * make sure that we are called after the mpm
1843      * initializes.
1844      */
1845     static const char *const aszPred[] = { "mpm_winnt.c", NULL};
1846     
1847     APR_REGISTER_OPTIONAL_FN(ap_proxy_lb_workers);
1848     /* handler */
1849     ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
1850     /* filename-to-URI translation */
1851     ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST);
1852     /* walk <Proxy > entries and suppress default TRACE behavior */
1853     ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
1854 #ifndef FIX_15207
1855     /* fixups */
1856     ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST);
1857 #endif
1858     /* post read_request handling */
1859     ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST);
1860     /* pre config handling */
1861     ap_hook_pre_config(proxy_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 
1862     /* post config handling */
1863     ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1864     /* child init handling */
1865     ap_hook_child_init(child_init, aszPred, NULL, APR_HOOK_MIDDLE); 
1866
1867 }
1868
1869 module AP_MODULE_DECLARE_DATA proxy_module =
1870 {
1871     STANDARD20_MODULE_STUFF,
1872     create_proxy_dir_config,    /* create per-directory config structure */
1873     merge_proxy_dir_config,     /* merge per-directory config structures */
1874     create_proxy_config,        /* create per-server config structure */
1875     merge_proxy_config,         /* merge per-server config structures */
1876     proxy_cmds,                 /* command table */
1877     register_hooks
1878 };
1879
1880 APR_HOOK_STRUCT(
1881     APR_HOOK_LINK(scheme_handler)
1882     APR_HOOK_LINK(canon_handler)
1883     APR_HOOK_LINK(pre_request)
1884     APR_HOOK_LINK(post_request)
1885 )
1886
1887 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, 
1888                                      (request_rec *r, proxy_worker *worker,
1889                                       proxy_server_conf *conf, 
1890                                       char *url, const char *proxyhost, 
1891                                       apr_port_t proxyport),(r,worker,conf,
1892                                       url,proxyhost,proxyport),DECLINED)
1893 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler, 
1894                                       (request_rec *r, char *url),(r,
1895                                       url),DECLINED)
1896 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, pre_request, (
1897                                       proxy_worker **worker,
1898                                       proxy_balancer **balancer,
1899                                       request_rec *r, 
1900                                       proxy_server_conf *conf,
1901                                       char **url),(worker,balancer,
1902                                       r,conf,url),DECLINED)
1903 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, post_request,
1904                                       (proxy_worker *worker,
1905                                        proxy_balancer *balancer,
1906                                        request_rec *r,
1907                                        proxy_server_conf *conf),(worker,
1908                                        balancer,r,conf),DECLINED)
1909 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups,
1910                                     (request_rec *r), (r),
1911                                     OK, DECLINED)