1 /* Copyright 1999-2004 The Apache Software Foundation
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 /* Load balancer module for Apache proxy */
20 #include "mod_proxy.h"
22 #include "scoreboard.h"
23 #include "apr_version.h"
26 #include <unistd.h> /* for getpid() */
29 module AP_MODULE_DECLARE_DATA proxy_balancer_module;
32 #define PROXY_BALANCER_LOCK(b) apr_thread_mutex_lock((b)->mutex)
33 #define PROXY_BALANCER_UNLOCK(b) apr_thread_mutex_unlock((b)->mutex)
35 #define PROXY_BALANCER_LOCK(b) APR_SUCCESS
36 #define PROXY_BALANCER_UNLOCK(b) APR_SUCCESS
39 static int init_runtime_score(apr_pool_t *pool, proxy_balancer *balancer)
42 double median, ffactor = 0.0;
43 proxy_runtime_worker *workers;
44 #if PROXY_HAS_SCOREBOARD
50 workers = (proxy_runtime_worker *)balancer->workers->elts;
52 for (i = 0; i < balancer->workers->nelts; i++) {
54 #if PROXY_HAS_SCOREBOARD
55 /* Get scoreboard slot */
56 if (ap_scoreboard_image)
57 score = ap_get_scoreboard_lb(workers[i].id);
60 score = apr_pcalloc(pool, sizeof(proxy_runtime_stat));
61 workers[i].s = (proxy_runtime_stat *)score;
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;
70 if (ffactor < 100.0) {
72 for (i = 0; i < balancer->workers->nelts; i++) {
73 if (workers[i].s->lbfactor == 0.0)
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;
84 median = (100.0 - ffactor) / balancer->workers->nelts;
85 for (i = 0; i < balancer->workers->nelts; i++)
86 workers[i].s->lbfactor += median;
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;
96 for (i = 0; i < balancer->workers->nelts; i++) {
97 /* Update the status entires */
98 workers[i].s->lbstatus = workers[i].s->lbfactor;
103 /* Retrieve the parameter with the given name
104 * Something like 'JSESSIONID=12345...N'
106 static char *get_path_param(apr_pool_t *pool, char *url,
111 for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
112 path += (strlen(name) + 1);
115 * Session path was found, get it's value
120 path = apr_pstrdup(pool, path);
121 if ((q = strchr(path, '?')))
130 static char *get_cookie_param(request_rec *r, const char *name)
133 const char *start_cookie;
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])) {
143 start_cookie += strlen(name);
144 while(*start_cookie && isspace(*start_cookie))
146 if (*start_cookie == '=' && start_cookie[1]) {
148 * Session cookie was found, get it's value
150 char *end_cookie, *cookie;
152 cookie = apr_pstrdup(r->pool, start_cookie);
153 if ((end_cookie = strchr(cookie, ';')) != NULL)
155 if((end_cookie = strchr(cookie, ',')) != NULL)
165 /* Find the worker that has the 'route' defined
167 static proxy_runtime_worker *find_route_worker(proxy_balancer *balancer,
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) {
181 static proxy_runtime_worker *find_session_route(proxy_balancer *balancer,
186 if (!balancer->sticky)
188 /* Try to find the sticky route inside url */
189 *route = get_path_param(r->pool, *url, balancer->sticky);
191 *route = get_cookie_param(r, balancer->sticky);
193 /* We have a route in path or in cookie
194 * Find the worker that has this route defined.
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.
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))
219 static proxy_runtime_worker *find_best_worker(proxy_balancer *balancer,
223 double total_factor = 0.0;
224 proxy_runtime_worker *worker = (proxy_runtime_worker *)balancer->workers->elts;
225 proxy_runtime_worker *candidate = NULL;
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
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.
240 if (PROXY_WORKER_IS_USABLE(worker->w)) {
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.
248 if (worker->w->cp->nfree > candidate->w->cp->nfree)
251 /* Total factor is always 100 if all workers are
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.
256 total_factor += worker->s->lbfactor;
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.
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.
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
280 balancer->timeout = 0;
281 step = timeout / 100;
282 while (tval < timeout) {
285 if ((candidate = find_best_worker(balancer, r)))
289 /* restore the timeout */
290 balancer->timeout = timeout;
295 /* We have at least one candidate that is not in
296 * error state or disabled.
297 * Now calculate the appropriate one.
300 /* Step one: Find the worker that has a lbfactor
301 * higher then a candidate found initially.
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) {
312 /* Step two: Recalculate the load statuses.
313 * Each usable worker load status is incremented
314 * by it's load factor.
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
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.
340 worker->s->lbstatus += worker->s->lbfactor;
341 if (worker->s->lbstatus >= total_factor)
342 worker->s->lbstatus = worker->s->lbfactor;
350 static int rewrite_url(request_rec *r, proxy_worker *worker,
353 const char *scheme = strstr(*url, "://");
354 const char *path = NULL;
357 path = ap_strchr_c(scheme + 3, '/');
359 /* we break the URL into host, port, uri */
361 return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool,
362 "missing worker. URI cannot be parsed: ", *url,
366 *url = apr_pstrcat(r->pool, worker->name, path, NULL);
371 static int proxy_balancer_pre_request(proxy_worker **worker,
372 proxy_balancer **balancer,
374 proxy_server_conf *conf, char **url)
377 proxy_runtime_worker *runtime;
382 /* Step 1: check if the url is for us
383 * The url we can handle starts with 'balancer://'
385 if (!(*balancer = ap_proxy_get_balancer(r->pool, conf, *url)))
388 /* Step 2: find the session route */
390 runtime = find_session_route(*balancer, r, &route, url);
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;
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
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;
418 /* Lock the LoadBalancer
419 * XXX: perhaps we need the process lock here
421 if ((rv = PROXY_BALANCER_LOCK(*balancer)) != APR_SUCCESS) {
422 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
423 "proxy: BALANCER: lock");
427 runtime = find_best_worker(*balancer, r);
429 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
430 "proxy: BALANCER: (%s). All workers are in error state",
433 PROXY_BALANCER_UNLOCK(*balancer);
434 return HTTP_SERVICE_UNAVAILABLE;
436 runtime->s->elected++;
437 *worker = runtime->w;
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
444 --(*worker)->cp->nfree;
446 PROXY_BALANCER_UNLOCK(*balancer);
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.
453 access_status = rewrite_url(r, *worker, url);
454 /* Add the session route to request notes if present */
456 apr_table_setn(r->notes, "session-sticky", (*balancer)->sticky);
457 apr_table_setn(r->notes, "session-route", route);
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",
464 (*worker)->cp->nfree);
466 return access_status;
469 static int proxy_balancer_post_request(proxy_worker *worker,
470 proxy_balancer *balancer,
472 proxy_server_conf *conf)
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;
481 /* increase the free channels number */
483 /* TODO: calculate the bytes transferred
484 * This will enable to elect the worker that has
486 * The bytes transferred depends on the protocol
487 * used, so each protocol handler should keep the
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);
498 static void recalc_factors(proxy_balancer *balancer,
499 proxy_runtime_worker *fixed)
502 double median, ffactor = 0.0;
503 proxy_runtime_worker *workers;
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
511 if (balancer->workers->nelts == 1) {
512 workers->s->lbstatus = workers->s->lbfactor = 100.0;
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;
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;
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;
536 median = (ffactor - 100.0) / balancer->workers->nelts;
537 for (i = 0; i < balancer->workers->nelts; i++) {
538 workers[i].s->lbfactor -= median;
541 for (i = 0; i < balancer->workers->nelts; i++) {
542 /* Update the status entires */
543 workers[i].s->lbstatus = workers[i].s->lbfactor;
547 /* Manages the loadfactors and member status
549 static int balancer_handler(request_rec *r)
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);
561 /* is this for us? */
562 if (strcmp(r->handler, "balancer-manager"))
564 r->allowed = (AP_METHOD_BIT << M_GET);
565 if (r->method_number != M_GET)
569 char *args = apr_pstrdup(r->pool, r->args);
571 while (args && *args) {
572 if ((val = ap_strchr(args, '='))) {
574 if ((tok = ap_strchr(val, '&')))
576 if ((access_status = ap_unescape_url(val)) != OK)
577 return access_status;
578 apr_table_setn(params, args, val);
582 return HTTP_BAD_REQUEST;
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");
591 proxy_worker *ws = NULL;
593 asname = apr_pstrcat(r->pool, sc, "://", name, NULL);
594 ws = ap_proxy_get_worker(r->pool, conf, asname);
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) {
607 /* First set the params */
610 if ((val = apr_table_get(params, "ss"))) {
612 bsel->sticky = apr_pstrdup(conf->pool, val);
616 if ((val = apr_table_get(params, "tm"))) {
617 int ival = atoi(val);
619 bsel->timeout = apr_time_from_sec(ival);
621 if ((val = apr_table_get(params, "fa"))) {
622 int ival = atoi(val);
624 bsel->max_attempts = ival;
625 bsel->max_attempts_set = 1;
630 if ((val = apr_table_get(params, "lf"))) {
632 double dval = strtod(val, &ep);
634 wsel->s->lbfactor = dval;
636 recalc_factors(bsel, wsel);
639 if ((val = apr_table_get(params, "wr"))) {
641 wsel->w->route = apr_pstrdup(conf->pool, val);
643 wsel->w->route = NULL;
645 if ((val = apr_table_get(params, "rr"))) {
647 wsel->w->redirect = apr_pstrdup(conf->pool, val);
649 wsel->w->redirect = NULL;
651 if ((val = apr_table_get(params, "dw")))
652 wsel->w->status |= PROXY_WORKER_DISABLED;
654 wsel->w->status &= ~PROXY_WORKER_DISABLED;
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);
679 ap_rputs(" </httpd:workers>\n", r);
680 ap_rputs(" </httpd:balancer>\n", r);
683 ap_rputs(" </httpd:balancers>\n", r);
684 ap_rputs("</httpd:manager>", r);
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++) {
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,
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>"
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>"
718 worker = (proxy_runtime_worker *)balancer->workers->elts;
719 for (n = 0; n < balancer->workers->nelts; n++) {
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,
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)
732 else if (worker->w->status & PROXY_WORKER_IN_ERROR)
734 else if (worker->w->status & PROXY_WORKER_INITIALIZED)
738 ap_rputs("</td></tr>\n", r);
742 ap_rputs("</table>\n", r);
745 ap_rputs("<hr />\n", r);
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);
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);
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",
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);
793 ap_rputs(ap_psignature("",r), r);
794 ap_rputs("</body></html>\n", r);
799 static void child_init(apr_pool_t *p, server_rec *s)
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;
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);
816 static void ap_proxy_balancer_register_hook(apr_pool_t *p)
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);
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 */