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