mod_proxy_balancer.c revision ac1df18dac383d0596ba2a6c7dc4ca9c8722a1da
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht/* Licensed to the Apache Software Foundation (ASF) under one or more
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht * contributor license agreements. See the NOTICE file distributed with
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht * this work for additional information regarding copyright ownership.
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht * The ASF licenses this file to You under the Apache License, Version 2.0
98890889ffb2e8f6f722b00e265a211f13b5a861Corneliu-Claudiu Prodescu * (the "License"); you may not use this file except in compliance with
2643008447e30b6025f742eb6a661f38be756b1eSimon Ulbricht * the License. You may obtain a copy of the License at
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht * Unless required by applicable law or agreed to in writing, software
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht * distributed under the License is distributed on an "AS IS" BASIS,
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0b152383e04dbeb10dba29bcdfaa0981e4d9df27Simon Ulbricht * See the License for the specific language governing permissions and
2643008447e30b6025f742eb6a661f38be756b1eSimon Ulbricht * limitations under the License.
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht/* Load balancer module for Apache proxy */
6150196e8d99f7161a622fdc1a872fecd378195fSimon Ulbrichtmodule AP_MODULE_DECLARE_DATA proxy_balancer_module;
9da6e0cb2ea6e43f5b09dcd2a9af5468a5d0fcf4Christian Maederstatic int proxy_balancer_canon(request_rec *r, char *url)
9da6e0cb2ea6e43f5b09dcd2a9af5468a5d0fcf4Christian Maeder ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
9da6e0cb2ea6e43f5b09dcd2a9af5468a5d0fcf4Christian Maeder "proxy: BALANCER: canonicalising URL %s", url);
9da6e0cb2ea6e43f5b09dcd2a9af5468a5d0fcf4Christian Maeder /* do syntatic check.
9da6e0cb2ea6e43f5b09dcd2a9af5468a5d0fcf4Christian Maeder * We break the URL into host, port, path, search
9da6e0cb2ea6e43f5b09dcd2a9af5468a5d0fcf4Christian Maeder err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht "error parsing URL %s: %s",
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht /* now parse path/search args, according to rfc1738 */
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht /* N.B. if this isn't a true proxy request, then the URL _path_
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht * has already been decoded. True proxy requests have r->uri
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht * == r->unparsed_uri, and no others have that property.
00962bbc8b21c9a4af3b08983bdf82591cb8067bSimon Ulbricht /* process path */
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq);
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht r->filename = apr_pstrcat(r->pool, "proxy:balancer://", host,
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbrichtstatic int init_balancer_members(proxy_server_conf *conf, server_rec *s,
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht workers = (proxy_worker *)balancer->workers->elts;
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht for (i = 0; i < balancer->workers->nelts; i++) {
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht worker_is_initialized = PROXY_WORKER_IS_INITIALIZED(workers);
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * If the worker is not initialized check whether its scoreboard
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * slot is already initialized.
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht slot = (proxy_worker_stat *) ap_get_scoreboard_lb(workers->id);
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht worker_is_initialized = slot->status & PROXY_WORKER_INITIALIZED;
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht ap_proxy_initialize_worker_share(conf, workers, s);
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht /* Set to the original configuration */
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht workers->s->lbstatus = workers->s->lbfactor =
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht /* Set default number of attempts to the number of
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht if (!balancer->max_attempts_set && balancer->workers->nelts > 1) {
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht balancer->max_attempts = balancer->workers->nelts - 1;
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht/* Retrieve the parameter with the given name
abea93ed557b22ea833e1524ee5ca11afc12208aSimon Ulbricht * Something like 'JSESSIONID=12345...N'
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbrichtstatic char *get_path_param(apr_pool_t *pool, char *url,
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht * Session path was found, get it's value
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht path = apr_strtok(apr_pstrdup(pool, path), "?&", &q);
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbrichtstatic char *get_cookie_param(request_rec *r, const char *name)
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht for (start_cookie = ap_strstr_c(cookies, name); start_cookie;
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht start_cookie = ap_strstr_c(start_cookie + 1, name)) {
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht while(*start_cookie && isspace(*start_cookie))
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if (*start_cookie == '=' && start_cookie[1]) {
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * Session cookie was found, get it's value
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if ((end_cookie = strchr(cookie, ';')) != NULL)
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if((end_cookie = strchr(cookie, ',')) != NULL)
1651c7f5055453e18a8c34f96c333e2aa702a34eSimon Ulbricht/* Find the worker that has the 'route' defined
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbrichtstatic proxy_worker *find_route_worker(proxy_balancer *balancer,
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht worker = (proxy_worker *)balancer->workers->elts;
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht for (i = 0; i < balancer->workers->nelts; i++, worker++) {
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) )
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) {
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if (worker && PROXY_WORKER_IS_USABLE(worker)) {
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * If the worker is in error state run
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * retry on that worker. It will be marked as
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * operational if the retry timeout is elapsed.
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht * The worker might still be unusable, but we try
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht ap_proxy_retry_worker("BALANCER", worker, r->server);
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * We have a worker that is unusable.
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * It can be in error or disabled, but in case
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht * it has a redirection set use that redirection worker.
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * This enables to safely remove the member from the
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * balancer. Of course you will need some kind of
1651c7f5055453e18a8c34f96c333e2aa702a34eSimon Ulbricht * session replication between those two remote.
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht rworker = find_route_worker(balancer, worker->s->redirect, r);
1651c7f5055453e18a8c34f96c333e2aa702a34eSimon Ulbricht /* Check if the redirect worker is usable */
1651c7f5055453e18a8c34f96c333e2aa702a34eSimon Ulbricht if (rworker && !PROXY_WORKER_IS_USABLE(rworker)) {
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht * If the worker is in error state run
1651c7f5055453e18a8c34f96c333e2aa702a34eSimon Ulbricht * retry on that worker. It will be marked as
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * operational if the retry timeout is elapsed.
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht * The worker might still be unusable, but we try
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht ap_proxy_retry_worker("BALANCER", rworker, r->server);
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht if (rworker && PROXY_WORKER_IS_USABLE(rworker))
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbrichtstatic proxy_worker *find_session_route(proxy_balancer *balancer,
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht /* Try to find the sticky route inside url */
369771f5d48a40eda134026b1f45f63b2c00bdb8Simon Ulbricht *route = get_path_param(r->pool, *url, balancer->sticky_path);
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht "proxy: BALANCER: Found value %s for "
6a26171b5bc5b6ec3e1b02ae30dcfb6f03d7ffefSimon Ulbricht "stickysession %s", *route, balancer->sticky_path);
7c84f8fd92ed59eb2b9a674e6b1ea93c0f945006Simon Ulbricht *route = get_cookie_param(r, balancer->sticky);
7c84f8fd92ed59eb2b9a674e6b1ea93c0f945006Simon Ulbricht ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
7c84f8fd92ed59eb2b9a674e6b1ea93c0f945006Simon Ulbricht "proxy: BALANCER: Found value %s for "
7c84f8fd92ed59eb2b9a674e6b1ea93c0f945006Simon Ulbricht "stickysession %s", *route, balancer->sticky);
7c84f8fd92ed59eb2b9a674e6b1ea93c0f945006Simon Ulbricht * If we found a value for sticksession, find the first '.' within.
7c84f8fd92ed59eb2b9a674e6b1ea93c0f945006Simon Ulbricht * Everything after '.' (if present) is our route.
a210c2e5add831cd438183c8602ed8e610922beaSimon Ulbricht if ((*route) && ((*route = strchr(*route, '.')) != NULL ))
return worker;
return NULL;
request_rec *r)
return NULL;
if (candidate)
#if APR_HAS_THREADS
return candidate;
char **url)
if (scheme)
if (!worker) {
NULL));
return OK;
int ok = 0;
if (!ok) {
request_rec *r,
int access_status;
if (!*balancer &&
return DECLINED;
return DECLINED;
if (runtime) {
int i, total_factor = 0;
workers++;
int i, member_of = 0;
workers++;
if (member_of) {
return HTTP_SERVICE_UNAVAILABLE;
if (!*worker) {
if (!runtime) {
return HTTP_SERVICE_UNAVAILABLE;
if (route) {
return access_status;
request_rec *r,
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
int access_status;
const char *name;
return DECLINED;
return DECLINED;
if (r->args) {
return access_status;
return HTTP_BAD_REQUEST;
++worker;
if (wsel) {
const char *val;
if (bsel)
++worker;
++balancer;
NULL);
++worker;
++balancer;
NULL);
NULL);
return OK;
balancer++;
s = s->next;
request_rec *r)
int total_factor = 0;
int cur_lbset = 0;
int max_lbset = 0;
int checking_standby;
int checked_standby;
cur_lbset++;
if (mycandidate) {
return mycandidate;
request_rec *r)
int cur_lbset = 0;
int max_lbset = 0;
int checking_standby;
int checked_standby;
cur_lbset++;
return mycandidate;