]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy.c
Fix for additional cases of URL rewriting with ProxyPassMatch or
[apache] / modules / proxy / mod_proxy.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  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 #include "mod_proxy.h"
18 #include "mod_core.h"
19 #include "apr_optional.h"
20 #include "scoreboard.h"
21 #include "mod_status.h"
22
23 #if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
24 #include "mod_ssl.h"
25 #else
26 APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
27 APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
28 APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
29 APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
30                         (apr_pool_t *, server_rec *,
31                          conn_rec *, request_rec *, char *));
32 #endif
33
34 #ifndef MAX
35 #define MAX(x,y) ((x) >= (y) ? (x) : (y))
36 #endif
37
38 /*
39  * A Web proxy module. Stages:
40  *
41  *  translate_name: set filename to proxy:<URL>
42  *  map_to_storage: run proxy_walk (rather than directory_walk/file_walk)
43  *                  can't trust directory_walk/file_walk since these are
44  *                  not in our filesystem.  Prevents mod_http from serving
45  *                  the TRACE request we will set aside to handle later.
46  *  type_checker:   set type to PROXY_MAGIC_TYPE if filename begins proxy:
47  *  fix_ups:        convert the URL stored in the filename to the
48  *                  canonical form.
49  *  handler:        handle proxy requests
50  */
51
52 /* -------------------------------------------------------------- */
53 /* Translate the URL into a 'filename' */
54
55 static const char *set_worker_param(apr_pool_t *p,
56                                     proxy_worker *worker,
57                                     const char *key,
58                                     const char *val)
59 {
60
61     int ival;
62     apr_interval_time_t timeout;
63
64     if (!strcasecmp(key, "loadfactor")) {
65         /* Normalized load factor. Used with BalancerMamber,
66          * it is a number between 1 and 100.
67          */
68         worker->s->lbfactor = atoi(val);
69         if (worker->s->lbfactor < 1 || worker->s->lbfactor > 100)
70             return "LoadFactor must be a number between 1..100";
71     }
72     else if (!strcasecmp(key, "retry")) {
73         /* If set it will give the retry timeout for the worker
74          * The default value is 60 seconds, meaning that if
75          * in error state, it will be retried after that timeout.
76          */
77         ival = atoi(val);
78         if (ival < 0)
79             return "Retry must be a positive value";
80         worker->s->retry = apr_time_from_sec(ival);
81         worker->s->retry_set = 1;
82     }
83     else if (!strcasecmp(key, "ttl")) {
84         /* Time in seconds that will destroy all the connections
85          * that exceed the smax
86          */
87         ival = atoi(val);
88         if (ival < 1)
89             return "TTL must be at least one second";
90         worker->s->ttl = apr_time_from_sec(ival);
91     }
92     else if (!strcasecmp(key, "min")) {
93         /* Initial number of connections to remote
94          */
95         ival = atoi(val);
96         if (ival < 0)
97             return "Min must be a positive number";
98         worker->s->min = ival;
99     }
100     else if (!strcasecmp(key, "max")) {
101         /* Maximum number of connections to remote
102          */
103         ival = atoi(val);
104         if (ival < 0)
105             return "Max must be a positive number";
106         worker->s->hmax = ival;
107     }
108     /* XXX: More inteligent naming needed */
109     else if (!strcasecmp(key, "smax")) {
110         /* Maximum number of connections to remote that
111          * will not be destroyed
112          */
113         ival = atoi(val);
114         if (ival < 0)
115             return "Smax must be a positive number";
116         worker->s->smax = ival;
117     }
118     else if (!strcasecmp(key, "acquire")) {
119         /* Acquire timeout in given unit (default is milliseconds).
120          * If set this will be the maximum time to
121          * wait for a free connection.
122          */
123         if (ap_timeout_parameter_parse(val, &timeout, "ms") != APR_SUCCESS)
124             return "Acquire timeout has wrong format";
125         if (timeout < 1000)
126             return "Acquire must be at least one millisecond";
127         worker->s->acquire = timeout;
128         worker->s->acquire_set = 1;
129     }
130     else if (!strcasecmp(key, "timeout")) {
131         /* Connection timeout in seconds.
132          * Defaults to server timeout.
133          */
134         ival = atoi(val);
135         if (ival < 1)
136             return "Timeout must be at least one second";
137         worker->s->timeout = apr_time_from_sec(ival);
138         worker->s->timeout_set = 1;
139     }
140     else if (!strcasecmp(key, "iobuffersize")) {
141         long s = atol(val);
142         if (s < 512 && s) {
143             return "IOBufferSize must be >= 512 bytes, or 0 for system default.";
144         }
145         worker->s->io_buffer_size = (s ? s : AP_IOBUFSIZE);
146         worker->s->io_buffer_size_set = 1;
147     }
148     else if (!strcasecmp(key, "receivebuffersize")) {
149         ival = atoi(val);
150         if (ival < 512 && ival != 0) {
151             return "ReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
152         }
153         worker->s->recv_buffer_size = ival;
154         worker->s->recv_buffer_size_set = 1;
155     }
156     else if (!strcasecmp(key, "keepalive")) {
157         if (!strcasecmp(val, "on"))
158             worker->s->keepalive = 1;
159         else if (!strcasecmp(val, "off"))
160             worker->s->keepalive = 0;
161         else
162             return "KeepAlive must be On|Off";
163         worker->s->keepalive_set = 1;
164     }
165     else if (!strcasecmp(key, "disablereuse")) {
166         if (!strcasecmp(val, "on"))
167             worker->s->disablereuse = 1;
168         else if (!strcasecmp(val, "off"))
169             worker->s->disablereuse = 0;
170         else
171             return "DisableReuse must be On|Off";
172         worker->s->disablereuse_set = 1;
173     }
174     else if (!strcasecmp(key, "route")) {
175         /* Worker route.
176          */
177         if (strlen(val) >= PROXY_WORKER_MAX_ROUTE_SIZE)
178             return "Route length must be < 64 characters";
179         PROXY_STRNCPY(worker->s->route, val);
180     }
181     else if (!strcasecmp(key, "redirect")) {
182         /* Worker redirection route.
183          */
184         if (strlen(val) >= PROXY_WORKER_MAX_ROUTE_SIZE)
185             return "Redirect length must be < 64 characters";
186         PROXY_STRNCPY(worker->s->redirect, val);
187     }
188     else if (!strcasecmp(key, "status")) {
189         const char *v;
190         int mode = 1;
191         apr_status_t rv;
192         /* Worker status.
193          */
194         for (v = val; *v; v++) {
195             if (*v == '+') {
196                 mode = 1;
197                 v++;
198             }
199             else if (*v == '-') {
200                 mode = 0;
201                 v++;
202             }
203             rv = ap_proxy_set_wstatus(*v, mode, worker);
204             if (rv != APR_SUCCESS)
205                 return "Unknown status parameter option";
206         }
207     }
208     else if (!strcasecmp(key, "flushpackets")) {
209         if (!strcasecmp(val, "on"))
210             worker->s->flush_packets = flush_on;
211         else if (!strcasecmp(val, "off"))
212             worker->s->flush_packets = flush_off;
213         else if (!strcasecmp(val, "auto"))
214             worker->s->flush_packets = flush_auto;
215         else
216             return "flushpackets must be on|off|auto";
217     }
218     else if (!strcasecmp(key, "flushwait")) {
219         ival = atoi(val);
220         if (ival > 1000 || ival < 0) {
221             return "flushwait must be <= 1000, or 0 for system default of 10 millseconds.";
222         }
223         if (ival == 0)
224             worker->s->flush_wait = PROXY_FLUSH_WAIT;
225         else
226             worker->s->flush_wait = ival * 1000;    /* change to microseconds */
227     }
228     else if (!strcasecmp(key, "ping")) {
229         /* Ping/Pong timeout in given unit (default is second).
230          */
231         if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS)
232             return "Ping/Pong timeout has wrong format";
233         if (timeout < 1000)
234             return "Ping/Pong timeout must be at least one millisecond";
235         worker->s->ping_timeout = timeout;
236         worker->s->ping_timeout_set = 1;
237     }
238     else if (!strcasecmp(key, "lbset")) {
239         ival = atoi(val);
240         if (ival < 0 || ival > 99)
241             return "lbset must be between 0 and 99";
242         worker->s->lbset = ival;
243     }
244     else if (!strcasecmp(key, "connectiontimeout")) {
245         /* Request timeout in given unit (default is second).
246          * Defaults to connection timeout
247          */
248         if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS)
249             return "Connectiontimeout has wrong format";
250         if (timeout < 1000)
251             return "Connectiontimeout must be at least one millisecond.";
252         worker->s->conn_timeout = timeout;
253         worker->s->conn_timeout_set = 1;
254     }
255     else if (!strcasecmp(key, "flusher")) {
256         if (strlen(val) >= PROXY_WORKER_MAX_SCHEME_SIZE)
257             return "flusher name length must be < 16 characters";
258         PROXY_STRNCPY(worker->s->flusher, val);
259     }
260     else {
261         return "unknown Worker parameter";
262     }
263     return NULL;
264 }
265
266 static const char *set_balancer_param(proxy_server_conf *conf,
267                                       apr_pool_t *p,
268                                       proxy_balancer *balancer,
269                                       const char *key,
270                                       const char *val)
271 {
272
273     int ival;
274     if (!strcasecmp(key, "stickysession")) {
275         char *path;
276         /* Balancer sticky session name.
277          * Set to something like JSESSIONID or
278          * PHPSESSIONID, etc..,
279          */
280         if (strlen(val) > (PROXY_BALANCER_MAX_STICKY_SIZE-1))
281             return "stickysession length must be < 64 characters";
282         PROXY_STRNCPY(balancer->s->sticky_path, val);
283         PROXY_STRNCPY(balancer->s->sticky, val);
284
285         if ((path = strchr((char *)balancer->s->sticky, '|'))) {
286             *path++ = '\0';
287             PROXY_STRNCPY(balancer->s->sticky_path, path);
288         }
289     }
290     else if (!strcasecmp(key, "nofailover")) {
291         /* If set to 'on' the session will break
292          * if the worker is in error state or
293          * disabled.
294          */
295         if (!strcasecmp(val, "on"))
296             balancer->s->sticky_force = 1;
297         else if (!strcasecmp(val, "off"))
298             balancer->s->sticky_force = 0;
299         else
300             return "failover must be On|Off";
301     }
302     else if (!strcasecmp(key, "timeout")) {
303         /* Balancer timeout in seconds.
304          * If set this will be the maximum time to
305          * wait for a free worker.
306          * Default is not to wait.
307          */
308         ival = atoi(val);
309         if (ival < 1)
310             return "timeout must be at least one second";
311         balancer->s->timeout = apr_time_from_sec(ival);
312     }
313     else if (!strcasecmp(key, "maxattempts")) {
314         /* Maximum number of failover attempts before
315          * giving up.
316          */
317         ival = atoi(val);
318         if (ival < 0)
319             return "maximum number of attempts must be a positive number";
320         balancer->s->max_attempts = ival;
321         balancer->s->max_attempts_set = 1;
322     }
323     else if (!strcasecmp(key, "lbmethod")) {
324         proxy_balancer_method *provider;
325         if (strlen(val) > (sizeof(balancer->s->lbpname)-1))
326             return "unknown lbmethod";
327         provider = ap_lookup_provider(PROXY_LBMETHOD, val, "0");
328         if (provider) {
329             balancer->lbmethod = provider;
330             if (PROXY_STRNCPY(balancer->s->lbpname, val) == APR_SUCCESS) {
331                 return NULL;
332             }
333             else {
334                 return "lbmethod name too large";
335             }
336         }
337         return "unknown lbmethod";
338     }
339     else if (!strcasecmp(key, "scolonpathdelim")) {
340         /* If set to 'on' then ';' will also be
341          * used as a session path separator/delim (ala
342          * mod_jk)
343          */
344         if (!strcasecmp(val, "on"))
345             balancer->s->scolonsep = 1;
346         else if (!strcasecmp(val, "off"))
347             balancer->s->scolonsep = 0;
348         else
349             return "scolonpathdelim must be On|Off";
350     }
351     else if (!strcasecmp(key, "failonstatus")) {
352         char *val_split;
353         char *status;
354         char *tok_state;
355
356         val_split = apr_pstrdup(p, val);
357
358         balancer->errstatuses = apr_array_make(p, 1, sizeof(int));
359
360         status = apr_strtok(val_split, ", ", &tok_state);
361         while (status != NULL) {
362             ival = atoi(status);
363             if (ap_is_HTTP_VALID_RESPONSE(ival)) {
364                 *(int *)apr_array_push(balancer->errstatuses) = ival;
365             }
366             else {
367                 return "failonstatus must be one or more HTTP response codes";
368             }
369             status = apr_strtok(NULL, ", ", &tok_state);
370         }
371
372     }
373     else if (!strcasecmp(key, "nonce")) {
374         if (!strcasecmp(val, "None")) {
375             *balancer->s->nonce = '\0';
376         }
377         else {
378             if (PROXY_STRNCPY(balancer->s->nonce, val) != APR_SUCCESS) {
379                 return "Provided nonce is too large";
380             }
381         }
382     }
383     else if (!strcasecmp(key, "growth")) {
384         ival = atoi(val);
385         if (ival < 1 || ival > 100)   /* arbitrary limit here */
386             return "growth must be between 1 and 100";
387         balancer->growth = ival;
388     }
389     else {
390         return "unknown Balancer parameter";
391     }
392     return NULL;
393 }
394
395 static int alias_match(const char *uri, const char *alias_fakename)
396 {
397     const char *end_fakename = alias_fakename + strlen(alias_fakename);
398     const char *aliasp = alias_fakename, *urip = uri;
399     const char *end_uri = uri + strlen(uri);
400
401     while (aliasp < end_fakename && urip < end_uri) {
402         if (*aliasp == '/') {
403             /* any number of '/' in the alias matches any number in
404              * the supplied URI, but there must be at least one...
405              */
406             if (*urip != '/')
407                 return 0;
408
409             while (*aliasp == '/')
410                 ++aliasp;
411             while (*urip == '/')
412                 ++urip;
413         }
414         else {
415             /* Other characters are compared literally */
416             if (*urip++ != *aliasp++)
417                 return 0;
418         }
419     }
420
421     /* fixup badly encoded stuff (e.g. % as last character) */
422     if (aliasp > end_fakename) {
423         aliasp = end_fakename;
424     }
425     if (urip > end_uri) {
426         urip = end_uri;
427     }
428
429    /* We reach the end of the uri before the end of "alias_fakename"
430     * for example uri is "/" and alias_fakename "/examples"
431     */
432    if (urip == end_uri && aliasp != end_fakename) {
433        return 0;
434    }
435
436     /* Check last alias path component matched all the way */
437     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
438         return 0;
439
440     /* Return number of characters from URI which matched (may be
441      * greater than length of alias, since we may have matched
442      * doubled slashes)
443      */
444
445     return urip - uri;
446 }
447
448 /* Detect if an absoluteURI should be proxied or not.  Note that we
449  * have to do this during this phase because later phases are
450  * "short-circuiting"... i.e. translate_names will end when the first
451  * module returns OK.  So for example, if the request is something like:
452  *
453  * GET http://othervhost/cgi-bin/printenv HTTP/1.0
454  *
455  * mod_alias will notice the /cgi-bin part and ScriptAlias it and
456  * short-circuit the proxy... just because of the ordering in the
457  * configuration file.
458  */
459 static int proxy_detect(request_rec *r)
460 {
461     void *sconf = r->server->module_config;
462     proxy_server_conf *conf =
463         (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
464
465     /* Ick... msvc (perhaps others) promotes ternary short results to int */
466
467     if (conf->req && r->parsed_uri.scheme) {
468         /* but it might be something vhosted */
469         if (!(r->parsed_uri.hostname
470               && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))
471               && ap_matches_request_vhost(r, r->parsed_uri.hostname,
472                                           (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port
473                                                        : ap_default_port(r))))) {
474             r->proxyreq = PROXYREQ_PROXY;
475             r->uri = r->unparsed_uri;
476             r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
477             r->handler = "proxy-server";
478         }
479     }
480     /* We need special treatment for CONNECT proxying: it has no scheme part */
481     else if (conf->req && r->method_number == M_CONNECT
482              && r->parsed_uri.hostname
483              && r->parsed_uri.port_str) {
484         r->proxyreq = PROXYREQ_PROXY;
485         r->uri = r->unparsed_uri;
486         r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
487         r->handler = "proxy-server";
488     }
489     return DECLINED;
490 }
491
492 static const char *proxy_interpolate(request_rec *r, const char *str)
493 {
494     /* Interpolate an env str in a configuration string
495      * Syntax ${var} --> value_of(var)
496      * Method: replace one var, and recurse on remainder of string
497      * Nothing clever here, and crap like nested vars may do silly things
498      * but we'll at least avoid sending the unwary into a loop
499      */
500     const char *start;
501     const char *end;
502     const char *var;
503     const char *val;
504     const char *firstpart;
505
506     start = ap_strstr_c(str, "${");
507     if (start == NULL) {
508         return str;
509     }
510     end = ap_strchr_c(start+2, '}');
511     if (end == NULL) {
512         return str;
513     }
514     /* OK, this is syntax we want to interpolate.  Is there such a var ? */
515     var = apr_pstrndup(r->pool, start+2, end-(start+2));
516     val = apr_table_get(r->subprocess_env, var);
517     firstpart = apr_pstrndup(r->pool, str, (start-str));
518
519     if (val == NULL) {
520         return apr_pstrcat(r->pool, firstpart,
521                            proxy_interpolate(r, end+1), NULL);
522     }
523     else {
524         return apr_pstrcat(r->pool, firstpart, val,
525                            proxy_interpolate(r, end+1), NULL);
526     }
527 }
528 static apr_array_header_t *proxy_vars(request_rec *r,
529                                       apr_array_header_t *hdr)
530 {
531     int i;
532     apr_array_header_t *ret = apr_array_make(r->pool, hdr->nelts,
533                                              sizeof (struct proxy_alias));
534     struct proxy_alias *old = (struct proxy_alias *) hdr->elts;
535
536     for (i = 0; i < hdr->nelts; ++i) {
537         struct proxy_alias *newcopy = apr_array_push(ret);
538         newcopy->fake = (old[i].flags & PROXYPASS_INTERPOLATE)
539                         ? proxy_interpolate(r, old[i].fake) : old[i].fake;
540         newcopy->real = (old[i].flags & PROXYPASS_INTERPOLATE)
541                         ? proxy_interpolate(r, old[i].real) : old[i].real;
542     }
543     return ret;
544 }
545
546 PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent,
547                                         proxy_dir_conf *dconf)
548 {
549     int len;
550     const char *fake;
551     const char *real;
552     ap_regmatch_t regm[AP_MAX_REG_MATCH];
553     ap_regmatch_t reg1[AP_MAX_REG_MATCH];
554     char *found = NULL;
555     int mismatch = 0;
556     unsigned int nocanon = ent->flags & PROXYPASS_NOCANON;
557     const char *use_uri = nocanon ? r->unparsed_uri : r->uri;
558
559     if (dconf && (dconf->interpolate_env == 1) && (ent->flags & PROXYPASS_INTERPOLATE)) {
560         fake = proxy_interpolate(r, ent->fake);
561         real = proxy_interpolate(r, ent->real);
562     }
563     else {
564         fake = ent->fake;
565         real = ent->real;
566     }
567     if (ent->regex) {
568         if (!ap_regexec(ent->regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) {
569             if ((real[0] == '!') && (real[1] == '\0')) {
570                 return DECLINED;
571             }
572             /* test that we haven't reduced the URI */
573             if (nocanon && ap_regexec(ent->regex, r->unparsed_uri,
574                     AP_MAX_REG_MATCH, reg1, 0)) {
575                 mismatch = 1;
576                 use_uri = r->uri;
577             }
578             found = ap_pregsub(r->pool, real, use_uri, AP_MAX_REG_MATCH,
579                     (use_uri == r->uri) ? regm : reg1);
580             if (!found) {
581                 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
582                               "Substitution in regular expression failed. "
583                               "Replacement too long?");
584                 return HTTP_INTERNAL_SERVER_ERROR;
585             }
586
587             /* Note: The strcmp() below catches cases where there
588              * was no regex substitution. This is so cases like:
589              *
590              *    ProxyPassMatch \.gif balancer://foo
591              *
592              * will work "as expected". The upshot is that the 2
593              * directives below act the exact same way (ie: $1 is implied):
594              *
595              *    ProxyPassMatch ^(/.*\.gif)$ balancer://foo
596              *    ProxyPassMatch ^(/.*\.gif)$ balancer://foo$1
597              *
598              * which may be confusing.
599              */
600             if (strcmp(found, real) != 0) {
601                 found = apr_pstrcat(r->pool, "proxy:", found, NULL);
602             }
603             else {
604                 found = apr_pstrcat(r->pool, "proxy:", real, use_uri, NULL);
605             }
606         }
607     }
608     else {
609         len = alias_match(r->uri, fake);
610
611         if (len != 0) {
612             if ((real[0] == '!') && (real[1] == '\0')) {
613                 return DECLINED;
614             }
615             if (nocanon && len != alias_match(r->unparsed_uri, ent->fake)) {
616                 mismatch = 1;
617                 use_uri = r->uri;
618             }
619             found = apr_pstrcat(r->pool, "proxy:", real, use_uri + len, NULL);
620         }
621     }
622     if (mismatch) {
623         /* We made a reducing transformation, so we can't safely use
624          * unparsed_uri.  Safe fallback is to ignore nocanon.
625          */
626         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
627                 "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon");
628     }
629
630     if (found) {
631         r->filename = found;
632         r->handler = "proxy-server";
633         r->proxyreq = PROXYREQ_REVERSE;
634         if (nocanon && !mismatch) {
635             /* mod_proxy_http needs to be told.  Different module. */
636             apr_table_setn(r->notes, "proxy-nocanon", "1");
637         }
638         return OK;
639     }
640
641     return DONE;
642 }
643
644 static int proxy_trans(request_rec *r)
645 {
646     int i;
647     struct proxy_alias *ent;
648     proxy_dir_conf *dconf;
649     proxy_server_conf *conf;
650
651     if (r->proxyreq) {
652         /* someone has already set up the proxy, it was possibly ourselves
653          * in proxy_detect
654          */
655         return OK;
656     }
657
658     if (strcmp(r->unparsed_uri, "*") == 0) {
659         /* "*" cannot be proxied. */
660         return DECLINED;
661     }
662
663     /* Check that the URI is valid. */
664     if (!r->uri || r->uri[0] != '/') {
665         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
666                      "Invalid URI in request %s", r->the_request);
667         return HTTP_BAD_REQUEST;
668     }
669
670     /* XXX: since r->uri has been manipulated already we're not really
671      * compliant with RFC1945 at this point.  But this probably isn't
672      * an issue because this is a hybrid proxy/origin server.
673      */
674
675     dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
676
677     /* short way - this location is reverse proxied? */
678     if (dconf->alias) {
679         int rv = ap_proxy_trans_match(r, dconf->alias, dconf);
680         if (DONE != rv) {
681             return rv;
682         }
683     }
684
685     conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config,
686                                                       &proxy_module);
687
688     /* long way - walk the list of aliases, find a match */
689     if (conf->aliases->nelts) {
690         ent = (struct proxy_alias *) conf->aliases->elts;
691         for (i = 0; i < conf->aliases->nelts; i++) {
692             int rv = ap_proxy_trans_match(r, &ent[i], dconf);
693             if (DONE != rv) {
694                 return rv;
695             }
696         }
697     }
698     return DECLINED;
699 }
700
701 static int proxy_walk(request_rec *r)
702 {
703     proxy_server_conf *sconf = ap_get_module_config(r->server->module_config,
704                                                     &proxy_module);
705     ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
706     ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts;
707     ap_conf_vector_t *entry_config;
708     proxy_dir_conf *entry_proxy;
709     int num_sec = sconf->sec_proxy->nelts;
710     /* XXX: shouldn't we use URI here?  Canonicalize it first?
711      * Pass over "proxy:" prefix
712      */
713     const char *proxyname = r->filename + 6;
714     int j;
715
716     for (j = 0; j < num_sec; ++j)
717     {
718         entry_config = sec_proxy[j];
719         entry_proxy = ap_get_module_config(entry_config, &proxy_module);
720
721         /* XXX: What about case insensitive matching ???
722          * Compare regex, fnmatch or string as appropriate
723          * If the entry doesn't relate, then continue
724          */
725         if (entry_proxy->r
726               ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0)
727               : (entry_proxy->p_is_fnmatch
728                    ? apr_fnmatch(entry_proxy->p, proxyname, 0)
729                    : strncmp(proxyname, entry_proxy->p,
730                                         strlen(entry_proxy->p)))) {
731             continue;
732         }
733         per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults,
734                                                              entry_config);
735     }
736
737     r->per_dir_config = per_dir_defaults;
738
739     return OK;
740 }
741
742 static int proxy_map_location(request_rec *r)
743 {
744     int access_status;
745
746     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
747         return DECLINED;
748
749     /* Don't let the core or mod_http map_to_storage hooks handle this,
750      * We don't need directory/file_walk, and we want to TRACE on our own.
751      */
752     if ((access_status = proxy_walk(r))) {
753         ap_die(access_status, r);
754         return access_status;
755     }
756
757     return OK;
758 }
759
760 /* -------------------------------------------------------------- */
761 /* Fixup the filename */
762
763 /*
764  * Canonicalise the URL
765  */
766 static int proxy_fixup(request_rec *r)
767 {
768     char *url, *p;
769     int access_status;
770     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
771                                                  &proxy_module);
772
773     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
774         return DECLINED;
775
776     /* XXX: Shouldn't we try this before we run the proxy_walk? */
777     url = &r->filename[6];
778
779     if ((dconf->interpolate_env == 1) && (r->proxyreq == PROXYREQ_REVERSE)) {
780         /* create per-request copy of reverse proxy conf,
781          * and interpolate vars in it
782          */
783         proxy_req_conf *rconf = apr_palloc(r->pool, sizeof(proxy_req_conf));
784         ap_set_module_config(r->request_config, &proxy_module, rconf);
785         rconf->raliases = proxy_vars(r, dconf->raliases);
786         rconf->cookie_paths = proxy_vars(r, dconf->cookie_paths);
787         rconf->cookie_domains = proxy_vars(r, dconf->cookie_domains);
788     }
789
790     /* canonicalise each specific scheme */
791     if ((access_status = proxy_run_canon_handler(r, url))) {
792         return access_status;
793     }
794
795     p = strchr(url, ':');
796     if (p == NULL || p == url)
797         return HTTP_BAD_REQUEST;
798
799     return OK;      /* otherwise; we've done the best we can */
800 }
801 /* Send a redirection if the request contains a hostname which is not */
802 /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
803 /* servers like Netscape's allow this and access hosts from the local */
804 /* domain in this case. I think it is better to redirect to a FQDN, since */
805 /* these will later be found in the bookmarks files. */
806 /* The "ProxyDomain" directive determines what domain will be appended */
807 static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
808 {
809     char *nuri;
810     const char *ref;
811
812     /* We only want to worry about GETs */
813     if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
814         return DECLINED;
815
816     /* If host does contain a dot already, or it is "localhost", decline */
817     if (strchr(r->parsed_uri.hostname, '.') != NULL /* has domain, or IPv4 literal */
818      || strchr(r->parsed_uri.hostname, ':') != NULL /* IPv6 literal */
819      || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
820         return DECLINED;    /* host name has a dot already */
821
822     ref = apr_table_get(r->headers_in, "Referer");
823
824     /* Reassemble the request, but insert the domain after the host name */
825     /* Note that the domain name always starts with a dot */
826     r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname,
827                                          domain, NULL);
828     nuri = apr_uri_unparse(r->pool,
829                            &r->parsed_uri,
830                            APR_URI_UNP_REVEALPASSWORD);
831
832     apr_table_setn(r->headers_out, "Location", nuri);
833     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
834                   "Domain missing: %s sent to %s%s%s", r->uri,
835                   apr_uri_unparse(r->pool, &r->parsed_uri,
836                                   APR_URI_UNP_OMITUSERINFO),
837                   ref ? " from " : "", ref ? ref : "");
838
839     return HTTP_MOVED_PERMANENTLY;
840 }
841
842 /* -------------------------------------------------------------- */
843 /* Invoke handler */
844
845 static int proxy_handler(request_rec *r)
846 {
847     char *uri, *scheme, *p;
848     const char *p2;
849     void *sconf = r->server->module_config;
850     proxy_server_conf *conf = (proxy_server_conf *)
851         ap_get_module_config(sconf, &proxy_module);
852     apr_array_header_t *proxies = conf->proxies;
853     struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
854     int i, rc, access_status;
855     int direct_connect = 0;
856     const char *str;
857     long maxfwd;
858     proxy_balancer *balancer = NULL;
859     proxy_worker *worker = NULL;
860     int attempts = 0, max_attempts = 0;
861     struct dirconn_entry *list = (struct dirconn_entry *)conf->dirconn->elts;
862
863     /* is this for us? */
864     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
865         return DECLINED;
866
867     /* handle max-forwards / OPTIONS / TRACE */
868     if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) {
869         maxfwd = strtol(str, NULL, 10);
870         if (maxfwd < 1) {
871             switch (r->method_number) {
872             case M_TRACE: {
873                 int access_status;
874                 r->proxyreq = PROXYREQ_NONE;
875                 if ((access_status = ap_send_http_trace(r)))
876                     ap_die(access_status, r);
877                 else
878                     ap_finalize_request_protocol(r);
879                 return OK;
880             }
881             case M_OPTIONS: {
882                 int access_status;
883                 r->proxyreq = PROXYREQ_NONE;
884                 if ((access_status = ap_send_http_options(r)))
885                     ap_die(access_status, r);
886                 else
887                     ap_finalize_request_protocol(r);
888                 return OK;
889             }
890             default: {
891                 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
892                                      "Max-Forwards has reached zero - proxy loop?");
893             }
894             }
895         }
896         maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0;
897     }
898     else {
899         /* set configured max-forwards */
900         maxfwd = conf->maxfwd;
901     }
902     if (maxfwd >= 0) {
903         apr_table_setn(r->headers_in, "Max-Forwards",
904                        apr_psprintf(r->pool, "%ld", maxfwd));
905     }
906
907     if (r->method_number == M_TRACE) {
908         core_server_config *coreconf = (core_server_config *)
909                                        ap_get_core_module_config(sconf);
910
911         if (coreconf->trace_enable == AP_TRACE_DISABLE)
912         {
913             /* Allow "error-notes" string to be printed by ap_send_error_response()
914              * Note; this goes nowhere, canned error response need an overhaul.
915              */
916             apr_table_setn(r->notes, "error-notes",
917                            "TRACE forbidden by server configuration");
918             apr_table_setn(r->notes, "verbose-error-to", "*");
919             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
920                           "TRACE forbidden by server configuration");
921             return HTTP_METHOD_NOT_ALLOWED;
922         }
923
924         /* Can't test ap_should_client_block, we aren't ready to send
925          * the client a 100 Continue response till the connection has
926          * been established
927          */
928         if (coreconf->trace_enable != AP_TRACE_EXTENDED
929             && (r->read_length || r->read_chunked || r->remaining))
930         {
931             /* Allow "error-notes" string to be printed by ap_send_error_response()
932              * Note; this goes nowhere, canned error response need an overhaul.
933              */
934             apr_table_setn(r->notes, "error-notes",
935                            "TRACE with request body is not allowed");
936             apr_table_setn(r->notes, "verbose-error-to", "*");
937             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
938                           "TRACE with request body is not allowed");
939             return HTTP_REQUEST_ENTITY_TOO_LARGE;
940         }
941     }
942
943     uri = r->filename + 6;
944     p = strchr(uri, ':');
945     if (p == NULL) {
946         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
947                       "proxy_handler no URL in %s", r->filename);
948         return HTTP_BAD_REQUEST;
949     }
950
951     /* If the host doesn't have a domain name, add one and redirect. */
952     if (conf->domain != NULL) {
953         rc = proxy_needsdomain(r, uri, conf->domain);
954         if (ap_is_HTTP_REDIRECT(rc))
955             return HTTP_MOVED_PERMANENTLY;
956     }
957
958     scheme = apr_pstrndup(r->pool, uri, p - uri);
959     /* Check URI's destination host against NoProxy hosts */
960     /* Bypass ProxyRemote server lookup if configured as NoProxy */
961     for (direct_connect = i = 0; i < conf->dirconn->nelts &&
962                                         !direct_connect; i++) {
963         direct_connect = list[i].matcher(&list[i], r);
964     }
965 #if DEBUGGING
966     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
967                 (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
968                 r->uri);
969 #endif
970
971     do {
972         char *url = uri;
973         /* Try to obtain the most suitable worker */
974         access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
975         if (access_status != OK) {
976             /*
977              * Only return if access_status is not HTTP_SERVICE_UNAVAILABLE
978              * This gives other modules the chance to hook into the
979              * request_status hook and decide what to do in this situation.
980              */
981             if (access_status != HTTP_SERVICE_UNAVAILABLE)
982                 return access_status;
983             /*
984              * Ensure that balancer is NULL if worker is NULL to prevent
985              * potential problems in the post_request hook.
986              */
987             if (!worker)
988                 balancer = NULL;
989             goto cleanup;
990         }
991
992         /* Initialise worker if needed, note the shared area must be initialized by the balancer logic */
993         if (balancer) {
994             ap_proxy_initialize_worker(worker, r->server, conf->pool);
995         }
996
997         if (balancer && balancer->s->max_attempts_set && !max_attempts)
998             max_attempts = balancer->s->max_attempts;
999         /* firstly, try a proxy, unless a NoProxy directive is active */
1000         if (!direct_connect) {
1001             for (i = 0; i < proxies->nelts; i++) {
1002                 p2 = ap_strchr_c(ents[i].scheme, ':');  /* is it a partial URL? */
1003                 if (strcmp(ents[i].scheme, "*") == 0 ||
1004                     (ents[i].use_regex &&
1005                      ap_regexec(ents[i].regexp, url, 0, NULL, 0) == 0) ||
1006                     (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
1007                     (p2 != NULL &&
1008                     strncasecmp(url, ents[i].scheme,
1009                                 strlen(ents[i].scheme)) == 0)) {
1010
1011                     /* handle the scheme */
1012                     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1013                                   "Trying to run scheme_handler against proxy");
1014                     access_status = proxy_run_scheme_handler(r, worker,
1015                                                              conf, url,
1016                                                              ents[i].hostname,
1017                                                              ents[i].port);
1018
1019                     /* Did the scheme handler process the request? */
1020                     if (access_status != DECLINED) {
1021                         const char *cl_a;
1022                         char *end;
1023                         apr_off_t cl;
1024
1025                         /*
1026                          * An fatal error or success, so no point in
1027                          * retrying with a direct connection.
1028                          */
1029                         if (access_status != HTTP_BAD_GATEWAY) {
1030                             goto cleanup;
1031                         }
1032                         cl_a = apr_table_get(r->headers_in, "Content-Length");
1033                         if (cl_a) {
1034                             apr_strtoff(&cl, cl_a, &end, 10);
1035                             /*
1036                              * The request body is of length > 0. We cannot
1037                              * retry with a direct connection since we already
1038                              * sent (parts of) the request body to the proxy
1039                              * and do not have any longer.
1040                              */
1041                             if (cl > 0) {
1042                                 goto cleanup;
1043                             }
1044                         }
1045                         /*
1046                          * Transfer-Encoding was set as input header, so we had
1047                          * a request body. We cannot retry with a direct
1048                          * connection for the same reason as above.
1049                          */
1050                         if (apr_table_get(r->headers_in, "Transfer-Encoding")) {
1051                             goto cleanup;
1052                         }
1053                     }
1054                 }
1055             }
1056         }
1057
1058         /* otherwise, try it direct */
1059         /* N.B. what if we're behind a firewall, where we must use a proxy or
1060         * give up??
1061         */
1062
1063         /* handle the scheme */
1064         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1065                       "Running scheme %s handler (attempt %d)",
1066                       scheme, attempts);
1067         AP_PROXY_RUN(r, worker, conf, url, attempts);
1068         access_status = proxy_run_scheme_handler(r, worker, conf,
1069                                                  url, NULL, 0);
1070         if (access_status == OK)
1071             break;
1072         else if (access_status == HTTP_INTERNAL_SERVER_ERROR) {
1073             /* Unrecoverable server error.
1074              * We can not failover to another worker.
1075              * Mark the worker as unusable if member of load balancer
1076              */
1077             if (balancer) {
1078                 worker->s->status |= PROXY_WORKER_IN_ERROR;
1079                 worker->s->error_time = apr_time_now();
1080             }
1081             break;
1082         }
1083         else if (access_status == HTTP_SERVICE_UNAVAILABLE) {
1084             /* Recoverable server error.
1085              * We can failover to another worker
1086              * Mark the worker as unusable if member of load balancer
1087              */
1088             if (balancer) {
1089                 worker->s->status |= PROXY_WORKER_IN_ERROR;
1090                 worker->s->error_time = apr_time_now();
1091             }
1092         }
1093         else {
1094             /* Unrecoverable error.
1095              * Return the origin status code to the client.
1096              */
1097             break;
1098         }
1099         /* Try again if the worker is unusable and the service is
1100          * unavailable.
1101          */
1102     } while (!PROXY_WORKER_IS_USABLE(worker) &&
1103              max_attempts > attempts++);
1104
1105     if (DECLINED == access_status) {
1106         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1107                       "No protocol handler was valid for the URL %s. "
1108                       "If you are using a DSO version of mod_proxy, make sure "
1109                       "the proxy submodules are included in the configuration "
1110                       "using LoadModule.", r->uri);
1111         access_status = HTTP_INTERNAL_SERVER_ERROR;
1112         goto cleanup;
1113     }
1114 cleanup:
1115     ap_proxy_post_request(worker, balancer, r, conf);
1116
1117     proxy_run_request_status(&access_status, r);
1118     AP_PROXY_RUN_FINISHED(r, attempts, access_status);
1119
1120     return access_status;
1121 }
1122
1123 /* -------------------------------------------------------------- */
1124 /* Setup configurable data */
1125
1126 static void * create_proxy_config(apr_pool_t *p, server_rec *s)
1127 {
1128     unsigned int id;
1129     proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
1130
1131     ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *));
1132     ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote));
1133     ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
1134     ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry));
1135     ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry));
1136     ps->workers = apr_array_make(p, 10, sizeof(proxy_worker));
1137     ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer));
1138     ps->forward = NULL;
1139     ps->reverse = NULL;
1140     ps->domain = NULL;
1141 #if 0
1142     id = ap_proxy_hashfunc(apr_psprintf(p, "%pp-%" APR_TIME_T_FMT, ps, apr_time_now()), PROXY_HASHFUNC_DEFAULT);
1143 #else
1144     id = ap_proxy_hashfunc(apr_psprintf(p, "%pp", ps), PROXY_HASHFUNC_DEFAULT);
1145 #endif
1146     ps->id = apr_psprintf(p, "s%x", id);
1147     ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
1148     ps->viaopt_set = 0; /* 0 means default */
1149     ps->req = 0;
1150     ps->max_balancers = 0;
1151     ps->bgrowth = 5;
1152     ps->bgrowth_set = 0;
1153     ps->req_set = 0;
1154     ps->recv_buffer_size = 0; /* this default was left unset for some reason */
1155     ps->recv_buffer_size_set = 0;
1156     ps->io_buffer_size = AP_IOBUFSIZE;
1157     ps->io_buffer_size_set = 0;
1158     ps->maxfwd = DEFAULT_MAX_FORWARDS;
1159     ps->maxfwd_set = 0;
1160     ps->timeout = 0;
1161     ps->timeout_set = 0;
1162     ps->badopt = bad_error;
1163     ps->badopt_set = 0;
1164     ps->source_address = NULL;
1165     ps->source_address_set = 0;
1166     ps->pool = p;
1167
1168     return ps;
1169 }
1170
1171 static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
1172 {
1173     proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
1174     proxy_server_conf *base = (proxy_server_conf *) basev;
1175     proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
1176
1177     ps->proxies = apr_array_append(p, base->proxies, overrides->proxies);
1178     ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
1179     ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
1180     ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
1181     ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
1182     ps->workers = apr_array_append(p, base->workers, overrides->workers);
1183     ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
1184     ps->forward = overrides->forward ? overrides->forward : base->forward;
1185     ps->reverse = overrides->reverse ? overrides->reverse : base->reverse;
1186
1187     ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
1188     ps->id = (overrides->id == NULL) ? base->id : overrides->id;
1189     ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
1190     ps->viaopt_set = overrides->viaopt_set || base->viaopt_set;
1191     ps->req = (overrides->req_set == 0) ? base->req : overrides->req;
1192     ps->req_set = overrides->req_set || base->req_set;
1193     ps->bgrowth = (overrides->bgrowth_set == 0) ? base->bgrowth : overrides->bgrowth;
1194     ps->bgrowth_set = overrides->bgrowth_set || base->bgrowth_set;
1195     ps->max_balancers = overrides->max_balancers || base->max_balancers;
1196     ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size;
1197     ps->recv_buffer_size_set = overrides->recv_buffer_size_set || base->recv_buffer_size_set;
1198     ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size;
1199     ps->io_buffer_size_set = overrides->io_buffer_size_set || base->io_buffer_size_set;
1200     ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd;
1201     ps->maxfwd_set = overrides->maxfwd_set || base->maxfwd_set;
1202     ps->timeout = (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
1203     ps->timeout_set = overrides->timeout_set || base->timeout_set;
1204     ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
1205     ps->badopt_set = overrides->badopt_set || base->badopt_set;
1206     ps->proxy_status = (overrides->proxy_status_set == 0) ? base->proxy_status : overrides->proxy_status;
1207     ps->proxy_status_set = overrides->proxy_status_set || base->proxy_status_set;
1208     ps->source_address = (overrides->source_address_set == 0) ? base->source_address : overrides->source_address;
1209     ps->source_address_set = overrides->source_address_set || base->source_address_set;
1210     ps->pool = p;
1211     return ps;
1212 }
1213 static const char *set_source_address(cmd_parms *parms, void *dummy,
1214                                       const char *arg)
1215 {
1216     proxy_server_conf *psf =
1217         ap_get_module_config(parms->server->module_config, &proxy_module);
1218     struct apr_sockaddr_t *addr;
1219
1220     if (APR_SUCCESS == apr_sockaddr_info_get(&addr, arg, APR_UNSPEC, 0, 0,
1221                                              psf->pool)) {
1222         psf->source_address = addr;
1223         psf->source_address_set = 1;
1224     }
1225     else {
1226         return "ProxySourceAddress invalid value";
1227     }
1228
1229     return NULL;
1230 }
1231
1232 static void *create_proxy_dir_config(apr_pool_t *p, char *dummy)
1233 {
1234     proxy_dir_conf *new =
1235         (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
1236
1237     /* Filled in by proxysection, when applicable */
1238
1239     /* Put these in the dir config so they work inside <Location> */
1240     new->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
1241     new->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias));
1242     new->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias));
1243     new->preserve_host_set = 0;
1244     new->preserve_host = 0;
1245     new->interpolate_env = -1; /* unset */
1246     new->error_override = 0;
1247     new->error_override_set = 0;
1248     new->add_forwarded_headers = 1;
1249
1250     return (void *) new;
1251 }
1252
1253 static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
1254 {
1255     proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
1256     proxy_dir_conf *add = (proxy_dir_conf *) addv;
1257     proxy_dir_conf *base = (proxy_dir_conf *) basev;
1258
1259     new->p = add->p;
1260     new->p_is_fnmatch = add->p_is_fnmatch;
1261     new->r = add->r;
1262
1263     /* Put these in the dir config so they work inside <Location> */
1264     new->raliases = apr_array_append(p, base->raliases, add->raliases);
1265     new->cookie_paths
1266         = apr_array_append(p, base->cookie_paths, add->cookie_paths);
1267     new->cookie_domains
1268         = apr_array_append(p, base->cookie_domains, add->cookie_domains);
1269     new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env
1270                                                         : add->interpolate_env;
1271     new->preserve_host = (add->preserve_host_set == 0) ? base->preserve_host
1272                                                         : add->preserve_host;
1273     new->preserve_host_set = add->preserve_host_set || base->preserve_host_set;
1274     new->error_override = (add->error_override_set == 0) ? base->error_override
1275                                                         : add->error_override;
1276     new->error_override_set = add->error_override_set || base->error_override_set;
1277     new->alias = (add->alias_set == 0) ? base->alias : add->alias;
1278     new->alias_set = add->alias_set || base->alias_set;
1279     new->add_forwarded_headers = add->add_forwarded_headers;
1280     return new;
1281 }
1282
1283
1284 static const char *
1285     add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex)
1286 {
1287     server_rec *s = cmd->server;
1288     proxy_server_conf *conf =
1289     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
1290     struct proxy_remote *new;
1291     char *p, *q;
1292     char *r, *f, *scheme;
1293     ap_regex_t *reg = NULL;
1294     int port;
1295
1296     r = apr_pstrdup(cmd->pool, r1);
1297     scheme = apr_pstrdup(cmd->pool, r1);
1298     f = apr_pstrdup(cmd->pool, f1);
1299     p = strchr(r, ':');
1300     if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') {
1301         if (regex)
1302             return "ProxyRemoteMatch: Bad syntax for a remote proxy server";
1303         else
1304             return "ProxyRemote: Bad syntax for a remote proxy server";
1305     }
1306     else {
1307         scheme[p-r] = 0;
1308     }
1309     q = strchr(p + 3, ':');
1310     if (q != NULL) {
1311         if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
1312             if (regex)
1313                 return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)";
1314             else
1315                 return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
1316         }
1317         *q = '\0';
1318     }
1319     else
1320         port = -1;
1321     *p = '\0';
1322     if (regex) {
1323         reg = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED);
1324         if (!reg)
1325             return "Regular expression for ProxyRemoteMatch could not be compiled.";
1326     }
1327     else
1328         if (strchr(f, ':') == NULL)
1329             ap_str_tolower(f);      /* lowercase scheme */
1330     ap_str_tolower(p + 3);      /* lowercase hostname */
1331
1332     if (port == -1) {
1333         port = apr_uri_port_of_scheme(scheme);
1334     }
1335
1336     new = apr_array_push(conf->proxies);
1337     new->scheme = f;
1338     new->protocol = r;
1339     new->hostname = p + 3;
1340     new->port = port;
1341     new->regexp = reg;
1342     new->use_regex = regex;
1343     return NULL;
1344 }
1345
1346 static const char *
1347     add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
1348 {
1349     return add_proxy(cmd, dummy, f1, r1, 0);
1350 }
1351
1352 static const char *
1353     add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
1354 {
1355     return add_proxy(cmd, dummy, f1, r1, 1);
1356 }
1357
1358 static const char *
1359     add_pass(cmd_parms *cmd, void *dummy, const char *arg, int is_regex)
1360 {
1361     proxy_dir_conf *dconf = (proxy_dir_conf *)dummy;
1362     server_rec *s = cmd->server;
1363     proxy_server_conf *conf =
1364     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
1365     struct proxy_alias *new;
1366     char *f = cmd->path;
1367     char *r = NULL;
1368     char *word;
1369     apr_table_t *params = apr_table_make(cmd->pool, 5);
1370     const apr_array_header_t *arr;
1371     const apr_table_entry_t *elts;
1372     int i;
1373     int use_regex = is_regex;
1374     unsigned int flags = 0;
1375     const char *err;
1376
1377     err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES);
1378     if (err) {
1379         return err;
1380     }
1381
1382     while (*arg) {
1383         word = ap_getword_conf(cmd->pool, &arg);
1384         if (!f) {
1385             if (!strcmp(word, "~")) {
1386                 if (is_regex) {
1387                     return "ProxyPassMatch invalid syntax ('~' usage).";
1388                 }
1389                 use_regex = 1;
1390                 continue;
1391             }
1392             f = word;
1393         }
1394         else if (!r) {
1395             r = word;
1396         }
1397         else if (!strcasecmp(word,"nocanon")) {
1398             flags |= PROXYPASS_NOCANON;
1399         }
1400         else if (!strcasecmp(word,"interpolate")) {
1401             flags |= PROXYPASS_INTERPOLATE;
1402         }
1403         else {
1404             char *val = strchr(word, '=');
1405             if (!val) {
1406                 if (cmd->path) {
1407                     if (*r == '/') {
1408                         return "ProxyPass|ProxyPassMatch can not have a path when defined in "
1409                                "a location.";
1410                     }
1411                     else {
1412                         return "Invalid ProxyPass|ProxyPassMatch parameter. Parameter must "
1413                                "be in the form 'key=value'.";
1414                     }
1415                 }
1416                 else {
1417                     return "Invalid ProxyPass|ProxyPassMatch parameter. Parameter must be "
1418                            "in the form 'key=value'.";
1419                 }
1420             }
1421             else
1422                 *val++ = '\0';
1423             apr_table_setn(params, word, val);
1424         }
1425     };
1426
1427     if (r == NULL) {
1428         return "ProxyPass|ProxyPassMatch needs a path when not defined in a location";
1429     }
1430
1431     /* if per directory, save away the single alias */
1432     if (cmd->path) {
1433         dconf->alias = apr_pcalloc(cmd->pool, sizeof(struct proxy_alias));
1434         dconf->alias_set = 1;
1435         new = dconf->alias;
1436         if (apr_fnmatch_test(f)) {
1437             use_regex = 1;
1438         }
1439     }
1440     /* if per server, add to the alias array */
1441     else {
1442         new = apr_array_push(conf->aliases);
1443     }
1444
1445     new->fake = apr_pstrdup(cmd->pool, f);
1446     new->real = apr_pstrdup(cmd->pool, r);
1447     new->flags = flags;
1448     if (use_regex) {
1449         new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED);
1450         if (new->regex == NULL)
1451             return "Regular expression could not be compiled.";
1452     }
1453     else {
1454         new->regex = NULL;
1455     }
1456
1457     if (r[0] == '!' && r[1] == '\0')
1458         return NULL;
1459
1460     arr = apr_table_elts(params);
1461     elts = (const apr_table_entry_t *)arr->elts;
1462     /* Distinguish the balancer from worker */
1463     if (ap_proxy_valid_balancer_name(r, 9)) {
1464         proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r, 0);
1465         if (!balancer) {
1466             const char *err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, r, f, 0);
1467             if (err)
1468                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1469         }
1470         else {
1471             ap_proxy_update_balancer(cmd->pool, balancer, f);
1472         }
1473         for (i = 0; i < arr->nelts; i++) {
1474             const char *err = set_balancer_param(conf, cmd->pool, balancer, elts[i].key,
1475                                                  elts[i].val);
1476             if (err)
1477                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1478         }
1479         new->balancer = balancer;
1480     }
1481     else {
1482         proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, r);
1483         int reuse = 0;
1484         if (!worker) {
1485             const char *err = ap_proxy_define_worker(cmd->pool, &worker, NULL, conf, r, 0);
1486             if (err)
1487                 return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1488
1489             PROXY_COPY_CONF_PARAMS(worker, conf);
1490         } else {
1491             reuse = 1;
1492             ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server,
1493                          "Sharing worker '%s' instead of creating new worker '%s'",
1494                          worker->s->name, new->real);
1495         }
1496
1497         for (i = 0; i < arr->nelts; i++) {
1498             if (reuse) {
1499                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
1500                              "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing",
1501                              elts[i].key, elts[i].val, worker->s->name);
1502             } else {
1503                 const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
1504                                                    elts[i].val);
1505                 if (err)
1506                     return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1507             }
1508         }
1509     }
1510     return NULL;
1511 }
1512
1513 static const char *
1514     add_pass_noregex(cmd_parms *cmd, void *dummy, const char *arg)
1515 {
1516     return add_pass(cmd, dummy, arg, 0);
1517 }
1518
1519 static const char *
1520     add_pass_regex(cmd_parms *cmd, void *dummy, const char *arg)
1521 {
1522     return add_pass(cmd, dummy, arg, 1);
1523 }
1524
1525
1526 static const char * add_pass_reverse(cmd_parms *cmd, void *dconf, const char *f,
1527                                      const char *r, const char *i)
1528 {
1529     proxy_dir_conf *conf = dconf;
1530     struct proxy_alias *new;
1531     const char *fake;
1532     const char *real;
1533     const char *interp;
1534     const char *err;
1535
1536     err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES);
1537     if (err) {
1538         return err;
1539     }
1540
1541     if (cmd->path == NULL) {
1542         if (r == NULL || !strcasecmp(r, "interpolate")) {
1543             return "ProxyPassReverse needs a path when not defined in a location";
1544         }
1545         fake = f;
1546         real = r;
1547         interp = i;
1548     }
1549     else {
1550         if (r && strcasecmp(r, "interpolate")) {
1551             return "ProxyPassReverse can not have a path when defined in a location";
1552         }
1553         fake = cmd->path;
1554         real = f;
1555         interp = r;
1556     }
1557
1558     new = apr_array_push(conf->raliases);
1559     new->fake = fake;
1560     new->real = real;
1561     new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
1562
1563     return NULL;
1564 }
1565 static const char* cookie_path(cmd_parms *cmd, void *dconf, const char *f,
1566                                const char *r, const char *interp)
1567 {
1568     proxy_dir_conf *conf = dconf;
1569     struct proxy_alias *new;
1570
1571     new = apr_array_push(conf->cookie_paths);
1572     new->fake = f;
1573     new->real = r;
1574     new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
1575
1576     return NULL;
1577 }
1578 static const char* cookie_domain(cmd_parms *cmd, void *dconf, const char *f,
1579                                  const char *r, const char *interp)
1580 {
1581     proxy_dir_conf *conf = dconf;
1582     struct proxy_alias *new;
1583
1584     new = apr_array_push(conf->cookie_domains);
1585     new->fake = f;
1586     new->real = r;
1587     new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
1588     return NULL;
1589 }
1590
1591 static const char *
1592     set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg)
1593 {
1594     server_rec *s = parms->server;
1595     proxy_server_conf *conf =
1596     ap_get_module_config(s->module_config, &proxy_module);
1597     struct noproxy_entry *new;
1598     struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
1599     struct apr_sockaddr_t *addr;
1600     int found = 0;
1601     int i;
1602
1603     /* Don't duplicate entries */
1604     for (i = 0; i < conf->noproxies->nelts; i++) {
1605         if (strcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */
1606             found = 1;
1607         }
1608     }
1609
1610     if (!found) {
1611         new = apr_array_push(conf->noproxies);
1612         new->name = arg;
1613         if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) {
1614             new->addr = addr;
1615         }
1616         else {
1617             new->addr = NULL;
1618         }
1619     }
1620     return NULL;
1621 }
1622
1623
1624 /* Similar to set_proxy_exclude(), but defining directly connected hosts,
1625  * which should never be accessed via the configured ProxyRemote servers
1626  */
1627 static const char *
1628     set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg)
1629 {
1630     server_rec *s = parms->server;
1631     proxy_server_conf *conf =
1632     ap_get_module_config(s->module_config, &proxy_module);
1633     struct dirconn_entry *New;
1634     struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
1635     int found = 0;
1636     int i;
1637
1638     /* Don't duplicate entries */
1639     for (i = 0; i < conf->dirconn->nelts; i++) {
1640         if (strcasecmp(arg, list[i].name) == 0)
1641             found = 1;
1642     }
1643
1644     if (!found) {
1645         New = apr_array_push(conf->dirconn);
1646         New->name = apr_pstrdup(parms->pool, arg);
1647         New->hostaddr = NULL;
1648
1649     if (ap_proxy_is_ipaddr(New, parms->pool)) {
1650 #if DEBUGGING
1651         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1652                      "Parsed addr %s", inet_ntoa(New->addr));
1653         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1654                      "Parsed mask %s", inet_ntoa(New->mask));
1655 #endif
1656     }
1657     else if (ap_proxy_is_domainname(New, parms->pool)) {
1658         ap_str_tolower(New->name);
1659 #if DEBUGGING
1660         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1661                      "Parsed domain %s", New->name);
1662 #endif
1663         }
1664         else if (ap_proxy_is_hostname(New, parms->pool)) {
1665             ap_str_tolower(New->name);
1666 #if DEBUGGING
1667             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1668                          "Parsed host %s", New->name);
1669 #endif
1670         }
1671         else {
1672             ap_proxy_is_word(New, parms->pool);
1673 #if DEBUGGING
1674             fprintf(stderr, "Parsed word %s\n", New->name);
1675 #endif
1676         }
1677     }
1678     return NULL;
1679 }
1680
1681 static const char *
1682     set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg)
1683 {
1684     proxy_server_conf *psf =
1685     ap_get_module_config(parms->server->module_config, &proxy_module);
1686
1687     if (arg[0] != '.')
1688         return "ProxyDomain: domain name must start with a dot.";
1689
1690     psf->domain = arg;
1691     return NULL;
1692 }
1693
1694 static const char *
1695     set_proxy_req(cmd_parms *parms, void *dummy, int flag)
1696 {
1697     proxy_server_conf *psf =
1698     ap_get_module_config(parms->server->module_config, &proxy_module);
1699
1700     psf->req = flag;
1701     psf->req_set = 1;
1702     return NULL;
1703 }
1704
1705 static const char *
1706     set_proxy_error_override(cmd_parms *parms, void *dconf, int flag)
1707 {
1708     proxy_dir_conf *conf = dconf;
1709
1710     conf->error_override = flag;
1711     conf->error_override_set = 1;
1712     return NULL;
1713 }
1714 static const char *
1715    add_proxy_http_headers(cmd_parms *parms, void *dconf, int flag)
1716 {
1717    proxy_dir_conf *conf = dconf;
1718    conf->add_forwarded_headers = flag;
1719    return NULL;
1720 }
1721 static const char *
1722     set_preserve_host(cmd_parms *parms, void *dconf, int flag)
1723 {
1724     proxy_dir_conf *conf = dconf;
1725
1726     conf->preserve_host = flag;
1727     conf->preserve_host_set = 1;
1728     return NULL;
1729 }
1730
1731 static const char *
1732     set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1733 {
1734     proxy_server_conf *psf =
1735     ap_get_module_config(parms->server->module_config, &proxy_module);
1736     int s = atoi(arg);
1737     if (s < 512 && s != 0) {
1738         return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
1739     }
1740
1741     psf->recv_buffer_size = s;
1742     psf->recv_buffer_size_set = 1;
1743     return NULL;
1744 }
1745
1746 static const char *
1747     set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1748 {
1749     proxy_server_conf *psf =
1750     ap_get_module_config(parms->server->module_config, &proxy_module);
1751     long s = atol(arg);
1752     if (s < 512 && s) {
1753         return "ProxyIOBufferSize must be >= 512 bytes, or 0 for system default.";
1754     }
1755     psf->io_buffer_size = (s ? s : AP_IOBUFSIZE);
1756     psf->io_buffer_size_set = 1;
1757     return NULL;
1758 }
1759
1760 static const char *
1761     set_max_forwards(cmd_parms *parms, void *dummy, const char *arg)
1762 {
1763     proxy_server_conf *psf =
1764     ap_get_module_config(parms->server->module_config, &proxy_module);
1765     long s = atol(arg);
1766
1767     psf->maxfwd = s;
1768     psf->maxfwd_set = 1;
1769     return NULL;
1770 }
1771 static const char*
1772     set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg)
1773 {
1774     proxy_server_conf *psf =
1775     ap_get_module_config(parms->server->module_config, &proxy_module);
1776     int timeout;
1777
1778     timeout = atoi(arg);
1779     if (timeout<1) {
1780         return "Proxy Timeout must be at least 1 second.";
1781     }
1782     psf->timeout_set = 1;
1783     psf->timeout = apr_time_from_sec(timeout);
1784
1785     return NULL;
1786 }
1787
1788 static const char*
1789     set_via_opt(cmd_parms *parms, void *dummy, const char *arg)
1790 {
1791     proxy_server_conf *psf =
1792     ap_get_module_config(parms->server->module_config, &proxy_module);
1793
1794     if (strcasecmp(arg, "Off") == 0)
1795         psf->viaopt = via_off;
1796     else if (strcasecmp(arg, "On") == 0)
1797         psf->viaopt = via_on;
1798     else if (strcasecmp(arg, "Block") == 0)
1799         psf->viaopt = via_block;
1800     else if (strcasecmp(arg, "Full") == 0)
1801         psf->viaopt = via_full;
1802     else {
1803         return "ProxyVia must be one of: "
1804             "off | on | full | block";
1805     }
1806
1807     psf->viaopt_set = 1;
1808     return NULL;
1809 }
1810
1811 static const char*
1812     set_bad_opt(cmd_parms *parms, void *dummy, const char *arg)
1813 {
1814     proxy_server_conf *psf =
1815     ap_get_module_config(parms->server->module_config, &proxy_module);
1816
1817     if (strcasecmp(arg, "IsError") == 0)
1818         psf->badopt = bad_error;
1819     else if (strcasecmp(arg, "Ignore") == 0)
1820         psf->badopt = bad_ignore;
1821     else if (strcasecmp(arg, "StartBody") == 0)
1822         psf->badopt = bad_body;
1823     else {
1824         return "ProxyBadHeader must be one of: "
1825             "IsError | Ignore | StartBody";
1826     }
1827
1828     psf->badopt_set = 1;
1829     return NULL;
1830 }
1831
1832 static const char*
1833     set_status_opt(cmd_parms *parms, void *dummy, const char *arg)
1834 {
1835     proxy_server_conf *psf =
1836     ap_get_module_config(parms->server->module_config, &proxy_module);
1837
1838     if (strcasecmp(arg, "Off") == 0)
1839         psf->proxy_status = status_off;
1840     else if (strcasecmp(arg, "On") == 0)
1841         psf->proxy_status = status_on;
1842     else if (strcasecmp(arg, "Full") == 0)
1843         psf->proxy_status = status_full;
1844     else {
1845         return "ProxyStatus must be one of: "
1846             "off | on | full";
1847     }
1848
1849     psf->proxy_status_set = 1;
1850     return NULL;
1851 }
1852
1853 static const char *set_bgrowth(cmd_parms *parms, void *dummy, const char *arg)
1854 {
1855     proxy_server_conf *psf =
1856     ap_get_module_config(parms->server->module_config, &proxy_module);
1857
1858     int growth = atoi(arg);
1859     if (growth < 0 || growth > 1000) {
1860         return "BalancerGrowth must be between 0 and 1000";
1861     }
1862     psf->bgrowth = growth;
1863     psf->bgrowth_set = 1;
1864
1865     return NULL;
1866 }
1867
1868 static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
1869 {
1870     server_rec *s = cmd->server;
1871     proxy_server_conf *conf =
1872     ap_get_module_config(s->module_config, &proxy_module);
1873     proxy_balancer *balancer;
1874     proxy_worker *worker;
1875     char *path = cmd->path;
1876     char *name = NULL;
1877     char *word;
1878     apr_table_t *params = apr_table_make(cmd->pool, 5);
1879     const apr_array_header_t *arr;
1880     const apr_table_entry_t *elts;
1881     int reuse = 0;
1882     int i;
1883     /* XXX: Should this be NOT_IN_DIRECTORY|NOT_IN_FILES? */
1884     const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
1885     if (err)
1886         return err;
1887
1888     if (cmd->path)
1889         path = apr_pstrdup(cmd->pool, cmd->path);
1890
1891     while (*arg) {
1892         char *val;
1893         word = ap_getword_conf(cmd->pool, &arg);
1894         val = strchr(word, '=');
1895
1896         if (!val) {
1897             if (!path)
1898                 path = word;
1899             else if (!name)
1900                 name = word;
1901             else {
1902                 if (cmd->path)
1903                     return "BalancerMember can not have a balancer name when defined in a location";
1904                 else
1905                     return "Invalid BalancerMember parameter. Parameter must "
1906                            "be in the form 'key=value'";
1907             }
1908         } else {
1909             *val++ = '\0';
1910             apr_table_setn(params, word, val);
1911         }
1912     }
1913     if (!path)
1914         return "BalancerMember must define balancer name when outside <Proxy > section";
1915     if (!name)
1916         return "BalancerMember must define remote proxy server";
1917
1918     ap_str_tolower(path);   /* lowercase scheme://hostname */
1919
1920     /* Try to find the balancer */
1921     balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, path, 0);
1922     if (!balancer) {
1923         err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, path, "/", 0);
1924         if (err)
1925             return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
1926     }
1927
1928     /* Try to find existing worker */
1929     worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, name);
1930     if (!worker) {
1931         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1932                      "Defining worker '%s' for balancer '%s'",
1933                      name, balancer->s->name);
1934         if ((err = ap_proxy_define_worker(cmd->pool, &worker, balancer, conf, name, 0)) != NULL)
1935             return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
1936         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1937                      "Defined worker '%s' for balancer '%s'",
1938                      worker->s->name, balancer->s->name);
1939         PROXY_COPY_CONF_PARAMS(worker, conf);
1940     } else {
1941         reuse = 1;
1942         ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server,
1943                      "Sharing worker '%s' instead of creating new worker '%s'",
1944                      worker->s->name, name);
1945     }
1946
1947     arr = apr_table_elts(params);
1948     elts = (const apr_table_entry_t *)arr->elts;
1949     for (i = 0; i < arr->nelts; i++) {
1950         if (reuse) {
1951             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
1952                          "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing",
1953                          elts[i].key, elts[i].val, worker->s->name);
1954         } else {
1955             err = set_worker_param(cmd->pool, worker, elts[i].key,
1956                                                elts[i].val);
1957             if (err)
1958                 return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
1959         }
1960     }
1961
1962     return NULL;
1963 }
1964
1965 static const char *
1966     set_proxy_param(cmd_parms *cmd, void *dummy, const char *arg)
1967 {
1968     server_rec *s = cmd->server;
1969     proxy_server_conf *conf =
1970     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
1971     char *name = NULL;
1972     char *word, *val;
1973     proxy_balancer *balancer = NULL;
1974     proxy_worker *worker = NULL;
1975     int in_proxy_section = 0;
1976     /* XXX: Should this be NOT_IN_DIRECTORY|NOT_IN_FILES? */
1977     const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
1978     if (err)
1979         return err;
1980
1981     if (cmd->directive->parent &&
1982         strncasecmp(cmd->directive->parent->directive,
1983                     "<Proxy", 6) == 0) {
1984         const char *pargs = cmd->directive->parent->args;
1985         /* Directive inside <Proxy section
1986          * Parent directive arg is the worker/balancer name.
1987          */
1988         name = ap_getword_conf(cmd->temp_pool, &pargs);
1989         if ((word = ap_strchr(name, '>')))
1990             *word = '\0';
1991         in_proxy_section = 1;
1992     }
1993     else {
1994         /* Standard set directive with worker/balancer
1995          * name as first param.
1996          */
1997         name = ap_getword_conf(cmd->temp_pool, &arg);
1998     }
1999
2000     if (ap_proxy_valid_balancer_name(name, 9)) {
2001         balancer = ap_proxy_get_balancer(cmd->pool, conf, name, 0);
2002         if (!balancer) {
2003             if (in_proxy_section) {
2004                 err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, name, "/", 0);
2005                 if (err)
2006                     return apr_pstrcat(cmd->temp_pool, "ProxySet ",
2007                                        err, NULL);
2008             }
2009             else
2010                 return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
2011                                    name, "' Balancer.", NULL);
2012         }
2013     }
2014     else {
2015         worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, name);
2016         if (!worker) {
2017             if (in_proxy_section) {
2018                 err = ap_proxy_define_worker(cmd->pool, &worker, NULL,
2019                                              conf, name, 0);
2020                 if (err)
2021                     return apr_pstrcat(cmd->temp_pool, "ProxySet ",
2022                                        err, NULL);
2023             }
2024             else
2025                 return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
2026                                    name, "' Worker.", NULL);
2027         }
2028     }
2029
2030     while (*arg) {
2031         word = ap_getword_conf(cmd->pool, &arg);
2032         val = strchr(word, '=');
2033         if (!val) {
2034             return "Invalid ProxySet parameter. Parameter must be "
2035                    "in the form 'key=value'";
2036         }
2037         else
2038             *val++ = '\0';
2039         if (worker)
2040             err = set_worker_param(cmd->pool, worker, word, val);
2041         else
2042             err = set_balancer_param(conf, cmd->pool, balancer, word, val);
2043
2044         if (err)
2045             return apr_pstrcat(cmd->temp_pool, "ProxySet: ", err, " ", word, "=", val, "; ", name, NULL);
2046     }
2047
2048     return NULL;
2049 }
2050
2051 static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config)
2052 {
2053     proxy_server_conf *sconf = ap_get_module_config(s->module_config,
2054                                                     &proxy_module);
2055     void **new_space = (void **)apr_array_push(sconf->sec_proxy);
2056
2057     *new_space = dir_config;
2058 }
2059
2060 static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
2061 {
2062     const char *errmsg;
2063     const char *endp = ap_strrchr_c(arg, '>');
2064     int old_overrides = cmd->override;
2065     char *old_path = cmd->path;
2066     proxy_dir_conf *conf;
2067     ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
2068     ap_regex_t *r = NULL;
2069     const command_rec *thiscmd = cmd->cmd;
2070     char *word, *val;
2071     proxy_balancer *balancer = NULL;
2072     proxy_worker *worker = NULL;
2073
2074     const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
2075     proxy_server_conf *sconf =
2076     (proxy_server_conf *) ap_get_module_config(cmd->server->module_config, &proxy_module);
2077
2078     if (err != NULL) {
2079         return err;
2080     }
2081
2082     if (endp == NULL) {
2083         return apr_pstrcat(cmd->pool, cmd->cmd->name,
2084                            "> directive missing closing '>'", NULL);
2085     }
2086
2087     arg = apr_pstrndup(cmd->pool, arg, endp-arg);
2088
2089     if (!arg) {
2090         if (thiscmd->cmd_data)
2091             return "<ProxyMatch > block must specify a path";
2092         else
2093             return "<Proxy > block must specify a path";
2094     }
2095
2096     cmd->path = ap_getword_conf(cmd->pool, &arg);
2097     cmd->override = OR_ALL|ACCESS_CONF;
2098
2099     if (!strncasecmp(cmd->path, "proxy:", 6))
2100         cmd->path += 6;
2101
2102     /* XXX Ignore case?  What if we proxy a case-insensitive server?!?
2103      * While we are at it, shouldn't we also canonicalize the entire
2104      * scheme?  See proxy_fixup()
2105      */
2106     if (thiscmd->cmd_data) { /* <ProxyMatch> */
2107         r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED);
2108         if (!r) {
2109             return "Regex could not be compiled";
2110         }
2111     }
2112     else if (!strcmp(cmd->path, "~")) {
2113         cmd->path = ap_getword_conf(cmd->pool, &arg);
2114         if (!cmd->path)
2115             return "<Proxy ~ > block must specify a path";
2116         if (strncasecmp(cmd->path, "proxy:", 6))
2117             cmd->path += 6;
2118         r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED);
2119         if (!r) {
2120             return "Regex could not be compiled";
2121         }
2122     }
2123
2124     /* initialize our config and fetch it */
2125     conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
2126                                  &proxy_module, cmd->pool);
2127
2128     errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
2129     if (errmsg != NULL)
2130         return errmsg;
2131
2132     conf->r = r;
2133     conf->p = cmd->path;
2134     conf->p_is_fnmatch = apr_fnmatch_test(conf->p);
2135
2136     ap_add_per_proxy_conf(cmd->server, new_dir_conf);
2137
2138     if (*arg != '\0') {
2139         if (thiscmd->cmd_data)
2140             return "Multiple <ProxyMatch> arguments not (yet) supported.";
2141         if (conf->p_is_fnmatch)
2142             return apr_pstrcat(cmd->pool, thiscmd->name,
2143                                "> arguments are not supported for wildchar url.",
2144                                NULL);
2145         if (!ap_strchr_c(conf->p, ':'))
2146             return apr_pstrcat(cmd->pool, thiscmd->name,
2147                                "> arguments are not supported for non url.",
2148                                NULL);
2149         if (ap_proxy_valid_balancer_name((char *)conf->p, 9)) {
2150             balancer = ap_proxy_get_balancer(cmd->pool, sconf, conf->p, 0);
2151             if (!balancer) {
2152                 err = ap_proxy_define_balancer(cmd->pool, &balancer,
2153                                                sconf, conf->p, "/", 0);
2154                 if (err)
2155                     return apr_pstrcat(cmd->temp_pool, thiscmd->name,
2156                                        " ", err, NULL);
2157             }
2158         }
2159         else {
2160             worker = ap_proxy_get_worker(cmd->temp_pool, NULL, sconf,
2161                                          conf->p);
2162             if (!worker) {
2163                 err = ap_proxy_define_worker(cmd->pool, &worker, NULL,
2164                                           sconf, conf->p, 0);
2165                 if (err)
2166                     return apr_pstrcat(cmd->temp_pool, thiscmd->name,
2167                                        " ", err, NULL);
2168             }
2169         }
2170         if (worker == NULL && balancer == NULL) {
2171             return apr_pstrcat(cmd->pool, thiscmd->name,
2172                                "> arguments are supported only for workers.",
2173                                NULL);
2174         }
2175         while (*arg) {
2176             word = ap_getword_conf(cmd->pool, &arg);
2177             val = strchr(word, '=');
2178             if (!val) {
2179                 return "Invalid Proxy parameter. Parameter must be "
2180                        "in the form 'key=value'";
2181             }
2182             else
2183                 *val++ = '\0';
2184             if (worker)
2185                 err = set_worker_param(cmd->pool, worker, word, val);
2186             else
2187                 err = set_balancer_param(sconf, cmd->pool, balancer,
2188                                          word, val);
2189             if (err)
2190                 return apr_pstrcat(cmd->temp_pool, thiscmd->name, " ", err, " ",
2191                                    word, "=", val, "; ", conf->p, NULL);
2192         }
2193     }
2194
2195     cmd->path = old_path;
2196     cmd->override = old_overrides;
2197
2198     return NULL;
2199 }
2200
2201 static const command_rec proxy_cmds[] =
2202 {
2203     AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF,
2204     "Container for directives affecting resources located in the proxied "
2205     "location"),
2206     AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF,
2207     "Container for directives affecting resources located in the proxied "
2208     "location, in regular expression syntax"),
2209     AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF,
2210      "on if the true proxy requests should be accepted"),
2211     AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF,
2212      "a scheme, partial URL or '*' and a proxy server"),
2213     AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
2214      "a regex pattern and a proxy server"),
2215     AP_INIT_FLAG("ProxyPassInterpolateEnv", ap_set_flag_slot_char,
2216         (void*)APR_OFFSETOF(proxy_dir_conf, interpolate_env),
2217         RSRC_CONF|ACCESS_CONF, "Interpolate Env Vars in reverse Proxy") ,
2218     AP_INIT_RAW_ARGS("ProxyPass", add_pass_noregex, NULL, RSRC_CONF|ACCESS_CONF,
2219      "a virtual path and a URL"),
2220     AP_INIT_RAW_ARGS("ProxyPassMatch", add_pass_regex, NULL, RSRC_CONF|ACCESS_CONF,
2221      "a virtual path and a URL"),
2222     AP_INIT_TAKE123("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
2223      "a virtual path and a URL for reverse proxy behaviour"),
2224     AP_INIT_TAKE23("ProxyPassReverseCookiePath", cookie_path, NULL,
2225        RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
2226     AP_INIT_TAKE23("ProxyPassReverseCookieDomain", cookie_domain, NULL,
2227        RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
2228     AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
2229      "A list of names, hosts or domains to which the proxy will not connect"),
2230     AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF,
2231      "Receive buffer size for outgoing HTTP and FTP connections in bytes"),
2232     AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF,
2233      "IO buffer size for outgoing HTTP and FTP connections in bytes"),
2234     AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF,
2235      "The maximum number of proxies a request may be forwarded through."),
2236     AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF,
2237      "A list of domains, hosts, or subnets to which the proxy will connect directly"),
2238     AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF,
2239      "The default intranet domain name (in absence of a domain in the URL)"),
2240     AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF,
2241      "Configure Via: proxy header header to one of: on | off | block | full"),
2242     AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF|ACCESS_CONF,
2243      "use our error handling pages instead of the servers' we are proxying"),
2244     AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF|ACCESS_CONF,
2245      "on if we should preserve host header while proxying"),
2246     AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF,
2247      "Set the timeout (in seconds) for a proxied connection. "
2248      "This overrides the server timeout"),
2249     AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF,
2250      "How to handle bad header line in response: IsError | Ignore | StartBody"),
2251     AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF,
2252      "A balancer name and scheme with list of params"),
2253     AP_INIT_TAKE1("BalancerGrowth", set_bgrowth, NULL, RSRC_CONF,
2254      "Number of additional Balancers that can be added post-config"),
2255     AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF,
2256      "Configure Status: proxy status to one of: on | off | full"),
2257     AP_INIT_RAW_ARGS("ProxySet", set_proxy_param, NULL, RSRC_CONF|ACCESS_CONF,
2258      "A balancer or worker name with list of params"),
2259     AP_INIT_TAKE1("ProxySourceAddress", set_source_address, NULL, RSRC_CONF,
2260      "Configure local source IP used for request forward"),
2261     AP_INIT_FLAG("ProxyAddHeaders", add_proxy_http_headers, NULL, RSRC_CONF|ACCESS_CONF,
2262      "on if X-Forwarded-* headers should be added or completed"),
2263     {NULL}
2264 };
2265
2266 static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL;
2267 static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL;
2268 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *proxy_is_https = NULL;
2269 static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *proxy_ssl_val = NULL;
2270
2271 PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
2272 {
2273     /*
2274      * if c == NULL just check if the optional function was imported
2275      * else run the optional function so ssl filters are inserted
2276      */
2277     if (proxy_ssl_enable) {
2278         return c ? proxy_ssl_enable(c) : 1;
2279     }
2280
2281     return 0;
2282 }
2283
2284 PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
2285 {
2286     if (proxy_ssl_disable) {
2287         return proxy_ssl_disable(c);
2288     }
2289
2290     return 0;
2291 }
2292
2293 PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c)
2294 {
2295     if (proxy_is_https) {
2296         return proxy_is_https(c);
2297     }
2298     else
2299         return 0;
2300 }
2301
2302 PROXY_DECLARE(const char *) ap_proxy_ssl_val(apr_pool_t *p, server_rec *s,
2303                                              conn_rec *c, request_rec *r,
2304                                              const char *var)
2305 {
2306     if (proxy_ssl_val) {
2307         /* XXX Perhaps the casting useless */
2308         return (const char *)proxy_ssl_val(p, s, c, r, (char *)var);
2309     }
2310     else
2311         return NULL;
2312 }
2313
2314 static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
2315                              apr_pool_t *ptemp, server_rec *s)
2316 {
2317
2318     proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
2319     proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
2320     proxy_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
2321     proxy_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
2322     ap_proxy_strmatch_path = apr_strmatch_precompile(pconf, "path=", 0);
2323     ap_proxy_strmatch_domain = apr_strmatch_precompile(pconf, "domain=", 0);
2324
2325     return OK;
2326 }
2327
2328 /*
2329  *  proxy Extension to mod_status
2330  */
2331 static int proxy_status_hook(request_rec *r, int flags)
2332 {
2333     int i, n;
2334     void *sconf = r->server->module_config;
2335     proxy_server_conf *conf = (proxy_server_conf *)
2336         ap_get_module_config(sconf, &proxy_module);
2337     proxy_balancer *balancer = NULL;
2338     proxy_worker **worker = NULL;
2339
2340     if (flags & AP_STATUS_SHORT || conf->balancers->nelts == 0 ||
2341         conf->proxy_status == status_off)
2342         return OK;
2343
2344     balancer = (proxy_balancer *)conf->balancers->elts;
2345     for (i = 0; i < conf->balancers->nelts; i++) {
2346         ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r);
2347         ap_rvputs(r, balancer->s->name, "</h1>\n\n", NULL);
2348         ap_rputs("\n\n<table border=\"0\"><tr>"
2349                  "<th>SSes</th><th>Timeout</th><th>Method</th>"
2350                  "</tr>\n<tr>", r);
2351         if (*balancer->s->sticky) {
2352             if (strcmp(balancer->s->sticky, balancer->s->sticky_path)) {
2353                 ap_rvputs(r, "<td>", balancer->s->sticky, " | ",
2354                           balancer->s->sticky_path, NULL);
2355             }
2356             else {
2357                 ap_rvputs(r, "<td>", balancer->s->sticky, NULL);
2358             }
2359         }
2360         else {
2361             ap_rputs("<td> - ", r);
2362         }
2363         ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
2364                    apr_time_sec(balancer->s->timeout));
2365         ap_rprintf(r, "<td>%s</td>\n",
2366                    balancer->lbmethod->name);
2367         ap_rputs("</table>\n", r);
2368         ap_rputs("\n\n<table border=\"0\"><tr>"
2369                  "<th>Sch</th><th>Host</th><th>Stat</th>"
2370                  "<th>Route</th><th>Redir</th>"
2371                  "<th>F</th><th>Set</th><th>Acc</th><th>Wr</th><th>Rd</th>"
2372                  "</tr>\n", r);
2373
2374         worker = (proxy_worker **)balancer->workers->elts;
2375         for (n = 0; n < balancer->workers->nelts; n++) {
2376             char fbuf[50];
2377             ap_rvputs(r, "<tr>\n<td>", (*worker)->s->scheme, "</td>", NULL);
2378             ap_rvputs(r, "<td>", (*worker)->s->hostname, "</td><td>", NULL);
2379             ap_rvputs(r, ap_proxy_parse_wstatus(r->pool, *worker), NULL);
2380             ap_rvputs(r, "</td><td>", (*worker)->s->route, NULL);
2381             ap_rvputs(r, "</td><td>", (*worker)->s->redirect, NULL);
2382             ap_rprintf(r, "</td><td>%d</td>", (*worker)->s->lbfactor);
2383             ap_rprintf(r, "<td>%d</td>", (*worker)->s->lbset);
2384             ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td><td>", (*worker)->s->elected);
2385             ap_rputs(apr_strfsize((*worker)->s->transferred, fbuf), r);
2386             ap_rputs("</td><td>", r);
2387             ap_rputs(apr_strfsize((*worker)->s->read, fbuf), r);
2388             ap_rputs("</td>\n", r);
2389
2390             /* TODO: Add the rest of dynamic worker data */
2391             ap_rputs("</tr>\n", r);
2392
2393             ++worker;
2394         }
2395         ap_rputs("</table>\n", r);
2396         ++balancer;
2397     }
2398     ap_rputs("<hr /><table>\n"
2399              "<tr><th>SSes</th><td>Sticky session name</td></tr>\n"
2400              "<tr><th>Timeout</th><td>Balancer Timeout</td></tr>\n"
2401              "<tr><th>Sch</th><td>Connection scheme</td></tr>\n"
2402              "<tr><th>Host</th><td>Backend Hostname</td></tr>\n"
2403              "<tr><th>Stat</th><td>Worker status</td></tr>\n"
2404              "<tr><th>Route</th><td>Session Route</td></tr>\n"
2405              "<tr><th>Redir</th><td>Session Route Redirection</td></tr>\n"
2406              "<tr><th>F</th><td>Load Balancer Factor</td></tr>\n"
2407              "<tr><th>Acc</th><td>Number of uses</td></tr>\n"
2408              "<tr><th>Wr</th><td>Number of bytes transferred</td></tr>\n"
2409              "<tr><th>Rd</th><td>Number of bytes read</td></tr>\n"
2410              "</table>", r);
2411
2412     return OK;
2413 }
2414
2415 static void child_init(apr_pool_t *p, server_rec *s)
2416 {
2417     proxy_worker *reverse = NULL;
2418
2419     /* TODO */
2420     while (s) {
2421         void *sconf = s->module_config;
2422         proxy_server_conf *conf;
2423         proxy_worker *worker;
2424         int i;
2425
2426         conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
2427         /*
2428          * NOTE: non-balancer members don't use shm at all...
2429          *       after all, why should they?
2430          */
2431         worker = (proxy_worker *)conf->workers->elts;
2432         for (i = 0; i < conf->workers->nelts; i++, worker++) {
2433             ap_proxy_initialize_worker(worker, s, conf->pool);
2434         }
2435         /* Create and initialize forward worker if defined */
2436         if (conf->req_set && conf->req) {
2437             proxy_worker *forward;
2438             ap_proxy_define_worker(p, &forward, NULL, NULL, "http://www.apache.org", 0);
2439             conf->forward = forward;
2440             PROXY_STRNCPY(conf->forward->s->name,     "proxy:forward");
2441             PROXY_STRNCPY(conf->forward->s->hostname, "*");
2442             PROXY_STRNCPY(conf->forward->s->scheme,   "*");
2443             conf->forward->hash.def = conf->forward->s->hash.def =
2444                 ap_proxy_hashfunc(conf->forward->s->name, PROXY_HASHFUNC_DEFAULT);
2445              conf->forward->hash.fnv = conf->forward->s->hash.fnv =
2446                 ap_proxy_hashfunc(conf->forward->s->name, PROXY_HASHFUNC_FNV);
2447             /* Do not disable worker in case of errors */
2448             conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS;
2449             /* Disable address cache for generic forward worker */
2450             conf->forward->s->is_address_reusable = 0;
2451             ap_proxy_initialize_worker(conf->forward, s, conf->pool);
2452         }
2453         if (!reverse) {
2454             ap_proxy_define_worker(p, &reverse, NULL, NULL, "http://www.apache.org", 0);
2455             PROXY_STRNCPY(reverse->s->name,     "proxy:reverse");
2456             PROXY_STRNCPY(reverse->s->hostname, "*");
2457             PROXY_STRNCPY(reverse->s->scheme,   "*");
2458             reverse->hash.def = reverse->s->hash.def =
2459                 ap_proxy_hashfunc(reverse->s->name, PROXY_HASHFUNC_DEFAULT);
2460             reverse->hash.fnv = reverse->s->hash.fnv =
2461                 ap_proxy_hashfunc(reverse->s->name, PROXY_HASHFUNC_FNV);
2462             /* Do not disable worker in case of errors */
2463             reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS;
2464             /* Disable address cache for generic reverse worker */
2465             reverse->s->is_address_reusable = 0;
2466         }
2467         conf->reverse = reverse;
2468         ap_proxy_initialize_worker(conf->reverse, s, conf->pool);
2469         s = s->next;
2470     }
2471 }
2472
2473 /*
2474  * This routine is called before the server processes the configuration
2475  * files.  There is no return value.
2476  */
2477 static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
2478                             apr_pool_t *ptemp)
2479 {
2480     APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL,
2481                       APR_HOOK_MIDDLE);
2482     /* Reset workers count on gracefull restart */
2483     proxy_lb_workers = 0;
2484     return OK;
2485 }
2486 static void register_hooks(apr_pool_t *p)
2487 {
2488     /* fixup before mod_rewrite, so that the proxied url will not
2489      * escaped accidentally by our fixup.
2490      */
2491     static const char * const aszSucc[] = { "mod_rewrite.c", NULL};
2492     /* Only the mpm_winnt has child init hook handler.
2493      * make sure that we are called after the mpm
2494      * initializes.
2495      */
2496     static const char *const aszPred[] = { "mpm_winnt.c", "mod_proxy_balancer.c", NULL};
2497
2498     /* handler */
2499     ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
2500     /* filename-to-URI translation */
2501     ap_hook_translate_name(proxy_trans, aszSucc, NULL, APR_HOOK_FIRST);
2502     /* walk <Proxy > entries and suppress default TRACE behavior */
2503     ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
2504     /* fixups */
2505     ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST);
2506     /* post read_request handling */
2507     ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST);
2508     /* pre config handling */
2509     ap_hook_pre_config(proxy_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
2510     /* post config handling */
2511     ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
2512     /* child init handling */
2513     ap_hook_child_init(child_init, aszPred, NULL, APR_HOOK_MIDDLE);
2514
2515 }
2516
2517 AP_DECLARE_MODULE(proxy) =
2518 {
2519     STANDARD20_MODULE_STUFF,
2520     create_proxy_dir_config,    /* create per-directory config structure */
2521     merge_proxy_dir_config,     /* merge per-directory config structures */
2522     create_proxy_config,        /* create per-server config structure */
2523     merge_proxy_config,         /* merge per-server config structures */
2524     proxy_cmds,                 /* command table */
2525     register_hooks
2526 };
2527
2528 APR_HOOK_STRUCT(
2529     APR_HOOK_LINK(scheme_handler)
2530     APR_HOOK_LINK(canon_handler)
2531     APR_HOOK_LINK(pre_request)
2532     APR_HOOK_LINK(post_request)
2533     APR_HOOK_LINK(request_status)
2534 )
2535
2536 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler,
2537                                      (request_rec *r, proxy_worker *worker,
2538                                       proxy_server_conf *conf,
2539                                       char *url, const char *proxyhost,
2540                                       apr_port_t proxyport),(r,worker,conf,
2541                                       url,proxyhost,proxyport),DECLINED)
2542 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler,
2543                                       (request_rec *r, char *url),(r,
2544                                       url),DECLINED)
2545 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, pre_request, (
2546                                       proxy_worker **worker,
2547                                       proxy_balancer **balancer,
2548                                       request_rec *r,
2549                                       proxy_server_conf *conf,
2550                                       char **url),(worker,balancer,
2551                                       r,conf,url),DECLINED)
2552 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, post_request,
2553                                       (proxy_worker *worker,
2554                                        proxy_balancer *balancer,
2555                                        request_rec *r,
2556                                        proxy_server_conf *conf),(worker,
2557                                        balancer,r,conf),DECLINED)
2558 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups,
2559                                     (request_rec *r), (r),
2560                                     OK, DECLINED)
2561 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, request_status,
2562                                     (int *status, request_rec *r),
2563                                     (status, r),
2564                                     OK, DECLINED)