]> granicus.if.org Git - apache/blobdiff - modules/proxy/mod_proxy.c
Close PR 32459, 15207. API change for PROXY_DECLARE ap_proxy_canonenc()
[apache] / modules / proxy / mod_proxy.c
index cb9c6ea247e7e119ea7c92a0a08673335067a63b..15372beb7b80ffe9cacd5c87508d61030dd7d99c 100644 (file)
@@ -1,69 +1,32 @@
-/* ====================================================================
- * The Apache Software License, Version 1.1
+/* Copyright 1999-2004 The Apache Software Foundation
  *
- * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
- * reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" must
- *    not be used to endorse or promote products derived from this
- *    software without prior written permission. For written
- *    permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- *    nor may "Apache" appear in their name, without prior written
- *    permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- * Portions of this software are based upon public domain software
- * originally written at the National Center for Supercomputing Applications,
- * University of Illinois, Urbana-Champaign.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 #define CORE_PRIVATE
 
 #include "mod_proxy.h"
 #include "mod_core.h"
-
 #include "apr_optional.h"
+#include "scoreboard.h"
+#include "mod_status.h"
 
-extern module AP_MODULE_DECLARE_DATA proxy_module;
+#if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
+#include "mod_ssl.h"
+#else
+APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
+#endif
 
 #ifndef MAX
 #define MAX(x,y) ((x) >= (y) ? (x) : (y))
@@ -86,12 +49,232 @@ extern module AP_MODULE_DECLARE_DATA proxy_module;
 /* -------------------------------------------------------------- */
 /* Translate the URL into a 'filename' */
 
+#ifdef FIX_15207
+/* XXX: EBCDIC safe? --nd */
+#define x2c(x) (((x >= '0') && (x <= '9'))         \
+                   ? (x - '0')                     \
+                   : (((x >= 'a') && (x <= 'f'))   \
+                       ? (10 + x - 'a')            \
+                       : ((x >= 'A') && (x <='F')) \
+                           ? (10 + x - 'A')        \
+                           : 0                     \
+                     )                             \
+               )
+
+static unsigned char hex2c(const char* p) {
+  const char c1 = p[1];
+  const char c2 = p[1] ? p[2]: '\0';
+  int i1 = c1 ? x2c(c1) : 0;
+  int i2 = c2 ? x2c(c2) : 0;
+  unsigned char ret = (i1 << 4) | i2;
+
+  return ret;
+}
+#endif
+
+#define PROXY_COPY_CONF_PARAMS(w, c) \
+    do {                             \
+        (w)->timeout              = (c)->timeout;               \
+        (w)->timeout_set          = (c)->timeout_set;           \
+        (w)->recv_buffer_size     = (c)->recv_buffer_size;      \
+        (w)->recv_buffer_size_set = (c)->recv_buffer_size_set;  \
+        (w)->io_buffer_size       = (c)->io_buffer_size;        \
+        (w)->io_buffer_size_set   = (c)->io_buffer_size_set;    \
+    } while (0)
+
+static const char *set_worker_param(apr_pool_t *p,
+                                    proxy_worker *worker,
+                                    const char *key,
+                                    const char *val)
+{
+
+    int ival;
+    if (!strcasecmp(key, "loadfactor")) {
+        /* Normalized load factor. Used with BalancerMamber,
+         * it is a number between 1 and 100.
+         */
+        worker->lbfactor = atoi(val);
+        if (worker->lbfactor < 1 || worker->lbfactor > 100)
+            return "LoadFactor must be number between 1..100";
+    }
+    else if (!strcasecmp(key, "retry")) {
+        /* If set it will give the retry timeout for the worker
+         * The default value is 60 seconds, meaning that if
+         * in error state, it will be retried after that timeout.
+         */
+        ival = atoi(val);
+        if (ival < 1)
+            return "Retry must be at least one second";
+        worker->retry = apr_time_from_sec(ival);
+    }
+    else if (!strcasecmp(key, "ttl")) {
+        /* Time in seconds that will destroy all the connections
+         * that exced the smax 
+         */
+        ival = atoi(val);
+        if (ival < 1)
+            return "TTL must be at least one second";
+        worker->ttl = apr_time_from_sec(ival);
+    }
+    else if (!strcasecmp(key, "min")) {
+        /* Initial number of connections to remote
+         */
+        ival = atoi(val);
+        if (ival < 0)
+            return "Min must be a positive number";
+        worker->min = ival;
+    }
+    else if (!strcasecmp(key, "max")) {
+        /* Maximum number of connections to remote
+         */
+        ival = atoi(val);
+        if (ival < 0)
+            return "Max must be a positive number";
+        worker->hmax = ival;
+    }
+    /* XXX: More inteligent naming needed */
+    else if (!strcasecmp(key, "smax")) {
+        /* Maximum number of connections to remote that
+         * will not be destroyed
+         */
+        ival = atoi(val);
+        if (ival < 0)
+            return "Smax must be a positive number";
+        worker->smax = ival;
+    }
+    else if (!strcasecmp(key, "acquire")) {
+        /* Acquire timeout in milliseconds.
+         * If set this will be the maximum time to
+         * wait for a free connection.
+         */
+        ival = atoi(val);
+        if (ival < 1)
+            return "Acquire must be at least one mili second";
+        worker->acquire = apr_time_make(0, ival * 1000);
+        worker->acquire_set = 1;
+    }
+    else if (!strcasecmp(key, "timeout")) {
+        /* Connection timeout in seconds.
+         * Defaults to server timeout.
+         */
+        ival = atoi(val);
+        if (ival < 1)
+            return "Timeout must be at least one second";
+        worker->timeout = apr_time_from_sec(ival);
+        worker->timeout_set = 1;
+    }
+    else if (!strcasecmp(key, "iobuffersize")) {
+        long s = atol(val);
+        worker->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
+        worker->io_buffer_size_set = 1;
+    }
+    else if (!strcasecmp(key, "receivebuffersize")) {
+        ival = atoi(val);
+        if (ival < 512 && ival != 0) {
+            return "ReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
+        }
+        worker->recv_buffer_size = ival;
+        worker->recv_buffer_size_set = 1;
+    }
+    else if (!strcasecmp(key, "keepalive")) {
+        if (!strcasecmp(val, "on"))
+            worker->keepalive = 1;
+        else if (!strcasecmp(val, "off"))
+            worker->keepalive = 0;
+        else
+            return "KeepAlive must be On|Off";
+        worker->keepalive_set = 1;
+    }    
+    else if (!strcasecmp(key, "route")) {
+        /* Worker route.
+         */
+        if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
+            return "Route length must be < 64 characters";
+        worker->route = apr_pstrdup(p, val);
+    }
+    else if (!strcasecmp(key, "redirect")) {
+        /* Worker redirection route.
+         */
+        if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
+            return "Redirect length must be < 64 characters";
+        worker->redirect = apr_pstrdup(p, val);
+    }
+    else {
+        return "unknown Worker parameter";
+    }
+    return NULL;
+}
+
+static const char *set_balancer_param(apr_pool_t *p,
+                                      proxy_balancer *balancer,
+                                      const char *key,
+                                      const char *val)
+{
+
+    int ival;
+    if (!strcasecmp(key, "stickysession")) {
+        /* Balancer sticky session name.
+         * Set to something like JSESSIONID or
+         * PHPSESSIONID, etc..,
+         */
+        balancer->sticky = apr_pstrdup(p, val);
+    }
+    else if (!strcasecmp(key, "nofailover")) {
+        /* If set to 'on' the session will break
+         * if the worker is in error state or
+         * disabled.
+         */
+        if (!strcasecmp(val, "on"))
+            balancer->sticky_force = 1;
+        else if (!strcasecmp(val, "off"))
+            balancer->sticky_force = 0;
+        else
+            return "failover must be On|Off";
+    }
+    else if (!strcasecmp(key, "timeout")) {
+        /* Balancer timeout in seconds.
+         * If set this will be the maximum time to
+         * wait for a free worker.
+         * Default is not to wait.
+         */
+        ival = atoi(val);
+        if (ival < 1)
+            return "timeout must be at least one second";
+        balancer->timeout = apr_time_from_sec(ival);
+    }
+    else if (!strcasecmp(key, "maxattempts")) {
+        /* Maximum number of failover attempts before
+         * giving up.
+         */
+        ival = atoi(val);
+        if (ival < 0)
+            return "maximum number of attempts must be a positive number";
+        balancer->max_attempts = ival;
+        balancer->max_attempts_set = 1;
+    }
+    else if (!strcasecmp(key, "lbmethod")) {
+        /* Which LB scheduler method */
+        if (!strcasecmp(val, "traffic"))
+            balancer->lbmethod = lbmethod_traffic;
+        else if (!strcasecmp(val, "requests"))
+            balancer->lbmethod = lbmethod_requests;
+        else
+            return "lbmethod must be Traffic|Requests";
+    }
+    else {
+        return "unknown Balancer parameter";
+    }
+    return NULL;
+}
+
 static int alias_match(const char *uri, const char *alias_fakename)
 {
     const char *end_fakename = alias_fakename + strlen(alias_fakename);
     const char *aliasp = alias_fakename, *urip = uri;
+    const char *end_uri = uri + strlen(uri);
+    unsigned char uric, aliasc;
 
-    while (aliasp < end_fakename) {
+    while (aliasp < end_fakename && urip < end_uri) {
         if (*aliasp == '/') {
             /* any number of '/' in the alias matches any number in
              * the supplied URI, but there must be at least one...
@@ -105,14 +288,47 @@ static int alias_match(const char *uri, const char *alias_fakename)
                 ++urip;
         }
         else {
+#ifndef FIX_15207
             /* Other characters are compared literally */
             if (*urip++ != *aliasp++)
                 return 0;
+#else
+            /* Other characters are canonicalised and compared literally */
+            if (*urip == '%') {
+                uric = hex2c(urip);
+                urip += 3;
+            } else {
+                uric = (unsigned char)*urip++;
+            }
+            if (*aliasp == '%') {
+                aliasc = hex2c(aliasp);
+                aliasp += 3;
+            } else {
+                aliasc = (unsigned char)*aliasp++;
+            }
+            if (uric != aliasc) {
+                return 0;
+            }
+#endif
         }
     }
 
-    /* Check last alias path component matched all the way */
+    /* fixup badly encoded stuff (e.g. % as last character) */
+    if (aliasp > end_fakename) {
+        aliasp = end_fakename;
+    }
+    if (urip > end_uri) {
+        urip = end_uri;
+    }
 
+   /* We reach the end of the uri before the end of "alias_fakename"
+    * for example uri is "/" and alias_fakename "/examples"
+    */
+   if (urip == end_uri && aliasp!=end_fakename) {
+       return 0;
+   }
+
+    /* Check last alias path component matched all the way */
     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
         return 0;
 
@@ -138,16 +354,19 @@ static int alias_match(const char *uri, const char *alias_fakename)
 static int proxy_detect(request_rec *r)
 {
     void *sconf = r->server->module_config;
-    proxy_server_conf *conf;
-
-    conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+    proxy_server_conf *conf =
+        (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+#ifdef FIX_15207
+    int i, len;
+    struct proxy_alias *ent = (struct proxy_alias *)conf->aliases->elts;
+#endif
 
     /* Ick... msvc (perhaps others) promotes ternary short results to int */
 
     if (conf->req && r->parsed_uri.scheme) {
         /* but it might be something vhosted */
         if (!(r->parsed_uri.hostname
-              && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
+              && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))
               && ap_matches_request_vhost(r, r->parsed_uri.hostname,
                                           (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port 
                                                        : ap_default_port(r))))) {
@@ -165,17 +384,37 @@ static int proxy_detect(request_rec *r)
         r->uri = r->unparsed_uri;
         r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
         r->handler = "proxy-server";
+#ifdef FIX_15207
+    } else {
+        /* test for a ProxyPass */
+        for (i = 0; i < conf->aliases->nelts; i++) {
+            len = alias_match(r->unparsed_uri, ent[i].fake);
+            if (len > 0) {
+                if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
+                    return DECLINED;
+                }
+                r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
+                                          r->unparsed_uri + len, NULL);
+                r->handler = "proxy-server";
+                r->proxyreq = PROXYREQ_REVERSE;
+                r->uri = r->unparsed_uri;
+                break;
+            }
+        }
+#endif
     }
     return DECLINED;
 }
 
 static int proxy_trans(request_rec *r)
 {
+#ifndef FIX_15207
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
     int i, len;
     struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
+#endif
 
     if (r->proxyreq) {
         /* someone has already set up the proxy, it was possibly ourselves
@@ -184,6 +423,7 @@ static int proxy_trans(request_rec *r)
         return OK;
     }
 
+#ifndef FIX_15207
     /* XXX: since r->uri has been manipulated already we're not really
      * compliant with RFC1945 at this point.  But this probably isn't
      * an issue because this is a hybrid proxy/origin server.
@@ -193,17 +433,18 @@ static int proxy_trans(request_rec *r)
         len = alias_match(r->uri, ent[i].fake);
 
        if (len > 0) {
-           if ((ent[i].real[0] == '!' ) && ( ent[i].real[1] == 0 )) {
+           if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
                return DECLINED;
            }
 
            r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
-                                 (r->uri + len ), NULL);
+                                     r->uri + len, NULL);
            r->handler = "proxy-server";
            r->proxyreq = PROXYREQ_REVERSE;
            return OK;
        }
     }
+#endif
     return DECLINED;
 }
 
@@ -252,7 +493,7 @@ static int proxy_map_location(request_rec *r)
 {
     int access_status;
 
-    if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
+    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
         return DECLINED;
 
     /* Don't let the core or mod_http map_to_storage hooks handle this,
@@ -265,7 +506,7 @@ static int proxy_map_location(request_rec *r)
 
     return OK;
 }
-
+#ifndef FIX_15207
 /* -------------------------------------------------------------- */
 /* Fixup the filename */
 
@@ -277,9 +518,18 @@ static int proxy_fixup(request_rec *r)
     char *url, *p;
     int access_status;
 
-    if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
+    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
         return DECLINED;
 
+#ifdef FIX_15207
+/* We definitely shouldn't canonicalize a proxy_pass.
+ * But should we really canonicalize a STD_PROXY??? -- Fahree
+ */
+    if (r->proxyreq == PROXYREQ_REVERSE) {
+        return OK;
+    }
+#endif
+
     /* XXX: Shouldn't we try this before we run the proxy_walk? */
     url = &r->filename[6];
 
@@ -292,9 +542,9 @@ static int proxy_fixup(request_rec *r)
     if (p == NULL || p == url)
         return HTTP_BAD_REQUEST;
 
-    return OK;         /* otherwise; we've done the best we can */
+    return OK;      /* otherwise; we've done the best we can */
 }
-
+#endif
 /* Send a redirection if the request contains a hostname which is not */
 /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
 /* servers like Netscape's allow this and access hosts from the local */
@@ -313,7 +563,7 @@ static int proxy_needsdomain(request_rec *r, const char *url, const char *domain
     /* If host does contain a dot already, or it is "localhost", decline */
     if (strchr(r->parsed_uri.hostname, '.') != NULL
      || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
-        return DECLINED;       /* host name has a dot already */
+        return DECLINED;    /* host name has a dot already */
 
     ref = apr_table_get(r->headers_in, "Referer");
 
@@ -326,7 +576,7 @@ static int proxy_needsdomain(request_rec *r, const char *url, const char *domain
                            APR_URI_UNP_REVEALPASSWORD);
 
     apr_table_set(r->headers_out, "Location", nuri);
-    ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, r,
+    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                   "Domain missing: %s sent to %s%s%s", r->uri,
                   apr_uri_unparse(r->pool, &r->parsed_uri,
                                   APR_URI_UNP_OMITUSERINFO),
@@ -351,9 +601,12 @@ static int proxy_handler(request_rec *r)
     int direct_connect = 0;
     const char *str;
     long maxfwd;
+    proxy_balancer *balancer = NULL;
+    proxy_worker *worker = NULL;
+    int attempts = 0, max_attempts = 0;
 
     /* is this for us? */
-    if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
+    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
         return DECLINED;
 
     /* handle max-forwards / OPTIONS / TRACE */
@@ -394,81 +647,114 @@ static int proxy_handler(request_rec *r)
     apr_table_set(r->headers_in, "Max-Forwards", 
                   apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0));
 
-    if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
-        return rc;
-
-    url = r->filename + 6;
-    p = strchr(url, ':');
-    if (p == NULL)
-        return HTTP_BAD_REQUEST;
-
-    /* If the host doesn't have a domain name, add one and redirect. */
-    if (conf->domain != NULL) {
-        rc = proxy_needsdomain(r, url, conf->domain);
-        if (ap_is_HTTP_REDIRECT(rc))
-            return HTTP_MOVED_PERMANENTLY;
-    }
-
-    *p = '\0';
-    scheme = apr_pstrdup(r->pool, url);
-    *p = ':';
-
-    /* Check URI's destination host against NoProxy hosts */
-    /* Bypass ProxyRemote server lookup if configured as NoProxy */
-    /* we only know how to handle communication to a proxy via http */
-    /*if (strcasecmp(scheme, "http") == 0) */
-    {
-        int ii;
-        struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
+    do {
+        url = r->filename + 6;
+        p = strchr(url, ':');
+        if (p == NULL) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "proxy_handler no URL in %s", r->filename);
+            return HTTP_BAD_REQUEST;
+        }
 
-        for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
-            direct_connect = list[ii].matcher(&list[ii], r);
+        /* If the host doesn't have a domain name, add one and redirect. */
+        if (conf->domain != NULL) {
+            rc = proxy_needsdomain(r, url, conf->domain);
+            if (ap_is_HTTP_REDIRECT(rc))
+                return HTTP_MOVED_PERMANENTLY;
         }
+
+        *p = '\0';
+        scheme = apr_pstrdup(r->pool, url);
+        *p = ':';
+
+        /* Check URI's destination host against NoProxy hosts */
+        /* Bypass ProxyRemote server lookup if configured as NoProxy */
+        /* we only know how to handle communication to a proxy via http */
+        /*if (strcasecmp(scheme, "http") == 0) */
+        {
+            int ii;
+            struct dirconn_entry *list = (struct dirconn_entry *)
+                                                conf->dirconn->elts;
+
+            for (direct_connect = ii = 0; ii < conf->dirconn->nelts &&
+                                               !direct_connect; ii++) {
+                direct_connect = list[ii].matcher(&list[ii], r);
+            }
 #if DEBUGGING
-        ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                       (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
                       r->uri);
 #endif
-    }
-
-    /* firstly, try a proxy, unless a NoProxy directive is active */
-    if (!direct_connect) {
-        for (i = 0; i < proxies->nelts; i++) {
-            p2 = ap_strchr_c(ents[i].scheme, ':');  /* is it a partial URL? */
-            if (strcmp(ents[i].scheme, "*") == 0 ||
-                (ents[i].use_regex && ap_regexec(ents[i].regexp, url, 0,NULL, 0)) ||
-                (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
-                (p2 != NULL &&
-                 strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
-
-                /* handle the scheme */
-                ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
-                             "Trying to run scheme_handler against proxy");
-                access_status = proxy_run_scheme_handler(r, conf, url, ents[i].hostname, ents[i].port);
-
-                /* an error or success */
-                if (access_status != DECLINED && access_status != HTTP_BAD_GATEWAY) {
-                    return access_status;
+        }
+    
+        /* Try to obtain the most suitable worker */
+        access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
+        if (access_status != OK)
+            return access_status;
+        if (balancer && balancer->max_attempts_set && !max_attempts)
+            max_attempts = balancer->max_attempts;
+        /* firstly, try a proxy, unless a NoProxy directive is active */
+        if (!direct_connect) {
+            for (i = 0; i < proxies->nelts; i++) {
+                p2 = ap_strchr_c(ents[i].scheme, ':');  /* is it a partial URL? */
+                if (strcmp(ents[i].scheme, "*") == 0 ||
+                    (ents[i].use_regex && ap_regexec(ents[i].regexp, url,
+                                                     0,NULL, 0)) ||
+                    (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
+                    (p2 != NULL &&
+                    strncasecmp(url, ents[i].scheme,
+                                strlen(ents[i].scheme)) == 0)) {
+
+                    /* handle the scheme */
+                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                                 "Trying to run scheme_handler against proxy");
+                    access_status = proxy_run_scheme_handler(r, worker,
+                                                             conf, url,
+                                                             ents[i].hostname,
+                                                             ents[i].port);
+
+                    /* an error or success */
+                    if (access_status != DECLINED &&
+                        access_status != HTTP_BAD_GATEWAY) {
+                        goto cleanup;
+                    }
+                    /* we failed to talk to the upstream proxy */
                 }
-                /* we failed to talk to the upstream proxy */
             }
         }
-    }
 
-    /* otherwise, try it direct */
-    /* N.B. what if we're behind a firewall, where we must use a proxy or
-     * give up??
-     */
+        /* otherwise, try it direct */
+        /* N.B. what if we're behind a firewall, where we must use a proxy or
+        * give up??
+        */
+
+        /* handle the scheme */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "Running scheme %s handler (attempt %d)",
+                     scheme, attempts);
+        if ((access_status = proxy_run_scheme_handler(r, worker, conf,
+                                                      url, NULL, 0)) == OK)
+            break;
+        
+    } while (!PROXY_WORKER_IS_USABLE(worker) && 
+             max_attempts > attempts++);
 
-    /* handle the scheme */
-    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
-                 "Trying to run scheme_handler");
-    access_status = proxy_run_scheme_handler(r, conf, url, NULL, 0);
     if (DECLINED == access_status) {
-        ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
-                     "Neither CONNECT, HTTP or FTP for %s",
-                     r->uri);
-        return HTTP_FORBIDDEN;
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                    "proxy: No protocol handler was valid for the URL %s. "
+                    "If you are using a DSO version of mod_proxy, make sure "
+                    "the proxy submodules are included in the configuration "
+                    "using LoadModule.", r->uri);
+        access_status = HTTP_FORBIDDEN;
+        goto cleanup;
+    }
+cleanup:
+    if (balancer) {
+        int post_status = proxy_run_post_request(worker, balancer, r, conf);
+        if (post_status == DECLINED) {
+            post_status = OK; /* no post_request handler available */
+            /* TODO: reclycle direct worker */
+        }
     }
     return access_status;
 }
@@ -478,15 +764,23 @@ static int proxy_handler(request_rec *r)
 
 static void * create_proxy_config(apr_pool_t *p, server_rec *s)
 {
-    proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf));
-
-    ps->sec_proxy = ap_make_array(p, 10, sizeof(ap_conf_vector_t *));
-    ps->proxies = ap_make_array(p, 10, sizeof(struct proxy_remote));
-    ps->aliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
-    ps->raliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
-    ps->noproxies = ap_make_array(p, 10, sizeof(struct noproxy_entry));
-    ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry));
-    ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int));
+    proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
+
+    ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *));
+    ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote));
+    ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->cookie_path_str = apr_strmatch_precompile(p, "path=", 0);
+    ps->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0);
+    ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry));
+    ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry));
+    ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int));
+    ps->workers = apr_array_make(p, 10, sizeof(proxy_worker));
+    ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer));
+    ps->forward = NULL;
+    ps->reverse = NULL;
     ps->domain = NULL;
     ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
     ps->viaopt_set = 0; /* 0 means default */
@@ -500,16 +794,19 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s)
     ps->maxfwd_set = 0;
     ps->error_override = 0; 
     ps->error_override_set = 0; 
-    ps->preserve_host_set =0;
-    ps->preserve_host =0;    
-    ps->timeout=0;
-    ps->timeout_set=0;
+    ps->preserve_host_set = 0;
+    ps->preserve_host = 0;    
+    ps->timeout = 0;
+    ps->timeout_set = 0;
+    ps->badopt = bad_error;
+    ps->badopt_set = 0;
+    ps->pool = p;
     return ps;
 }
 
 static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
 {
-    proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf));
+    proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
     proxy_server_conf *base = (proxy_server_conf *) basev;
     proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
 
@@ -517,9 +814,19 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
     ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
     ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
     ps->raliases = apr_array_append(p, base->raliases, overrides->raliases);
+    ps->cookie_paths
+        = apr_array_append(p, base->cookie_paths, overrides->cookie_paths);
+    ps->cookie_domains
+        = apr_array_append(p, base->cookie_domains, overrides->cookie_domains);
+    ps->cookie_path_str = base->cookie_path_str;
+    ps->cookie_domain_str = base->cookie_domain_str;
     ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
     ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
     ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
+    ps->workers = apr_array_append(p, base->workers, overrides->workers);
+    ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
+    ps->forward = overrides->forward ? overrides->forward : base->forward;
+    ps->reverse = overrides->reverse ? overrides->reverse : base->reverse;
 
     ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
     ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
@@ -530,7 +837,9 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
     ps->error_override = (overrides->error_override_set == 0) ? base->error_override : overrides->error_override;
     ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host;
     ps->timeout= (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
-
+    ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
+    ps->proxy_status = (overrides->proxy_status_set == 0) ? base->proxy_status : overrides->proxy_status;
+    ps->pool = p;
     return ps;
 }
 
@@ -601,11 +910,11 @@ static const char *
     }
     else
         if (strchr(f, ':') == NULL)
-            ap_str_tolower(f);         /* lowercase scheme */
-    ap_str_tolower(p + 3);             /* lowercase hostname */
+            ap_str_tolower(f);      /* lowercase scheme */
+    ap_str_tolower(p + 3);      /* lowercase hostname */
 
     if (port == -1) {
-        port = apr_uri_default_port_for_scheme(scheme);
+        port = apr_uri_port_of_scheme(scheme);
     }
 
     new = apr_array_push(conf->proxies);
@@ -631,28 +940,86 @@ static const char *
 }
 
 static const char *
-    add_pass(cmd_parms *cmd, void *dummy, const char *f, const char *r)
+    add_pass(cmd_parms *cmd, void *dummy, const char *arg)
 {
     server_rec *s = cmd->server;
     proxy_server_conf *conf =
     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
     struct proxy_alias *new;
-    if (r!=NULL && cmd->path == NULL ) {
-        new = apr_array_push(conf->aliases);
-        new->fake = f;
-        new->real = r;
-    } else if (r==NULL && cmd->path != NULL) {
-        new = apr_array_push(conf->aliases);
-        new->fake = cmd->path;
-        new->real = f;
-    } else {
-        if ( r== NULL)
-            return "ProxyPass needs a path when not defined in a location";
-        else 
-            return "ProxyPass can not have a path when defined in a location";
+    char *f = cmd->path;
+    char *r = NULL;
+    char *word;
+    apr_table_t *params = apr_table_make(cmd->pool, 5);
+    const apr_array_header_t *arr;
+    const apr_table_entry_t *elts;
+    int i;
+    
+    while (*arg) {
+        word = ap_getword_conf(cmd->pool, &arg);
+        if (!f)
+            f = word;
+        else if (!r)
+            r = word;
+        else {
+            char *val = strchr(word, '=');
+            if (!val) {
+                if (cmd->path)
+                    return "Invalid ProxyPass parameter.  Parameter must be "
+                           "in the form 'key=value'";
+                else
+                    return "ProxyPass can not have a path when defined in a location"; 
+            }
+            else
+                *val++ = '\0';
+            apr_table_setn(params, word, val);
+        }
+    };
+
+    if (r == NULL)
+        return "ProxyPass needs a path when not defined in a location";
+
+    new = apr_array_push(conf->aliases);
+    new->fake = apr_pstrdup(cmd->pool, f);
+    new->real = apr_pstrdup(cmd->pool, r);
+    if (r[0] == '!' && r[1] == '\0')
+        return NULL;
+    
+    arr = apr_table_elts(params);
+    elts = (const apr_table_entry_t *)arr->elts;
+    /* Distinguish the balancer from woker */
+    if (strncasecmp(r, "balancer:", 9) == 0) {
+        proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
+        if (!balancer) {
+            const char *err = ap_proxy_add_balancer(&balancer,
+                                                    cmd->pool,
+                                                    conf, r);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }        
+        for (i = 0; i < arr->nelts; i++) {
+            const char *err = set_balancer_param(cmd->pool, balancer, elts[i].key,
+                                                 elts[i].val);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }
     }
+    else {
+        proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, conf, r);
+        if (!worker) {
+            const char *err = ap_proxy_add_worker(&worker, cmd->pool, conf, r);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }
+        PROXY_COPY_CONF_PARAMS(worker, conf);
 
-     return NULL;
+        for (i = 0; i < arr->nelts; i++) {
+            const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
+                                               elts[i].val);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }
+    }
+    return NULL;
 }
 
 static const char *
@@ -681,6 +1048,36 @@ static const char *
 
     return NULL;
 }
+static const char*
+    cookie_path(cmd_parms *cmd, void *dummy, const char *f, const char *r)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf;
+    struct proxy_alias *new;
+
+    conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
+                                                     &proxy_module);
+    new = apr_array_push(conf->cookie_paths);
+    new->fake = f;
+    new->real = r;
+
+    return NULL;
+}
+static const char*
+    cookie_domain(cmd_parms *cmd, void *dummy, const char *f, const char *r)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf;
+    struct proxy_alias *new;
+
+    conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
+                                                     &proxy_module);
+    new = apr_array_push(conf->cookie_domains);
+    new->fake = f;
+    new->real = r;
+
+    return NULL;
+}
 
 static const char *
     set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg)
@@ -758,25 +1155,25 @@ static const char *
         New->name = apr_pstrdup(parms->pool, arg);
         New->hostaddr = NULL;
 
-       if (ap_proxy_is_ipaddr(New, parms->pool)) {
+    if (ap_proxy_is_ipaddr(New, parms->pool)) {
 #if DEBUGGING
-            ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
-                         "Parsed addr %s", inet_ntoa(New->addr));
-            ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
-                         "Parsed mask %s", inet_ntoa(New->mask));
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "Parsed addr %s", inet_ntoa(New->addr));
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "Parsed mask %s", inet_ntoa(New->mask));
 #endif
-       }
-       else if (ap_proxy_is_domainname(New, parms->pool)) {
-            ap_str_tolower(New->name);
+    }
+    else if (ap_proxy_is_domainname(New, parms->pool)) {
+        ap_str_tolower(New->name);
 #if DEBUGGING
-            ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
-                         "Parsed domain %s", New->name);
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "Parsed domain %s", New->name);
 #endif
         }
         else if (ap_proxy_is_hostname(New, parms->pool)) {
             ap_str_tolower(New->name);
 #if DEBUGGING
-            ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
                          "Parsed host %s", New->name);
 #endif
         }
@@ -811,8 +1208,16 @@ static const char *
 
     psf->req = flag;
     psf->req_set = 1;
+
+    if (flag && !psf->forward) {
+        psf->forward = ap_proxy_create_worker(parms->pool);
+        psf->forward->name     = "proxy:forward";
+        psf->forward->hostname = "*";
+        psf->forward->scheme   = "*";
+    }
     return NULL;
 }
+
 static const char *
     set_proxy_error_override(cmd_parms *parms, void *dummy, int flag)
 {
@@ -887,7 +1292,7 @@ static const char*
         return "Proxy Timeout must be at least 1 second.";
     }
     psf->timeout_set=1;
-    psf->timeout=timeout;
+    psf->timeout=apr_time_from_sec(timeout);
 
     return NULL;    
 }
@@ -915,10 +1320,193 @@ static const char*
     return NULL;    
 }
 
+static const char*
+    set_bad_opt(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    if (strcasecmp(arg, "IsError") == 0)
+        psf->badopt = bad_error;
+    else if (strcasecmp(arg, "Ignore") == 0)
+        psf->badopt = bad_ignore;
+    else if (strcasecmp(arg, "StartBody") == 0)
+        psf->badopt = bad_body;
+    else {
+        return "ProxyBadHeader must be one of: "
+            "IsError | Ignore | StartBody";
+    }
+
+    psf->badopt_set = 1;
+    return NULL;    
+}
+
+static const char*
+    set_status_opt(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    if (strcasecmp(arg, "Off") == 0)
+        psf->proxy_status = status_off;
+    else if (strcasecmp(arg, "On") == 0)
+        psf->proxy_status = status_on;
+    else if (strcasecmp(arg, "Full") == 0)
+        psf->proxy_status = status_full;
+    else {
+        return "ProxyStatus must be one of: "
+            "off | on | block";
+    }
+
+    psf->proxy_status_set = 1;
+    return NULL;    
+}
+
+static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf =
+    ap_get_module_config(s->module_config, &proxy_module);
+    proxy_balancer *balancer;
+    proxy_worker *worker;
+    char *path = cmd->path;
+    char *name = NULL;
+    char *word;
+    apr_table_t *params = apr_table_make(cmd->pool, 5);
+    const apr_array_header_t *arr;
+    const apr_table_entry_t *elts;
+    int i;
+    
+    if (cmd->path)
+        path = apr_pstrdup(cmd->pool, cmd->path);
+    while (*arg) {
+        word = ap_getword_conf(cmd->pool, &arg);
+        if (!path)
+            path = word;
+        else if (!name)
+            name = word;
+        else {
+            char *val = strchr(word, '=');
+            if (!val)
+                if (cmd->path)
+                    return "BalancerMember can not have a balancer name when defined in a location";
+                else
+                    return "Invalid BalancerMember parameter. Parameter must "
+                           "be in the form 'key=value'";
+            else
+                *val++ = '\0';
+            apr_table_setn(params, word, val);
+        }
+    }
+    if (!path)
+        return "BalancerMember must define balancer name when outside <Proxy > section";
+    if (!name)
+        return "BalancerMember must define remote proxy server";
+    
+    ap_str_tolower(path);   /* lowercase scheme://hostname */
+    ap_str_tolower(name);   /* lowercase scheme://hostname */
+
+    /* Try to find existing worker */
+    worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
+    if (!worker) {
+        const char *err;
+        if ((err = ap_proxy_add_worker(&worker, cmd->pool, conf, name)) != NULL)
+            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); 
+    }
+    PROXY_COPY_CONF_PARAMS(worker, conf);
+    
+    arr = apr_table_elts(params);
+    elts = (const apr_table_entry_t *)arr->elts;
+    for (i = 0; i < arr->nelts; i++) {
+        const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
+                                           elts[i].val);
+        if (err)
+            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+    }
+    /* Try to find the balancer */
+    balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, path); 
+    if (!balancer) {
+        const char *err = ap_proxy_add_balancer(&balancer,
+                                                cmd->pool,
+                                                conf, path);
+        if (err)
+            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+    }
+    /* Add the worker to the load balancer */
+    ap_proxy_add_worker_to_balancer(cmd->pool, balancer, worker);
+    return NULL;
+}
+
+static const char *
+    set_proxy_param(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf =
+    (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
+    char *name = NULL;
+    char *word, *val;
+    proxy_balancer *balancer = NULL;
+    proxy_worker *worker = NULL;
+    const char *err;
+
+    if (cmd->directive->parent &&
+        strncasecmp(cmd->directive->parent->directive,
+                    "<Proxy", 6) == 0) {
+        const char *pargs = cmd->directive->parent->args;
+        /* Directive inside <Proxy section
+         * Parent directive arg is the worker/balancer name.
+         */
+        name = ap_getword_conf(cmd->temp_pool, &pargs);
+        if ((word = ap_strchr(name, '>')))
+            *word = '\0';
+    }
+    else {
+        /* Standard set directive with worker/balancer
+         * name as first param.
+         */
+        name = ap_getword_conf(cmd->temp_pool, &arg);
+    }
+    if (strncasecmp(name, "balancer:", 9) == 0) {
+        balancer = ap_proxy_get_balancer(cmd->pool, conf, name);
+        if (!balancer) {
+            return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
+                               name, "' Balancer.", NULL);
+        }        
+    }
+    else {
+        worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
+        if (!worker) {
+            return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
+                               name, "' Worker.", NULL);
+        }
+    }
+
+    while (*arg) {
+        word = ap_getword_conf(cmd->pool, &arg);
+        val = strchr(word, '=');
+        if (!val) {
+            return "Invalid ProxySet parameter. Parameter must be "
+                   "in the form 'key=value'";
+        }
+        else
+            *val++ = '\0';
+        if (worker)
+            err = set_worker_param(cmd->pool, worker, word, val);
+        else
+            err = set_balancer_param(cmd->pool, balancer, word, val);
+
+        if (err)
+            return apr_pstrcat(cmd->temp_pool, "ProxySet ", err, " ", word, " ", name, NULL);
+    }
+
+    return NULL;
+}
+
 static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config)
 {
     proxy_server_conf *sconf = ap_get_module_config(s->module_config,
-                                                   &proxy_module);
+                                                    &proxy_module);
     void **new_space = (void **)apr_array_push(sconf->sec_proxy);
     
     *new_space = dir_config;
@@ -967,6 +1555,9 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
      */
     if (thiscmd->cmd_data) { /* <ProxyMatch> */
         r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+        if (!r) {
+            return "Regex could not be compiled";
+        }
     }
     else if (!strcmp(cmd->path, "~")) {
         cmd->path = ap_getword_conf(cmd->pool, &arg);
@@ -975,6 +1566,9 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
         if (strncasecmp(cmd->path, "proxy:", 6))
             cmd->path += 6;
         r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+        if (!r) {
+            return "Regex could not be compiled";
+        }
     }
 
     /* initialize our config and fetch it */
@@ -987,7 +1581,7 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
 
     conf->r = r;
     conf->p = cmd->path;
-    conf->p_is_fnmatch = apr_is_fnmatch(conf->p);
+    conf->p_is_fnmatch = apr_fnmatch_test(conf->p);
 
     ap_add_per_proxy_conf(cmd->server, new_dir_conf);
 
@@ -1016,10 +1610,14 @@ static const command_rec proxy_cmds[] =
      "a scheme, partial URL or '*' and a proxy server"),
     AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
      "a regex pattern and a proxy server"),
-    AP_INIT_TAKE12("ProxyPass", add_pass, NULL, RSRC_CONF|ACCESS_CONF,
+    AP_INIT_RAW_ARGS("ProxyPass", add_pass, NULL, RSRC_CONF|ACCESS_CONF,
      "a virtual path and a URL"),
     AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
      "a virtual path and a URL for reverse proxy behaviour"),
+    AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL,
+       RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
+    AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL,
+       RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
     AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
      "A list of names, hosts or domains to which the proxy will not connect"),
     AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF,
@@ -1043,13 +1641,19 @@ static const command_rec proxy_cmds[] =
     AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF,
      "Set the timeout (in seconds) for a proxied connection. "
      "This overrides the server timeout"),
+    AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF,
+     "How to handle bad header line in response: IsError | Ignore | StartBody"),
+    AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF,
+     "A balancer name and scheme with list of params"), 
+    AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF,
+     "Configure Status: proxy status to one of: on | off | full"),
+    AP_INIT_RAW_ARGS("ProxySet", set_proxy_param, NULL, RSRC_CONF|ACCESS_CONF,
+     "A balancer or worker name with list of params"),
     {NULL}
 };
 
-APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
-
 static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL;
+static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL;
 
 PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
 {
@@ -1064,28 +1668,201 @@ PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
     return 0;
 }
 
+PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
+{
+    if (proxy_ssl_disable) {
+        return proxy_ssl_disable(c);
+    }
+
+    return 0;
+}
+
 static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
                              apr_pool_t *ptemp, server_rec *s)
 {
+
     proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
+    proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
+
+    return OK;
+}
+
+/*
+ *  proxy Extension to mod_status
+ */
+static int proxy_status_hook(request_rec *r, int flags)
+{
+    int i, n;
+    void *sconf = r->server->module_config;
+    proxy_server_conf *conf = (proxy_server_conf *)
+        ap_get_module_config(sconf, &proxy_module);
+    proxy_balancer *balancer = NULL;
+    proxy_worker *worker = NULL;
+
+    if (flags & AP_STATUS_SHORT || conf->balancers->nelts == 0 ||
+        conf->proxy_status == status_off)
+        return OK;
+
+    balancer = (proxy_balancer *)conf->balancers->elts;
+    for (i = 0; i < conf->balancers->nelts; i++) {
+        ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r);
+        ap_rvputs(r, balancer->name, "</h1>\n\n", NULL);
+        ap_rputs("\n\n<table border=\"0\"><tr>"
+                 "<th>SSes</th><th>Timeout</th><th>Method</th>"
+                 "</tr>\n<tr>", r);                
+        ap_rvputs(r, "<td>", balancer->sticky, NULL);
+        ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
+                   apr_time_sec(balancer->timeout));
+        ap_rprintf(r, "<td>%s</td>\n",
+                   balancer->lbmethod == lbmethod_requests ? "Requests" :
+                   balancer->lbmethod == lbmethod_traffic ? "Traffic" :
+                   "Unknown");
+        ap_rputs("</table>\n", r);
+        ap_rputs("\n\n<table border=\"0\"><tr>"
+                 "<th>Sch</th><th>Host</th><th>Stat</th>"
+                 "<th>Route</th><th>Redir</th>"
+                 "<th>F</th><th>Acc</th><th>Wr</th><th>Rd</th>"
+                 "</tr>\n", r);
+
+        worker = (proxy_worker *)balancer->workers->elts;
+        for (n = 0; n < balancer->workers->nelts; n++) {
+            char fbuf[50];
+            ap_rvputs(r, "<tr>\n<td>", worker->scheme, "</td>", NULL);
+            ap_rvputs(r, "<td>", worker->hostname, "</td><td>", NULL);
+            if (worker->s->status & PROXY_WORKER_DISABLED)
+                ap_rputs("Dis", r);
+            else if (worker->s->status & PROXY_WORKER_IN_ERROR)
+                ap_rputs("Err", r);
+            else if (worker->s->status & PROXY_WORKER_INITIALIZED)
+                ap_rputs("Ok", r);
+            else
+                ap_rputs("-", r);
+            ap_rvputs(r, "</td><td>", worker->s->route, NULL);
+            ap_rvputs(r, "</td><td>", worker->s->redirect, NULL);
+            ap_rprintf(r, "</td><td>%d</td>", worker->s->lbfactor);
+            ap_rprintf(r, "<td>%d</td><td>", (int)(worker->s->elected));
+            ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r);
+            ap_rputs("</td><td>", r);
+            ap_rputs(apr_strfsize(worker->s->read, fbuf), r);
+            ap_rputs("</td>\n", r);
+
+            /* TODO: Add the rest of dynamic worker data */
+            ap_rputs("</tr>\n", r);
+
+            ++worker;
+        }
+        ap_rputs("</table>\n", r);
+        ++balancer;
+    }
+    ap_rputs("<hr /><table>\n"
+             "<tr><th>SSes</th><td>Sticky session name</td></tr>\n"
+             "<tr><th>Timeout</th><td>Balancer Timeout</td></tr>\n"
+             "<tr><th>Sch</th><td>Connection scheme</td></tr>\n"
+             "<tr><th>Host</th><td>Backend Hostname</td></tr>\n"
+             "<tr><th>Stat</th><td>Worker status</td></tr>\n"
+             "<tr><th>Route</th><td>Session Route</td></tr>\n"
+             "<tr><th>Redir</th><td>Session Route Redirection</td></tr>\n"
+             "<tr><th>F</th><td>Load Balancer Factor in %</td></tr>\n"
+             "<tr><th>Acc</th><td>Number of requests</td></tr>\n"
+             "<tr><th>Wr</th><td>Number of bytes transferred</td></tr>\n"
+             "<tr><th>Rd</th><td>Number of bytes read</td></tr>\n"
+             "</table>", r);
 
     return OK;
 }
 
+static void child_init(apr_pool_t *p, server_rec *s)
+{
+    proxy_worker *reverse = NULL;
+    
+    while (s) {
+        void *sconf = s->module_config;
+        proxy_server_conf *conf;
+        proxy_worker *worker;
+        int i;
+
+        conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
+        /* Initialize worker's shared scoreboard data */ 
+        worker = (proxy_worker *)conf->workers->elts;
+        for (i = 0; i < conf->workers->nelts; i++) {
+            ap_proxy_initialize_worker_share(conf, worker, s);
+            ap_proxy_initialize_worker(worker, s);
+            worker++;
+        }
+        /* Initialize forward worker if defined */
+        if (conf->forward) {
+            ap_proxy_initialize_worker_share(conf, conf->forward, s);
+            ap_proxy_initialize_worker(conf->forward, s);
+            /* Do not disable worker in case of errors */
+            conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS;
+            /* Disable address cache for generic forward worker */ 
+            conf->forward->is_address_reusable = 0;
+        }
+        if (!reverse) {
+            reverse = ap_proxy_create_worker(p);
+            reverse->name     = "proxy:reverse";
+            reverse->hostname = "*";
+            reverse->scheme   = "*";
+            ap_proxy_initialize_worker_share(conf, reverse, s);
+            ap_proxy_initialize_worker(reverse, s);
+            /* Do not disable worker in case of errors */
+            reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS;
+            /* Disable address cache for generic reverse worker */ 
+            reverse->is_address_reusable = 0;
+        }
+        conf->reverse = reverse;
+        s = s->next;
+    }
+}
+
+/*
+ * This routine is called before the server processes the configuration
+ * files.  There is no return value.
+ */
+static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+                            apr_pool_t *ptemp)
+{
+    APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL,
+                      APR_HOOK_MIDDLE);
+    /* Reset workers count on gracefull restart */ 
+    proxy_lb_workers = 0;
+    return OK;
+}
+
 static void register_hooks(apr_pool_t *p)
 {
+    /* fixup before mod_rewrite, so that the proxied url will not
+     * escaped accidentally by our fixup.
+     */
+#ifndef FIX_15207
+    static const char * const aszSucc[]={ "mod_rewrite.c", NULL };
+#endif
+    /* Only the mpm_winnt has child init hook handler.
+     * make sure that we are called after the mpm
+     * initializes.
+     */
+    static const char *const aszPred[] = { "mpm_winnt.c", NULL};
+    
+    APR_REGISTER_OPTIONAL_FN(ap_proxy_lb_workers);
     /* handler */
     ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
     /* filename-to-URI translation */
     ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST);
     /* walk <Proxy > entries and suppress default TRACE behavior */
     ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
+#ifndef FIX_15207
     /* fixups */
-    ap_hook_fixups(proxy_fixup, NULL, NULL, APR_HOOK_FIRST);
+    ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST);
+#endif
     /* post read_request handling */
     ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST);
+    /* pre config handling */
+    ap_hook_pre_config(proxy_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 
     /* post config handling */
     ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+    /* child init handling */
+    ap_hook_child_init(child_init, aszPred, NULL, APR_HOOK_MIDDLE); 
+
 }
 
 module AP_MODULE_DECLARE_DATA proxy_module =
@@ -1093,22 +1870,41 @@ module AP_MODULE_DECLARE_DATA proxy_module =
     STANDARD20_MODULE_STUFF,
     create_proxy_dir_config,    /* create per-directory config structure */
     merge_proxy_dir_config,     /* merge per-directory config structures */
-    create_proxy_config,       /* create per-server config structure */
-    merge_proxy_config,                /* merge per-server config structures */
-    proxy_cmds,                        /* command table */
+    create_proxy_config,        /* create per-server config structure */
+    merge_proxy_config,         /* merge per-server config structures */
+    proxy_cmds,                 /* command table */
     register_hooks
 };
 
 APR_HOOK_STRUCT(
-       APR_HOOK_LINK(scheme_handler)
-       APR_HOOK_LINK(canon_handler)
+    APR_HOOK_LINK(scheme_handler)
+    APR_HOOK_LINK(canon_handler)
+    APR_HOOK_LINK(pre_request)
+    APR_HOOK_LINK(post_request)
 )
 
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, 
-                                     (request_rec *r, proxy_server_conf *conf, 
-                                     char *url, const char *proxyhost, 
-                                     apr_port_t proxyport),(r,conf,url,
-                                     proxyhost,proxyport),DECLINED)
+                                     (request_rec *r, proxy_worker *worker,
+                                      proxy_server_conf *conf, 
+                                      char *url, const char *proxyhost, 
+                                      apr_port_t proxyport),(r,worker,conf,
+                                      url,proxyhost,proxyport),DECLINED)
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler, 
-                                     (request_rec *r, char *url),(r,
-                                     url),DECLINED)
+                                      (request_rec *r, char *url),(r,
+                                      url),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, pre_request, (
+                                      proxy_worker **worker,
+                                      proxy_balancer **balancer,
+                                      request_rec *r, 
+                                      proxy_server_conf *conf,
+                                      char **url),(worker,balancer,
+                                      r,conf,url),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, post_request,
+                                      (proxy_worker *worker,
+                                       proxy_balancer *balancer,
+                                       request_rec *r,
+                                       proxy_server_conf *conf),(worker,
+                                       balancer,r,conf),DECLINED)
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups,
+                                    (request_rec *r), (r),
+                                    OK, DECLINED)