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