Apache HTTPD
mod_proxy_balancer.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* Load balancer module for Apache proxy */
18
19#include "mod_proxy.h"
20#include "proxy_util.h"
21#include "scoreboard.h"
22#include "ap_mpm.h"
23#include "apr_version.h"
24#include "ap_hooks.h"
25#include "apr_date.h"
26#include "util_md5.h"
27#include "mod_watchdog.h"
28
29static const char *balancer_mutex_type = "proxy-balancer-shm";
31
32module AP_MODULE_DECLARE_DATA proxy_balancer_module;
33
35
37 proxy_worker *worker, server_rec *s) = NULL;
38
42
43
44/*
45 * Register our mutex type before the config is read so we
46 * can adjust the mutex settings using the Mutex directive.
47 */
65
66#if 0
67extern void proxy_update_members(proxy_balancer **balancer, request_rec *r,
68 proxy_server_conf *conf);
69#endif
70
72{
73 char *host;
74 apr_port_t port = 0;
75 const char *err;
76
77 /* TODO: offset of BALANCER_PREFIX ?? */
78 if (ap_cstr_casecmpn(url, "balancer:", 9) == 0) {
79 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url);
80 url += 9;
81 }
82 else {
83 return DECLINED;
84 }
85
86 /* do syntatic check.
87 * We break the URL into host, port, path
88 */
90 if (err) {
92 "error parsing URL %s: %s",
93 url, err);
94 return HTTP_BAD_REQUEST;
95 }
96
97 /* The canon_handler hooks are run per the BalancerMember in
98 * balancer_fixup(), keep the original/raw path for now.
99 */
101 host, "/", url, NULL);
102
103 return OK;
104}
105
107 proxy_balancer *balancer)
108{
109 int i;
111
112 workers = (proxy_worker **)balancer->workers->elts;
113
114 for (i = 0; i < balancer->workers->nelts; i++) {
116 proxy_worker *worker = *workers;
118 "Looking at %s -> %s initialized?", balancer->s->name,
119 ap_proxy_worker_name(p, worker));
123 }
124 ++workers;
125 }
126
127 /* Set default number of attempts to the number of
128 * workers.
129 */
130 if (!balancer->s->max_attempts_set && balancer->workers->nelts > 1) {
131 balancer->s->max_attempts = balancer->workers->nelts - 1;
132 balancer->s->max_attempts_set = 1;
133 }
134}
135
136/* Retrieve the parameter with the given name
137 * Something like 'JSESSIONID=12345...N'
138 */
139static char *get_path_param(apr_pool_t *pool, char *url,
140 const char *name, int scolon_sep)
141{
142 char *path = NULL;
143 char *pathdelims = "?&";
144
145 if (scolon_sep) {
146 pathdelims = ";?&";
147 }
148 for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
149 path += strlen(name);
150 if (*path == '=') {
151 /*
152 * Session path was found, get its value
153 */
154 ++path;
155 if (*path) {
156 char *q;
158 return path;
159 }
160 }
161 }
162 return NULL;
163}
164
165static char *get_cookie_param(request_rec *r, const char *name)
166{
167 const char *cookies;
168 const char *start_cookie;
169
170 if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
173 if (start_cookie == cookies ||
174 start_cookie[-1] == ';' ||
175 start_cookie[-1] == ',' ||
176 isspace(start_cookie[-1])) {
177
178 start_cookie += strlen(name);
180 ++start_cookie;
181 if (*start_cookie++ == '=' && *start_cookie) {
182 /*
183 * Session cookie was found, get its value
184 */
185 char *end_cookie, *cookie;
186 cookie = apr_pstrdup(r->pool, start_cookie);
187 if ((end_cookie = strchr(cookie, ';')) != NULL)
188 *end_cookie = '\0';
189 if((end_cookie = strchr(cookie, ',')) != NULL)
190 *end_cookie = '\0';
191 return cookie;
192 }
193 }
194 }
195 }
196 return NULL;
197}
198
199/* Find the worker that has the 'route' defined
200 */
202 const char *route, request_rec *r,
203 int recursion)
204{
205 int i;
207 int checked_standby;
208
210
212 while (!checked_standby) {
213 workers = (proxy_worker **)balancer->workers->elts;
214 for (i = 0; i < balancer->workers->nelts; i++, workers++) {
215 proxy_worker *worker = *workers;
217 continue;
218 if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) {
219 if (PROXY_WORKER_IS_USABLE(worker)) {
220 return worker;
221 } else {
222 /*
223 * If the worker is in error state run
224 * retry on that worker. It will be marked as
225 * operational if the retry timeout is elapsed.
226 * The worker might still be unusable, but we try
227 * anyway.
228 */
229 ap_proxy_retry_worker_fn("BALANCER", worker, r->server);
230 if (PROXY_WORKER_IS_USABLE(worker)) {
231 return worker;
232 } else {
233 /*
234 * We have a worker that is unusable.
235 * It can be in error or disabled, but in case
236 * it has a redirection set use that redirection worker.
237 * This enables to safely remove the member from the
238 * balancer. Of course you will need some kind of
239 * session replication between those two remote.
240 * Also check that we haven't gone thru all the
241 * balancer members by means of redirects.
242 * This should avoid redirect cycles.
243 */
244 if ((*worker->s->redirect)
245 && (recursion < balancer->workers->nelts)) {
247 rworker = find_route_worker(balancer, worker->s->redirect,
248 r, recursion + 1);
249 /* Check if the redirect worker is usable */
251 /*
252 * If the worker is in error state run
253 * retry on that worker. It will be marked as
254 * operational if the retry timeout is elapsed.
255 * The worker might still be unusable, but we try
256 * anyway.
257 */
259 }
261 return rworker;
262 }
263 }
264 }
265 }
266 }
268 }
269 return NULL;
270}
271
273 request_rec *r,
274 char **route,
275 const char **sticky_used,
276 char **url)
277{
278 proxy_worker *worker = NULL;
279
280 if (!*balancer->s->sticky)
281 return NULL;
282 /* Try to find the sticky route inside url */
283 *route = get_path_param(r->pool, *url, balancer->s->sticky_path, balancer->s->scolonsep);
284 if (*route) {
286 "Found value %s for stickysession %s",
287 *route, balancer->s->sticky_path);
288 *sticky_used = balancer->s->sticky_path;
289 }
290 else {
291 *route = get_cookie_param(r, balancer->s->sticky);
292 if (*route) {
293 *sticky_used = balancer->s->sticky;
295 "Found value %s for stickysession %s",
296 *route, balancer->s->sticky);
297 }
298 }
299 /*
300 * If we found a value for stickysession, find the first '.' (or whatever
301 * sticky_separator is set to) within. Everything after '.' (if present)
302 * is our route.
303 */
304 if ((*route) && (balancer->s->sticky_separator != 0) && ((*route = strchr(*route, balancer->s->sticky_separator)) != NULL ))
305 (*route)++;
306 if ((*route) && (**route)) {
307 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01161) "Found route %s", *route);
308 /* We have a route in path or in cookie
309 * Find the worker that has this route defined.
310 */
311 worker = find_route_worker(balancer, *route, r, 1);
312 if (worker && strcmp(*route, worker->s->route)) {
313 /*
314 * Notice that the route of the worker chosen is different from
315 * the route supplied by the client.
316 */
317 apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1");
319 "Route changed from %s to %s",
320 *route, worker->s->route);
321 }
322 return worker;
323 }
324 else
325 return NULL;
326}
327
329 request_rec *r)
330{
332 apr_status_t rv;
333
334#if APR_HAS_THREADS
335 if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
337 "%s: Lock failed for find_best_worker()",
338 balancer->s->name);
339 return NULL;
340 }
341#endif
342
343 candidate = (*balancer->lbmethod->finder)(balancer, r);
344
345 if (candidate)
346 candidate->s->elected++;
347
348#if APR_HAS_THREADS
349 if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
351 "%s: Unlock failed for find_best_worker()",
352 balancer->s->name);
353 }
354#endif
355
356 if (candidate == NULL) {
357 /* All the workers are in error state or disabled.
358 * If the balancer has a timeout sleep for a while
359 * and try again to find the worker. The chances are
360 * that some other thread will release a connection.
361 * By default the timeout is not set, and the server
362 * returns SERVER_BUSY.
363 */
364 if (balancer->s->timeout) {
365 /* XXX: This can perhaps be build using some
366 * smarter mechanism, like tread_cond.
367 * But since the statuses can came from
368 * different children, use the provided algo.
369 */
371 apr_interval_time_t step, tval = 0;
372 /* Set the timeout to 0 so that we don't
373 * end in infinite loop
374 */
375 balancer->s->timeout = 0;
376 step = timeout / 100;
377 while (tval < timeout) {
378 apr_sleep(step);
379 /* Try again */
380 if ((candidate = find_best_worker(balancer, r)))
381 break;
382 tval += step;
383 }
384 /* restore the timeout */
385 balancer->s->timeout = timeout;
386 }
387 }
388
389 return candidate;
390
391}
392
393static int balancer_fixup(request_rec *r, proxy_worker *worker, char **url)
394{
395 const char *path;
396 int rc;
397
398 /* Build the proxy URL from the worker URL and the actual path */
399 path = strstr(*url, "://");
400 if (path) {
401 path = ap_strchr_c(path + 3, '/');
402 }
403 r->filename = apr_pstrcat(r->pool, "proxy:", worker->s->name_ex, path, NULL);
404
405 /* Canonicalize r->filename per the worker scheme's canon_handler hook */
407 if (rc == OK) {
408 AP_DEBUG_ASSERT(strncmp(r->filename, "proxy:", 6) == 0);
409 *url = apr_pstrdup(r->pool, r->filename + 6);
410 }
411 return rc;
412}
413
415{
416 int i;
417 int ok = 0;
418 proxy_worker **worker;
419
420 worker = (proxy_worker **)balancer->workers->elts;
421 for (i = 0; i < balancer->workers->nelts; i++, worker++) {
422 if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) {
423 ok = 1;
424 break;
425 }
426 else {
427 /* Try if we can recover */
428 ap_proxy_retry_worker_fn("BALANCER", *worker, s);
429 if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) {
430 ok = 1;
431 break;
432 }
433 }
434 }
435 if (!ok && balancer->s->forcerecovery) {
436 /* If all workers are in error state force the recovery.
437 */
438 worker = (proxy_worker **)balancer->workers->elts;
439 for (i = 0; i < balancer->workers->nelts; i++, worker++) {
440 ++(*worker)->s->retries;
441 (*worker)->s->status &= ~PROXY_WORKER_IN_ERROR;
443 "%s: Forcing recovery for worker (%s:%d)",
444 balancer->s->name, (*worker)->s->hostname_ex,
445 (int)(*worker)->s->port);
446 }
447 }
448}
449
451{
452 proxy_worker *worker = worker_;
453
454 if (worker->s->busy) {
455 worker->s->busy--;
456 }
457
458 return APR_SUCCESS;
459}
460
462 proxy_balancer **balancer,
463 request_rec *r,
464 proxy_server_conf *conf, char **url)
465{
466 int access_status;
468 char *route = NULL;
469 const char *sticky = NULL;
470 apr_status_t rv;
471
472 *worker = NULL;
473 /* Step 1: check if the url is for us
474 * The url we can handle starts with 'balancer://'
475 * If balancer is already provided skip the search
476 * for balancer, because this is failover attempt.
477 */
478 if (!*balancer &&
480 || !(*balancer = ap_proxy_get_balancer(r->pool, conf, *url, 1))))
481 return DECLINED;
482
483 /* Step 2: Lock the LoadBalancer
484 * XXX: perhaps we need the process lock here
485 */
486#if APR_HAS_THREADS
487 if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) {
489 "%s: Lock failed for pre_request", (*balancer)->s->name);
490 return DECLINED;
491 }
492#endif
493
494 /* Step 3: force recovery */
495 force_recovery(*balancer, r->server);
496
497 /* Step 3.5: Update member list for the balancer */
498 /* TODO: Implement as provider! */
499 ap_proxy_sync_balancer(*balancer, r->server, conf);
500
501 /* Step 4: find the session route */
502 runtime = find_session_route(*balancer, r, &route, &sticky, url);
503 if (runtime) {
504 if ((*balancer)->lbmethod && (*balancer)->lbmethod->updatelbstatus) {
505 /* Call the LB implementation */
506 (*balancer)->lbmethod->updatelbstatus(*balancer, runtime, r->server);
507 }
508 else { /* Use the default one */
509 int i, total_factor = 0;
511 /* We have a sticky load balancer
512 * Update the workers status
513 * so that even session routes get
514 * into account.
515 */
516 workers = (proxy_worker **)(*balancer)->workers->elts;
517 for (i = 0; i < (*balancer)->workers->nelts; i++) {
518 /* Take into calculation only the workers that are
519 * not in error state or not disabled.
520 */
522 (*workers)->s->lbstatus += (*workers)->s->lbfactor;
523 total_factor += (*workers)->s->lbfactor;
524 }
525 workers++;
526 }
527 runtime->s->lbstatus -= total_factor;
528 }
529 runtime->s->elected++;
530
531 *worker = runtime;
532 }
533 else if (route && (*balancer)->s->sticky_force) {
534 int i, member_of = 0;
536 /*
537 * We have a route provided that doesn't match the
538 * balancer name. See if the provider route is the
539 * member of the same balancer in which case return 503
540 */
541 workers = (proxy_worker **)(*balancer)->workers->elts;
542 for (i = 0; i < (*balancer)->workers->nelts; i++) {
543 if (*((*workers)->s->route) && strcmp((*workers)->s->route, route) == 0) {
544 member_of = 1;
545 break;
546 }
547 workers++;
548 }
549 if (member_of) {
551 "%s: All workers are in error state for route (%s)",
552 (*balancer)->s->name, route);
553#if APR_HAS_THREADS
554 if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) {
556 "%s: Unlock failed for pre_request",
557 (*balancer)->s->name);
558 }
559#endif
561 }
562 }
563
564#if APR_HAS_THREADS
565 if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) {
567 "%s: Unlock failed for pre_request",
568 (*balancer)->s->name);
569 }
570#endif
571 if (!*worker) {
572 runtime = find_best_worker(*balancer, r);
573 if (!runtime) {
574 if ((*balancer)->workers->nelts) {
576 "%s: All workers are in error state",
577 (*balancer)->s->name);
578 } else {
580 "%s: No workers in balancer",
581 (*balancer)->s->name);
582 }
583
585 }
586 if (*(*balancer)->s->sticky && runtime) {
587 /*
588 * This balancer has sticky sessions and the client either has not
589 * supplied any routing information or all workers for this route
590 * including possible redirect and hotstandby workers are in error
591 * state, but we have found another working worker for this
592 * balancer where we can send the request. Thus notice that we have
593 * changed the route to the backend.
594 */
595 apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1");
596 }
597 *worker = runtime;
598 }
599
600 (*worker)->s->busy++;
603
604 /* Add balancer/worker info to env. */
606 "BALANCER_NAME", (*balancer)->s->name);
608 "BALANCER_WORKER_NAME", (*worker)->s->name_ex);
610 "BALANCER_WORKER_ROUTE", (*worker)->s->route);
611
612 /* Rewrite the url from 'balancer://url'
613 * to the 'worker_scheme://worker_hostname[:worker_port]/url'
614 * This replaces the balancers fictional name with the real
615 * hostname of the elected worker and canonicalizes according
616 * to the worker scheme (calls canon_handler hooks).
617 */
618 access_status = balancer_fixup(r, *worker, url);
619
620 /* Add the session route to request notes if present */
621 if (route) {
622 apr_table_setn(r->notes, "session-sticky", sticky);
623 apr_table_setn(r->notes, "session-route", route);
624
625 /* Add session info to env. */
627 "BALANCER_SESSION_STICKY", sticky);
629 "BALANCER_SESSION_ROUTE", route);
630 }
632 "%s: worker (%s) rewritten to %s",
633 (*balancer)->s->name, (*worker)->s->name_ex, *url);
634
635 return access_status;
636}
637
639 proxy_balancer *balancer,
640 request_rec *r,
641 proxy_server_conf *conf)
642{
643
644 apr_status_t rv;
645
646#if APR_HAS_THREADS
647 if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
649 "%s: Lock failed for post_request",
650 balancer->s->name);
652 }
653#endif
654
655 if (!apr_is_empty_array(balancer->errstatuses)
656 && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
657 int i;
658 for (i = 0; i < balancer->errstatuses->nelts; i++) {
659 int val = ((int *)balancer->errstatuses->elts)[i];
660 if (r->status == val) {
662 "%s: Forcing worker (%s) into error state "
663 "due to status code %d matching 'failonstatus' "
664 "balancer parameter",
665 balancer->s->name, ap_proxy_worker_name(r->pool, worker),
666 val);
667 worker->s->status |= PROXY_WORKER_IN_ERROR;
668 worker->s->error_time = apr_time_now();
669 break;
670 }
671 }
672 }
673
674 if (balancer->failontimeout
675 && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)
676 && (apr_table_get(r->notes, "proxy_timedout")) != NULL) {
678 "%s: Forcing worker (%s) into error state "
679 "due to timeout and 'failontimeout' parameter being set",
680 balancer->s->name, ap_proxy_worker_name(r->pool, worker));
681 worker->s->status |= PROXY_WORKER_IN_ERROR;
682 worker->s->error_time = apr_time_now();
683
684 }
685#if APR_HAS_THREADS
686 if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
688 "%s: Unlock failed for post_request", balancer->s->name);
689 }
690#endif
692 "proxy_balancer_post_request for (%s)", balancer->s->name);
693
694 return OK;
695}
696
697static void recalc_factors(proxy_balancer *balancer)
698{
699 int i;
701
702
703 /* Recalculate lbfactors */
704 workers = (proxy_worker **)balancer->workers->elts;
705 /* Special case if there is only one worker its
706 * load factor will always be 100
707 */
708 if (balancer->workers->nelts == 1) {
709 (*workers)->s->lbstatus = (*workers)->s->lbfactor = 100;
710 return;
711 }
712 for (i = 0; i < balancer->workers->nelts; i++) {
713 /* Update the status entries */
714 workers[i]->s->lbstatus = workers[i]->s->lbfactor;
715 }
716}
717
719{
720 int i;
721 proxy_balancer *balancer;
722 server_rec *s = data;
723 void *sconf = s->module_config;
725
726 balancer = (proxy_balancer *)conf->balancers->elts;
727 for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
728 if (balancer->gmutex) {
730 balancer->gmutex = NULL;
731 }
732 }
733 return(0);
734}
735
736/*
737 * Compute an ID for a vhost based on what makes it selected by requests.
738 * The second and more Host(s)/IP(s):port(s), and the ServerAlias(es) are
739 * optional (see make_servers_ids() below).
740 */
741 static const char *make_server_id(server_rec *s, apr_pool_t *p, int full)
742{
744 unsigned char md5[APR_MD5_DIGESTSIZE];
745 char id[2 * APR_MD5_DIGESTSIZE + 1];
746 char host_ip[64]; /* for any IPv[46] string */
747 server_addr_rec *sar;
748 int i;
749
751 for (sar = s->addrs; sar; sar = sar->next) {
752 host_ip[0] = '\0';
754 apr_md5_update(&md5_ctx, (void *)sar->virthost, strlen(sar->virthost));
755 apr_md5_update(&md5_ctx, (void *)host_ip, strlen(host_ip));
756 apr_md5_update(&md5_ctx, (void *)&sar->host_port,
757 sizeof(sar->host_port));
758 if (!full) {
759 break;
760 }
761 }
762 if (s->server_hostname) {
763 apr_md5_update(&md5_ctx, (void *)s->server_hostname,
764 strlen(s->server_hostname));
765 }
766 if (full) {
767 if (s->names) {
768 for (i = 0; i < s->names->nelts; ++i) {
769 const char *name = APR_ARRAY_IDX(s->names, i, char *);
770 apr_md5_update(&md5_ctx, (void *)name, strlen(name));
771 }
772 }
773 if (s->wild_names) {
774 for (i = 0; i < s->wild_names->nelts; ++i) {
775 const char *name = APR_ARRAY_IDX(s->wild_names, i, char *);
776 apr_md5_update(&md5_ctx, (void *)name, strlen(name));
777 }
778 }
779 }
782
783 return apr_pstrmemdup(p, id, sizeof(id) - 1);
784}
785
786/*
787 * First try to compute an unique ID for each vhost with minimal criteria,
788 * that is the first Host/IP:port and ServerName. For most cases this should
789 * be enough and avoids changing the ID unnecessarily across restart (or
790 * stop/start w.r.t. persisted files) for things that this module does not
791 * care about.
792 *
793 * But if it's not enough (collisions) do a second pass for the full monty,
794 * that is additionally the other Host(s)/IP(s):port(s) and ServerAlias(es).
795 *
796 * Finally, for pathological configs where this is still not enough, let's
797 * append a counter to duplicates, because we really want that ID to be unique
798 * even if the vhost will never be selected to handle requests at run time, at
799 * load time a duplicate may steal the original slotmems (depending on its
800 * balancers' configurations), see how mod_slotmem_shm reuses slots/files based
801 * solely on this ID and resets them if the sizes don't match.
802 */
804{
806 apr_array_header_t *ids = apr_array_make(p, 10, sizeof(const char *));
808 int idx, *dup, full_monty = 0;
809 const char *id;
810
811 for (idx = 0, s = main_s; s; s = s->next, ++idx) {
812 id = make_server_id(s, p, 0);
815 apr_pmemdup(p, &idx, sizeof(int)));
816 if (dup) {
817 full_monty = 1;
818 APR_ARRAY_IDX(ids, *dup, const char *) = NULL;
819 APR_ARRAY_PUSH(ids, const char *) = NULL;
820 }
821 else {
822 APR_ARRAY_PUSH(ids, const char *) = id;
823 }
824 }
825 if (full_monty) {
827 for (idx = 0, s = main_s; s; s = s->next, ++idx) {
828 id = APR_ARRAY_IDX(ids, idx, const char *);
829 if (id) {
830 /* Preserve non-duplicates */
831 continue;
832 }
833 id = make_server_id(s, p, 1);
835 id = apr_psprintf(p, "%s_%x", id, idx);
836 }
837 else {
838 apr_hash_set(dups, id, APR_HASH_KEY_STRING, (void *)-1);
839 }
840 APR_ARRAY_IDX(ids, idx, const char *) = id;
841 }
842 }
843
844 return ids;
845}
846
847/* post_config hook: */
849 apr_pool_t *ptemp, server_rec *s)
850{
851 apr_status_t rv;
852 proxy_server_conf *conf;
856 int idx;
857
858 /* balancer_post_config() will be called twice during startup. So, don't
859 * set up the static data the 1st time through. */
861 return OK;
862 }
863
868 "mod_proxy must be loaded for mod_proxy_balancer");
869 return !OK;
870 }
871
872 /*
873 * Get slotmem setups
874 */
877 if (!storage) {
879 "Failed to lookup provider 'shm' for '%s': is "
880 "mod_slotmem_shm loaded??",
882 return !OK;
883 }
884
885 ids = make_servers_ids(s, ptemp);
886
888 /*
889 * Go thru each Vhost and create the shared mem slotmem for
890 * each balancer's workers
891 */
892 for (idx = 0; s; ++idx) {
893 int i,j;
894 const char *id;
895 proxy_balancer *balancer;
897 void *sconf = s->module_config;
898 conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
899 /*
900 * During create_proxy_config() we created a dummy id. Now that
901 * we have identifying info, we can create the real id
902 */
903 id = APR_ARRAY_IDX(ids, idx, const char *);
904 conf->id = apr_psprintf(pconf, "p%x",
906 if (conf->bslot) {
907 /* Shared memory already created for this proxy_server_conf.
908 */
909 s = s->next;
910 continue;
911 }
912 if (conf->bal_persist) {
914 } else {
915 type = 0;
916 }
917 if (conf->balancers->nelts) {
918 conf->max_balancers = conf->balancers->nelts + conf->bgrowth;
919 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01178) "Doing balancers create: %d, %d (%d)",
921 (int)conf->balancers->nelts, conf->max_balancers);
922
923 rv = storage->create(&new, conf->id,
925 conf->max_balancers, type, pconf);
926 if (rv != APR_SUCCESS) {
927 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01179) "balancer slotmem_create failed");
928 return !OK;
929 }
930 conf->bslot = new;
931 }
932 conf->storage = storage;
933
934 /* Initialize shared scoreboard data */
935 balancer = (proxy_balancer *)conf->balancers->elts;
936 for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
938 proxy_worker *worker;
940 const char *sname;
941 unsigned int index;
942
943 /* now that we have the right id, we need to redo the sname field */
944 ap_pstr2_alnum(pconf, balancer->s->name + sizeof(BALANCER_PREFIX) - 1,
945 &sname);
946 sname = apr_pstrcat(pconf, conf->id, "_", sname, NULL);
947 PROXY_STRNCPY(balancer->s->sname, sname); /* We know this will succeed */
948
949 balancer->max_workers = balancer->workers->nelts + balancer->growth;
950 /* Create global mutex */
952 balancer->s->sname, s, pconf, 0);
953 if (rv != APR_SUCCESS || !balancer->gmutex) {
955 "mutex creation of %s : %s failed", balancer_mutex_type,
956 balancer->s->sname);
958 }
961
962 /* setup shm for balancers */
963 bshm = ap_proxy_find_balancershm(storage, conf->bslot, balancer, &index);
964 if (bshm) {
965 if ((rv = storage->fgrab(conf->bslot, index)) != APR_SUCCESS) {
966 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02408) "balancer slotmem_fgrab failed");
967 return !OK;
968 }
969 }
970 else {
971 if ((rv = storage->grab(conf->bslot, &index)) != APR_SUCCESS) {
972 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01181) "balancer slotmem_grab failed");
973 return !OK;
974 }
975 if ((rv = storage->dptr(conf->bslot, index, (void *)&bshm)) != APR_SUCCESS) {
976 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01182) "balancer slotmem_dptr failed");
977 return !OK;
978 }
979 }
980 if ((rv = ap_proxy_share_balancer(balancer, bshm, index)) != APR_SUCCESS) {
981 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01183) "Cannot share balancer");
982 return !OK;
983 }
984
985 /* create slotmem slots for workers */
986 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01184) "Doing workers create: %s (%s), %d, %d [%u]",
987 balancer->s->name, balancer->s->sname,
989 (int)balancer->max_workers, i);
990
991 rv = storage->create(&new, balancer->s->sname,
993 balancer->max_workers, type, pconf);
994 if (rv != APR_SUCCESS) {
995 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01185) "worker slotmem_create failed");
996 return !OK;
997 }
998 balancer->wslot = new;
999 balancer->storage = storage;
1000
1001 /* sync all timestamps */
1002 balancer->wupdated = balancer->s->wupdated = tstamp;
1003
1004 /* now go thru each worker */
1005 workers = (proxy_worker **)balancer->workers->elts;
1006 for (j = 0; j < balancer->workers->nelts; j++, workers++) {
1008
1009 worker = *workers;
1010
1011 shm = ap_proxy_find_workershm(storage, balancer->wslot, worker, &index);
1012 if (shm) {
1013 if ((rv = storage->fgrab(balancer->wslot, index)) != APR_SUCCESS) {
1014 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02409) "worker slotmem_fgrab failed");
1015 return !OK;
1016 }
1017 }
1018 else {
1019 if ((rv = storage->grab(balancer->wslot, &index)) != APR_SUCCESS) {
1020 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01186) "worker slotmem_grab failed");
1021 return !OK;
1022
1023 }
1024 if ((rv = storage->dptr(balancer->wslot, index, (void *)&shm)) != APR_SUCCESS) {
1025 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01187) "worker slotmem_dptr failed");
1026 return !OK;
1027 }
1028 }
1029 if ((rv = ap_proxy_share_worker(worker, shm, index)) != APR_SUCCESS) {
1030 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01188) "Cannot share worker");
1031 return !OK;
1032 }
1033 worker->s->updated = tstamp;
1034 }
1035 if (conf->bal_persist) {
1036 /* We could have just read-in a persisted config. Force a sync. */
1037 balancer->wupdated--;
1038 ap_proxy_sync_balancer(balancer, s, conf);
1039 }
1040 }
1041 s = s->next;
1042 }
1043
1044 return OK;
1045}
1046
1047static void create_radio(const char *name, unsigned int flag, request_rec *r)
1048{
1049 ap_rvputs(r, "<td><label for='", name, "1'>On</label> <input name='", name, "' id='", name, "1' value='1' type=radio", NULL);
1050 if (flag)
1051 ap_rputs(" checked", r);
1052 ap_rvputs(r, "> <br/> <label for='", name, "0'>Off</label> <input name='", name, "' id='", name, "0' value='0' type=radio", NULL);
1053 if (!flag)
1054 ap_rputs(" checked", r);
1055 ap_rputs("></td>\n", r);
1056}
1057
1058static void push2table(const char *input, apr_table_t *params,
1059 const char *allowed[], apr_pool_t *p)
1060{
1061 char *args;
1062 char *tok, *val;
1063 char *key;
1064
1065 if (input == NULL) {
1066 return;
1067 }
1068 args = apr_pstrdup(p, input);
1069
1070 key = apr_strtok(args, "&", &tok);
1071 while (key) {
1072 val = strchr(key, '=');
1073 if (val) {
1074 *val++ = '\0';
1075 }
1076 else {
1077 val = "";
1078 }
1081 /* hcuri, worker name, balancer name, at least are escaped when building the form, so twice */
1083 if (allowed == NULL) { /* allow all */
1085 }
1086 else {
1087 const char **ok = allowed;
1088 while (*ok) {
1089 if (strcmp(*ok, key) == 0) {
1091 break;
1092 }
1093 ok++;
1094 }
1095 }
1096 key = apr_strtok(NULL, "&", &tok);
1097 }
1098}
1099
1100/* Returns non-zero if the Referer: header value passed matches the
1101 * host of the request. */
1102static int safe_referer(request_rec *r, const char *ref)
1103{
1104 apr_uri_t uri;
1105
1106 if (apr_uri_parse(r->pool, ref, &uri) || !uri.hostname)
1107 return 0;
1108
1109 return strcasecmp(uri.hostname, ap_get_server_name(r)) == 0;
1110}
1111
1112/*
1113 * Process the paramters and add or update the worker of the
1114 * balancer. Must only be called if the nonce has been validated to
1115 * match, to avoid XSS attacks.
1116 */
1121
1122{
1123 apr_status_t rv;
1124 /* First set the params */
1125 if (wsel) {
1126 const char *val;
1128
1129 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01192) "settings worker params");
1130
1131 if ((val = apr_table_get(params, "w_lf"))) {
1132 int ival;
1133 double fval = atof(val);
1134 ival = fval * 100.0;
1135 if (ival >= 100 && ival <= 10000) {
1136 wsel->s->lbfactor = ival;
1137 if (bsel)
1139 }
1140 }
1141 if ((val = apr_table_get(params, "w_wr"))) {
1142 if (strlen(val) && strlen(val) < sizeof(wsel->s->route))
1143 strcpy(wsel->s->route, val);
1144 else
1145 *wsel->s->route = '\0';
1146 }
1147 if ((val = apr_table_get(params, "w_rr"))) {
1148 if (strlen(val) && strlen(val) < sizeof(wsel->s->redirect))
1149 strcpy(wsel->s->redirect, val);
1150 else
1151 *wsel->s->redirect = '\0';
1152 }
1153 /*
1154 * TODO: Look for all 'w_status_#' keys and then loop thru
1155 * on that # character, since the character == the flag
1156 */
1157 if ((val = apr_table_get(params, "w_status_I"))) {
1159 }
1160 if ((val = apr_table_get(params, "w_status_N"))) {
1162 }
1163 if ((val = apr_table_get(params, "w_status_D"))) {
1165 }
1166 if ((val = apr_table_get(params, "w_status_H"))) {
1168 }
1169 if ((val = apr_table_get(params, "w_status_R"))) {
1171 }
1172 if ((val = apr_table_get(params, "w_status_S"))) {
1174 }
1175 if ((val = apr_table_get(params, "w_status_C"))) {
1177 }
1178 if ((val = apr_table_get(params, "w_ls"))) {
1179 int ival = atoi(val);
1180 if (ival >= 0 && ival <= 99) {
1181 wsel->s->lbset = ival;
1182 }
1183 }
1184 if ((val = apr_table_get(params, "w_hi"))) {
1187 if (hci >= AP_WD_TM_SLICE) {
1188 wsel->s->interval = hci;
1189 }
1190 }
1191 }
1192 if ((val = apr_table_get(params, "w_hp"))) {
1193 int ival = atoi(val);
1194 if (ival >= 1) {
1195 wsel->s->passes = ival;
1196 }
1197 }
1198 if ((val = apr_table_get(params, "w_hf"))) {
1199 int ival = atoi(val);
1200 if (ival >= 1) {
1201 wsel->s->fails = ival;
1202 }
1203 }
1204 if ((val = apr_table_get(params, "w_hm"))) {
1206 for (; method->name; method++) {
1207 if (!ap_cstr_casecmp(method->name, val) && method->implemented)
1208 wsel->s->method = method->method;
1209 }
1210 }
1211 if ((val = apr_table_get(params, "w_hu"))) {
1212 if (strlen(val) && strlen(val) < sizeof(wsel->s->hcuri))
1213 strcpy(wsel->s->hcuri, val);
1214 else
1215 *wsel->s->hcuri = '\0';
1216 }
1217 if (hc_valid_expr_f && (val = apr_table_get(params, "w_he"))) {
1218 if (strlen(val) && hc_valid_expr_f(r, val) && strlen(val) < sizeof(wsel->s->hcexpr))
1219 strcpy(wsel->s->hcexpr, val);
1220 else
1221 *wsel->s->hcexpr = '\0';
1222 }
1223 /* If the health check method doesn't support an expr, then null it */
1224 if (wsel->s->method == NONE || wsel->s->method == TCP || wsel->s->method == CPING) {
1225 *wsel->s->hcexpr = '\0';
1226 }
1227 /* if enabling, we need to reset all lb params */
1229 bsel->s->need_reset = 1;
1230 }
1231
1232 }
1233
1234 if (bsel) {
1235 const char *val;
1236 int ival;
1238 "settings balancer params");
1239 if ((val = apr_table_get(params, "b_lbm"))) {
1240 if ((strlen(val) < (sizeof(bsel->s->lbpname)-1)) &&
1241 strcmp(val, bsel->s->lbpname)) {
1242 proxy_balancer_method *lbmethod;
1243 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, val, "0");
1244 if (lbmethod) {
1245 PROXY_STRNCPY(bsel->s->lbpname, val);
1246 bsel->lbmethod = lbmethod;
1247 bsel->s->wupdated = apr_time_now();
1248 bsel->s->need_reset = 1;
1249 }
1250 }
1251 }
1252 if ((val = apr_table_get(params, "b_tmo"))) {
1253 ival = atoi(val);
1254 if (ival >= 0 && ival <= 7200) { /* 2 hrs enuff? */
1255 bsel->s->timeout = apr_time_from_sec(ival);
1256 }
1257 }
1258 if ((val = apr_table_get(params, "b_max"))) {
1259 ival = atoi(val);
1260 if (ival >= 0 && ival <= 99) {
1261 bsel->s->max_attempts = ival;
1262 }
1263 }
1264 if ((val = apr_table_get(params, "b_sforce"))) {
1265 ival = atoi(val);
1266 bsel->s->sticky_force = (ival != 0);
1267 }
1268 if ((val = apr_table_get(params, "b_ss")) && *val) {
1269 if (strlen(val) < (sizeof(bsel->s->sticky_path)-1)) {
1270 if (*val == '-' && *(val+1) == '\0')
1271 *bsel->s->sticky_path = *bsel->s->sticky = '\0';
1272 else {
1273 char *path;
1274 PROXY_STRNCPY(bsel->s->sticky_path, val);
1275 PROXY_STRNCPY(bsel->s->sticky, val);
1276
1277 if ((path = strchr((char *)bsel->s->sticky, '|'))) {
1278 *path++ = '\0';
1279 PROXY_STRNCPY(bsel->s->sticky_path, path);
1280 }
1281 }
1282 }
1283 }
1284 if ((val = apr_table_get(params, "b_wyes")) &&
1285 (*val == '1' && *(val+1) == '\0') &&
1286 (val = apr_table_get(params, "b_nwrkr"))) {
1287 char *ret;
1290 if (!nworker && storage->num_free_slots(bsel->wslot)) {
1291#if APR_HAS_THREADS
1292 if ((rv = PROXY_GLOBAL_LOCK(bsel)) != APR_SUCCESS) {
1294 "%s: Lock failed for adding worker",
1295 bsel->s->name);
1296 }
1297#endif
1298 ret = ap_proxy_define_worker(conf->pool, &nworker, bsel, conf, val, 0);
1299 if (!ret) {
1300 unsigned int index;
1303 if ((rv = storage->grab(bsel->wslot, &index)) != APR_SUCCESS) {
1305 "worker slotmem_grab failed");
1306#if APR_HAS_THREADS
1307 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1309 "%s: Unlock failed for adding worker",
1310 bsel->s->name);
1311 }
1312#endif
1313 return HTTP_BAD_REQUEST;
1314 }
1315 if ((rv = storage->dptr(bsel->wslot, index, (void *)&shm)) != APR_SUCCESS) {
1317 "worker slotmem_dptr failed");
1318#if APR_HAS_THREADS
1319 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1321 "%s: Unlock failed for adding worker",
1322 bsel->s->name);
1323 }
1324#endif
1325 return HTTP_BAD_REQUEST;
1326 }
1327 if ((rv = ap_proxy_share_worker(nworker, shm, index)) != APR_SUCCESS) {
1329 "Cannot share worker");
1330#if APR_HAS_THREADS
1331 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1333 "%s: Unlock failed for adding worker",
1334 bsel->s->name);
1335 }
1336#endif
1337 return HTTP_BAD_REQUEST;
1338 }
1339 if ((rv = ap_proxy_initialize_worker(nworker, r->server, conf->pool)) != APR_SUCCESS) {
1341 "Cannot init worker");
1342#if APR_HAS_THREADS
1343 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1345 "%s: Unlock failed for adding worker",
1346 bsel->s->name);
1347 }
1348#endif
1349 return HTTP_BAD_REQUEST;
1350 }
1351 /* sync all timestamps */
1352 bsel->wupdated = bsel->s->wupdated = nworker->s->updated = apr_time_now();
1353 /* by default, all new workers are disabled */
1355 } else {
1357 "%s: failed to add worker %s",
1358 bsel->s->name, val);
1359#if APR_HAS_THREADS
1361#endif
1362 return HTTP_BAD_REQUEST;
1363 }
1364#if APR_HAS_THREADS
1365 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1367 "%s: Unlock failed for adding worker",
1368 bsel->s->name);
1369 }
1370#endif
1371 } else {
1373 "%s: failed to add worker %s",
1374 bsel->s->name, val);
1375 return HTTP_BAD_REQUEST;
1376 }
1377
1378 }
1379
1380 }
1381 return APR_SUCCESS;
1382}
1383
1384/*
1385 * Process a request for balancer or worker management from another module
1386 */
1388{
1389 void *sconf;
1390 proxy_server_conf *conf;
1393 const char *name;
1395 conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
1396
1397 /* Process the parameters */
1398 if ((name = apr_table_get(params, "b"))) {
1399 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage "
1400 "balancer: %s", name);
1403 }
1404
1405 if ((name = apr_table_get(params, "w"))) {
1406 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage "
1407 "worker: %s", name);
1408 wsel = ap_proxy_get_worker(r->pool, bsel, conf, name);
1409 }
1410 if (bsel) {
1411 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage "
1412 "balancer: %s", bsel->s->name);
1414 }
1415 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage failed: "
1416 "No balancer!");
1417 return HTTP_BAD_REQUEST;
1418}
1419
1420/*
1421 * builds the page and links to configure via HTLM or XML.
1422 */
1426 int usexml)
1427{
1428 const char *action;
1429 proxy_balancer *balancer;
1430 proxy_worker *worker;
1432 int i, n;
1433 action = ap_construct_url(r->pool, r->uri, r);
1434 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01204) "genning page");
1435
1436 if (usexml) {
1437 char date[APR_RFC822_DATE_LEN];
1438 ap_set_content_type_ex(r, "text/xml", 1);
1439 ap_rputs("<?xml version='1.0' encoding='UTF-8' ?>\n", r);
1440 ap_rputs("<httpd:manager xmlns:httpd='http://httpd.apache.org'>\n", r);
1441 ap_rputs(" <httpd:balancers>\n", r);
1442 balancer = (proxy_balancer *)conf->balancers->elts;
1443 for (i = 0; i < conf->balancers->nelts; i++) {
1444 ap_rputs(" <httpd:balancer>\n", r);
1445 /* Start proxy_balancer */
1446 ap_rvputs(r, " <httpd:name>", balancer->s->name, "</httpd:name>\n", NULL);
1447 if (*balancer->s->sticky) {
1448 ap_rvputs(r, " <httpd:stickysession>", ap_escape_html(r->pool, balancer->s->sticky),
1449 "</httpd:stickysession>\n", NULL);
1450 ap_rprintf(r,
1451 " <httpd:nofailover>%s</httpd:nofailover>\n",
1452 (balancer->s->sticky_force ? "On" : "Off"));
1453 }
1454 ap_rprintf(r,
1455 " <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>",
1456 apr_time_sec(balancer->s->timeout));
1457 if (balancer->s->max_attempts_set) {
1458 ap_rprintf(r,
1459 " <httpd:maxattempts>%d</httpd:maxattempts>\n",
1460 balancer->s->max_attempts);
1461 }
1462 ap_rvputs(r, " <httpd:lbmethod>", balancer->lbmethod->name,
1463 "</httpd:lbmethod>\n", NULL);
1464 if (*balancer->s->sticky) {
1465 ap_rprintf(r,
1466 " <httpd:scolonpathdelim>%s</httpd:scolonpathdelim>\n",
1467 (balancer->s->scolonsep ? "On" : "Off"));
1468 }
1469 /* End proxy_balancer */
1470 ap_rputs(" <httpd:workers>\n", r);
1471 workers = (proxy_worker **)balancer->workers->elts;
1472 for (n = 0; n < balancer->workers->nelts; n++) {
1473 worker = *workers;
1474 /* Start proxy_worker */
1475 ap_rputs(" <httpd:worker>\n", r);
1476 ap_rvputs(r, " <httpd:name>", ap_proxy_worker_name(r->pool, worker),
1477 "</httpd:name>\n", NULL);
1478 ap_rvputs(r, " <httpd:scheme>", worker->s->scheme,
1479 "</httpd:scheme>\n", NULL);
1480 ap_rvputs(r, " <httpd:hostname>", worker->s->hostname_ex,
1481 "</httpd:hostname>\n", NULL);
1482 ap_rprintf(r, " <httpd:loadfactor>%.2f</httpd:loadfactor>\n",
1483 (float)(worker->s->lbfactor)/100.0);
1484 ap_rprintf(r,
1485 " <httpd:port>%d</httpd:port>\n",
1486 worker->s->port);
1487 ap_rprintf(r, " <httpd:min>%d</httpd:min>\n",
1488 worker->s->min);
1489 ap_rprintf(r, " <httpd:smax>%d</httpd:smax>\n",
1490 worker->s->smax);
1491 ap_rprintf(r, " <httpd:max>%d</httpd:max>\n",
1492 worker->s->hmax);
1493 ap_rprintf(r,
1494 " <httpd:ttl>%" APR_TIME_T_FMT "</httpd:ttl>\n",
1495 apr_time_sec(worker->s->ttl));
1496 if (worker->s->timeout_set) {
1497 ap_rprintf(r,
1498 " <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>\n",
1499 apr_time_sec(worker->s->timeout));
1500 }
1501 if (worker->s->acquire_set) {
1502 ap_rprintf(r,
1503 " <httpd:acquire>%" APR_TIME_T_FMT "</httpd:acquire>\n",
1504 apr_time_msec(worker->s->acquire));
1505 }
1506 if (worker->s->recv_buffer_size_set) {
1507 ap_rprintf(r,
1508 " <httpd:recv_buffer_size>%" APR_SIZE_T_FMT "</httpd:recv_buffer_size>\n",
1509 worker->s->recv_buffer_size);
1510 }
1511 if (worker->s->io_buffer_size_set) {
1512 ap_rprintf(r,
1513 " <httpd:io_buffer_size>%" APR_SIZE_T_FMT "</httpd:io_buffer_size>\n",
1514 worker->s->io_buffer_size);
1515 }
1516 if (worker->s->keepalive_set) {
1517 ap_rprintf(r,
1518 " <httpd:keepalive>%s</httpd:keepalive>\n",
1519 (worker->s->keepalive ? "On" : "Off"));
1520 }
1521 /* Begin proxy_worker_stat */
1522 ap_rputs(" <httpd:status>", r);
1524 ap_rputs("</httpd:status>\n", r);
1525 if ((worker->s->error_time > 0) && apr_rfc822_date(date, worker->s->error_time) == APR_SUCCESS) {
1526 ap_rvputs(r, " <httpd:error_time>", date,
1527 "</httpd:error_time>\n", NULL);
1528 }
1529 ap_rprintf(r,
1530 " <httpd:retries>%d</httpd:retries>\n",
1531 worker->s->retries);
1532 ap_rprintf(r,
1533 " <httpd:lbstatus>%d</httpd:lbstatus>\n",
1534 worker->s->lbstatus);
1535 ap_rprintf(r,
1536 " <httpd:loadfactor>%.2f</httpd:loadfactor>\n",
1537 (float)(worker->s->lbfactor)/100.0);
1538 ap_rprintf(r,
1539 " <httpd:transferred>%" APR_OFF_T_FMT "</httpd:transferred>\n",
1540 worker->s->transferred);
1541 ap_rprintf(r,
1542 " <httpd:read>%" APR_OFF_T_FMT "</httpd:read>\n",
1543 worker->s->read);
1544 ap_rprintf(r,
1545 " <httpd:elected>%" APR_SIZE_T_FMT "</httpd:elected>\n",
1546 worker->s->elected);
1547 ap_rvputs(r, " <httpd:route>",
1548 ap_escape_html(r->pool, worker->s->route),
1549 "</httpd:route>\n", NULL);
1550 ap_rvputs(r, " <httpd:redirect>",
1551 ap_escape_html(r->pool, worker->s->redirect),
1552 "</httpd:redirect>\n", NULL);
1553 ap_rprintf(r,
1554 " <httpd:busy>%" APR_SIZE_T_FMT "</httpd:busy>\n",
1555 worker->s->busy);
1556 ap_rprintf(r, " <httpd:lbset>%d</httpd:lbset>\n",
1557 worker->s->lbset);
1558 /* End proxy_worker_stat */
1559 if (!ap_cstr_casecmp(worker->s->scheme, "ajp")) {
1560 ap_rputs(" <httpd:flushpackets>", r);
1561 switch (worker->s->flush_packets) {
1562 case flush_off:
1563 ap_rputs("Off", r);
1564 break;
1565 case flush_on:
1566 ap_rputs("On", r);
1567 break;
1568 case flush_auto:
1569 ap_rputs("Auto", r);
1570 break;
1571 }
1572 ap_rputs("</httpd:flushpackets>\n", r);
1573 if (worker->s->flush_packets == flush_auto) {
1574 ap_rprintf(r,
1575 " <httpd:flushwait>%d</httpd:flushwait>\n",
1576 worker->s->flush_wait);
1577 }
1578 if (worker->s->ping_timeout_set) {
1579 ap_rprintf(r,
1580 " <httpd:ping>%" APR_TIME_T_FMT "</httpd:ping>",
1581 apr_time_msec(worker->s->ping_timeout));
1582 }
1583 }
1584 if (worker->s->disablereuse_set) {
1585 ap_rprintf(r,
1586 " <httpd:disablereuse>%s</httpd:disablereuse>\n",
1587 (worker->s->disablereuse ? "On" : "Off"));
1588 }
1589 if (worker->s->conn_timeout_set) {
1590 ap_rprintf(r,
1591 " <httpd:connectiontimeout>%" APR_TIME_T_FMT "</httpd:connectiontimeout>\n",
1592 apr_time_msec(worker->s->conn_timeout));
1593 }
1594 if (worker->s->retry_set) {
1595 ap_rprintf(r,
1596 " <httpd:retry>%" APR_TIME_T_FMT "</httpd:retry>\n",
1597 apr_time_sec(worker->s->retry));
1598 }
1599 ap_rputs(" </httpd:worker>\n", r);
1600 ++workers;
1601 }
1602 ap_rputs(" </httpd:workers>\n", r);
1603 ap_rputs(" </httpd:balancer>\n", r);
1604 ++balancer;
1605 }
1606 ap_rputs(" </httpd:balancers>\n", r);
1607 ap_rputs("</httpd:manager>", r);
1608 }
1609 else {
1610 ap_set_content_type(r, "text/html; charset=ISO-8859-1");
1612 "<html><head><title>Balancer Manager</title>\n", r);
1613 ap_rputs("<style type='text/css'>\n"
1614 "table {\n"
1615 " border-width: 1px;\n"
1616 " border-spacing: 3px;\n"
1617 " border-style: solid;\n"
1618 " border-color: gray;\n"
1619 " border-collapse: collapse;\n"
1620 " background-color: white;\n"
1621 " text-align: center;\n"
1622 "}\n"
1623 "th {\n"
1624 " border-width: 1px;\n"
1625 " padding: 2px;\n"
1626 " border-style: dotted;\n"
1627 " border-color: gray;\n"
1628 " background-color: lightgray;\n"
1629 " text-align: center;\n"
1630 "}\n"
1631 "td {\n"
1632 " border-width: 1px;\n"
1633 " padding: 2px;\n"
1634 " border-style: dotted;\n"
1635 " border-color: gray;\n"
1636 " background-color: white;\n"
1637 " text-align: center;\n"
1638 "}\n"
1639 "</style>\n</head>\n", r);
1640 ap_rputs("<body><h1>Load Balancer Manager for ", r);
1642 "</h1>\n\n", NULL);
1643 ap_rvputs(r, "<dl><dt>Server Version: ",
1644 ap_get_server_description(), "</dt>\n", NULL);
1645 ap_rvputs(r, "<dt>Server Built: ",
1646 ap_get_server_built(), "</dt>\n", NULL);
1647 ap_rvputs(r, "<dt>Balancer changes will ", conf->bal_persist ? "" : "NOT ",
1648 "be persisted on restart.</dt>", NULL);
1649 ap_rvputs(r, "<dt>Balancers are ", conf->inherit ? "" : "NOT ",
1650 "inherited from main server.</dt>", NULL);
1651 ap_rvputs(r, "<dt>ProxyPass settings are ", conf->ppinherit ? "" : "NOT ",
1652 "inherited from main server.</dt>", NULL);
1653 ap_rputs("</dl>\n", r);
1654 balancer = (proxy_balancer *)conf->balancers->elts;
1655 for (i = 0; i < conf->balancers->nelts; i++) {
1656
1657 ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r);
1658 ap_rvputs(r, "<a href=\"", ap_escape_uri(r->pool, r->uri), "?b=",
1659 balancer->s->name + sizeof(BALANCER_PREFIX) - 1,
1660 "&amp;nonce=", balancer->s->nonce,
1661 "\">", NULL);
1662 ap_rvputs(r, balancer->s->name, "</a> [",balancer->s->sname, "]</h3>\n", NULL);
1663 ap_rputs("\n\n<table><tr>"
1664 "<th>MaxMembers</th><th>StickySession</th><th>DisableFailover</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>"
1665 "<th>Path</th><th>Active</th></tr>\n<tr>", r);
1666 /* the below is a safe cast, since the number of slots total will
1667 * never be more than max_workers, which is restricted to int */
1668 ap_rprintf(r, "<td>%d [%d Used]</td>\n", balancer->max_workers,
1669 balancer->max_workers - (int)storage->num_free_slots(balancer->wslot));
1670 if (*balancer->s->sticky) {
1671 if (strcmp(balancer->s->sticky, balancer->s->sticky_path)) {
1672 ap_rvputs(r, "<td>", ap_escape_html(r->pool, balancer->s->sticky), " | ",
1673 ap_escape_html(r->pool, balancer->s->sticky_path), NULL);
1674 }
1675 else {
1676 ap_rvputs(r, "<td>", ap_escape_html(r->pool, balancer->s->sticky), NULL);
1677 }
1678 }
1679 else {
1680 ap_rputs("<td> (None) ", r);
1681 }
1682 ap_rprintf(r, "</td><td>%s</td>\n",
1683 balancer->s->sticky_force ? "On" : "Off");
1684 ap_rprintf(r, "<td>%" APR_TIME_T_FMT "</td>",
1685 apr_time_sec(balancer->s->timeout));
1686 ap_rprintf(r, "<td>%d</td>\n", balancer->s->max_attempts);
1687 ap_rprintf(r, "<td>%s</td>\n",
1688 balancer->s->lbpname);
1689 ap_rputs("<td>", r);
1690 if (*balancer->s->vhost) {
1691 ap_rvputs(r, balancer->s->vhost, " -> ", NULL);
1692 }
1693 ap_rvputs(r, balancer->s->vpath, "</td>\n", NULL);
1694 ap_rprintf(r, "<td>%s</td>\n",
1695 !balancer->s->inactive ? "Yes" : "No");
1696 ap_rputs("</tr>\n</table>\n<br />", r);
1697 ap_rputs("\n\n<table><tr>"
1698 "<th>Worker URL</th>"
1699 "<th>Route</th><th>RouteRedir</th>"
1700 "<th>Factor</th><th>Set</th><th>Status</th>"
1701 "<th>Elected</th><th>Busy</th><th>Load</th><th>To</th><th>From</th>", r);
1703 ap_rputs("<th>HC Method</th><th>HC Interval</th><th>Passes</th><th>Fails</th><th>HC uri</th><th>HC Expr</th>", r);
1704 }
1705 ap_rputs("</tr>\n", r);
1706
1707 workers = (proxy_worker **)balancer->workers->elts;
1708 for (n = 0; n < balancer->workers->nelts; n++) {
1709 char fbuf[50];
1710 worker = *workers;
1711 ap_rvputs(r, "<tr>\n<td><a href=\"",
1712 ap_escape_uri(r->pool, r->uri), "?b=",
1713 balancer->s->name + sizeof(BALANCER_PREFIX) - 1, "&amp;w=",
1714 ap_escape_uri(r->pool, worker->s->name_ex),
1715 "&amp;nonce=", balancer->s->nonce,
1716 "\">", NULL);
1717 ap_rvputs(r, (*worker->s->uds_path ? "<i>" : ""), ap_proxy_worker_name(r->pool, worker),
1718 (*worker->s->uds_path ? "</i>" : ""), "</a></td>", NULL);
1719 ap_rvputs(r, "<td>", ap_escape_html(r->pool, worker->s->route),
1720 NULL);
1721 ap_rvputs(r, "</td><td>",
1722 ap_escape_html(r->pool, worker->s->redirect), NULL);
1723 ap_rprintf(r, "</td><td>%.2f</td>", (float)(worker->s->lbfactor)/100.0);
1724 ap_rprintf(r, "<td>%d</td><td>", worker->s->lbset);
1726 ap_rputs("</td>", r);
1727 ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->elected);
1728 ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->busy);
1729 ap_rprintf(r, "<td>%d</td><td>", worker->s->lbstatus);
1730 ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r);
1731 ap_rputs("</td><td>", r);
1732 ap_rputs(apr_strfsize(worker->s->read, fbuf), r);
1734 ap_rprintf(r, "</td><td>%s</td>", ap_proxy_show_hcmethod(worker->s->method));
1735 ap_rprintf(r, "<td>%" APR_TIME_T_FMT "ms</td>", apr_time_as_msec(worker->s->interval));
1736 ap_rprintf(r, "<td>%d (%d)</td>", worker->s->passes,worker->s->pcount);
1737 ap_rprintf(r, "<td>%d (%d)</td>", worker->s->fails, worker->s->fcount);
1738 ap_rprintf(r, "<td>%s</td>", ap_escape_html(r->pool, worker->s->hcuri));
1739 ap_rprintf(r, "<td>%s", worker->s->hcexpr);
1740 }
1741 ap_rputs("</td></tr>\n", r);
1742
1743 ++workers;
1744 }
1745 ap_rputs("</table>\n", r);
1746 ++balancer;
1747 }
1748 ap_rputs("<hr />\n", r);
1749 if (hc_show_exprs_f) {
1751 }
1752 if (wsel && bsel) {
1753 ap_rputs("<h3>Edit worker settings for ", r);
1754 ap_rvputs(r, (*wsel->s->uds_path?"<i>":""), ap_proxy_worker_name(r->pool, wsel), (*wsel->s->uds_path?"</i>":""), "</h3>\n", NULL);
1755 ap_rputs("<form method='POST' enctype='application/x-www-form-urlencoded' action=\"", r);
1756 ap_rvputs(r, ap_escape_uri(r->pool, action), "\">\n", NULL);
1757 ap_rputs("<table><tr><td>Load factor:</td><td><input name='w_lf' id='w_lf' type=text ", r);
1758 ap_rprintf(r, "value='%.2f'></td></tr>\n", (float)(wsel->s->lbfactor)/100.0);
1759 ap_rputs("<tr><td>LB Set:</td><td><input name='w_ls' id='w_ls' type=text ", r);
1760 ap_rprintf(r, "value='%d'></td></tr>\n", wsel->s->lbset);
1761 ap_rputs("<tr><td>Route:</td><td><input name='w_wr' id='w_wr' type=text ", r);
1762 ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->route),
1763 NULL);
1764 ap_rputs("\"></td></tr>\n", r);
1765 ap_rputs("<tr><td>Route Redirect:</td><td><input name='w_rr' id='w_rr' type=text ", r);
1766 ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->redirect),
1767 NULL);
1768 ap_rputs("\"></td></tr>\n", r);
1769 ap_rputs("<tr><td>Status:</td>", r);
1770 ap_rputs("<td><table><tr>"
1771 "<th>Ignore Errors</th>"
1772 "<th>Draining Mode</th>"
1773 "<th>Disabled</th>"
1774 "<th>Hot Standby</th>"
1775 "<th>Hot Spare</th>", r);
1776 if (hc_show_exprs_f) {
1777 ap_rputs("<th>HC Fail</th>", r);
1778 }
1779 ap_rputs("<th>Stopped</th></tr>\n<tr>", r);
1785 if (hc_show_exprs_f) {
1787 }
1789 ap_rputs("</tr></table></td></tr>\n", r);
1790 if (hc_select_exprs_f) {
1792 ap_rputs("<tr><td colspan='2'>\n<table align='center'><tr><th>Health Check param</th><th>Value</th></tr>\n", r);
1793 ap_rputs("<tr><td>Method</td><td><select name='w_hm'>\n", r);
1794 for (; method->name; method++) {
1795 if (method->implemented) {
1796 ap_rprintf(r, "<option value='%s' %s >%s</option>\n",
1797 method->name,
1798 (wsel->s->method == method->method) ? "selected" : "",
1799 method->name);
1800 }
1801 }
1802 ap_rputs("</select>\n</td></tr>\n", r);
1803 ap_rputs("<tr><td>Expr</td><td><select name='w_he'>\n", r);
1804 hc_select_exprs_f(r, wsel->s->hcexpr);
1805 ap_rputs("</select>\n</td></tr>\n", r);
1806 ap_rprintf(r, "<tr><td>Interval (ms)</td><td><input name='w_hi' id='w_hi' type='text' "
1807 "value='%" APR_TIME_T_FMT "'></td></tr>\n", apr_time_as_msec(wsel->s->interval));
1808 ap_rprintf(r, "<tr><td>Passes trigger</td><td><input name='w_hp' id='w_hp' type='text' "
1809 "value='%d'></td></tr>\n", wsel->s->passes);
1810 ap_rprintf(r, "<tr><td>Fails trigger)</td><td><input name='w_hf' id='w_hf' type='text' "
1811 "value='%d'></td></tr>\n", wsel->s->fails);
1812 ap_rprintf(r, "<tr><td>HC uri</td><td><input name='w_hu' id='w_hu' type='text' "
1813 "value=\"%s\"></td></tr>\n", ap_escape_html(r->pool, wsel->s->hcuri));
1814 ap_rputs("</table>\n</td></tr>\n", r);
1815 }
1816 ap_rputs("<tr><td colspan='2'><input type=submit value='Submit'></td></tr>\n", r);
1817 ap_rvputs(r, "</table>\n<input type=hidden name='w' id='w' ", NULL);
1818 ap_rvputs(r, "value=\"", ap_escape_uri(r->pool, wsel->s->name_ex), "\">\n", NULL);
1819 ap_rvputs(r, "<input type=hidden name='b' id='b' ", NULL);
1820 ap_rvputs(r, "value=\"", ap_escape_html(r->pool, bsel->s->name + sizeof(BALANCER_PREFIX) - 1),
1821 "\">\n", NULL);
1822 ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='",
1823 bsel->s->nonce, "'>\n", NULL);
1824 ap_rputs("</form>\n", r);
1825 ap_rputs("<hr />\n", r);
1826 } else if (bsel) {
1828 const ap_list_provider_names_t *pname;
1829 int i;
1830 ap_rputs("<h3>Edit balancer settings for ", r);
1831 ap_rvputs(r, ap_escape_html(r->pool, bsel->s->name), "</h3>\n", NULL);
1832 ap_rputs("<form method='POST' enctype='application/x-www-form-urlencoded' action=\"", r);
1833 ap_rvputs(r, ap_escape_uri(r->pool, action), "\">\n", NULL);
1834 ap_rputs("<table>\n", r);
1836 if (provs) {
1837 ap_rputs("<tr><td>LBmethod:</td>", r);
1838 ap_rputs("<td>\n<select name='b_lbm' id='b_lbm'>", r);
1839 pname = (ap_list_provider_names_t *)provs->elts;
1840 for (i = 0; i < provs->nelts; i++, pname++) {
1841 ap_rvputs(r,"<option value='", pname->provider_name, "'", NULL);
1842 if (strcmp(pname->provider_name, bsel->s->lbpname) == 0)
1843 ap_rputs(" selected ", r);
1844 ap_rvputs(r, ">", pname->provider_name, "\n", NULL);
1845 }
1846 ap_rputs("</select>\n</td></tr>\n", r);
1847 }
1848 ap_rputs("<tr><td>Timeout:</td><td><input name='b_tmo' id='b_tmo' type=text ", r);
1849 ap_rprintf(r, "value='%" APR_TIME_T_FMT "'></td></tr>\n", apr_time_sec(bsel->s->timeout));
1850 ap_rputs("<tr><td>Failover Attempts:</td><td><input name='b_max' id='b_max' type=text ", r);
1851 ap_rprintf(r, "value='%d'></td></tr>\n", bsel->s->max_attempts);
1852 ap_rputs("<tr><td>Disable Failover:</td>", r);
1853 create_radio("b_sforce", bsel->s->sticky_force, r);
1854 ap_rputs("</tr>\n", r);
1855 ap_rputs("<tr><td>Sticky Session:</td><td><input name='b_ss' id='b_ss' size=64 type=text ", r);
1856 if (strcmp(bsel->s->sticky, bsel->s->sticky_path)) {
1857 ap_rvputs(r, "value =\"", ap_escape_html(r->pool, bsel->s->sticky), " | ",
1858 ap_escape_html(r->pool, bsel->s->sticky_path), NULL);
1859 }
1860 else {
1861 ap_rvputs(r, "value =\"", ap_escape_html(r->pool, bsel->s->sticky), NULL);
1862 }
1863 ap_rputs("\">&nbsp;&nbsp;&nbsp;&nbsp;(Use '-' to delete)</td></tr>\n", r);
1864 if (storage->num_free_slots(bsel->wslot) != 0) {
1865 ap_rputs("<tr><td>Add New Worker:</td><td><input name='b_nwrkr' id='b_nwrkr' size=32 type=text>"
1866 "&nbsp;&nbsp;&nbsp;&nbsp;Are you sure? <input name='b_wyes' id='b_wyes' type=checkbox value='1'>"
1867 "</td></tr>", r);
1868 }
1869 ap_rputs("<tr><td colspan=2><input type=submit value='Submit'></td></tr>\n", r);
1870 ap_rvputs(r, "</table>\n<input type=hidden name='b' id='b' ", NULL);
1871 ap_rvputs(r, "value=\"", ap_escape_html(r->pool, bsel->s->name + sizeof(BALANCER_PREFIX) - 1),
1872 "\">\n", NULL);
1873 ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='",
1874 bsel->s->nonce, "'>\n", NULL);
1875 ap_rputs("</form>\n", r);
1876 ap_rputs("<hr />\n", r);
1877 }
1878 ap_rputs(ap_psignature("",r), r);
1879 ap_rputs("</body></html>\n", r);
1880 ap_rflush(r);
1881 }
1882}
1883
1884/* Manages the loadfactors and member status
1885 * The balancer, worker and nonce are obtained from
1886 * the request args (?b=...&w=...&nonce=....).
1887 * All other params are pulled from any POST
1888 * data that exists.
1889 * TODO:
1890 * /.../<whatever>/balancer/worker/nonce
1891 */
1893{
1894 void *sconf;
1895 proxy_server_conf *conf;
1896 proxy_balancer *balancer, *bsel = NULL;
1899 int i;
1900 const char *name, *ref;
1901 apr_status_t rv;
1902
1903 /* is this for us? */
1904 if (strcmp(r->handler, "balancer-manager")) {
1905 return DECLINED;
1906 }
1907
1908 r->allowed = 0
1909 | (AP_METHOD_BIT << M_GET)
1910 | (AP_METHOD_BIT << M_POST);
1911 if ((r->method_number != M_GET) && (r->method_number != M_POST)) {
1912 return DECLINED;
1913 }
1914
1916 conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
1917 params = apr_table_make(r->pool, 10);
1918
1919 balancer = (proxy_balancer *)conf->balancers->elts;
1920 for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
1921#if APR_HAS_THREADS
1922 if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
1924 "%s: Lock failed for balancer_handler",
1925 balancer->s->name);
1926 }
1927#endif
1928 ap_proxy_sync_balancer(balancer, r->server, conf);
1929#if APR_HAS_THREADS
1930 if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
1932 "%s: Unlock failed for balancer_handler",
1933 balancer->s->name);
1934 }
1935#endif
1936 }
1937
1938 if (r->args && (r->method_number == M_GET)) {
1939 const char *allowed[] = { "w", "b", "nonce", "xml", NULL };
1940 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01191) "parsing r->args");
1941
1942 push2table(r->args, params, allowed, r->pool);
1943 }
1944 if (r->method_number == M_POST) {
1946 apr_size_t len = 1024;
1947 char *buf = apr_pcalloc(r->pool, len+1);
1948
1952 if (rv != APR_SUCCESS) {
1954 }
1956 buf[len] = '\0';
1958 }
1959
1960 /* Ignore parameters if this looks like XSRF */
1961 ref = apr_table_get(r->headers_in, "Referer");
1963 && (!ref || !safe_referer(r, ref))) {
1965 "ignoring params in balancer-manager cross-site access %s: %s", ref, ap_get_server_name(r));
1967 }
1968
1969 /* Process the parameters */
1970 if ((name = apr_table_get(params, "b")))
1973
1974 if ((name = apr_table_get(params, "w"))) {
1975 wsel = ap_proxy_get_worker(r->pool, bsel, conf, name);
1976 }
1977
1978
1979 /* Check that the supplied nonce matches this server's nonce;
1980 * otherwise ignore all parameters, to prevent a CSRF
1981 * attack. */
1982 if (bsel
1983 && (*bsel->s->nonce
1984 && ((name = apr_table_get(params, "nonce")) != NULL
1985 && strcmp(bsel->s->nonce, name) == 0))) {
1986 /* Process the parameters and add the worker to the balancer */
1988 if (rv != APR_SUCCESS) {
1989 return HTTP_BAD_REQUEST;
1990 }
1991 }
1992
1993 /* display the HTML or XML page */
1994 if (apr_table_get(params, "xml")) {
1995 balancer_display_page(r, conf, bsel, wsel, 1);
1996 } else {
1997 balancer_display_page(r, conf, bsel, wsel, 0);
1998 }
1999 return DONE;
2000}
2001
2003{
2004 while (s) {
2005 proxy_balancer *balancer;
2006 int i;
2007 void *sconf = s->module_config;
2009 apr_status_t rv;
2010
2011 if (conf->balancers->nelts) {
2013 unsigned int num;
2014 storage->attach(&(conf->bslot), conf->id, &size, &num, p);
2015 if (!conf->bslot) {
2016 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01205) "slotmem_attach failed");
2017 exit(1); /* Ugly, but what else? */
2018 }
2019 }
2020
2021 balancer = (proxy_balancer *)conf->balancers->elts;
2022 for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
2023 rv = ap_proxy_initialize_balancer(balancer, s, p);
2024
2025 if (rv != APR_SUCCESS) {
2027 "Failed to init balancer %s in child",
2028 balancer->s->name);
2029 exit(1); /* Ugly, but what else? */
2030 }
2031 init_balancer_members(p, s, balancer);
2032 }
2033 s = s->next;
2034 }
2035
2036}
2037
2039{
2040 /* Only the mpm_winnt has child init hook handler.
2041 * make sure that we are called after the mpm
2042 * initializes
2043 */
2044 static const char *const aszPred[] = { "mpm_winnt.c", "mod_slotmem_shm.c", NULL};
2045 static const char *const aszPred2[] = { "mod_proxy.c", NULL};
2046 /* manager handler */
2055}
2056
2059 NULL, /* create per-directory config structure */
2060 NULL, /* merge per-directory config structures */
2061 NULL, /* create per-server config structure */
2062 NULL, /* merge per-server config structures */
2063 NULL, /* command apr_table_t */
2064 ap_proxy_balancer_register_hook /* register hooks */
2065};
ap hook functions and macros
Apache Multi-Processing Module library.
int n
Definition ap_regex.h:278
const char apr_size_t len
Definition ap_regex.h:187
char * strstr(char *s1, char *s2)
APR-UTIL date routines.
APR Versioning Interface.
static apr_pool_t * pconf
Definition event.c:441
#define ap_get_module_config(v, m)
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_DECLARE_MODULE(foo)
void ap_hook_handler(ap_HOOK_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:170
void ap_hook_pre_config(ap_HOOK_pre_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:91
request_rec int int apr_table_t const char * path
request_rec * r
void ap_hook_child_init(ap_HOOK_child_init_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:167
const char * ap_get_server_built(void)
Definition buildmark.c:26
#define DOCTYPE_HTML_3_2
Definition httpd.h:238
const char * ap_get_server_description(void)
Definition core.c:3587
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define DONE
Definition httpd.h:458
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
#define AP_SQ_MS_CREATE_PRE_CONFIG
Definition http_core.h:1047
int ap_state_query(int query_code)
Definition core.c:5378
const char * ap_get_server_name(request_rec *r)
Definition core.c:1145
#define AP_SQ_MAIN_STATE
Definition http_core.h:1030
char * ap_construct_url(apr_pool_t *p, const char *uri, request_rec *r)
Definition core.c:1246
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_CRIT
Definition http_log.h:66
#define APLOG_EMERG
Definition http_log.h:64
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
const unsigned char * buf
Definition util_md5.h:50
apr_status_t ap_global_mutex_create(apr_global_mutex_t **mutex, const char **name, const char *type, const char *instance_id, server_rec *server, apr_pool_t *pool, apr_int32_t options)
Definition util_mutex.c:407
apr_status_t ap_mutex_register(apr_pool_t *pconf, const char *type, const char *default_dir, apr_lockmech_e default_mech, apr_int32_t options)
Definition util_mutex.c:254
int int ap_rflush(request_rec *r)
Definition protocol.c:2253
int ap_rvputs(request_rec *r,...)
Definition protocol.c:2220
int ap_map_http_request_error(apr_status_t rv, int status)
int ap_rprintf(request_rec *r, const char *fmt,...) __attribute__((format(printf
static APR_INLINE int ap_rputs(const char *str, request_rec *r)
void ap_set_content_type_ex(request_rec *r, const char *ct, int trusted)
void ap_set_content_type(request_rec *r, const char *ct)
apr_array_header_t * ap_list_provider_names(apr_pool_t *pool, const char *provider_group, const char *provider_version)
Definition provider.c:127
void * ap_lookup_provider(const char *provider_group, const char *provider_name, const char *provider_version)
Definition provider.c:99
const char apr_port_t port
Definition http_vhost.h:125
const char * host
Definition http_vhost.h:124
#define APR_MD5_DIGESTSIZE
Definition apr_md5.h:68
@ APR_BLOCK_READ
Definition apr_buckets.h:58
apr_pool_t const char * params
Definition apr_dbd.h:141
const char * url
Definition apr_escape.h:120
#define APR_HOOK_FIRST
Definition apr_hooks.h:301
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
#define APR_RETRIEVE_OPTIONAL_FN(name)
#define APR_OPTIONAL_FN_TYPE(name)
#define APR_REGISTER_OPTIONAL_FN(name)
apr_redis_t * rc
Definition apr_redis.h:173
const char * uri
Definition apr_uri.h:159
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_SERVICE_UNAVAILABLE
Definition httpd.h:538
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define AP_SLOTMEM_TYPE_PERSIST
Definition ap_slotmem.h:70
unsigned int ap_slotmem_type_t
Definition ap_slotmem.h:55
#define AP_SLOTMEM_TYPE_CLEARINUSE
Definition ap_slotmem.h:73
#define AP_SLOTMEM_PROVIDER_GROUP
Definition ap_slotmem.h:52
#define AP_SLOTMEM_PROVIDER_VERSION
Definition ap_slotmem.h:53
#define PROXY_WORKER_IS_USABLE(f)
Definition mod_proxy.h:358
#define PROXY_WORKER_DRAIN_FLAG
Definition mod_proxy.h:336
void proxy_hook_pre_request(proxy_HOOK_pre_request_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_proxy.c:3419
apr_status_t ap_proxy_set_wstatus(char c, int set, proxy_worker *w)
#define PROXY_WORKER_DISABLED_FLAG
Definition mod_proxy.h:339
#define PROXY_WORKER_STOPPED
Definition mod_proxy.h:326
#define PROXY_STRNCPY(dst, src)
Definition mod_proxy.h:395
char * ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, char **passwordp, char **hostp, apr_port_t *port)
Definition proxy_util.c:342
#define PROXY_GLOBAL_LOCK(x)
Definition mod_proxy.h:595
#define PROXY_WORKER_IN_ERROR
Definition mod_proxy.h:327
#define PROXY_WORKER_HC_FAIL
Definition mod_proxy.h:330
#define PROXY_WORKER_HOT_SPARE_FLAG
Definition mod_proxy.h:345
proxy_worker * ap_proxy_get_worker(apr_pool_t *p, proxy_balancer *balancer, proxy_server_conf *conf, const char *url)
#define PROXY_WORKER_IGNORE_ERRORS
Definition mod_proxy.h:321
char * ap_proxy_define_worker(apr_pool_t *p, proxy_worker **worker, proxy_balancer *balancer, proxy_server_conf *conf, const char *url, int do_malloc)
#define PROXY_COPY_CONF_PARAMS(w, c)
Definition mod_proxy.h:397
proxy_worker_shared * ap_proxy_find_workershm(ap_slotmem_provider_t *storage, ap_slotmem_instance_t *slot, proxy_worker *worker, unsigned int *index)
#define PROXY_THREAD_LOCK(x)
Definition mod_proxy.h:592
#define PROXY_WORKER_IGNORE_ERRORS_FLAG
Definition mod_proxy.h:335
#define PROXY_WORKER_STOPPED_FLAG
Definition mod_proxy.h:340
#define PROXY_WORKER_HOT_STANDBY
Definition mod_proxy.h:328
#define PROXY_LBMETHOD
Definition mod_proxy.h:1444
proxy_balancer * ap_proxy_get_balancer(apr_pool_t *p, proxy_server_conf *conf, const char *url, int careactive)
#define PROXY_THREAD_UNLOCK(x)
Definition mod_proxy.h:593
#define ALIGNED_PROXY_BALANCER_SHARED_SIZE
Definition mod_proxy.h:556
#define PROXY_WORKER_HOT_SPARE
Definition mod_proxy.h:331
#define ALIGNED_PROXY_WORKER_SHARED_SIZE
Definition mod_proxy.h:498
apr_status_t ap_proxy_share_worker(proxy_worker *worker, proxy_worker_shared *shm, int i)
#define PROXY_WORKER_IS(f, b)
Definition mod_proxy.h:369
char * ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w)
apr_status_t ap_proxy_sync_balancer(proxy_balancer *b, server_rec *s, proxy_server_conf *conf)
proxy_balancer_shared * ap_proxy_find_balancershm(ap_slotmem_provider_t *storage, ap_slotmem_instance_t *slot, proxy_balancer *balancer, unsigned int *index)
void proxy_hook_canon_handler(proxy_HOOK_canon_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_proxy.c:3412
apr_status_t ap_proxy_share_balancer(proxy_balancer *balancer, proxy_balancer_shared *shm, int i)
unsigned int ap_proxy_hashfunc(const char *str, proxy_hash_t method)
const char * ap_proxy_show_hcmethod(hcmethod_t method)
void proxy_hook_post_request(proxy_HOOK_post_request_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_proxy.c:3425
apr_status_t ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s, apr_pool_t *p)
#define BALANCER_PREFIX
Definition mod_proxy.h:99
#define PROXY_WORKER_HOT_STANDBY_FLAG
Definition mod_proxy.h:342
apr_status_t ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p)
#define PROXY_WORKER_DRAIN
Definition mod_proxy.h:322
#define PROXY_WORKER_HC_FAIL_FLAG
Definition mod_proxy.h:344
#define PROXY_WORKER_IS_INITIALIZED(f)
Definition mod_proxy.h:352
#define PROXY_WORKER_DISABLED
Definition mod_proxy.h:325
proxy_hcmethods_t proxy_hcmethods[]
Definition mod_proxy.c:47
#define PROXY_WORKER_IS_STANDBY(f)
Definition mod_proxy.h:354
char * ap_proxy_worker_name(apr_pool_t *p, proxy_worker *worker)
#define PROXY_GLOBAL_UNLOCK(x)
Definition mod_proxy.h:596
@ TCP
Definition mod_proxy.h:84
@ CPING
Definition mod_proxy.h:84
@ NONE
Definition mod_proxy.h:84
@ PROXY_HASHFUNC_DEFAULT
Definition mod_proxy.h:1234
#define AP_WD_TM_SLICE
#define AP_METHOD_BIT
Definition httpd.h:629
#define M_POST
Definition httpd.h:594
#define M_GET
Definition httpd.h:592
#define STANDARD20_MODULE_STUFF
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
const char * ap_psignature(const char *prefix, request_rec *r)
Definition core.c:3516
#define ap_escape_uri(ppool, path)
Definition httpd.h:1836
#define ap_strstr_c(s, c)
Definition httpd.h:2361
void ap_bin2hex(const void *src, apr_size_t srclen, char *dest)
Definition util.c:2314
#define ap_strchr_c(s, c)
Definition httpd.h:2353
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define ap_escape_html(p, s)
Definition httpd.h:1860
int ap_unescape_url(char *url)
Definition util.c:1939
apr_status_t ap_pstr2_alnum(apr_pool_t *p, const char *src, const char **dest)
Definition util.c:2733
apr_status_t ap_timeout_parameter_parse(const char *timeout_parameter, apr_interval_time_t *timeout, const char *default_time_unit)
Definition util.c:2622
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
const char * input
Definition apr_cstr.h:93
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
const char apr_int32_t flag
void * data
int type
int strcasecmp(const char *a, const char *b)
#define APR_HASH_KEY_STRING
Definition apr_hash.h:47
apr_uint16_t apr_port_t
const char apr_uint32_t * id
apr_uint32_t apr_pool_t apr_uint32_t apr_pollset_method_e method
Definition apr_poll.h:195
apr_interval_time_t apr_int32_t * num
Definition apr_poll.h:273
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_shm_t * shm
@ APR_LOCK_DEFAULT
const char * s
Definition apr_strings.h:95
#define APR_ARRAY_PUSH(ary, type)
Definition apr_tables.h:150
#define APR_ARRAY_IDX(ary, i, type)
Definition apr_tables.h:141
apr_int32_t apr_int32_t apr_int32_t err
const char const char *const * args
#define apr_time_msec(time)
Definition apr_time.h:69
#define apr_time_as_msec(time)
Definition apr_time.h:72
#define APR_RFC822_DATE_LEN
Definition apr_time.h:186
#define APR_TIME_T_FMT
Definition apr_time.h:52
apr_int64_t apr_interval_time_t
Definition apr_time.h:55
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_sec(time)
Definition apr_time.h:63
#define apr_time_from_sec(sec)
Definition apr_time.h:78
static struct h2_workers * workers
Definition h2_c1.c:48
apr_pool_t * p
Definition md_event.c:32
Proxy Extension Module for Apache.
static void balancer_display_page(request_rec *r, proxy_server_conf *conf, proxy_balancer *bsel, proxy_worker *wsel, int usexml)
static apr_OFN_hc_select_exprs_t * hc_select_exprs_f
static int proxy_balancer_post_request(proxy_worker *worker, proxy_balancer *balancer, request_rec *r, proxy_server_conf *conf)
static void push2table(const char *input, apr_table_t *params, const char *allowed[], apr_pool_t *p)
static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
static char * get_path_param(apr_pool_t *pool, char *url, const char *name, int scolon_sep)
static apr_OFN_set_worker_hc_param_t * set_worker_hc_param_f
static int safe_referer(request_rec *r, const char *ref)
static proxy_worker * find_route_worker(proxy_balancer *balancer, const char *route, request_rec *r, int recursion)
static proxy_worker * find_best_worker(proxy_balancer *balancer, request_rec *r)
static int proxy_balancer_canon(request_rec *r, char *url)
static char * get_cookie_param(request_rec *r, const char *name)
static apr_array_header_t * make_servers_ids(server_rec *main_s, apr_pool_t *p)
static void ap_proxy_balancer_register_hook(apr_pool_t *p)
ap_slotmem_provider_t * storage
static void recalc_factors(proxy_balancer *balancer)
static int balancer_process_balancer_worker(request_rec *r, proxy_server_conf *conf, proxy_balancer *bsel, proxy_worker *wsel, apr_table_t *params)
static void create_radio(const char *name, unsigned int flag, request_rec *r)
static int proxy_balancer_pre_request(proxy_worker **worker, proxy_balancer **balancer, request_rec *r, proxy_server_conf *conf, char **url)
static apr_OFN_hc_valid_expr_t * hc_valid_expr_f
static void init_balancer_members(apr_pool_t *p, server_rec *s, proxy_balancer *balancer)
static int balancer_handler(request_rec *r)
static apr_status_t decrement_busy_count(void *worker_)
static int balancer_fixup(request_rec *r, proxy_worker *worker, char **url)
static const char * balancer_mutex_type
static apr_status_t lock_remove(void *data)
static const char * make_server_id(server_rec *s, apr_pool_t *p, int full)
static void force_recovery(proxy_balancer *balancer, server_rec *s)
static apr_OFN_hc_show_exprs_t * hc_show_exprs_f
static void balancer_child_init(apr_pool_t *p, server_rec *s)
static proxy_worker * find_session_route(proxy_balancer *balancer, request_rec *r, char **route, const char **sticky_used, char **url)
static int balancer_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
static int(* ap_proxy_retry_worker_fn)(const char *proxy_function, proxy_worker *worker, server_rec *s)
static apr_status_t balancer_manage(request_rec *r, apr_table_t *params)
static void hc_show_exprs(request_rec *r)
static int hc_valid_expr(request_rec *r, const char *expr)
static const char * set_worker_hc_param(apr_pool_t *p, server_rec *s, proxy_worker *worker, const char *key, const char *val, void *v)
static void hc_select_exprs(request_rec *r, const char *expr)
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
sconf
Definition mod_so.c:349
Watchdog module for Apache.
int ap_proxy_canon_url(request_rec *r)
static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, server_rec *s)
Internal interfaces private to mod_proxy.
Apache scoreboard library.
char * name
const char * provider_name
Definition ap_provider.h:36
apr_status_t(* create)(ap_slotmem_instance_t **inst, const char *name, apr_size_t item_size, unsigned int item_num, ap_slotmem_type_t type, apr_pool_t *pool)
Definition ap_slotmem.h:111
unsigned int(* num_free_slots)(ap_slotmem_instance_t *s)
Definition ap_slotmem.h:162
apr_status_t(* fgrab)(ap_slotmem_instance_t *s, unsigned int item_id)
Definition ap_slotmem.h:189
apr_status_t(* dptr)(ap_slotmem_instance_t *s, unsigned int item_id, void **mem)
Definition ap_slotmem.h:130
apr_status_t(* grab)(ap_slotmem_instance_t *s, unsigned int *item_id)
Definition ap_slotmem.h:175
apr_status_t(* attach)(ap_slotmem_instance_t **inst, const char *name, apr_size_t *item_size, unsigned int *item_num, apr_pool_t *pool)
Definition ap_slotmem.h:122
apr_pool_t * pool
Definition httpd.h:1154
struct apr_bucket_alloc_t * bucket_alloc
Definition httpd.h:1201
server_rec * s
Definition h2_workers.c:76
proxy_worker *(* finder)(proxy_balancer *balancer, request_rec *r)
Definition mod_proxy.h:584
const char * name
Definition mod_proxy.h:583
unsigned int forcerecovery
Definition mod_proxy.h:547
unsigned int max_attempts_set
Definition mod_proxy.h:542
unsigned int scolonsep
Definition mod_proxy.h:541
unsigned int inactive
Definition mod_proxy.h:546
unsigned int sticky_force
Definition mod_proxy.h:540
apr_interval_time_t timeout
Definition mod_proxy.h:535
ap_slotmem_instance_t * wslot
Definition mod_proxy.h:561
proxy_balancer_shared * s
Definition mod_proxy.h:574
apr_time_t wupdated
Definition mod_proxy.h:566
ap_slotmem_provider_t * storage
Definition mod_proxy.h:562
apr_array_header_t * errstatuses
Definition mod_proxy.h:560
apr_global_mutex_t * gmutex
Definition mod_proxy.h:568
proxy_balancer_method * lbmethod
Definition mod_proxy.h:567
apr_array_header_t * workers
Definition mod_proxy.h:559
hcmethod_t method
Definition mod_proxy.h:88
const char * id
Definition mod_proxy.h:165
ap_slotmem_provider_t * storage
Definition mod_proxy.h:193
unsigned int inherit
Definition mod_proxy.h:206
apr_pool_t * pool
Definition mod_proxy.h:166
unsigned int bal_persist
Definition mod_proxy.h:205
unsigned int ppinherit
Definition mod_proxy.h:208
apr_array_header_t * balancers
Definition mod_proxy.h:161
ap_slotmem_instance_t * bslot
Definition mod_proxy.h:192
apr_interval_time_t ttl
Definition mod_proxy.h:450
unsigned int conn_timeout_set
Definition mod_proxy.h:472
unsigned int keepalive
Definition mod_proxy.h:465
unsigned int timeout_set
Definition mod_proxy.h:469
apr_size_t recv_buffer_size
Definition mod_proxy.h:457
apr_interval_time_t timeout
Definition mod_proxy.h:453
char hostname_ex[256]
Definition mod_proxy.h:488
apr_time_t updated
Definition mod_proxy.h:448
hcmethod_t method
Definition mod_proxy.h:485
apr_off_t transferred
Definition mod_proxy.h:462
apr_time_t error_time
Definition mod_proxy.h:449
apr_interval_time_t retry
Definition mod_proxy.h:452
unsigned int ping_timeout_set
Definition mod_proxy.h:471
unsigned int disablereuse_set
Definition mod_proxy.h:476
enum proxy_worker_shared::@35 flush_packets
unsigned int status
Definition mod_proxy.h:442
apr_size_t elected
Definition mod_proxy.h:459
unsigned int io_buffer_size_set
Definition mod_proxy.h:474
unsigned int keepalive_set
Definition mod_proxy.h:475
apr_size_t io_buffer_size
Definition mod_proxy.h:458
apr_interval_time_t interval
Definition mod_proxy.h:486
apr_interval_time_t conn_timeout
Definition mod_proxy.h:456
unsigned int retry_set
Definition mod_proxy.h:468
apr_interval_time_t ping_timeout
Definition mod_proxy.h:455
unsigned int recv_buffer_size_set
Definition mod_proxy.h:473
unsigned int disablereuse
Definition mod_proxy.h:466
apr_interval_time_t acquire
Definition mod_proxy.h:454
unsigned int acquire_set
Definition mod_proxy.h:470
proxy_worker_shared * s
Definition mod_proxy.h:505
A structure that represents the current request.
Definition httpd.h:845
int status
Definition httpd.h:891
char * uri
Definition httpd.h:1016
const char * handler
Definition httpd.h:994
apr_table_t * notes
Definition httpd.h:985
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
conn_rec * connection
Definition httpd.h:849
struct ap_filter_t * input_filters
Definition httpd.h:1072
apr_table_t * headers_in
Definition httpd.h:976
apr_int64_t allowed
Definition httpd.h:922
apr_table_t * subprocess_env
Definition httpd.h:983
server_rec * server
Definition httpd.h:851
char * args
Definition httpd.h:1026
A structure to be used for Per-vhost config.
Definition httpd.h:1301
apr_port_t host_port
Definition httpd.h:1309
server_addr_rec * next
Definition httpd.h:1303
char * virthost
Definition httpd.h:1305
apr_sockaddr_t * host_addr
Definition httpd.h:1307
A structure to store information for each virtual server.
Definition httpd.h:1322
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
apr_status_t apr_rfc822_date(char *date_str, apr_time_t t)
Definition timestr.c:42
@ AP_MODE_READBYTES
Definition util_filter.h:43
Apache MD5 library.
IN ULONG IN INT timeout
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray