-#define FIX_15207
/* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
#include "mod_proxy.h"
#include "mod_core.h"
#include "apr_optional.h"
+#include "scoreboard.h"
#include "mod_status.h"
#if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
int ival;
if (!strcasecmp(key, "loadfactor")) {
+ /* Normalized load factor. Used with BalancerMamber,
+ * it is a number between 1 and 100.
+ */
worker->lbfactor = atoi(val);
if (worker->lbfactor < 1 || worker->lbfactor > 100)
return "LoadFactor must be number between 1..100";
}
else if (!strcasecmp(key, "retry")) {
+ /* If set it will give the retry timeout for the worker
+ * The default value is 60 seconds, meaning that if
+ * in error state, it will be retried after that timeout.
+ */
ival = atoi(val);
if (ival < 1)
return "Retry must be at least one second";
worker->retry = apr_time_from_sec(ival);
}
else if (!strcasecmp(key, "ttl")) {
+ /* Time in seconds that will destroy all the connections
+ * that exced the smax
+ */
ival = atoi(val);
if (ival < 1)
return "TTL must be at least one second";
worker->ttl = apr_time_from_sec(ival);
}
else if (!strcasecmp(key, "min")) {
+ /* Initial number of connections to remote
+ */
ival = atoi(val);
if (ival < 0)
return "Min must be a positive number";
worker->min = ival;
}
else if (!strcasecmp(key, "max")) {
+ /* Maximum number of connections to remote
+ */
ival = atoi(val);
if (ival < 0)
return "Max must be a positive number";
}
/* XXX: More inteligent naming needed */
else if (!strcasecmp(key, "smax")) {
+ /* Maximum number of connections to remote that
+ * will not be destroyed
+ */
ival = atoi(val);
if (ival < 0)
return "Smax must be a positive number";
worker->smax = ival;
}
else if (!strcasecmp(key, "acquire")) {
+ /* Acquire timeout in milliseconds.
+ * If set this will be the maximum time to
+ * wait for a free connection.
+ */
ival = atoi(val);
if (ival < 1)
return "Acquire must be at least one mili second";
worker->acquire_set = 1;
}
else if (!strcasecmp(key, "timeout")) {
+ /* Connection timeout in seconds.
+ * Defaults to server timeout.
+ */
ival = atoi(val);
if (ival < 1)
return "Timeout must be at least one second";
worker->keepalive_set = 1;
}
else if (!strcasecmp(key, "route")) {
+ /* Worker route.
+ */
+ if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
+ return "Route length must be < 64 characters";
worker->route = apr_pstrdup(p, val);
}
else if (!strcasecmp(key, "redirect")) {
+ /* Worker redirection route.
+ */
+ if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
+ return "Redirect length must be < 64 characters";
worker->redirect = apr_pstrdup(p, val);
}
else {
int ival;
if (!strcasecmp(key, "stickysession")) {
+ /* Balancer sticky session name.
+ * Set to something like JSESSIONID or
+ * PHPSESSIONID, etc..,
+ */
balancer->sticky = apr_pstrdup(p, val);
}
else if (!strcasecmp(key, "nofailover")) {
+ /* If set to 'on' the session will break
+ * if the worker is in error state or
+ * disabled.
+ */
if (!strcasecmp(val, "on"))
balancer->sticky_force = 1;
else if (!strcasecmp(val, "off"))
return "failover must be On|Off";
}
else if (!strcasecmp(key, "timeout")) {
+ /* Balancer timeout in seconds.
+ * If set this will be the maximum time to
+ * wait for a free worker.
+ * Default is not to wait.
+ */
ival = atoi(val);
if (ival < 1)
return "timeout must be at least one second";
balancer->timeout = apr_time_from_sec(ival);
}
+ else if (!strcasecmp(key, "maxattempts")) {
+ /* Maximum number of failover attempts before
+ * giving up.
+ */
+ ival = atoi(val);
+ if (ival < 0)
+ return "maximum number of attempts must be a positive number";
+ balancer->max_attempts = ival;
+ balancer->max_attempts_set = 1;
+ }
+ else if (!strcasecmp(key, "lbmethod")) {
+ /* Which LB scheduler method */
+ if (!strcasecmp(val, "traffic"))
+ balancer->lbmethod = lbmethod_traffic;
+ else if (!strcasecmp(val, "requests"))
+ balancer->lbmethod = lbmethod_requests;
+ else
+ return "lbmethod must be Traffic|Requests";
+ }
else {
return "unknown Balancer parameter";
}
if (conf->req && r->parsed_uri.scheme) {
/* but it might be something vhosted */
if (!(r->parsed_uri.hostname
- && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
+ && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))
&& ap_matches_request_vhost(r, r->parsed_uri.hostname,
(apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port
: ap_default_port(r))))) {
for (i = 0; i < conf->aliases->nelts; i++) {
len = alias_match(r->unparsed_uri, ent[i].fake);
if (len > 0) {
+ if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
+ return DECLINED;
+ }
r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
r->unparsed_uri + len, NULL);
r->handler = "proxy-server";
long maxfwd;
proxy_balancer *balancer = NULL;
proxy_worker *worker = NULL;
+ int attempts = 0, max_attempts = 0;
/* is this for us? */
if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
apr_table_set(r->headers_in, "Max-Forwards",
apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0));
- url = r->filename + 6;
- p = strchr(url, ':');
- if (p == NULL) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
- "proxy_handler no URL in %s", r->filename);
- return HTTP_BAD_REQUEST;
- }
-
- /* If the host doesn't have a domain name, add one and redirect. */
- if (conf->domain != NULL) {
- rc = proxy_needsdomain(r, url, conf->domain);
- if (ap_is_HTTP_REDIRECT(rc))
- return HTTP_MOVED_PERMANENTLY;
- }
-
- *p = '\0';
- scheme = apr_pstrdup(r->pool, url);
- *p = ':';
-
- /* Check URI's destination host against NoProxy hosts */
- /* Bypass ProxyRemote server lookup if configured as NoProxy */
- /* we only know how to handle communication to a proxy via http */
- /*if (strcasecmp(scheme, "http") == 0) */
- {
- int ii;
- struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
+ do {
+ url = r->filename + 6;
+ p = strchr(url, ':');
+ if (p == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy_handler no URL in %s", r->filename);
+ return HTTP_BAD_REQUEST;
+ }
- for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
- direct_connect = list[ii].matcher(&list[ii], r);
+ /* If the host doesn't have a domain name, add one and redirect. */
+ if (conf->domain != NULL) {
+ rc = proxy_needsdomain(r, url, conf->domain);
+ if (ap_is_HTTP_REDIRECT(rc))
+ return HTTP_MOVED_PERMANENTLY;
}
+
+ *p = '\0';
+ scheme = apr_pstrdup(r->pool, url);
+ *p = ':';
+
+ /* Check URI's destination host against NoProxy hosts */
+ /* Bypass ProxyRemote server lookup if configured as NoProxy */
+ /* we only know how to handle communication to a proxy via http */
+ /*if (strcasecmp(scheme, "http") == 0) */
+ {
+ int ii;
+ struct dirconn_entry *list = (struct dirconn_entry *)
+ conf->dirconn->elts;
+
+ for (direct_connect = ii = 0; ii < conf->dirconn->nelts &&
+ !direct_connect; ii++) {
+ direct_connect = list[ii].matcher(&list[ii], r);
+ }
#if DEBUGGING
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
(direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
r->uri);
#endif
- }
-
- /* Try to obtain the most suitable worker */
- access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
- if (access_status != OK)
- return access_status;
+ }
- /* firstly, try a proxy, unless a NoProxy directive is active */
- if (!direct_connect) {
- for (i = 0; i < proxies->nelts; i++) {
- p2 = ap_strchr_c(ents[i].scheme, ':'); /* is it a partial URL? */
- if (strcmp(ents[i].scheme, "*") == 0 ||
- (ents[i].use_regex && ap_regexec(ents[i].regexp, url, 0,NULL, 0)) ||
- (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
- (p2 != NULL &&
- strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
-
- /* handle the scheme */
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "Trying to run scheme_handler against proxy");
- access_status = proxy_run_scheme_handler(r, worker, conf, url, ents[i].hostname, ents[i].port);
-
- /* an error or success */
- if (access_status != DECLINED && access_status != HTTP_BAD_GATEWAY) {
- goto cleanup;
+ /* Try to obtain the most suitable worker */
+ access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
+ if (access_status != OK)
+ return access_status;
+ if (balancer && balancer->max_attempts_set && !max_attempts)
+ max_attempts = balancer->max_attempts;
+ /* firstly, try a proxy, unless a NoProxy directive is active */
+ if (!direct_connect) {
+ for (i = 0; i < proxies->nelts; i++) {
+ p2 = ap_strchr_c(ents[i].scheme, ':'); /* is it a partial URL? */
+ if (strcmp(ents[i].scheme, "*") == 0 ||
+ (ents[i].use_regex && ap_regexec(ents[i].regexp, url,
+ 0,NULL, 0)) ||
+ (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
+ (p2 != NULL &&
+ strncasecmp(url, ents[i].scheme,
+ strlen(ents[i].scheme)) == 0)) {
+
+ /* handle the scheme */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Trying to run scheme_handler against proxy");
+ access_status = proxy_run_scheme_handler(r, worker,
+ conf, url,
+ ents[i].hostname,
+ ents[i].port);
+
+ /* an error or success */
+ if (access_status != DECLINED &&
+ access_status != HTTP_BAD_GATEWAY) {
+ goto cleanup;
+ }
+ /* we failed to talk to the upstream proxy */
}
- /* we failed to talk to the upstream proxy */
}
}
- }
- /* otherwise, try it direct */
- /* N.B. what if we're behind a firewall, where we must use a proxy or
- * give up??
- */
+ /* otherwise, try it direct */
+ /* N.B. what if we're behind a firewall, where we must use a proxy or
+ * give up??
+ */
+
+ /* handle the scheme */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Running scheme %s handler (attempt %d)",
+ scheme, attempts);
+ if ((access_status = proxy_run_scheme_handler(r, worker, conf,
+ url, NULL, 0)) == OK)
+ break;
+
+ } while (!PROXY_WORKER_IS_USABLE(worker) &&
+ max_attempts > attempts++);
- /* handle the scheme */
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "Trying to run scheme_handler");
- access_status = proxy_run_scheme_handler(r, worker, conf, url, NULL, 0);
if (DECLINED == access_status) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
"proxy: No protocol handler was valid for the URL %s. "
access_status = HTTP_FORBIDDEN;
goto cleanup;
}
-
cleanup:
if (balancer) {
int post_status = proxy_run_post_request(worker, balancer, r, conf);
ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int));
ps->workers = apr_array_make(p, 10, sizeof(proxy_worker));
ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer));
+ ps->forward = NULL;
+ ps->reverse = NULL;
ps->domain = NULL;
ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
ps->viaopt_set = 0; /* 0 means default */
ps->timeout_set = 0;
ps->badopt = bad_error;
ps->badopt_set = 0;
+ ps->pool = p;
return ps;
}
ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
ps->workers = apr_array_append(p, base->workers, overrides->workers);
ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
+ ps->forward = overrides->forward ? overrides->forward : base->forward;
+ ps->reverse = overrides->reverse ? overrides->reverse : base->reverse;
ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host;
ps->timeout= (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
-
+ ps->proxy_status = (overrides->proxy_status_set == 0) ? base->proxy_status : overrides->proxy_status;
+ ps->pool = p;
return ps;
}
new = apr_array_push(conf->aliases);
new->fake = apr_pstrdup(cmd->pool, f);
new->real = apr_pstrdup(cmd->pool, r);
+ if (r[0] == '!' && r[1] == '\0')
+ return NULL;
arr = apr_table_elts(params);
elts = (const apr_table_entry_t *)arr->elts;
psf->req = flag;
psf->req_set = 1;
+
+ if (flag && !psf->forward) {
+ psf->forward = ap_proxy_create_worker(parms->pool);
+ psf->forward->name = "proxy:forward";
+ psf->forward->hostname = "*";
+ psf->forward->scheme = "*";
+ }
return NULL;
}
+
static const char *
set_proxy_error_override(cmd_parms *parms, void *dummy, int flag)
{
}
/* Add the worker to the load balancer */
ap_proxy_add_worker_to_balancer(cmd->pool, balancer, worker);
-
return NULL;
}
* Parent directive arg is the worker/balancer name.
*/
name = ap_getword_conf(cmd->temp_pool, &pargs);
- if ((word = ap_strchr_c(name, '>')))
+ if ((word = ap_strchr(name, '>')))
*word = '\0';
}
else {
static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
+
proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
return OK;
}
-
-#define KBYTE 1024
-#define MBYTE 1048576L
-#define GBYTE 1073741824L
-
-/* Format the number of bytes nicely */
-static void format_byte_out(request_rec *r, apr_off_t bytes)
-{
-
- if (bytes < (5 * KBYTE))
- ap_rprintf(r, "%d B", (int) bytes);
- else if (bytes < (MBYTE / 2))
- ap_rprintf(r, "%.1f kB", (float) bytes / KBYTE);
- else if (bytes < (GBYTE / 2))
- ap_rprintf(r, "%.1f MB", (float) bytes / MBYTE);
- else
- ap_rprintf(r, "%.1f GB", (float) bytes / GBYTE);
-}
-
/*
* proxy Extension to mod_status
*/
proxy_server_conf *conf = (proxy_server_conf *)
ap_get_module_config(sconf, &proxy_module);
proxy_balancer *balancer = NULL;
- proxy_runtime_worker *worker = NULL;
+ proxy_worker *worker = NULL;
if (flags & AP_STATUS_SHORT || conf->balancers->nelts == 0 ||
conf->proxy_status == status_off)
ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r);
ap_rvputs(r, balancer->name, "</h1>\n\n", NULL);
ap_rputs("\n\n<table border=\"0\"><tr>"
- "<th>SSes</th><th>Timeout</th>"
+ "<th>SSes</th><th>Timeout</th><th>Method</th>"
"</tr>\n<tr>", r);
ap_rvputs(r, "<td>", balancer->sticky, NULL);
- ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>\n",
+ ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
apr_time_sec(balancer->timeout));
+ ap_rprintf(r, "<td>%s</td>\n",
+ balancer->lbmethod == lbmethod_requests ? "Requests" :
+ balancer->lbmethod == lbmethod_traffic ? "Traffic" :
+ "Unknown");
ap_rputs("</table>\n", r);
ap_rputs("\n\n<table border=\"0\"><tr>"
- "<th>Sch</th><th>Host</th>"
+ "<th>Sch</th><th>Host</th><th>Stat</th>"
"<th>Route</th><th>Redir</th>"
"<th>F</th><th>Acc</th><th>Wr</th><th>Rd</th>"
"</tr>\n", r);
- worker = (proxy_runtime_worker *)balancer->workers->elts;
+ worker = (proxy_worker *)balancer->workers->elts;
for (n = 0; n < balancer->workers->nelts; n++) {
-
- ap_rvputs(r, "<tr>\n<td>", worker->w->scheme, "</td>", NULL);
- ap_rvputs(r, "<td>", worker->w->hostname, "</td>", NULL);
- ap_rvputs(r, "<td>", worker->w->route, NULL);
- ap_rvputs(r, "</td><td>", worker->w->redirect, NULL);
- ap_rprintf(r, "</td><td>%.2f</td>", worker->s->lbfactor);
+ char fbuf[50];
+ ap_rvputs(r, "<tr>\n<td>", worker->scheme, "</td>", NULL);
+ ap_rvputs(r, "<td>", worker->hostname, "</td><td>", NULL);
+ if (worker->s->status & PROXY_WORKER_DISABLED)
+ ap_rputs("Dis", r);
+ else if (worker->s->status & PROXY_WORKER_IN_ERROR)
+ ap_rputs("Err", r);
+ else if (worker->s->status & PROXY_WORKER_INITIALIZED)
+ ap_rputs("Ok", r);
+ else
+ ap_rputs("-", r);
+ ap_rvputs(r, "</td><td>", worker->s->route, NULL);
+ ap_rvputs(r, "</td><td>", worker->s->redirect, NULL);
+ ap_rprintf(r, "</td><td>%d</td>", worker->s->lbfactor);
ap_rprintf(r, "<td>%d</td><td>", (int)(worker->s->elected));
- format_byte_out(r, worker->s->transfered);
+ ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r);
ap_rputs("</td><td>", r);
- format_byte_out(r, worker->s->transfered);
+ ap_rputs(apr_strfsize(worker->s->read, fbuf), r);
ap_rputs("</td>\n", r);
/* TODO: Add the rest of dynamic worker data */
"<tr><th>Timeout</th><td>Balancer Timeout</td></tr>\n"
"<tr><th>Sch</th><td>Connection scheme</td></tr>\n"
"<tr><th>Host</th><td>Backend Hostname</td></tr>\n"
+ "<tr><th>Stat</th><td>Worker status</td></tr>\n"
"<tr><th>Route</th><td>Session Route</td></tr>\n"
"<tr><th>Redir</th><td>Session Route Redirection</td></tr>\n"
"<tr><th>F</th><td>Load Balancer Factor in %</td></tr>\n"
"<tr><th>Acc</th><td>Number of requests</td></tr>\n"
- "<tr><th>Wr</th><td>Number of bytes transfered</td></tr>\n"
- "<tr><th>Rd</th><td>Number of bytes readed</td></tr>\n"
+ "<tr><th>Wr</th><td>Number of bytes transferred</td></tr>\n"
+ "<tr><th>Rd</th><td>Number of bytes read</td></tr>\n"
"</table>", r);
return OK;
}
+static void child_init(apr_pool_t *p, server_rec *s)
+{
+ proxy_worker *reverse = NULL;
+
+ while (s) {
+ void *sconf = s->module_config;
+ proxy_server_conf *conf;
+ proxy_worker *worker;
+ int i;
+
+ conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
+ /* Initialize worker's shared scoreboard data */
+ worker = (proxy_worker *)conf->workers->elts;
+ for (i = 0; i < conf->workers->nelts; i++) {
+ ap_proxy_initialize_worker_share(conf, worker, s);
+ ap_proxy_initialize_worker(worker, s);
+ worker++;
+ }
+ /* Initialize forward worker if defined */
+ if (conf->forward) {
+ ap_proxy_initialize_worker_share(conf, conf->forward, s);
+ ap_proxy_initialize_worker(conf->forward, s);
+ /* Do not disable worker in case of errors */
+ conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS;
+ /* Disable address cache for generic forward worker */
+ conf->forward->is_address_reusable = 0;
+ }
+ if (!reverse) {
+ reverse = ap_proxy_create_worker(p);
+ reverse->name = "proxy:reverse";
+ reverse->hostname = "*";
+ reverse->scheme = "*";
+ ap_proxy_initialize_worker_share(conf, reverse, s);
+ ap_proxy_initialize_worker(reverse, s);
+ /* Do not disable worker in case of errors */
+ reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS;
+ /* Disable address cache for generic reverse worker */
+ reverse->is_address_reusable = 0;
+ }
+ conf->reverse = reverse;
+ s = s->next;
+ }
+}
+
/*
* This routine is called before the server processes the configuration
* files. There is no return value.
{
APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL,
APR_HOOK_MIDDLE);
+ /* Reset workers count on gracefull restart */
+ proxy_lb_workers = 0;
return OK;
}
#ifndef FIX_15207
static const char * const aszSucc[]={ "mod_rewrite.c", NULL };
#endif
-
+ /* Only the mpm_winnt has child init hook handler.
+ * make sure that we are called after the mpm
+ * initializes.
+ */
+ static const char *const aszPred[] = { "mpm_winnt.c", NULL};
+
+ APR_REGISTER_OPTIONAL_FN(ap_proxy_lb_workers);
/* handler */
ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
/* filename-to-URI translation */
ap_hook_pre_config(proxy_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
/* post config handling */
ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+ /* child init handling */
+ ap_hook_child_init(child_init, aszPred, NULL, APR_HOOK_MIDDLE);
+
}
module AP_MODULE_DECLARE_DATA proxy_module =