]> granicus.if.org Git - apache/blob - modules/proxy/proxy_balancer.c
Move the shared scoreboard data initization to the balancer module.
[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 module AP_MODULE_DECLARE_DATA proxy_balancer_module;
26
27 #if APR_HAS_THREADS
28 #define PROXY_BALANCER_LOCK(b)      apr_thread_mutex_lock((b)->mutex)
29 #define PROXY_BALANCER_UNLOCK(b)    apr_thread_mutex_unlock((b)->mutex)
30 #else
31 #define PROXY_BALANCER_LOCK(b)      APR_SUCCESS
32 #define PROXY_BALANCER_UNLOCK(b)    APR_SUCCESS
33 #endif
34
35 static int init_runtime_score(apr_pool_t *pool, proxy_balancer *balancer)
36 {
37     int i;
38     double median, ffactor = 0.0;
39     proxy_runtime_worker *workers;    
40 #if PROXY_HAS_SCOREBOARD
41     lb_score *score;
42     int mpm_daemons;
43 #else
44     void *score;
45 #endif
46
47     workers = (proxy_runtime_worker *)balancer->workers->elts;
48
49     for (i = 0; i < balancer->workers->nelts; i++) {
50 #if PROXY_HAS_SCOREBOARD
51         ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &mpm_daemons);
52         /* Check if we are prefork or single child */
53         if (workers[i].w->hmax && mpm_daemons > 1) {
54             score = ap_get_scoreboard_lb(getpid(), workers[i].id);
55         }
56         else
57 #endif
58         {
59             /* Use the plain memory */
60             score = apr_pcalloc(pool, sizeof(proxy_runtime_stat));
61         }
62         workers[i].s = (proxy_runtime_stat *)score;
63     }
64
65     /* Recalculate lbfactors */
66     for (i = 0; i < balancer->workers->nelts; i++) {
67         /* Set to the original configuration */
68         workers[i].s->lbfactor = workers[i].w->lbfactor;
69         ffactor += workers[i].s->lbfactor;
70     }
71     if (ffactor < 100.0) {
72         int z = 0;
73         for (i = 0; i < balancer->workers->nelts; i++) {
74             if (workers[i].s->lbfactor == 0.0) 
75                 ++z;
76         }
77         if (z) {
78             median = (100.0 - ffactor) / z;
79             for (i = 0; i < balancer->workers->nelts; i++) {
80                 if (workers[i].s->lbfactor == 0.0) 
81                     workers[i].s->lbfactor = median;
82             }
83         }
84         else {
85             median = (100.0 - ffactor) / balancer->workers->nelts;
86             for (i = 0; i < balancer->workers->nelts; i++)
87                 workers[i].s->lbfactor += median;
88         }
89     }
90     else if (ffactor > 100.0) {
91         median = (ffactor - 100.0) / balancer->workers->nelts;
92         for (i = 0; i < balancer->workers->nelts; i++) {
93             if (workers[i].s->lbfactor > median)
94                 workers[i].s->lbfactor -= median;
95         }
96     } 
97     for (i = 0; i < balancer->workers->nelts; i++) {
98         /* Update the status entires */
99         workers[i].s->lbstatus = workers[i].s->lbfactor;
100     }
101     balancer->status = 1;
102     return 0;
103 }
104
105 /* Retrieve the parameter with the given name                                */
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 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         proxy_runtime_worker *worker =  find_route_worker(balancer, *route);
192         /* TODO: make worker status codes */
193         /* See if we have a redirection route */
194         if (worker && !PROXY_WORKER_IS_USABLE(worker->w)) {
195             if (worker->w->redirect)
196                 worker = find_route_worker(balancer, worker->w->redirect);
197             /* Check if the redirect worker is usable */
198             if (worker && !PROXY_WORKER_IS_USABLE(worker->w))
199                 worker = NULL;
200         }
201         else
202             worker = NULL;
203         return worker;
204     }
205     else
206         return NULL;
207 }
208
209 static proxy_runtime_worker *find_best_worker(proxy_balancer *balancer,
210                                               request_rec *r)
211 {
212     int i;
213     double total_factor = 0.0;
214     proxy_runtime_worker *worker = (proxy_runtime_worker *)balancer->workers->elts;
215     proxy_runtime_worker *candidate = NULL;
216
217     /* First try to see if we have available candidate */
218     for (i = 0; i < balancer->workers->nelts; i++) {
219         /* See if the retry timeout is ellapsed
220          * for the workers flagged as IN_ERROR
221          */
222         if (!PROXY_WORKER_IS_USABLE(worker->w))
223             ap_proxy_retry_worker("BALANCER", worker->w, r->server);
224         /* If the worker is not in error state
225          * or not disabled.
226          */
227         if (PROXY_WORKER_IS_USABLE(worker->w)) {
228             if (!candidate)
229                 candidate = worker;
230             else {
231                 /* See if the worker has a larger number of free channels */
232                 if (worker->w->cp->nfree > candidate->w->cp->nfree)
233                     candidate = worker;
234             }
235             /* Total factor should allways be 100.
236              * This is for cases when worker is in error state.
237              * It will force the even request distribution
238              */
239             total_factor += worker->s->lbfactor;
240         }
241         worker++;
242     }
243     if (!candidate) {
244         /* All the workers are in error state or disabled.
245          * If the balancer has a timeout wait.
246          */
247 #if APR_HAS_THREADS
248         if (balancer->timeout) {
249             /* XXX: This can perhaps be build using some 
250              * smarter mechanism, like tread_cond.
251              * But since the statuses can came from 
252              * different childs, use the provided algo. 
253              */
254             apr_interval_time_t timeout = balancer->timeout;
255             apr_interval_time_t step, tval = 0;
256             balancer->timeout = 0;
257             step = timeout / 100;
258             while (tval < timeout) {
259                 apr_sleep(step);
260                 /* Try again */
261                 if ((candidate = find_best_worker(balancer, r)))
262                     break;
263                 tval += step;
264             }
265             /* restore the timeout */
266             balancer->timeout = timeout;
267         }
268 #endif
269     }
270     else {
271         /* We have at least one candidate that is not in
272          * error state or disabled.
273          * Now calculate the appropriate one 
274          */
275         worker = (proxy_runtime_worker *)balancer->workers->elts;
276         for (i = 0; i < balancer->workers->nelts; i++) {
277             /* If the worker is not error state
278              * or not in disabled mode
279              */
280             if (PROXY_WORKER_IS_USABLE(worker->w)) {
281                 /* 1. Find the worker with higher lbstatus.
282                  * Lbstatus is of higher importance then
283                  * the number of empty slots.
284                  */
285                 if (worker->s->lbstatus > candidate->s->lbstatus) {
286                     candidate = worker;
287                 }
288             }
289             worker++;
290         }
291         worker = (proxy_runtime_worker *)balancer->workers->elts;
292         for (i = 0; i < balancer->workers->nelts; i++) {
293             /* If the worker is not error state
294              * or not in disabled mode
295              */
296             if (PROXY_WORKER_IS_USABLE(worker->w)) {
297                 /* XXX: The lbfactor can be update using bytes transfered
298                  * Right now, use the round-robin scheme
299                  */
300                 worker->s->lbstatus += worker->s->lbfactor;
301                 if (worker->s->lbstatus >= total_factor)
302                     worker->s->lbstatus = worker->s->lbfactor;
303             }
304             worker++;
305         }
306     }
307     return candidate;
308 }
309
310 static int rewrite_url(request_rec *r, proxy_worker *worker,
311                         char **url)
312 {
313     const char *scheme = strstr(*url, "://");
314     const char *path = NULL;
315     
316     if (scheme)
317         path = ap_strchr_c(scheme + 3, '/');
318
319     /* we break the URL into host, port, uri */
320     if (!worker) {
321         return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool,
322                              "missing worker. URI cannot be parsed: ", *url,
323                              NULL));
324     }
325
326     *url = apr_pstrcat(r->pool, worker->name, path, NULL);
327    
328     return OK;
329 }
330
331 static int proxy_balancer_pre_request(proxy_worker **worker,
332                                       proxy_balancer **balancer,
333                                       request_rec *r,
334                                       proxy_server_conf *conf, char **url)
335 {
336     int access_status;
337     proxy_runtime_worker *runtime;
338     char *route;
339     apr_status_t rv;
340
341     *worker = NULL;
342     /* Step 1: check if the url is for us */
343     if (!(*balancer = ap_proxy_get_balancer(r->pool, conf, *url)))
344         return DECLINED;
345     
346     /* Initialize shared scoreboard data */ 
347     if (!((*balancer)->status))
348         init_runtime_score(conf->pool, *balancer);
349
350     /* Step 2: find the session route */
351     
352     runtime = find_session_route(*balancer, r, &route, url);
353     if (!runtime) {
354         if (route && (*balancer)->sticky_force) {
355             ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
356                          "proxy: BALANCER: (%s). All workers are in error state for route (%s)",
357                          (*balancer)->name, route);
358             return HTTP_SERVICE_UNAVAILABLE;
359         }
360     }
361     else {
362         int i;
363         proxy_runtime_worker *workers;
364         /* We have a sticky load balancer */
365         runtime->s->elected++;
366         *worker = runtime->w;
367         /* Update the workers status 
368          * so that even session routes get
369          * into account.
370          */
371         workers = (proxy_runtime_worker *)(*balancer)->workers->elts;
372         for (i = 0; i < (*balancer)->workers->nelts; i++) {
373             /* For now assume that all workers are OK */
374             workers->s->lbstatus += workers->s->lbfactor;
375             if (workers->s->lbstatus >= 100.0)
376                 workers->s->lbstatus = workers->s->lbfactor;
377             workers++;
378         }
379     }
380     /* Lock the LoadBalancer
381      * XXX: perhaps we need the process lock here
382      */
383     if ((rv = PROXY_BALANCER_LOCK(*balancer)) != APR_SUCCESS) {
384         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
385                      "proxy: BALANCER: lock");
386         return DECLINED;
387     }
388     if (!*worker) {
389         runtime = find_best_worker(*balancer, r);
390         if (!runtime) {
391             ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
392                          "proxy: BALANCER: (%s). All workers are in error state",
393                          (*balancer)->name);
394         
395             PROXY_BALANCER_UNLOCK(*balancer);
396             return HTTP_SERVICE_UNAVAILABLE;
397         }
398         runtime->s->elected++;
399         *worker = runtime->w;
400     }
401     /* Decrease the free channels number */
402     /* XXX: This should be the function of apr_reslist */
403     --(*worker)->cp->nfree;
404
405     PROXY_BALANCER_UNLOCK(*balancer);
406     
407     access_status = rewrite_url(r, *worker, url);
408     /* Add the session route to request notes if present */
409     if (route) {
410         apr_table_setn(r->notes, "session-sticky", (*balancer)->sticky);
411         apr_table_setn(r->notes, "session-route", route);
412     }
413     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
414                  "proxy_balancer_pre_request rewriting to %s", *url);
415     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
416                  "proxy_balancer_pre_request worker (%s) free %d",
417                  (*worker)->name,
418                  (*worker)->cp->nfree);
419
420     return access_status;
421
422
423 static int proxy_balancer_post_request(proxy_worker *worker,
424                                        proxy_balancer *balancer,
425                                        request_rec *r,
426                                        proxy_server_conf *conf)
427 {
428     apr_status_t rv;
429
430     if ((rv = PROXY_BALANCER_LOCK(balancer)) != APR_SUCCESS) {
431         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
432             "proxy: BALANCER: lock");
433         return HTTP_INTERNAL_SERVER_ERROR;
434     }
435     /* increase the free channels number */
436     worker->cp->nfree++;
437     /* TODO: calculate the bytes transfered */
438
439     /* TODO: update the scoreboard status */
440
441     PROXY_BALANCER_UNLOCK(balancer);        
442     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
443                  "proxy_balancer_post_request for (%s)", balancer->name);
444
445     return OK;
446
447
448 static void recalc_factors(proxy_balancer *balancer,
449                            proxy_runtime_worker *fixed)
450 {
451     int i;
452     double median, ffactor = 0.0;
453     proxy_runtime_worker *workers;    
454
455
456     /* Recalculate lbfactors */
457     workers = (proxy_runtime_worker *)balancer->workers->elts;
458     /* Special case if there is only one worker it's
459      * load factor will always be 100
460      */
461     if (balancer->workers->nelts == 1) {
462         workers->s->lbstatus = workers->s->lbfactor = 100.0;
463         return;
464     }
465     for (i = 0; i < balancer->workers->nelts; i++) {
466         if (workers[i].s->lbfactor > 100.0)
467             workers[i].s->lbfactor = 100.0;
468         ffactor += workers[i].s->lbfactor;
469     }
470     if (ffactor < 100.0) {
471         median = (100.0 - ffactor) / (balancer->workers->nelts - 1);
472         for (i = 0; i < balancer->workers->nelts; i++) {
473             if (&(workers[i]) != fixed)
474                 workers[i].s->lbfactor += median;
475         }
476     }
477     else if (fixed->s->lbfactor < 100.0) {
478         median = (ffactor - 100.0) / (balancer->workers->nelts - 1);
479         for (i = 0; i < balancer->workers->nelts; i++) {
480             if (workers[i].s->lbfactor > median &&
481                 &(workers[i]) != fixed)
482                 workers[i].s->lbfactor -= median;
483         }
484     } 
485     else {
486         median = (ffactor - 100.0) / balancer->workers->nelts;
487         for (i = 0; i < balancer->workers->nelts; i++) {
488             workers[i].s->lbfactor -= median;
489         }
490     } 
491     for (i = 0; i < balancer->workers->nelts; i++) {
492         /* Update the status entires */
493         workers[i].s->lbstatus = workers[i].s->lbfactor;
494     }
495 }
496
497 /* Invoke handler */
498 static int balancer_handler(request_rec *r)
499 {
500     void *sconf = r->server->module_config;
501     proxy_server_conf *conf = (proxy_server_conf *)
502         ap_get_module_config(sconf, &proxy_module);
503     proxy_balancer *balancer, *bsel = NULL;
504     proxy_runtime_worker *worker, *wsel = NULL;
505     apr_table_t *params = apr_table_make(r->pool, 10);
506     int access_status;
507     int i, n;
508     const char *name;
509
510     /* is this for us? */
511     if (strcmp(r->handler, "balancer-manager"))
512         return DECLINED;
513     r->allowed = (AP_METHOD_BIT << M_GET);
514     if (r->method_number != M_GET)
515         return DECLINED;
516
517     if (r->args) {
518         char *args = apr_pstrdup(r->pool, r->args);
519         char *tok, *val;
520         while (args && *args) {
521             if ((val = ap_strchr(args, '='))) {
522                 *val++ = '\0';
523                 if ((tok = ap_strchr(val, '&')))
524                     *tok++ = '\0';
525                 if ((access_status = ap_unescape_url(val)) != OK)
526                     return access_status;
527                 apr_table_setn(params, args, val);
528                 args = tok;
529             }
530             else
531                 return HTTP_BAD_REQUEST;
532         }
533     }
534     if ((name = apr_table_get(params, "b")))
535         bsel = ap_proxy_get_balancer(r->pool, conf,
536             apr_pstrcat(r->pool, "balancer://", name, NULL));
537     if ((name = apr_table_get(params, "w"))) {
538         const char *sc = apr_table_get(params, "s");
539         char *asname = NULL;
540         proxy_worker *ws = NULL;
541         if (sc) {
542             asname = apr_pstrcat(r->pool, sc, "://", name, NULL);
543             ws = ap_proxy_get_worker(r->pool, conf, asname);
544         }
545         if (ws) {
546             worker = (proxy_runtime_worker *)bsel->workers->elts;
547             for (n = 0; n < bsel->workers->nelts; n++) {
548                 if (strcasecmp(worker->w->name, ws->name) == 0) {
549                     wsel = worker;
550                     break;
551                 }
552                 ++worker;
553             }
554         }
555     }
556     /* First set the params */
557     if (bsel) {
558         const char *val;
559         if (!bsel->status)
560             init_runtime_score(conf->pool, bsel);
561
562         if ((val = apr_table_get(params, "ss"))) {
563             if (strlen(val))
564                 bsel->sticky = apr_pstrdup(conf->pool, val);
565             else
566                 bsel->sticky = NULL;
567         }
568         if ((val = apr_table_get(params, "tm"))) {
569             int ival = atoi(val);
570             if (ival >= 0)
571                 bsel->timeout = apr_time_from_sec(ival);
572         }
573     }
574     if (wsel) {
575         const char *val;
576         if ((val = apr_table_get(params, "lf"))) {
577             char *ep;
578             double dval = strtod(val, &ep);
579             if (dval > 1) {
580                 wsel->s->lbfactor = dval;
581                 if (bsel)
582                     recalc_factors(bsel, wsel);
583             }
584         }
585         if ((val = apr_table_get(params, "wr"))) {
586             if (strlen(val))
587                 wsel->w->route = apr_pstrdup(conf->pool, val);
588             else
589                 wsel->w->route = NULL;
590         }
591         if ((val = apr_table_get(params, "rr"))) {
592             if (strlen(val))
593                 wsel->w->redirect = apr_pstrdup(conf->pool, val);
594             else
595                 wsel->w->redirect = NULL;
596         }
597         if ((val = apr_table_get(params, "dw")))
598             wsel->w->status |= PROXY_WORKER_DISABLED;
599         else
600             wsel->w->status &= ~PROXY_WORKER_DISABLED;
601
602     }
603     if (apr_table_get(params, "xml")) {
604         ap_set_content_type(r, "text/xml");
605         ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n", r);
606         ap_rputs("<httpd:manager xmlns:httpd=\"http://httpd.apache.org\">\n", r);
607         ap_rputs("  <httpd:balancers>\n", r);
608         balancer = (proxy_balancer *)conf->balancers->elts;
609         for (i = 0; i < conf->balancers->nelts; i++) {
610             ap_rputs("    <httpd:balancer>\n", r);
611             ap_rvputs(r, "      <httpd:name>", balancer->name, "</httpd:name>\n", NULL);
612             ap_rputs("      <httpd:workers>\n", r);
613             worker = (proxy_runtime_worker *)balancer->workers->elts;
614             for (n = 0; n < balancer->workers->nelts; n++) {
615                 ap_rputs("        <httpd:worker>\n", r);
616                 ap_rvputs(r, "          <httpd:scheme>", worker->w->scheme,
617                           "</httpd:scheme>\n", NULL);                
618                 ap_rvputs(r, "          <httpd:hostname>", worker->w->hostname,
619                           "</httpd:hostname>\n", NULL);                
620                ap_rprintf(r, "          <httpd:loadfactor>%.2f</httpd:loadfactor>\n",
621                           worker->s->lbfactor);
622                 ap_rputs("        </httpd:worker>\n", r);
623                 ++worker;
624             }
625             ap_rputs("      </httpd:workers>\n", r);
626             ap_rputs("    </httpd:balancer>\n", r);
627             ++balancer;
628         }
629         ap_rputs("  </httpd:balancers>\n", r);
630         ap_rputs("</httpd:manager>", r);         
631     }
632     else {
633         ap_set_content_type(r, "text/html");
634         ap_rputs(DOCTYPE_HTML_3_2
635                  "<html><head><title>Balancer Manager</title></head>\n", r);
636         ap_rputs("<body><h1>Load Balancer Manager for ", r);
637         ap_rvputs(r, ap_get_server_name(r), "</h1>\n\n", NULL);
638         ap_rvputs(r, "<dl><dt>Server Version: ",
639                   ap_get_server_version(), "</dt>\n", NULL);
640         ap_rvputs(r, "<dt>Server Built: ",
641                   ap_get_server_built(), "\n</dt></dl>\n", NULL);
642         balancer = (proxy_balancer *)conf->balancers->elts;
643         for (i = 0; i < conf->balancers->nelts; i++) {
644             if (!balancer->status)
645                 init_runtime_score(conf->pool, balancer);
646
647             ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r);
648             ap_rvputs(r, "<a href=\"", r->uri, "?b=",
649                       balancer->name + sizeof("balancer://") - 1,
650                       "\">", NULL); 
651             ap_rvputs(r, balancer->name, "</a></h3>\n\n", NULL);
652             ap_rputs("\n\n<table border=\"0\"><tr>"
653                 "<th>StickySesion</th><th>Timeout</th>"
654                 "</tr>\n<tr>", r);                
655             ap_rvputs(r, "<td>", balancer->sticky, NULL);
656             ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>\n",
657                 apr_time_sec(balancer->timeout));
658             ap_rputs("</table>\n", r);
659             ap_rputs("\n\n<table border=\"0\"><tr>"
660                 "<th>Scheme</th><th>Host</th>"
661                 "<th>Route</th><th>RouteRedir</th>"
662                 "<th>Factor</th><th>Status</th>"
663                 "</tr>\n", r);
664
665             worker = (proxy_runtime_worker *)balancer->workers->elts;
666             for (n = 0; n < balancer->workers->nelts; n++) {
667
668                 ap_rvputs(r, "<tr>\n<td>", worker->w->scheme, "</td><td>", NULL);
669                 ap_rvputs(r, "<a href=\"", r->uri, "?b=", 
670                           balancer->name + sizeof("balancer://") - 1,
671                           "&s=", worker->w->scheme, "&w=", worker->w->hostname,
672                           "\">", NULL); 
673                 ap_rvputs(r, worker->w->hostname, "</a></td>", NULL);
674                 ap_rvputs(r, "<td>", worker->w->route, NULL);
675                 ap_rvputs(r, "</td><td>", worker->w->redirect, NULL);
676                 ap_rprintf(r, "</td><td>%.2f</td><td>", worker->s->lbfactor);
677                 if (worker->w->status & PROXY_WORKER_DISABLED)
678                     ap_rputs("Dis", r);
679                 else if (worker->w->status & PROXY_WORKER_IN_ERROR)
680                     ap_rputs("Err", r);
681                 else if (worker->w->status & PROXY_WORKER_INITIALIZED)
682                     ap_rputs("Ok", r);
683                 else
684                     ap_rputs("-", r);
685                 ap_rputs("</td></tr>\n", r);
686
687                 ++worker;
688             }
689             ap_rputs("</table>\n", r);
690             ++balancer;
691         }
692         ap_rputs("<hr />\n", r);
693         if (wsel && bsel) {
694             ap_rputs("<h3>Edit worker settings for ", r);
695             ap_rvputs(r, wsel->w->name, "</h3>\n", NULL);
696             ap_rvputs(r, "<form method=\"GET\" action=\"", NULL);
697             ap_rvputs(r, r->uri, "\">\n<dl>", NULL); 
698             ap_rputs("<table><tr><td>Load factor:</td><td><input name=\"lf\" type=text ", r);
699             ap_rprintf(r, "value=\"%.2f\"></td><tr>\n", wsel->s->lbfactor);            
700             ap_rputs("<tr><td>Route:</td><td><input name=\"wr\" type=text ", r);
701             ap_rvputs(r, "value=\"", wsel->w->route, NULL); 
702             ap_rputs("\"></td><tr>\n", r);            
703             ap_rputs("<tr><td>Route Redirect:</td><td><input name=\"rr\" type=text ", r);
704             ap_rvputs(r, "value=\"", wsel->w->redirect, NULL); 
705             ap_rputs("\"></td><tr>\n", r);            
706             ap_rputs("<tr><td>Disabled:</td><td><input name=\"dw\" type=checkbox", r);
707             if (wsel->w->status & PROXY_WORKER_DISABLED)
708                 ap_rputs(" checked", r);
709             ap_rputs("></td><tr>\n", r);            
710             ap_rputs("<tr><td colspan=2><input type=submit value=\"Submit\"></td></tr>\n", r);
711             ap_rvputs(r, "</table>\n<input type=hidden name=\"s\" ", NULL);
712             ap_rvputs(r, "value=\"", wsel->w->scheme, "\">\n", NULL);
713             ap_rvputs(r, "<input type=hidden name=\"w\" ", NULL);
714             ap_rvputs(r, "value=\"", wsel->w->hostname, "\">\n", NULL);
715             ap_rvputs(r, "<input type=hidden name=\"b\" ", NULL);
716             ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1,
717                       "\">\n</form>\n", NULL);
718             ap_rputs("<hr />\n", r);
719         }
720         else if (bsel) {
721             ap_rputs("<h3>Edit balancer settings for ", r);
722             ap_rvputs(r, bsel->name, "</h3>\n", NULL);
723             ap_rvputs(r, "<form method=\"GET\" action=\"", NULL);
724             ap_rvputs(r, r->uri, "\">\n<dl>", NULL); 
725             ap_rputs("<table><tr><td>StickySession Identifier:</td><td><input name=\"ss\" type=text ", r);
726             if (bsel->sticky)
727                 ap_rvputs(r, "value=\"", bsel->sticky, "\"", NULL);
728             ap_rputs("></td><tr>\n<tr><td>Timeout:</td><td><input name=\"tm\" type=text ", r);
729             ap_rprintf(r, "value=\"%" APR_TIME_T_FMT "\"></td></tr>\n",
730                        apr_time_sec(bsel->timeout));
731             ap_rputs("<tr><td colspan=2><input type=submit value=\"Submit\"></td></tr>\n", r);
732             ap_rvputs(r, "</table>\n<input type=hidden name=\"b\" ", NULL);
733             ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1,
734                       "\">\n</form>\n", NULL);
735             ap_rputs("<hr />\n", r);
736         }
737         ap_rputs(ap_psignature("",r), r);
738         ap_rputs("</body></html>\n", r);
739     }
740     return OK;
741 }
742
743 static void ap_proxy_balancer_register_hook(apr_pool_t *p)
744 {
745     /* manager handler */
746     ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST);
747     proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST);    
748     proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST);    
749 }
750
751 module AP_MODULE_DECLARE_DATA proxy_balancer_module = {
752     STANDARD20_MODULE_STUFF,
753     NULL,       /* create per-directory config structure */
754     NULL,       /* merge per-directory config structures */
755     NULL,       /* create per-server config structure */
756     NULL,       /* merge per-server config structures */
757     NULL,       /* command apr_table_t */
758     ap_proxy_balancer_register_hook /* register hooks */
759 };