]> granicus.if.org Git - apache/blob - modules/proxy/proxy_balancer.c
Add failover attempts management to balancer-manager.
[apache] / modules / proxy / proxy_balancer.c
1 /* Copyright 1999-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /* Load balancer module for Apache proxy */
17
18 #define CORE_PRIVATE
19
20 #include "mod_proxy.h"
21 #include "ap_mpm.h"
22 #include "scoreboard.h"
23 #include "apr_version.h"
24
25 #if APR_HAVE_UNISTD_H
26 #include <unistd.h>         /* for getpid() */
27 #endif
28
29 module AP_MODULE_DECLARE_DATA proxy_balancer_module;
30
31 #if APR_HAS_THREADS
32 #define PROXY_BALANCER_LOCK(b)      apr_thread_mutex_lock((b)->mutex)
33 #define PROXY_BALANCER_UNLOCK(b)    apr_thread_mutex_unlock((b)->mutex)
34 #else
35 #define PROXY_BALANCER_LOCK(b)      APR_SUCCESS
36 #define PROXY_BALANCER_UNLOCK(b)    APR_SUCCESS
37 #endif
38
39 static int init_runtime_score(apr_pool_t *pool, proxy_balancer *balancer)
40 {
41     int i;
42     double median, ffactor = 0.0;
43     proxy_runtime_worker *workers;    
44 #if PROXY_HAS_SCOREBOARD
45     lb_score *score;
46 #else
47     void *score;
48 #endif
49
50     workers = (proxy_runtime_worker *)balancer->workers->elts;
51
52     for (i = 0; i < balancer->workers->nelts; i++) {
53         score = NULL;
54 #if PROXY_HAS_SCOREBOARD
55         /* Get scoreboard slot */
56         if (ap_scoreboard_image)
57             score = ap_get_scoreboard_lb(workers[i].id);
58 #endif
59         if (!score)
60             score = apr_pcalloc(pool, sizeof(proxy_runtime_stat));
61         workers[i].s = (proxy_runtime_stat *)score;
62     }
63
64     /* Recalculate lbfactors */
65     for (i = 0; i < balancer->workers->nelts; i++) {
66         /* Set to the original configuration */
67         workers[i].s->lbfactor = workers[i].w->lbfactor;
68         ffactor += workers[i].s->lbfactor;
69     }
70     if (ffactor < 100.0) {
71         int z = 0;
72         for (i = 0; i < balancer->workers->nelts; i++) {
73             if (workers[i].s->lbfactor == 0.0) 
74                 ++z;
75         }
76         if (z) {
77             median = (100.0 - ffactor) / z;
78             for (i = 0; i < balancer->workers->nelts; i++) {
79                 if (workers[i].s->lbfactor == 0.0) 
80                     workers[i].s->lbfactor = median;
81             }
82         }
83         else {
84             median = (100.0 - ffactor) / balancer->workers->nelts;
85             for (i = 0; i < balancer->workers->nelts; i++)
86                 workers[i].s->lbfactor += median;
87         }
88     }
89     else if (ffactor > 100.0) {
90         median = (ffactor - 100.0) / balancer->workers->nelts;
91         for (i = 0; i < balancer->workers->nelts; i++) {
92             if (workers[i].s->lbfactor > median)
93                 workers[i].s->lbfactor -= median;
94         }
95     } 
96     for (i = 0; i < balancer->workers->nelts; i++) {
97         /* Update the status entires */
98         workers[i].s->lbstatus = workers[i].s->lbfactor;
99     }
100     return 0;
101 }
102
103 /* Retrieve the parameter with the given name
104  * Something like 'JSESSIONID=12345...N'
105  */
106 static char *get_path_param(apr_pool_t *pool, char *url,
107                             const char *name)
108 {
109     char *path = NULL;
110     
111     for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
112         path += (strlen(name) + 1);
113         if (*path == '=') {
114             /*
115              * Session path was found, get it's value
116              */
117             ++path;
118             if (strlen(path)) {
119                 char *q;
120                 path = apr_pstrdup(pool, path);
121                 if ((q = strchr(path, '?')))
122                     *q = '\0';
123                 return path;
124             }
125         }
126     }
127     return NULL;
128 }
129
130 static char *get_cookie_param(request_rec *r, const char *name)
131 {
132     const char *cookies;
133     const char *start_cookie;
134
135     if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
136         for (start_cookie = ap_strstr_c(cookies, name); start_cookie; 
137              start_cookie = ap_strstr_c(start_cookie + 1, name)) {
138             if (start_cookie == cookies ||
139                 start_cookie[-1] == ';' ||
140                 start_cookie[-1] == ',' ||
141                 isspace(start_cookie[-1])) {
142                 
143                 start_cookie += strlen(name);
144                 while(*start_cookie && isspace(*start_cookie))
145                     ++start_cookie;
146                 if (*start_cookie == '=' && start_cookie[1]) {
147                     /*
148                      * Session cookie was found, get it's value
149                      */
150                     char *end_cookie, *cookie;
151                     ++start_cookie;
152                     cookie = apr_pstrdup(r->pool, start_cookie);
153                     if ((end_cookie = strchr(cookie, ';')) != NULL)
154                         *end_cookie = '\0';
155                     if((end_cookie = strchr(cookie, ',')) != NULL)
156                         *end_cookie = '\0';
157                     return cookie;
158                 }
159             }
160         }     
161     }
162     return NULL;
163 }
164
165 /* Find the worker that has the 'route' defined
166  */
167 static proxy_runtime_worker *find_route_worker(proxy_balancer *balancer,
168                                                const char *route)
169 {
170     int i;
171     proxy_runtime_worker *worker = (proxy_runtime_worker *)balancer->workers->elts;
172     for (i = 0; i < balancer->workers->nelts; i++) {
173         if (worker->w->route && strcmp(worker->w->route, route) == 0) {
174             return worker;
175         }
176         worker++;
177     }
178     return NULL;
179 }
180
181 static proxy_runtime_worker *find_session_route(proxy_balancer *balancer,
182                                                 request_rec *r,
183                                                 char **route,
184                                                 char **url)
185 {
186     if (!balancer->sticky)
187         return NULL;
188     /* Try to find the sticky route inside url */
189     *route = get_path_param(r->pool, *url, balancer->sticky);
190     if (!*route)
191         *route = get_cookie_param(r, balancer->sticky);
192     if (*route) {
193         /* We have a route in path or in cookie
194          * Find the worker that has this route defined.
195          */
196         proxy_runtime_worker *worker =  find_route_worker(balancer, *route);
197         if (worker && !PROXY_WORKER_IS_USABLE(worker->w)) {
198             /* We have a worker that is unusable.
199              * It can be in error or disabled, but in case
200              * it has a redirection set use that redirection worker.
201              * This enables to safely remove the member from the
202              * balancer. Of course you will need a some kind of
203              * session replication between those two remote.
204              */
205             if (worker->w->redirect)
206                 worker = find_route_worker(balancer, worker->w->redirect);
207             /* Check if the redirect worker is usable */
208             if (worker && !PROXY_WORKER_IS_USABLE(worker->w))
209                 worker = NULL;
210         }
211         else
212             worker = NULL;
213         return worker;
214     }
215     else
216         return NULL;
217 }
218
219 static proxy_runtime_worker *find_best_worker(proxy_balancer *balancer,
220                                               request_rec *r)
221 {
222     int i;
223     double total_factor = 0.0;
224     proxy_runtime_worker *worker = (proxy_runtime_worker *)balancer->workers->elts;
225     proxy_runtime_worker *candidate = NULL;
226
227     /* First try to see if we have available candidate */
228     for (i = 0; i < balancer->workers->nelts; i++) {
229         /* If the worker is in error state run
230          * retry on that worker. It will be marked as
231          * operational if the retry timeout is elapsed.
232          * The worker might still be unusable, but we try
233          * anyway.
234          */
235         if (!PROXY_WORKER_IS_USABLE(worker->w))
236             ap_proxy_retry_worker("BALANCER", worker->w, r->server);
237         /* Take into calculation only the workers that are
238          * not in error state or not disabled.
239          */
240         if (PROXY_WORKER_IS_USABLE(worker->w)) {
241             if (!candidate)
242                 candidate = worker;
243             else {
244                 /* See if the worker has a larger number of free channels.
245                  * This is used to favor the worker with the same balance
246                  * factor and higher free channel number.
247                  */
248                 if (worker->w->cp->nfree > candidate->w->cp->nfree)
249                     candidate = worker;
250             }
251             /* Total factor is always 100 if all workers are
252              * operational.
253              * If we have a 60-20-20 load factors and the third goes down
254              * the effective factors for the rest two will be 70-30.
255              */
256             total_factor += worker->s->lbfactor;
257         }
258         worker++;
259     }
260     if (!candidate) {
261         /* All the workers are in error state or disabled.
262          * If the balancer has a timeout sleep for a while
263          * and try again to find the worker. The chances are
264          * that some other thread will release a connection.
265          * By default the timeout is not set, and the server
266          * returns SERVER_BUSY.
267          */
268 #if APR_HAS_THREADS
269         if (balancer->timeout) {
270             /* XXX: This can perhaps be build using some 
271              * smarter mechanism, like tread_cond.
272              * But since the statuses can came from 
273              * different childs, use the provided algo. 
274              */
275             apr_interval_time_t timeout = balancer->timeout;
276             apr_interval_time_t step, tval = 0;
277             /* Set the timeout to 0 so that we don't
278              * end in infinite loop
279              */
280             balancer->timeout = 0;
281             step = timeout / 100;
282             while (tval < timeout) {
283                 apr_sleep(step);
284                 /* Try again */
285                 if ((candidate = find_best_worker(balancer, r)))
286                     break;
287                 tval += step;
288             }
289             /* restore the timeout */
290             balancer->timeout = timeout;
291         }
292 #endif
293     }
294     else {
295         /* We have at least one candidate that is not in
296          * error state or disabled.
297          * Now calculate the appropriate one.
298          */
299
300         /* Step one: Find the worker that has a lbfactor
301          * higher then a candidate found initially.
302          */
303         worker = (proxy_runtime_worker *)balancer->workers->elts;
304         for (i = 0; i < balancer->workers->nelts; i++) {
305             if (PROXY_WORKER_IS_USABLE(worker->w)) {
306                 if (worker->s->lbstatus > candidate->s->lbstatus) {
307                     candidate = worker;
308                 }
309             }
310             worker++;
311         }
312         /* Step two: Recalculate the load statuses.
313          * Each usable worker load status is incremented
314          * by it's load factor.
315          */
316         worker = (proxy_runtime_worker *)balancer->workers->elts;
317         for (i = 0; i < balancer->workers->nelts; i++) {
318             if (PROXY_WORKER_IS_USABLE(worker->w)) {
319                 /* Algo for w0(70%) w1(30%) tot(100):
320                  * E1. Elected w0 -- highest lbstatus
321                  *     w0 += 70 -> w0 = 140 -- ovreflow set to 70
322                  *     w1 += 30 -> w1 = 60
323                  * E2. Elected w0 -- highest lbstatus
324                  *     w0 += 70 -> w0 = 140 -- ovreflow set to 70
325                  *     w1 += 30 -> w1 = 90
326                  * E3. Elected w1 -- highest lbstatus
327                  *     w0 += 70 -> w0 = 140 -- ovreflow set to 70
328                  *     w1 += 30 -> w1 = 120 -- ovreflow set to 30
329                  * E4. Elected w0 -- highest lbstatus
330                  *     w0 += 70 -> w0 = 140 -- ovreflow set to 70
331                  *     w1 += 30 -> w1 = 60
332                  * etc...
333                  * Note:
334                  * Although the load is 70-30, the actual load is
335                  * 66-33, cause we can not have 2.33 : 1 requests,
336                  * but rather 2:1 that is the closest integer number.
337                  * In upper example the w0 will serve as twice as
338                  * many requests as w1 does.
339                  */
340                 worker->s->lbstatus += worker->s->lbfactor;
341                 if (worker->s->lbstatus >= total_factor)
342                     worker->s->lbstatus = worker->s->lbfactor;
343             }
344             worker++;
345         }
346     }
347     return candidate;
348 }
349
350 static int rewrite_url(request_rec *r, proxy_worker *worker,
351                         char **url)
352 {
353     const char *scheme = strstr(*url, "://");
354     const char *path = NULL;
355     
356     if (scheme)
357         path = ap_strchr_c(scheme + 3, '/');
358
359     /* we break the URL into host, port, uri */
360     if (!worker) {
361         return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool,
362                              "missing worker. URI cannot be parsed: ", *url,
363                              NULL));
364     }
365
366     *url = apr_pstrcat(r->pool, worker->name, path, NULL);
367    
368     return OK;
369 }
370
371 static int proxy_balancer_pre_request(proxy_worker **worker,
372                                       proxy_balancer **balancer,
373                                       request_rec *r,
374                                       proxy_server_conf *conf, char **url)
375 {
376     int access_status;
377     proxy_runtime_worker *runtime;
378     char *route;
379     apr_status_t rv;
380
381     *worker = NULL;
382     /* Step 1: check if the url is for us 
383      * The url we can handle starts with 'balancer://'
384      */
385     if (!(*balancer = ap_proxy_get_balancer(r->pool, conf, *url)))
386         return DECLINED;
387     
388     /* Step 2: find the session route */
389     
390     runtime = find_session_route(*balancer, r, &route, url);
391     if (!runtime) {
392         if (route && (*balancer)->sticky_force) {
393             ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
394                          "proxy: BALANCER: (%s). All workers are in error state for route (%s)",
395                          (*balancer)->name, route);
396             return HTTP_SERVICE_UNAVAILABLE;
397         }
398     }
399     else {
400         int i;
401         proxy_runtime_worker *workers;
402         /* We have a sticky load balancer */
403         runtime->s->elected++;
404         *worker = runtime->w;
405         /* Update the workers status 
406          * so that even session routes get
407          * into account.
408          */
409         workers = (proxy_runtime_worker *)(*balancer)->workers->elts;
410         for (i = 0; i < (*balancer)->workers->nelts; i++) {
411             /* For now assume that all workers are OK */
412             workers->s->lbstatus += workers->s->lbfactor;
413             if (workers->s->lbstatus >= 100.0)
414                 workers->s->lbstatus = workers->s->lbfactor;
415             workers++;
416         }
417     }
418     /* Lock the LoadBalancer
419      * XXX: perhaps we need the process lock here
420      */
421     if ((rv = PROXY_BALANCER_LOCK(*balancer)) != APR_SUCCESS) {
422         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
423                      "proxy: BALANCER: lock");
424         return DECLINED;
425     }
426     if (!*worker) {
427         runtime = find_best_worker(*balancer, r);
428         if (!runtime) {
429             ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
430                          "proxy: BALANCER: (%s). All workers are in error state",
431                          (*balancer)->name);
432         
433             PROXY_BALANCER_UNLOCK(*balancer);
434             return HTTP_SERVICE_UNAVAILABLE;
435         }
436         runtime->s->elected++;
437         *worker = runtime->w;
438     }
439     /* Decrease the free channels number */
440     /* XXX: This should be the function of apr_reslist
441      * There is a pending patch for apr_reslist that will
442      * enable to drop the need to maintain the free channels number
443      */
444     --(*worker)->cp->nfree;
445
446     PROXY_BALANCER_UNLOCK(*balancer);
447     
448     /* Rewrite the url from 'balancer://url'
449      * to the 'worker_scheme://worker_hostname[:worker_port]/url'
450      * This replaces the balancers fictional name with the
451      * real hostname of the elected worker.
452      */
453     access_status = rewrite_url(r, *worker, url);
454     /* Add the session route to request notes if present */
455     if (route) {
456         apr_table_setn(r->notes, "session-sticky", (*balancer)->sticky);
457         apr_table_setn(r->notes, "session-route", route);
458     }
459     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
460                  "proxy_balancer_pre_request rewriting to %s", *url);
461     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
462                  "proxy_balancer_pre_request worker (%s) free %d",
463                  (*worker)->name,
464                  (*worker)->cp->nfree);
465
466     return access_status;
467
468
469 static int proxy_balancer_post_request(proxy_worker *worker,
470                                        proxy_balancer *balancer,
471                                        request_rec *r,
472                                        proxy_server_conf *conf)
473 {
474     apr_status_t rv;
475
476     if ((rv = PROXY_BALANCER_LOCK(balancer)) != APR_SUCCESS) {
477         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
478             "proxy: BALANCER: lock");
479         return HTTP_INTERNAL_SERVER_ERROR;
480     }
481     /* increase the free channels number */
482     worker->cp->nfree++;
483     /* TODO: calculate the bytes transferred
484      * This will enable to elect the worker that has
485      * the lowest load.
486      * The bytes transferred depends on the protocol
487      * used, so each protocol handler should keep the
488      * track on that.
489      */
490
491     PROXY_BALANCER_UNLOCK(balancer);        
492     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
493                  "proxy_balancer_post_request for (%s)", balancer->name);
494
495     return OK;
496
497
498 static void recalc_factors(proxy_balancer *balancer,
499                            proxy_runtime_worker *fixed)
500 {
501     int i;
502     double median, ffactor = 0.0;
503     proxy_runtime_worker *workers;    
504
505
506     /* Recalculate lbfactors */
507     workers = (proxy_runtime_worker *)balancer->workers->elts;
508     /* Special case if there is only one worker it's
509      * load factor will always be 100
510      */
511     if (balancer->workers->nelts == 1) {
512         workers->s->lbstatus = workers->s->lbfactor = 100.0;
513         return;
514     }
515     for (i = 0; i < balancer->workers->nelts; i++) {
516         if (workers[i].s->lbfactor > 100.0)
517             workers[i].s->lbfactor = 100.0;
518         ffactor += workers[i].s->lbfactor;
519     }
520     if (ffactor < 100.0) {
521         median = (100.0 - ffactor) / (balancer->workers->nelts - 1);
522         for (i = 0; i < balancer->workers->nelts; i++) {
523             if (&(workers[i]) != fixed)
524                 workers[i].s->lbfactor += median;
525         }
526     }
527     else if (fixed->s->lbfactor < 100.0) {
528         median = (ffactor - 100.0) / (balancer->workers->nelts - 1);
529         for (i = 0; i < balancer->workers->nelts; i++) {
530             if (workers[i].s->lbfactor > median &&
531                 &(workers[i]) != fixed)
532                 workers[i].s->lbfactor -= median;
533         }
534     } 
535     else {
536         median = (ffactor - 100.0) / balancer->workers->nelts;
537         for (i = 0; i < balancer->workers->nelts; i++) {
538             workers[i].s->lbfactor -= median;
539         }
540     } 
541     for (i = 0; i < balancer->workers->nelts; i++) {
542         /* Update the status entires */
543         workers[i].s->lbstatus = workers[i].s->lbfactor;
544     }
545 }
546
547 /* Manages the loadfactors and member status 
548  */
549 static int balancer_handler(request_rec *r)
550 {
551     void *sconf = r->server->module_config;
552     proxy_server_conf *conf = (proxy_server_conf *)
553         ap_get_module_config(sconf, &proxy_module);
554     proxy_balancer *balancer, *bsel = NULL;
555     proxy_runtime_worker *worker, *wsel = NULL;
556     apr_table_t *params = apr_table_make(r->pool, 10);
557     int access_status;
558     int i, n;
559     const char *name;
560
561     /* is this for us? */
562     if (strcmp(r->handler, "balancer-manager"))
563         return DECLINED;
564     r->allowed = (AP_METHOD_BIT << M_GET);
565     if (r->method_number != M_GET)
566         return DECLINED;
567
568     if (r->args) {
569         char *args = apr_pstrdup(r->pool, r->args);
570         char *tok, *val;
571         while (args && *args) {
572             if ((val = ap_strchr(args, '='))) {
573                 *val++ = '\0';
574                 if ((tok = ap_strchr(val, '&')))
575                     *tok++ = '\0';
576                 if ((access_status = ap_unescape_url(val)) != OK)
577                     return access_status;
578                 apr_table_setn(params, args, val);
579                 args = tok;
580             }
581             else
582                 return HTTP_BAD_REQUEST;
583         }
584     }
585     if ((name = apr_table_get(params, "b")))
586         bsel = ap_proxy_get_balancer(r->pool, conf,
587             apr_pstrcat(r->pool, "balancer://", name, NULL));
588     if ((name = apr_table_get(params, "w"))) {
589         const char *sc = apr_table_get(params, "s");
590         char *asname = NULL;
591         proxy_worker *ws = NULL;
592         if (sc) {
593             asname = apr_pstrcat(r->pool, sc, "://", name, NULL);
594             ws = ap_proxy_get_worker(r->pool, conf, asname);
595         }
596         if (ws) {
597             worker = (proxy_runtime_worker *)bsel->workers->elts;
598             for (n = 0; n < bsel->workers->nelts; n++) {
599                 if (strcasecmp(worker->w->name, ws->name) == 0) {
600                     wsel = worker;
601                     break;
602                 }
603                 ++worker;
604             }
605         }
606     }
607     /* First set the params */
608     if (bsel) {
609         const char *val;
610         if ((val = apr_table_get(params, "ss"))) {
611             if (strlen(val))
612                 bsel->sticky = apr_pstrdup(conf->pool, val);
613             else
614                 bsel->sticky = NULL;
615         }
616         if ((val = apr_table_get(params, "tm"))) {
617             int ival = atoi(val);
618             if (ival >= 0)
619                 bsel->timeout = apr_time_from_sec(ival);
620         }
621         if ((val = apr_table_get(params, "fa"))) {
622             int ival = atoi(val);
623             if (ival >= 0)
624                 bsel->max_attempts = ival;
625             bsel->max_attempts_set = 1;
626         }
627     }
628     if (wsel) {
629         const char *val;
630         if ((val = apr_table_get(params, "lf"))) {
631             char *ep;
632             double dval = strtod(val, &ep);
633             if (dval > 1) {
634                 wsel->s->lbfactor = dval;
635                 if (bsel)
636                     recalc_factors(bsel, wsel);
637             }
638         }
639         if ((val = apr_table_get(params, "wr"))) {
640             if (strlen(val))
641                 wsel->w->route = apr_pstrdup(conf->pool, val);
642             else
643                 wsel->w->route = NULL;
644         }
645         if ((val = apr_table_get(params, "rr"))) {
646             if (strlen(val))
647                 wsel->w->redirect = apr_pstrdup(conf->pool, val);
648             else
649                 wsel->w->redirect = NULL;
650         }
651         if ((val = apr_table_get(params, "dw")))
652             wsel->w->status |= PROXY_WORKER_DISABLED;
653         else
654             wsel->w->status &= ~PROXY_WORKER_DISABLED;
655
656     }
657     if (apr_table_get(params, "xml")) {
658         ap_set_content_type(r, "text/xml");
659         ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n", r);
660         ap_rputs("<httpd:manager xmlns:httpd=\"http://httpd.apache.org\">\n", r);
661         ap_rputs("  <httpd:balancers>\n", r);
662         balancer = (proxy_balancer *)conf->balancers->elts;
663         for (i = 0; i < conf->balancers->nelts; i++) {
664             ap_rputs("    <httpd:balancer>\n", r);
665             ap_rvputs(r, "      <httpd:name>", balancer->name, "</httpd:name>\n", NULL);
666             ap_rputs("      <httpd:workers>\n", r);
667             worker = (proxy_runtime_worker *)balancer->workers->elts;
668             for (n = 0; n < balancer->workers->nelts; n++) {
669                 ap_rputs("        <httpd:worker>\n", r);
670                 ap_rvputs(r, "          <httpd:scheme>", worker->w->scheme,
671                           "</httpd:scheme>\n", NULL);                
672                 ap_rvputs(r, "          <httpd:hostname>", worker->w->hostname,
673                           "</httpd:hostname>\n", NULL);                
674                ap_rprintf(r, "          <httpd:loadfactor>%.2f</httpd:loadfactor>\n",
675                           worker->s->lbfactor);
676                 ap_rputs("        </httpd:worker>\n", r);
677                 ++worker;
678             }
679             ap_rputs("      </httpd:workers>\n", r);
680             ap_rputs("    </httpd:balancer>\n", r);
681             ++balancer;
682         }
683         ap_rputs("  </httpd:balancers>\n", r);
684         ap_rputs("</httpd:manager>", r);         
685     }
686     else {
687         ap_set_content_type(r, "text/html");
688         ap_rputs(DOCTYPE_HTML_3_2
689                  "<html><head><title>Balancer Manager</title></head>\n", r);
690         ap_rputs("<body><h1>Load Balancer Manager for ", r);
691         ap_rvputs(r, ap_get_server_name(r), "</h1>\n\n", NULL);
692         ap_rvputs(r, "<dl><dt>Server Version: ",
693                   ap_get_server_version(), "</dt>\n", NULL);
694         ap_rvputs(r, "<dt>Server Built: ",
695                   ap_get_server_built(), "\n</dt></dl>\n", NULL);
696         balancer = (proxy_balancer *)conf->balancers->elts;
697         for (i = 0; i < conf->balancers->nelts; i++) {
698
699             ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r);
700             ap_rvputs(r, "<a href=\"", r->uri, "?b=",
701                       balancer->name + sizeof("balancer://") - 1,
702                       "\">", NULL); 
703             ap_rvputs(r, balancer->name, "</a></h3>\n\n", NULL);
704             ap_rputs("\n\n<table border=\"0\"><tr>"
705                 "<th>StickySesion</th><th>Timeout</th><th>FailoverAttempts</th>"
706                 "</tr>\n<tr>", r);                
707             ap_rvputs(r, "<td>", balancer->sticky, NULL);
708             ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
709                 apr_time_sec(balancer->timeout));
710             ap_rprintf(r, "<td>%d</td>\n", balancer->max_attempts);
711             ap_rputs("</table>\n", r);
712             ap_rputs("\n\n<table border=\"0\"><tr>"
713                 "<th>Scheme</th><th>Host</th>"
714                 "<th>Route</th><th>RouteRedir</th>"
715                 "<th>Factor</th><th>Status</th>"
716                 "</tr>\n", r);
717
718             worker = (proxy_runtime_worker *)balancer->workers->elts;
719             for (n = 0; n < balancer->workers->nelts; n++) {
720
721                 ap_rvputs(r, "<tr>\n<td>", worker->w->scheme, "</td><td>", NULL);
722                 ap_rvputs(r, "<a href=\"", r->uri, "?b=", 
723                           balancer->name + sizeof("balancer://") - 1,
724                           "&s=", worker->w->scheme, "&w=", worker->w->hostname,
725                           "\">", NULL); 
726                 ap_rvputs(r, worker->w->hostname, "</a></td>", NULL);
727                 ap_rvputs(r, "<td>", worker->w->route, NULL);
728                 ap_rvputs(r, "</td><td>", worker->w->redirect, NULL);
729                 ap_rprintf(r, "</td><td>%.2f</td><td>", worker->s->lbfactor);
730                 if (worker->w->status & PROXY_WORKER_DISABLED)
731                     ap_rputs("Dis", r);
732                 else if (worker->w->status & PROXY_WORKER_IN_ERROR)
733                     ap_rputs("Err", r);
734                 else if (worker->w->status & PROXY_WORKER_INITIALIZED)
735                     ap_rputs("Ok", r);
736                 else
737                     ap_rputs("-", r);
738                 ap_rputs("</td></tr>\n", r);
739
740                 ++worker;
741             }
742             ap_rputs("</table>\n", r);
743             ++balancer;
744         }
745         ap_rputs("<hr />\n", r);
746         if (wsel && bsel) {
747             ap_rputs("<h3>Edit worker settings for ", r);
748             ap_rvputs(r, wsel->w->name, "</h3>\n", NULL);
749             ap_rvputs(r, "<form method=\"GET\" action=\"", NULL);
750             ap_rvputs(r, r->uri, "\">\n<dl>", NULL); 
751             ap_rputs("<table><tr><td>Load factor:</td><td><input name=\"lf\" type=text ", r);
752             ap_rprintf(r, "value=\"%.2f\"></td><tr>\n", wsel->s->lbfactor);            
753             ap_rputs("<tr><td>Route:</td><td><input name=\"wr\" type=text ", r);
754             ap_rvputs(r, "value=\"", wsel->w->route, NULL); 
755             ap_rputs("\"></td><tr>\n", r);            
756             ap_rputs("<tr><td>Route Redirect:</td><td><input name=\"rr\" type=text ", r);
757             ap_rvputs(r, "value=\"", wsel->w->redirect, NULL); 
758             ap_rputs("\"></td><tr>\n", r);            
759             ap_rputs("<tr><td>Disabled:</td><td><input name=\"dw\" type=checkbox", r);
760             if (wsel->w->status & PROXY_WORKER_DISABLED)
761                 ap_rputs(" checked", r);
762             ap_rputs("></td><tr>\n", r);            
763             ap_rputs("<tr><td colspan=2><input type=submit value=\"Submit\"></td></tr>\n", r);
764             ap_rvputs(r, "</table>\n<input type=hidden name=\"s\" ", NULL);
765             ap_rvputs(r, "value=\"", wsel->w->scheme, "\">\n", NULL);
766             ap_rvputs(r, "<input type=hidden name=\"w\" ", NULL);
767             ap_rvputs(r, "value=\"", wsel->w->hostname, "\">\n", NULL);
768             ap_rvputs(r, "<input type=hidden name=\"b\" ", NULL);
769             ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1,
770                       "\">\n</form>\n", NULL);
771             ap_rputs("<hr />\n", r);
772         }
773         else if (bsel) {
774             ap_rputs("<h3>Edit balancer settings for ", r);
775             ap_rvputs(r, bsel->name, "</h3>\n", NULL);
776             ap_rvputs(r, "<form method=\"GET\" action=\"", NULL);
777             ap_rvputs(r, r->uri, "\">\n<dl>", NULL); 
778             ap_rputs("<table><tr><td>StickySession Identifier:</td><td><input name=\"ss\" type=text ", r);
779             if (bsel->sticky)
780                 ap_rvputs(r, "value=\"", bsel->sticky, "\"", NULL);
781             ap_rputs("></td><tr>\n<tr><td>Timeout:</td><td><input name=\"tm\" type=text ", r);
782             ap_rprintf(r, "value=\"%" APR_TIME_T_FMT "\"></td></tr>\n",
783                        apr_time_sec(bsel->timeout));
784             ap_rputs("<tr><td>Failover Attempts:</td><td><input name=\"fa\" type=text ", r);
785             ap_rprintf(r, "value=\"%d\"></td></tr>\n",
786                        bsel->max_attempts);
787             ap_rputs("<tr><td colspan=2><input type=submit value=\"Submit\"></td></tr>\n", r);
788             ap_rvputs(r, "</table>\n<input type=hidden name=\"b\" ", NULL);
789             ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1,
790                       "\">\n</form>\n", NULL);
791             ap_rputs("<hr />\n", r);
792         }
793         ap_rputs(ap_psignature("",r), r);
794         ap_rputs("</body></html>\n", r);
795     }
796     return OK;
797 }
798
799 static void child_init(apr_pool_t *p, server_rec *s)
800 {
801     void *sconf = s->module_config;
802     proxy_server_conf *conf = (proxy_server_conf *)
803         ap_get_module_config(sconf, &proxy_module);
804     proxy_balancer *balancer;
805     int i;
806     
807     /* Initialize shared scoreboard data */ 
808     balancer = (proxy_balancer *)conf->balancers->elts;
809     for (i = 0; i < conf->balancers->nelts; i++) {
810         init_runtime_score(conf->pool, balancer);
811         balancer++;
812     }
813
814 }
815
816 static void ap_proxy_balancer_register_hook(apr_pool_t *p)
817 {
818     /* manager handler */
819     ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST);
820     ap_hook_child_init(child_init, NULL, NULL, APR_HOOK_MIDDLE); 
821     proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST);    
822     proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST);    
823 }
824
825 module AP_MODULE_DECLARE_DATA proxy_balancer_module = {
826     STANDARD20_MODULE_STUFF,
827     NULL,       /* create per-directory config structure */
828     NULL,       /* merge per-directory config structures */
829     NULL,       /* create per-server config structure */
830     NULL,       /* merge per-server config structures */
831     NULL,       /* command apr_table_t */
832     ap_proxy_balancer_register_hook /* register hooks */
833 };