/* Licensed to the Apache Software Foundation (ASF) under one or more *contributorlicenseagreements.SeetheNOTICEfiledistributedwith *thisworkforadditionalinformationregardingcopyrightownership. *TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0 *(the"License");youmaynotusethisfileexceptincompliancewith *theLicense.YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
if (!strcasecmp(key, "loadfactor")) { /* Normalized load factor. Used with BalancerMember, *itisanumberbetween1and100.
*/ double fval = atof(val);
ival = fval * 100.0; if (ival < 100 || ival > 10000) return"LoadFactor must be a number between 1..100";
worker->s->lbfactor = ival;
} elseif (!strcasecmp(key, "retry")) { /* If set it will give the retry timeout for the worker *Thedefaultvalueis60seconds,meaningthatif *inerrorstate,itwillberetriedafterthattimeout.
*/
ival = atoi(val); if (ival < 0) return"Retry must be a positive value";
worker->s->retry = apr_time_from_sec(ival);
worker->s->retry_set = 1;
} elseif (!strcasecmp(key, "ttl")) { /* Time in seconds that will destroy all the connections *thatexceedthesmax
*/
ival = atoi(val); if (ival < 1) return"TTL must be at least one second";
worker->s->ttl = apr_time_from_sec(ival);
} elseif (!strcasecmp(key, "min")) { /* Initial number of connections to remote
*/
ival = atoi(val); if (ival < 0) return"Min must be a positive number";
worker->s->min = ival;
} elseif (!strcasecmp(key, "max")) { /* Maximum number of connections to remote
*/
ival = atoi(val); if (ival < 0) return"Max must be a positive number";
worker->s->hmax = ival;
} /* XXX: More intelligent naming needed */ elseif (!strcasecmp(key, "smax")) { /* Maximum number of connections to remote that *willnotbedestroyed
*/
ival = atoi(val); if (ival < 0) return"Smax must be a positive number";
worker->s->smax = ival;
} elseif (!strcasecmp(key, "acquire")) { /* Acquire timeout in given unit (default is milliseconds). *Ifsetthiswillbethemaximumtimeto *waitforafreeconnection.
*/ if (ap_timeout_parameter_parse(val, &timeout, "ms") != APR_SUCCESS) return"Acquire timeout has wrong format"; if (timeout < 1000) return"Acquire must be at least one millisecond";
worker->s->acquire = timeout;
worker->s->acquire_set = 1;
} elseif (!strcasecmp(key, "timeout")) { /* Connection timeout in seconds. *Defaultstoservertimeout.
*/
ival = atoi(val); if (ival < 1) return"Timeout must be at least one second";
worker->s->timeout = apr_time_from_sec(ival);
worker->s->timeout_set = 1;
} elseif (!strcasecmp(key, "iobuffersize")) { long s = atol(val); if (s < 512 && s) { return"IOBufferSize must be >= 512 bytes, or 0 for system default.";
}
worker->s->io_buffer_size = (s ? s : AP_IOBUFSIZE);
worker->s->io_buffer_size_set = 1;
} elseif (!strcasecmp(key, "receivebuffersize")) {
ival = atoi(val); if (ival < 512 && ival != 0) { return"ReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
}
worker->s->recv_buffer_size = ival;
worker->s->recv_buffer_size_set = 1;
} elseif (!strcasecmp(key, "keepalive")) { if (!strcasecmp(val, "on"))
worker->s->keepalive = 1; elseif (!strcasecmp(val, "off"))
worker->s->keepalive = 0; else return"KeepAlive must be On|Off";
worker->s->keepalive_set = 1;
} elseif (!strcasecmp(key, "disablereuse")) { if (!strcasecmp(val, "on"))
worker->s->disablereuse = 1; elseif (!strcasecmp(val, "off"))
worker->s->disablereuse = 0; else return"DisableReuse must be On|Off";
worker->s->disablereuse_set = 1;
} elseif (!strcasecmp(key, "enablereuse")) { if (!strcasecmp(val, "on"))
worker->s->disablereuse = 0; elseif (!strcasecmp(val, "off"))
worker->s->disablereuse = 1; else return"EnableReuse must be On|Off";
worker->s->disablereuse_set = 1;
} elseif (!strcasecmp(key, "addressttl")) { /* Address TTL in seconds
*/
apr_interval_time_t ttl; if (strcmp(val, "-1") == 0) {
worker->s->address_ttl = -1;
} elseif (ap_timeout_parameter_parse(val, &ttl, "s") == APR_SUCCESS
&& (ttl <= apr_time_from_sec(APR_INT32_MAX))
&& (ttl % apr_time_from_sec(1)) == 0) {
worker->s->address_ttl = apr_time_sec(ttl);
} else { return"AddressTTL must be -1 or a number of seconds not " "exceeding " APR_STRINGIFY(APR_INT32_MAX);
}
worker->s->address_ttl_set = 1;
} elseif (!strcasecmp(key, "route")) { /* Worker route.
*/ if (strlen(val) >= sizeof(worker->s->route)) return apr_psprintf(p, "Route length must be < %d characters",
(int)sizeof(worker->s->route));
PROXY_STRNCPY(worker->s->route, val);
} elseif (!strcasecmp(key, "redirect")) { /* Worker redirection route.
*/ if (strlen(val) >= sizeof(worker->s->redirect)) return apr_psprintf(p, "Redirect length must be < %d characters",
(int)sizeof(worker->s->redirect));
PROXY_STRNCPY(worker->s->redirect, val);
} elseif (!strcasecmp(key, "status")) { constchar *v; int mode = 1;
apr_status_t rv; /* Worker status.
*/ for (v = val; *v; v++) { if (*v == '+') {
mode = 1;
v++;
} elseif (*v == '-') {
mode = 0;
v++;
}
rv = ap_proxy_set_wstatus(*v, mode, worker); if (rv != APR_SUCCESS) return"Unknown status parameter option";
}
} elseif (!strcasecmp(key, "flushpackets")) { if (!strcasecmp(val, "on"))
worker->s->flush_packets = flush_on; elseif (!strcasecmp(val, "off"))
worker->s->flush_packets = flush_off; elseif (!strcasecmp(val, "auto"))
worker->s->flush_packets = flush_auto; else return"flushpackets must be on|off|auto";
} elseif (!strcasecmp(key, "flushwait")) {
ival = atoi(val); if (ival > 1000 || ival < 0) { return"flushwait must be <= 1000, or 0 for system default of 10 millseconds.";
} if (ival == 0)
worker->s->flush_wait = PROXY_FLUSH_WAIT; else
worker->s->flush_wait = ival * 1000; /* change to microseconds */
} elseif (!strcasecmp(key, "ping")) { /* Ping/Pong timeout in given unit (default is second).
*/ if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) return"Ping/Pong timeout has wrong format"; if (timeout < 1000) return"Ping/Pong timeout must be at least one millisecond";
worker->s->ping_timeout = timeout;
worker->s->ping_timeout_set = 1;
} elseif (!strcasecmp(key, "lbset")) {
ival = atoi(val); if (ival < 0 || ival > 99) return"lbset must be between 0 and 99";
worker->s->lbset = ival;
} elseif (!strcasecmp(key, "connectiontimeout")) { /* Request timeout in given unit (default is second). *Defaultstoconnectiontimeout
*/ if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) return"Connectiontimeout has wrong format"; if (timeout < 1000) return"Connectiontimeout must be at least one millisecond.";
worker->s->conn_timeout = timeout;
worker->s->conn_timeout_set = 1;
} elseif (!strcasecmp(key, "flusher")) { if (PROXY_STRNCPY(worker->s->flusher, val) != APR_SUCCESS) { return apr_psprintf(p, "flusher name length must be < %d characters",
(int)sizeof(worker->s->flusher));
}
} elseif (!strcasecmp(key, "upgrade")) { if (PROXY_STRNCPY(worker->s->upgrade,
strcasecmp(val, "ANY") ? val : "*") != APR_SUCCESS) { return apr_psprintf(p, "upgrade protocol length must be < %d characters",
(int)sizeof(worker->s->upgrade));
}
} elseif (!strcasecmp(key, "responsefieldsize")) { long s = atol(val); if (s < 0) { return"ResponseFieldSize must be greater than 0 bytes, or 0 for system default.";
}
worker->s->response_field_size = (s ? s : HUGE_STRING_LEN);
worker->s->response_field_size_set = 1;
} elseif (!strcasecmp(key, "secret")) { if (PROXY_STRNCPY(worker->s->secret, val) != APR_SUCCESS) { return apr_psprintf(p, "Secret length must be < %d characters",
(int)sizeof(worker->s->secret));
}
} else { if (set_worker_hc_param_f) { return set_worker_hc_param_f(p, s, worker, key, val, NULL);
} else { return"unknown Worker parameter";
}
} return NULL;
}
int ival; if (!strcasecmp(key, "stickysession")) { char *path; /* Balancer sticky session name. *SettosomethinglikeJSESSIONIDor *PHPSESSIONID,etc..,
*/ if (strlen(val) >= sizeof(balancer->s->sticky_path))
apr_psprintf(p, "stickysession length must be < %d characters",
(int)sizeof(balancer->s->sticky_path));
PROXY_STRNCPY(balancer->s->sticky_path, val);
PROXY_STRNCPY(balancer->s->sticky, val);
if ((path = strchr((char *)balancer->s->sticky, '|'))) {
*path++ = '\0';
PROXY_STRNCPY(balancer->s->sticky_path, path);
}
} elseif (!strcasecmp(key, "stickysessionsep")) { /* separator/delimiter for sessionid and route, *normally'.'
*/ if (strlen(val) != 1) { if (!strcasecmp(val, "off"))
balancer->s->sticky_separator = 0; else return"stickysessionsep must be a single character or Off";
} else
balancer->s->sticky_separator = *val;
balancer->s->sticky_separator_set = 1;
} elseif (!strcasecmp(key, "nofailover")) { /* If set to 'on' the session will break *iftheworkerisinerrorstateor *disabled.
*/ if (!strcasecmp(val, "on"))
balancer->s->sticky_force = 1; elseif (!strcasecmp(val, "off"))
balancer->s->sticky_force = 0; else return"failover must be On|Off";
balancer->s->sticky_force_set = 1;
} elseif (!strcasecmp(key, "timeout")) { /* Balancer timeout in seconds. *Ifsetthiswillbethemaximumtimeto *waitforafreeworker. *Defaultisnottowait.
*/
ival = atoi(val); if (ival < 1) return"timeout must be at least one second";
balancer->s->timeout = apr_time_from_sec(ival);
} elseif (!strcasecmp(key, "maxattempts")) { /* Maximum number of failover attempts before *givingup.
*/
ival = atoi(val); if (ival < 0) return"maximum number of attempts must be a positive number";
balancer->s->max_attempts = ival;
balancer->s->max_attempts_set = 1;
} elseif (!strcasecmp(key, "lbmethod")) {
proxy_balancer_method *provider; if (strlen(val) > (sizeof(balancer->s->lbpname)-1)) return"unknown lbmethod";
provider = ap_lookup_provider(PROXY_LBMETHOD, val, "0"); if (provider) {
balancer->lbmethod = provider; if (PROXY_STRNCPY(balancer->s->lbpname, val) == APR_SUCCESS) {
balancer->lbmethod_set = 1; return NULL;
} else { return"lbmethod name too large";
}
} return"unknown lbmethod";
} elseif (!strcasecmp(key, "scolonpathdelim")) { /* If set to 'on' then ';' will also be *usedasasessionpathseparator/delim(ala *mod_jk)
*/ if (!strcasecmp(val, "on"))
balancer->s->scolonsep = 1; elseif (!strcasecmp(val, "off"))
balancer->s->scolonsep = 0; else return"scolonpathdelim must be On|Off";
balancer->s->scolonsep_set = 1;
} elseif (!strcasecmp(key, "failonstatus")) { char *val_split; char *status; char *tok_state;
status = apr_strtok(val_split, ", ", &tok_state); while (status != NULL) {
ival = atoi(status); if (ap_is_HTTP_VALID_RESPONSE(ival)) {
*(int *)apr_array_push(balancer->errstatuses) = ival;
} else { return"failonstatus must be one or more HTTP response codes";
}
status = apr_strtok(NULL, ", ", &tok_state);
}
} elseif (!strcasecmp(key, "failontimeout")) { if (!strcasecmp(val, "on"))
balancer->failontimeout = 1; elseif (!strcasecmp(val, "off"))
balancer->failontimeout = 0; else return"failontimeout must be On|Off";
balancer->failontimeout_set = 1;
} elseif (!strcasecmp(key, "nonce")) { if (!strcasecmp(val, "None")) {
*balancer->s->nonce = '\0';
} else { if (PROXY_STRNCPY(balancer->s->nonce, val) != APR_SUCCESS) { return"Provided nonce is too large";
}
}
balancer->s->nonce_set = 1;
} elseif (!strcasecmp(key, "growth")) {
ival = atoi(val); if (ival < 1 || ival > 100) /* arbitrary limit here */ return"growth must be between 1 and 100";
balancer->growth = ival;
balancer->growth_set = 1;
} elseif (!strcasecmp(key, "forcerecovery")) { if (!strcasecmp(val, "on"))
balancer->s->forcerecovery = 1; elseif (!strcasecmp(val, "off"))
balancer->s->forcerecovery = 0; else return"forcerecovery must be On|Off";
balancer->s->forcerecovery_set = 1;
} else { return"unknown Balancer parameter";
} return NULL;
}
while (aliasp < end_fakename && urip < end_uri) { if (*aliasp == '/') { /* any number of '/' in the alias matches any number in *thesuppliedURI,buttheremustbeatleastone...
*/ if (*urip != '/') return0;
while (*aliasp == '/')
++aliasp; while (*urip == '/')
++urip;
} else { /* Other characters are compared literally */ if (*urip++ != *aliasp++) return0;
}
}
/* fixup badly encoded stuff (e.g. % as last character) */ if (aliasp > end_fakename) {
aliasp = end_fakename;
} if (urip > end_uri) {
urip = end_uri;
}
/* We reach the end of the uri before the end of "alias_fakename" *forexampleuriis"/"andalias_fakename"/examples"
*/ if (urip == end_uri && aliasp != end_fakename) { return0;
}
/* Check last alias path component matched all the way */ if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') return0;
/* Return number of characters from URI which matched (may be *greaterthanlengthofalias,sincewemayhavematched *doubledslashes)
*/
/* Remove /xx/../ segments */ if (uri[uri_pos + 1] == '.'
&& (uri[uri_pos + 2] == '/'
|| uri[uri_pos + 2] == ';'
|| uri[uri_pos + 2] == '\0')) { /* Wind map segment back the previous one */ if (map_pos == 1) { /* Above root */ return0;
} do {
map_pos--;
} while (map[map_pos - 1] != '/');
map[map_pos] = '\0';
/* Wind alias segment back, unless in deeper segment */ if (alias_depth == stack->nelts) { if (alias[alias_pos] == '\0') {
alias_pos--;
} while (alias_pos > 0 && alias[alias_pos] == '/') {
alias_pos--;
} while (alias_pos > 0 && alias[alias_pos - 1] != '/') {
alias_pos--;
}
AP_DEBUG_ASSERT(alias_pos > 0);
alias_depth--;
}
apr_array_pop(stack);
/* Move uri forward to the next segment */
uri_pos += 2; if (uri[uri_pos] == '/') {
uri_pos++;
}
first_pos = 0; continue;
}
} if (first_pos) { while (uri[first_pos] == '/') {
first_pos++;
}
}
/* New segment */
APR_ARRAY_PUSH(stack, int) = first_pos ? first_pos : uri_pos; if (alias[alias_pos] != '\0') { if (alias[alias_pos - 1] != '/') { /* Remain in pair with uri segments */ do {
alias_pos++;
} while (alias[alias_pos - 1] != '/' && alias[alias_pos]);
} while (alias[alias_pos] == '/') {
alias_pos++;
} if (alias[alias_pos] != '\0') {
alias_depth++;
}
}
}
if (alias[alias_pos] != '\0') { int *match = &APR_ARRAY_IDX(stack, alias_depth - 1, int); if (*match) { if (alias[alias_pos] != uri[uri_pos]) { /* Current segment does not match */
*match = 0;
} elseif (alias[alias_pos + 1] == '\0'
&& alias[alias_pos] != '/') { if (uri[uri_pos + 1] == ';') { /* We'll preserve the parameters of the last *segmentifitdoesnotendwith'/',somark *thematchasnegativeforbelowhandling.
*/
*match = -(uri_pos + 1);
} elseif (uri[uri_pos + 1] != '/'
&& uri[uri_pos + 1] != '\0') { /* Last segment does not match all the way */
*match = 0;
}
}
} /* Don't go past the segment if the uri isn't there yet */ if (alias[alias_pos] != '/' || uri[uri_pos] == '/') {
alias_pos++;
}
}
/* Can't reach the end of uri before the end of the alias, *forexampleifuriis"/"andaliasis"/examples"
*/ if (alias[alias_pos] != '\0') { return0;
}
/* Check whether each alias segment matched */ for (depth = 0; depth < alias_depth; ++depth) { if (!APR_ARRAY_IDX(stack, depth, int)) { return0;
}
}
/* If alias_depth == stack->nelts we have a full match, i.e. *uri==aliassowecanreturnuri_posasis(theendofuri)
*/ if (alias_depth < stack->nelts) { /* Return the segment following the alias */
uri_pos = APR_ARRAY_IDX(stack, alias_depth, int); if (alias_depth) { /* But if the last segment of the alias does not end with '/' *andthecorrespondingsegmentoftheurihasparameters, *wewanttoforwardthoseparameters(seeaboveforthe *negativepostrick/mark).
*/ int pos = APR_ARRAY_IDX(stack, alias_depth - 1, int); if (pos < 0) {
uri_pos = -pos;
}
}
} /* If the alias lacks a trailing slash, take it from the uri (if any) */ if (alias[alias_pos - 1] != '/' && uri[uri_pos - 1] == '/') {
uri_pos--;
}
*urip = map; return uri_pos;
}
/* Detect if an absoluteURI should be proxied or not. Note that we *havetodothisduringthisphasebecauselaterphasesare *"short-circuiting"...i.e.translate_nameswillendwhenthefirst *modulereturnsOK.Soforexample,iftherequestissomethinglike: * *GEThttp://othervhost/cgi-bin/printenv HTTP/1.0 * *mod_aliaswillnoticethe/cgi-binpartandScriptAliasitand *short-circuittheproxy...justbecauseoftheorderinginthe *configurationfile.
*/ staticint proxy_detect(request_rec *r)
{ void *sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
/* Ick... msvc (perhaps others) promotes ternary short results to int */
if (conf->req && r->parsed_uri.scheme) { /* but it might be something vhosted */ if (!r->parsed_uri.hostname
|| ap_cstr_casecmp(r->parsed_uri.scheme, ap_http_scheme(r)) != 0
|| !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)))) {
r->proxyreq = PROXYREQ_PROXY;
r->uri = r->unparsed_uri;
r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server";
}
} /* We need special treatment for CONNECT proxying: it has no scheme part */ elseif (conf->req && r->method_number == M_CONNECT
&& r->parsed_uri.hostname
&& r->parsed_uri.port_str) {
r->proxyreq = PROXYREQ_PROXY;
r->uri = r->unparsed_uri;
r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server";
} return DECLINED;
}
if (dconf && (dconf->interpolate_env == 1) && (ent->flags & PROXYPASS_INTERPOLATE)) {
fake = ap_proxy_interpolate(r, ent->fake);
real = ap_proxy_interpolate(r, ent->real);
} else {
fake = ent->fake;
real = ent->real;
}
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(03461) "attempting to match URI path '%s' against %s '%s' for " "proxying", r->uri, (ent->regex ? "pattern" : "prefix"),
fake);
if (ent->regex) { if (!ap_regexec(ent->regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) { if ((real[0] == '!') && (real[1] == '\0')) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03462) "proxying is explicitly disabled for URI path " "'%s'; declining", r->uri); return DECLINED;
} /* test that we haven't reduced the URI */ if (nocanon && ap_regexec(ent->regex, r->unparsed_uri,
AP_MAX_REG_MATCH, reg1, 0)) {
mismatch = 1;
use_uri = r->uri;
}
found = ap_pregsub(r->pool, real, use_uri, AP_MAX_REG_MATCH,
(use_uri == r->uri) ? regm : reg1); if (!found) {
ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01135) "Substitution in regular expression failed. " "Replacement too long?"); return HTTP_INTERNAL_SERVER_ERROR;
}
/* Note: The strcmp() below catches cases where there *wasnoregexsubstitution.Thisissocaseslike: * *ProxyPassMatch\.gifbalancer://foo * *willwork"asexpected".Theupshotisthatthe2 *directivesbelowacttheexactsameway(ie:$1isimplied): * *ProxyPassMatch^(/.*\.gif)$balancer://foo *ProxyPassMatch^(/.*\.gif)$balancer://foo$1 * *whichmaybeconfusing.
*/ if (strcmp(found, real) != 0) {
found = apr_pstrcat(r->pool, "proxy:", found, NULL);
} else {
found = apr_pstrcat(r->pool, "proxy:", real, use_uri, NULL);
}
}
} else { if ((ent->flags & PROXYPASS_MAP_SERVLET) == PROXYPASS_MAP_SERVLET) {
servlet_uri = r->uri;
len = alias_match_servlet(r->pool, &servlet_uri, fake);
nocanon = 0; /* ignored since servlet's normalization applies */
} else {
len = alias_match(r->uri, fake);
}
if (len != 0) { if ((real[0] == '!') && (real[1] == '\0')) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03463) "proxying is explicitly disabled for URI path " "'%s'; declining", r->uri); return DECLINED;
} if (nocanon && len != alias_match(r->unparsed_uri, fake)) {
mismatch = 1;
use_uri = r->uri;
}
found = apr_pstrcat(r->pool, "proxy:", real, use_uri + len, NULL);
}
} if (mismatch) { /* We made a reducing transformation, so we can't safely use *unparsed_uri.Safefallbackistoignorenocanon.
*/
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01136) "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon");
}
if (found) { unsignedint encoded = ent->flags & PROXYPASS_MAP_ENCODED;
/* A proxy module is assigned this URL, check whether it's interested *intherequestitself(e.g.proxy_wstunnelcaresaboutUpgrade *requestsonly,andcouldhandovertoproxy_httpotherwise).
*/ int rc = proxy_run_check_trans(r, found + 6); if (rc != OK && rc != DECLINED) { return HTTP_CONTINUE;
}
r->filename = found;
r->handler = "proxy-server";
r->proxyreq = PROXYREQ_REVERSE; if (nocanon && !mismatch) { /* mod_proxy_http needs to be told. Different module. */
apr_table_setn(r->notes, "proxy-nocanon", "1");
} if (ent->flags & PROXYPASS_NOQUERY) {
apr_table_setn(r->notes, "proxy-noquery", "1");
} if (encoded) {
apr_table_setn(r->notes, "proxy-noencode", "1");
}
staticint proxy_trans(request_rec *r, int pre_trans)
{ int i, enc; struct proxy_alias *ent;
proxy_dir_conf *dconf;
proxy_server_conf *conf;
if (r->proxyreq) { /* someone has already set up the proxy, it was possibly ourselves *inproxy_detect(DONEwillpreventfurtherdecodingofr->uri, *onlyifproxyreqissetbeforepre_transalready).
*/ return pre_trans ? DONE : OK;
}
/* In early pre_trans hook, r->uri was not manipulated yet so we are *compliantwithRFC1945atthispoint.Otherwise,itprobablyisn't *anissuebecausethisisahybridproxy/originserver.
*/
/* Always and only do PROXY_MAP_ENCODED mapping in pre_trans, when *r->uriisstillencoded,orwemightconsiderforinstancethat *adecodedsub-delimisnowadelimiter(e.g."%3B"=>';'for *pathparameters),whichit'snot.
*/ if ((pre_trans && !conf->map_encoded_one)
|| (!pre_trans && conf->map_encoded_all)) { /* Fast path, nothing at this stage */ return DECLINED;
}
if (apr_table_get(r->subprocess_env, "no-proxy")) { return DECLINED;
}
/* short way - this location is reverse proxied? */ if (dconf->alias) {
enc = (dconf->alias->flags & PROXYPASS_MAP_ENCODED) != 0; if (!(pre_trans ^ enc)) { int rv = ap_proxy_trans_match(r, dconf->alias, dconf); if (rv != HTTP_CONTINUE) { return rv;
}
}
}
/* long way - walk the list of aliases, find a match */ for (i = 0; i < conf->aliases->nelts; i++) {
ent = &((struct proxy_alias *)conf->aliases->elts)[i];
enc = (ent->flags & PROXYPASS_MAP_ENCODED) != 0; if (!(pre_trans ^ enc)) { int rv = ap_proxy_trans_match(r, ent, dconf); if (rv != HTTP_CONTINUE) { return rv;
}
}
}
/* XXX: Shouldn't we try this before we run the proxy_walk? */
return ap_proxy_canon_url(r);
}
/* Send a redirection if the request contains a hostname which is not */ /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */ /* servers like Netscape's allow this and access hosts from the local */ /* domain in this case. I think it is better to redirect to a FQDN, since */ /* these will later be found in the bookmarks files. */ /* The "ProxyDomain" directive determines what domain will be appended */ staticint proxy_needsdomain(request_rec *r, constchar *url, constchar *domain)
{ char *nuri; constchar *ref;
/* We only want to worry about GETs */ if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname) return DECLINED;
/* If host does contain a dot already, or it is "localhost", decline */ if (strchr(r->parsed_uri.hostname, '.') != NULL /* has domain, or IPv4 literal */
|| strchr(r->parsed_uri.hostname, ':') != NULL /* IPv6 literal */
|| ap_cstr_casecmp(r->parsed_uri.hostname, "localhost") == 0) return DECLINED; /* host name has a dot already */
ref = apr_table_get(r->headers_in, "Referer");
/* Reassemble the request, but insert the domain after the host name */ /* Note that the domain name always starts with a dot */
r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname,
domain, NULL);
nuri = apr_uri_unparse(r->pool,
&r->parsed_uri,
APR_URI_UNP_REVEALPASSWORD);
apr_table_setn(r->headers_out, "Location", nuri);
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01138) "Domain missing: %s sent to %s%s%s", r->uri,
apr_uri_unparse(r->pool, &r->parsed_uri,
APR_URI_UNP_OMITUSERINFO),
ref ? " from " : "", ref ? ref : "");
/* is this for us? */ if (!r->filename) { return DECLINED;
}
/* We may have forced the proxy handler via config or .htaccess */ if (!r->proxyreq && r->handler && strncmp(r->handler, "proxy:", 6) == 0) { char *old_filename = r->filename;
if (coreconf->trace_enable == AP_TRACE_DISABLE)
{ /* Allow "error-notes" string to be printed by ap_send_error_response() *Note;thisgoesnowhere,cannederrorresponseneedanoverhaul.
*/
apr_table_setn(r->notes, "error-notes", "TRACE forbidden by server configuration");
apr_table_setn(r->notes, "verbose-error-to", "*");
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01139) "TRACE forbidden by server configuration"); return HTTP_METHOD_NOT_ALLOWED;
}
/* Can't test ap_should_client_block, we aren't ready to send *theclienta100Continueresponsetilltheconnectionhas *beenestablished
*/ if (coreconf->trace_enable != AP_TRACE_EXTENDED
&& (r->read_length || r->read_chunked || r->remaining))
{ /* Allow "error-notes" string to be printed by ap_send_error_response() *Note;thisgoesnowhere,cannederrorresponseneedanoverhaul.
*/
apr_table_setn(r->notes, "error-notes", "TRACE with request body is not allowed");
apr_table_setn(r->notes, "verbose-error-to", "*");
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01140) "TRACE with request body is not allowed"); return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
}
/* If the host doesn't have a domain name, add one and redirect. */ if (conf->domain != NULL) {
rc = proxy_needsdomain(r, uri, conf->domain); if (ap_is_HTTP_REDIRECT(rc)) return HTTP_MOVED_PERMANENTLY;
}
/* Check URI's destination host against NoProxy hosts */ /* Bypass ProxyRemote server lookup if configured as NoProxy */ for (direct_connect = i = 0; i < conf->dirconn->nelts &&
!direct_connect; i++) {
direct_connect = list[i].matcher(&list[i], r);
} #if DEBUGGING
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
(direct_connect) ? APLOGNO(03231) "NoProxy for %s" : APLOGNO(03232) "UseProxy for %s",
r->uri); #endif
do { char *url = uri; /* Try to obtain the most suitable worker */
access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url); if (access_status != OK) { /* *Onlyreturnifaccess_statusisnotHTTP_SERVICE_UNAVAILABLE *Thisgivesothermodulesthechancetohookintothe *request_statushookanddecidewhattodointhissituation.
*/ if (access_status != HTTP_SERVICE_UNAVAILABLE) return access_status; /* *EnsurethatbalancerisNULLifworkerisNULLtoprevent *potentialproblemsinthepost_requesthook.
*/ if (!worker)
balancer = NULL; goto cleanup;
}
/* Initialise worker if needed, note the shared area must be initialized by the balancer logic */ if (balancer) {
ap_proxy_initialize_worker(worker, r->server, conf->pool);
}
if (balancer && balancer->s->max_attempts_set && !max_attempts)
max_attempts = balancer->s->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) == 0) ||
(p2 == NULL && ap_cstr_casecmp(scheme, ents[i].scheme) == 0) ||
(p2 != NULL &&
ap_cstr_casecmpn(url, ents[i].scheme,
strlen(ents[i].scheme)) == 0)) {
/* handle the scheme */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01142) "Trying to run scheme_handler against proxy");
/* Check if the balancer is defined in both override and base configs: *a)Ifitis,Createcopyofbasebalancerandchangetheconfiguration *whichcanbechangedbyProxyPass. *b)Otherwise,copythebalancertotocopyarrayandmergeitlater.
*/
b1 = (proxy_balancer *) base->elts; for (y = 0; y < base->nelts; y++) {
b2 = (proxy_balancer *) overrides->elts; for (x = 0, found = 0; x < overrides->nelts; x++) { if (b1->hash.def == b2->hash.def && b1->hash.fnv == b2->hash.fnv) {
tmp = *b2;
*b2 = *b1;
b2->s = tmp.s;
/* For shared memory entries, b2->s belongs to override *balancer,soifsomeentryisnotsetthere,wehaveto
* update it according to the base balancer. */ if (*b2->s->sticky == 0 && *b1->s->sticky) {
PROXY_STRNCPY(b2->s->sticky_path, b1->s->sticky_path);
PROXY_STRNCPY(b2->s->sticky, b1->s->sticky);
} if (!b2->s->sticky_separator_set
&& b1->s->sticky_separator_set) {
b2->s->sticky_separator_set = b1->s->sticky_separator_set;
b2->s->sticky_separator = b1->s->sticky_separator;
} if (!b2->s->timeout && b1->s->timeout) {
b2->s->timeout = b1->s->timeout;
} if (!b2->s->max_attempts_set && b1->s->max_attempts_set) {
b2->s->max_attempts_set = b1->s->max_attempts_set;
b2->s->max_attempts = b1->s->max_attempts;
} if (!b2->s->nonce_set && b1->s->nonce_set) {
b2->s->nonce_set = b1->s->nonce_set;
PROXY_STRNCPY(b2->s->nonce, b1->s->nonce);
} if (!b2->s->sticky_force_set && b1->s->sticky_force_set) {
b2->s->sticky_force_set = b1->s->sticky_force_set;
b2->s->sticky_force = b1->s->sticky_force;
} if (!b2->s->scolonsep_set && b1->s->scolonsep_set) {
b2->s->scolonsep_set = b1->s->scolonsep_set;
b2->s->scolonsep = b1->s->scolonsep;
} if (!b2->s->forcerecovery_set && b1->s->forcerecovery_set) {
b2->s->forcerecovery_set = b1->s->forcerecovery_set;
b2->s->forcerecovery = b1->s->forcerecovery;
}
/* For non-shared memory entries, b2 is copy of b1, so we have
* to use tmp copy of b1 to detect changes done in override. */ if (tmp.lbmethod_set) {
b2->lbmethod_set = tmp.lbmethod_set;
b2->lbmethod = tmp.lbmethod;
} if (tmp.growth_set) {
b2->growth_set = tmp.growth_set;
b2->growth = tmp.growth;
} if (tmp.failontimeout_set) {
b2->failontimeout_set = tmp.failontimeout_set;
b2->failontimeout = tmp.failontimeout;
} if (!apr_is_empty_array(tmp.errstatuses)) {
apr_array_cat(tmp.errstatuses, b2->errstatuses);
b2->errstatuses = tmp.errstatuses;
}
found = 1; break;
}
b2++;
} if (!found) {
*(proxy_balancer *)apr_array_push(tocopy) = *b1;
}
b1++;
}
r = apr_pstrdup(cmd->pool, r1);
scheme = apr_pstrdup(cmd->pool, r1);
f = apr_pstrdup(cmd->pool, f1);
p = strchr(r, ':'); if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') { if (regex) return"ProxyRemoteMatch: Bad syntax for a remote proxy server"; else return"ProxyRemote: Bad syntax for a remote proxy server";
} else {
scheme[p-r] = 0;
}
q = strchr(p + 3, ':'); if (q != NULL) { if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) { if (regex) return"ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)"; else return"ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
}
*q = '\0';
} else
port = -1;
*p = '\0'; if (regex) {
reg = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED); if (!reg) return"Regular expression for ProxyRemoteMatch could not be compiled.";
} else if (strchr(f, ':') == NULL)
ap_str_tolower(f); /* lowercase scheme */
ap_str_tolower(p + 3); /* lowercase hostname */
if (port == -1) {
port = apr_uri_port_of_scheme(scheme);
}
err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES); if (err) { return err;
}
while (*arg) {
word = ap_getword_conf(cmd->pool, &arg); if (!f) { if (!strcmp(word, "~")) { if (is_regex) { return"ProxyPassMatch invalid syntax ('~' usage).";
}
worker_type = AP_PROXY_WORKER_IS_MATCH; continue;
}
f = word;
} elseif (!r) {
r = word;
} elseif (!strcasecmp(word,"nocanon")) {
flags |= PROXYPASS_NOCANON;
} elseif (!strcasecmp(word,"interpolate")) {
flags |= PROXYPASS_INTERPOLATE;
} elseif (!strcasecmp(word,"noquery")) {
flags |= PROXYPASS_NOQUERY;
} else { char *val = strchr(word, '='); if (!val) { if (cmd->path) { if (*r == '/') { return"ProxyPass|ProxyPassMatch can not have a path when defined in " "a location.";
} else { return"Invalid ProxyPass|ProxyPassMatch parameter. Parameter must " "be in the form 'key=value'.";
}
} else { return"Invalid ProxyPass|ProxyPassMatch parameter. Parameter must be " "in the form 'key=value'.";
}
} else {
*val++ = '\0';
} if (!strcasecmp(word, "mapping")) { if (!strcasecmp(val, "encoded")) {
flags |= PROXYPASS_MAP_ENCODED;
} elseif (!strcasecmp(val, "servlet")) {
flags |= PROXYPASS_MAP_SERVLET;
} else { return"unknown mapping";
}
} else {
apr_table_setn(params, word, val);
}
}
} if (flags & PROXYPASS_MAP_ENCODED) {
conf->map_encoded_one = 1;
} else {
conf->map_encoded_all = 0;
}
if (r == NULL) { return"ProxyPass|ProxyPassMatch needs a path when not defined in a location";
} if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, r))) { return"ProxyPass|ProxyPassMatch uses an invalid \"unix:\" URL";
}
/* if per directory, save away the single alias */ if (cmd->path) {
dconf->alias = apr_pcalloc(cmd->pool, sizeof(struct proxy_alias));
dconf->alias_set = 1; new = dconf->alias; if (apr_fnmatch_test(f)) {
worker_type = AP_PROXY_WORKER_IS_MATCH;
}
} /* if per server, add to the alias array */ else { new = apr_array_push(conf->aliases);
}
new->fake = apr_pstrdup(cmd->pool, f); new->real = apr_pstrdup(cmd->pool, real); new->flags = flags; if (worker_type & AP_PROXY_WORKER_IS_MATCH) { new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED); if (new->regex == NULL) return"Regular expression could not be compiled.";
} else { new->regex = NULL;
}
if (r[0] == '!' && r[1] == '\0') return NULL;
arr = apr_table_elts(params);
elts = (const apr_table_entry_t *)arr->elts; /* Distinguish the balancer from worker */ if (ap_proxy_valid_balancer_name(r, 9)) {
proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r, 0); char *fake_copy;
err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES); if (err) { return err;
}
if (cmd->path == NULL) { if (r == NULL || !strcasecmp(r, "interpolate")) { return"ProxyPassReverse needs a path when not defined in a location";
}
fake = f;
real = r;
interp = i;
} else { if (r && strcasecmp(r, "interpolate")) { return"ProxyPassReverse can not have a path when defined in a location";
}
fake = cmd->path;
real = f;
interp = r;
}
/* Don't duplicate entries */ for (i = 0; i < conf->noproxies->nelts; i++) { if (strcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */
found = 1; break;
}
}
if (strcasecmp(arg, "Off") == 0) {
conf->error_override = 0;
conf->error_override_set = 1;
} elseif (strcasecmp(arg, "On") == 0) {
conf->error_override = 1;
conf->error_override_set = 1;
} elseif (conf->error_override_set == 1) { int *newcode; int argcode, i; if (!apr_isdigit(arg[0])) return"ProxyErrorOverride: status codes to intercept must be numeric"; if (!conf->error_override) return"ProxyErrorOverride: status codes must follow a value of 'on'";
argcode = strtol(arg, NULL, 10); if (!ap_is_HTTP_ERROR(argcode)) return"ProxyErrorOverride: status codes to intercept must be valid HTTP Status Codes >=400 && <600";
/* Keep the array sorted for binary search. */ for (i = conf->error_override_codes->nelts - 1; i > 0; --i) { int *oldcode = &((int *)conf->error_override_codes->elts)[i - 1]; if (*oldcode <= argcode) { break;
}
*newcode = *oldcode;
*oldcode = argcode;
newcode = oldcode;
}
} else return"ProxyErrorOverride first parameter must be one of: off | on";
staticconstchar *
set_recv_buffer_size(cmd_parms *parms, void *dummy, constchar *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module); int s = atoi(arg); if (s < 512 && s != 0) { return"ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
}
staticconstchar *
set_io_buffer_size(cmd_parms *parms, void *dummy, constchar *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module); long s = atol(arg); if (s < 512 && s) { return"ProxyIOBufferSize must be >= 512 bytes, or 0 for system default.";
}
psf->io_buffer_size = (s ? s : AP_IOBUFSIZE);
psf->io_buffer_size_set = 1; return NULL;
}
staticconstchar *
set_max_forwards(cmd_parms *parms, void *dummy, constchar *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module); long s = atol(arg);
timeout = atoi(arg); if (timeout<1) { return"Proxy Timeout must be at least 1 second.";
}
psf->timeout_set = 1;
psf->timeout = apr_time_from_sec(timeout);
int growth = atoi(arg); if (growth < 0 || growth > 1000) { return"BalancerGrowth must be between 0 and 1000";
}
psf->bgrowth = growth;
psf->bgrowth_set = 1;
if (cmd->path)
path = apr_pstrdup(cmd->pool, cmd->path);
while (*arg) { char *val;
word = ap_getword_conf(cmd->pool, &arg);
val = strchr(word, '=');
if (!val) { if (!path)
path = word; elseif (!name)
name = word; else { if (cmd->path) return"BalancerMember can not have a balancer name when defined in a location"; else return"Invalid BalancerMember parameter. Parameter must " "be in the form 'key=value'";
}
} else {
*val++ = '\0';
apr_table_setn(params, word, val);
}
} if (!path) return"BalancerMember must define balancer name when outside <Proxy > section"; if (!name) return"BalancerMember must define remote proxy server"; if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) { return"BalancerMember uses an invalid \"unix:\" URL";
}
if (cmd->directive->parent &&
strncasecmp(cmd->directive->parent->directive, "<Proxy", 6) == 0) { constchar *pargs = cmd->directive->parent->args; /* Directive inside <Proxy section *Parentdirectiveargistheworker/balancername.
*/
name = ap_getword_conf(cmd->temp_pool, &pargs); if ((word = ap_strchr(name, '>')))
*word = '\0'; if (strncasecmp(cmd->directive->parent->directive + 6, "Match", 5) == 0) {
worker_type = AP_PROXY_WORKER_IS_MATCH;
} else {
worker_type = AP_PROXY_WORKER_IS_PREFIX;
}
in_proxy_section = 1;
} else { /* Standard set directive with worker/balancer *nameasfirstparam.
*/
name = ap_getword_conf(cmd->temp_pool, &arg);
}
if (ap_proxy_valid_balancer_name(name, 9)) {
balancer = ap_proxy_get_balancer(cmd->pool, conf, name, 0); if (!balancer) { if (in_proxy_section) {
err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, name, "/", 0); if (err) return apr_pstrcat(cmd->temp_pool, "ProxySet ",
err, NULL);
} else return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
name, "' Balancer.", NULL);
}
} else { constchar *real;
if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) { return"ProxySet uses an invalid \"unix:\" URL";
}
worker = ap_proxy_get_worker_ex(cmd->temp_pool, NULL, conf,
real, worker_type); if (!worker) { if (in_proxy_section) {
err = ap_proxy_define_worker_ex(cmd->pool, &worker, NULL,
conf, name, worker_type); if (err) return apr_pstrcat(cmd->temp_pool, "ProxySet ",
err, NULL);
} else return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
name, "' Worker.", NULL);
}
}
while (*arg) {
word = ap_getword_conf(cmd->pool, &arg);
val = strchr(word, '='); if (!val) { return"Invalid ProxySet parameter. Parameter must be " "in the form 'key=value'";
} else
*val++ = '\0'; if (worker)
err = set_worker_param(cmd->pool, cmd->server, worker, word, val); else
err = set_balancer_param(conf, cmd->pool, balancer, word, val);
if (err) return apr_pstrcat(cmd->temp_pool, "ProxySet: ", err, " ", word, "=", val, "; ", name, NULL);
}
if (!strncasecmp(cmd->path, "proxy:", 6))
cmd->path += 6;
/* XXX Ignore case? What if we proxy a case-insensitive server?!? *Whileweareatit,shouldn'twealsocanonicalizetheentire *scheme?Seeproxy_fixup()
*/ if (thiscmd->cmd_data) { /* <ProxyMatch> */
r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED); if (!r) { return"Regex could not be compiled";
}
worker_type = AP_PROXY_WORKER_IS_MATCH;
}
/* initialize our config and fetch it */
conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
&proxy_module, cmd->pool);
staticconst command_rec proxy_cmds[] =
{
AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF, "Container for directives affecting resources located in the proxied " "location"),
AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF, "Container for directives affecting resources located in the proxied " "location, in regular expression syntax"),
AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF, "on if the true proxy requests should be accepted"),
AP_INIT_TAKE23("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF, "a scheme, partial URL or '*' and a proxy server"),
AP_INIT_TAKE23("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF, "a regex pattern and a proxy server"),
AP_INIT_FLAG("ProxyPassInterpolateEnv", ap_set_flag_slot_char,
(void*)APR_OFFSETOF(proxy_dir_conf, interpolate_env),
RSRC_CONF|ACCESS_CONF, "Interpolate Env Vars in reverse Proxy") ,
AP_INIT_RAW_ARGS("ProxyPass", add_pass_noregex, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL"),
AP_INIT_RAW_ARGS("ProxyPassMatch", add_pass_regex, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL"),
AP_INIT_TAKE123("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL for reverse proxy behaviour"),
AP_INIT_TAKE23("ProxyPassReverseCookiePath", cookie_path, NULL,
RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
AP_INIT_TAKE23("ProxyPassReverseCookieDomain", cookie_domain, NULL,
RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, "A list of names, hosts or domains to which the proxy will not connect"),
AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, "Receive buffer size for outgoing HTTP and FTP connections in bytes"),
AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF, "IO buffer size for outgoing HTTP and FTP connections in bytes"),
AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF, "The maximum number of proxies a request may be forwarded through."),
AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, "A list of domains, hosts, or subnets to which the proxy will connect directly"),
AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, "The default intranet domain name (in absence of a domain in the URL)"),
AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF, "Configure Via: proxy header header to one of: on | off | block | full"),
AP_INIT_ITERATE("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF|ACCESS_CONF, "use our error handling pages instead of the servers' we are proxying"),
AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF|ACCESS_CONF, "on if we should preserve host header while proxying"),
AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF, "Set the timeout (in seconds) for a proxied connection. " "This overrides the server timeout"),
AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF, "How to handle bad header line in response: IsError | Ignore | StartBody"),
AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF, "A balancer name and scheme with list of params"),
AP_INIT_TAKE1("BalancerGrowth", set_bgrowth, NULL, RSRC_CONF, "Number of additional Balancers that can be added post-config"),
AP_INIT_FLAG("BalancerPersist", set_persist, NULL, RSRC_CONF, "on if the balancer should persist changes on reboot/restart made via the Balancer Manager"),
AP_INIT_FLAG("BalancerInherit", set_inherit, NULL, RSRC_CONF, "on if this server should inherit Balancers and Workers defined in the main server " "(Setting to off recommended if using the Balancer Manager)"),
AP_INIT_FLAG("ProxyPassInherit", set_ppinherit, NULL, RSRC_CONF, "on if this server should inherit all ProxyPass directives defined in the main server " "(Setting to off recommended if using the Balancer Manager)"),
AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF, "Configure Status: proxy status to one of: on | off | full"),
AP_INIT_RAW_ARGS("ProxySet", set_proxy_param, NULL, RSRC_CONF|ACCESS_CONF, "A balancer or worker name with list of params"),
AP_INIT_TAKE1("ProxySourceAddress", set_source_address, NULL, RSRC_CONF, "Configure local source IP used for request forward"),
AP_INIT_FLAG("ProxyAddHeaders", add_proxy_http_headers, NULL, RSRC_CONF|ACCESS_CONF, "on if X-Forwarded-* headers should be added or completed"),
AP_INIT_FLAG("Proxy100Continue", forward_100_continue, NULL, RSRC_CONF|ACCESS_CONF, "on if 100-Continue should be forwarded to the origin server, off if the " "proxy should handle it by itself"),
{NULL}
};
/* TODO: Add the rest of dynamic worker data */
ap_rputs("</tr>\n", r);
} else {
ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Name: %s\n",
i, n, (*worker)->s->name_ex);
ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Status: %s\n",
i, n, ap_proxy_parse_wstatus(r->pool, *worker));
ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Elected: %"
APR_SIZE_T_FMT "\n",
i, n, (*worker)->s->elected);
ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Busy: %"
APR_SIZE_T_FMT "\n",
i, n, (*worker)->s->busy);
ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Sent: %"
APR_OFF_T_FMT "K\n",
i, n, (*worker)->s->transferred >> 10);
ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Rcvd: %"
APR_OFF_T_FMT "K\n",
i, n, (*worker)->s->read >> 10);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.309Bemerkung:
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.