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