]> 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 3d61c85a01fa0d8c620331f1c8635df03dca781b..15372beb7b80ffe9cacd5c87508d61030dd7d99c 100644 (file)
@@ -1,4 +1,3 @@
-#define FIX_15207
 /* Copyright 1999-2004 The Apache Software Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +18,7 @@
 #include "mod_proxy.h"
 #include "mod_core.h"
 #include "apr_optional.h"
+#include "scoreboard.h"
 #include "mod_status.h"
 
 #if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
@@ -90,29 +90,43 @@ static const char *set_worker_param(apr_pool_t *p,
 
     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";
@@ -120,12 +134,19 @@ static const char *set_worker_param(apr_pool_t *p,
     }
     /* 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";
@@ -133,6 +154,9 @@ static const char *set_worker_param(apr_pool_t *p,
         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";
@@ -162,9 +186,17 @@ static const char *set_worker_param(apr_pool_t *p,
         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 {
@@ -181,9 +213,17 @@ static const char *set_balancer_param(apr_pool_t *p,
 
     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"))
@@ -192,11 +232,35 @@ static const char *set_balancer_param(apr_pool_t *p,
             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";
     }
@@ -302,7 +366,7 @@ static int proxy_detect(request_rec *r)
     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))))) {
@@ -326,6 +390,9 @@ static int proxy_detect(request_rec *r)
         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";
@@ -536,6 +603,7 @@ static int proxy_handler(request_rec *r)
     long maxfwd;
     proxy_balancer *balancer = NULL;
     proxy_worker *worker = NULL;
+    int attempts = 0, max_attempts = 0;
 
     /* is this for us? */
     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
@@ -579,81 +647,98 @@ static int proxy_handler(request_rec *r)
     apr_table_set(r->headers_in, "Max-Forwards", 
                   apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0));
 
-    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;
-    }
-
-    /* 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, 0, r,
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                       (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
                       r->uri);
 #endif
-    }
-    
-    /* 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;
+        }
     
-    /* 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;
+        /* 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, 0, r->server,
-                 "Trying to run scheme_handler");
-    access_status = proxy_run_scheme_handler(r, worker, conf, url, NULL, 0);
     if (DECLINED == access_status) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
                     "proxy: No protocol handler was valid for the URL %s. "
@@ -663,7 +748,6 @@ static int proxy_handler(request_rec *r)
         access_status = HTTP_FORBIDDEN;
         goto cleanup;
     }
-
 cleanup:
     if (balancer) {
         int post_status = proxy_run_post_request(worker, balancer, r, conf);
@@ -695,6 +779,8 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s)
     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 */
@@ -714,6 +800,7 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s)
     ps->timeout_set = 0;
     ps->badopt = bad_error;
     ps->badopt_set = 0;
+    ps->pool = p;
     return ps;
 }
 
@@ -738,6 +825,8 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
     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;
@@ -749,7 +838,8 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
     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;
 }
 
@@ -891,6 +981,8 @@ static const char *
     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;
@@ -1116,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)
 {
@@ -1334,7 +1434,6 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
     }
     /* Add the worker to the load balancer */
     ap_proxy_add_worker_to_balancer(cmd->pool, balancer, worker);
-
     return NULL;
 }
 
@@ -1358,7 +1457,7 @@ static const char *
          * Parent directive arg is the worker/balancer name.
          */
         name = ap_getword_conf(cmd->temp_pool, &pargs);
-        if ((word = ap_strchr_c(name, '>')))
+        if ((word = ap_strchr(name, '>')))
             *word = '\0';
     }
     else {
@@ -1581,31 +1680,13 @@ PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
 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;
 }
 
-
-#define KBYTE 1024
-#define MBYTE 1048576L
-#define GBYTE 1073741824L
-
-/* Format the number of bytes nicely */
-static void format_byte_out(request_rec *r, apr_off_t bytes)
-{
-
-    if (bytes < (5 * KBYTE))
-        ap_rprintf(r, "%d B", (int) bytes);
-    else if (bytes < (MBYTE / 2))
-        ap_rprintf(r, "%.1f kB", (float) bytes / KBYTE);
-    else if (bytes < (GBYTE / 2))
-        ap_rprintf(r, "%.1f MB", (float) bytes / MBYTE);
-    else
-        ap_rprintf(r, "%.1f GB", (float) bytes / GBYTE);
-}
-
 /*
  *  proxy Extension to mod_status
  */
@@ -1616,7 +1697,7 @@ static int proxy_status_hook(request_rec *r, int flags)
     proxy_server_conf *conf = (proxy_server_conf *)
         ap_get_module_config(sconf, &proxy_module);
     proxy_balancer *balancer = NULL;
-    proxy_runtime_worker *worker = NULL;
+    proxy_worker *worker = NULL;
 
     if (flags & AP_STATUS_SHORT || conf->balancers->nelts == 0 ||
         conf->proxy_status == status_off)
@@ -1627,30 +1708,42 @@ static int proxy_status_hook(request_rec *r, int flags)
         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>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>\n",
+        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>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_runtime_worker *)balancer->workers->elts;
+        worker = (proxy_worker *)balancer->workers->elts;
         for (n = 0; n < balancer->workers->nelts; n++) {
-
-            ap_rvputs(r, "<tr>\n<td>", worker->w->scheme, "</td>", NULL);
-            ap_rvputs(r, "<td>", worker->w->hostname, "</td>", NULL);
-            ap_rvputs(r, "<td>", worker->w->route, NULL);
-            ap_rvputs(r, "</td><td>", worker->w->redirect, NULL);
-            ap_rprintf(r, "</td><td>%.2f</td>", worker->s->lbfactor);
+            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));
-            format_byte_out(r, worker->s->transfered);
+            ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r);
             ap_rputs("</td><td>", r);
-            format_byte_out(r, worker->s->transfered);
+            ap_rputs(apr_strfsize(worker->s->read, fbuf), r);
             ap_rputs("</td>\n", r);
 
             /* TODO: Add the rest of dynamic worker data */
@@ -1666,17 +1759,62 @@ static int proxy_status_hook(request_rec *r, int flags)
              "<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 transfered</td></tr>\n"
-             "<tr><th>Rd</th><td>Number of bytes readed</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.
@@ -1686,6 +1824,8 @@ static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
 {
     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;
 }
 
@@ -1697,7 +1837,13 @@ static void register_hooks(apr_pool_t *p)
 #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 */
@@ -1714,6 +1860,9 @@ static void register_hooks(apr_pool_t *p)
     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 =