/* 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.
*/
/* *ConvertaURL-encodedstringtocanonicalform. *Itdecodescharacterswhichneednotbeencoded, *andencodesthosewhichmustbeencoded,anddoesnottouch *thosewhichmustnotbetouched.
*/
PROXY_DECLARE(char *)ap_proxy_canonenc_ex(apr_pool_t *p, constchar *x, int len, enum enctype t, int flags, int proxyreq)
{ int i, j, ch; char *y; char *allowed; /* characters which should not be encoded */ char *reserved; /* characters which much not be en/de-coded */ int forcedec = flags & PROXY_CANONENC_FORCEDEC; int noencslashesenc = flags & PROXY_CANONENC_NOENCODEDSLASHENCODING;
user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0); if (user == NULL) { return"Bad %-escape in URL (username)";
}
} if (userp != NULL) {
*userp = user;
} if (passwordp != NULL) {
*passwordp = password;
}
/* *Parsethehoststringtoseparatehostportionfromoptionalport. *Performrangecheckingonport.
*/
rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p); if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) { return"Invalid host/port";
} if (tmp_port != 0) { /* only update caller's port if port was specified */
*port = tmp_port;
}
ap_str_tolower(addr); /* DNS names are case-insensitive */
*urlp = url;
*hostp = addr;
return NULL;
}
staticint proxyerror_core(request_rec *r, int statuscode, constchar *message,
apr_status_t rv)
{
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00898) "%s returned by %s", message, r->uri);
apr_table_setn(r->notes, "error-notes",
apr_pstrcat(r->pool, "The proxy server could not handle the request<p>" "Reason: <strong>", ap_escape_html(r->pool, message), "</strong></p>",
NULL));
/* Allow "error-notes" string to be printed by ap_send_error_response() */
apr_table_setn(r->notes, "verbose-error-to", "*");
return host; /* ought to return the port, too */
}
/* Return TRUE if addr represents an IP address (or an IP network address) */
PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
{ constchar *addr = This->name; long ip_addr[4]; int i, quads; long bits;
/* Return TRUE if addr represents an IP address (or an IP network address) */ staticint proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
{ int i, ip_addr[4]; struct in_addr addr, *ip; constchar *host = proxy_get_host_of_request(r);
/* Try to deal with multiple IP addr's for a host */ /* FIXME: This needs to be able to deal with IPv6 */ while (reqaddr) {
ip = (struct in_addr *) reqaddr->ipaddr_ptr; if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) { #if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00910) "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip));
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00911) "%s/", inet_ntoa(This->addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00912) "%s", inet_ntoa(This->mask)); #endif return1;
} #if DEBUGGING else {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00913) "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip));
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00914) "%s/", inet_ntoa(This->addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00915) "%s", inet_ntoa(This->mask));
} #endif
reqaddr = reqaddr->next;
}
}
return0;
}
/* Return TRUE if addr represents a domain name */
PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
{ char *addr = This->name; int i;
/* Domain name must start with a '.' */ if (addr[0] != '.') { return0;
}
/* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) { continue;
}
#if0 if (addr[i] == ':') {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03234) "@@@@ handle optional port in proxy_is_domainname()"); /* @@@@ handle optional port */
} #endif
if (addr[i] != '\0') { return0;
}
/* Strip trailing dots */ for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) {
addr[i] = '\0';
}
/* Return TRUE if host "host" is in domain "domain" */ staticint proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
{ constchar *host = proxy_get_host_of_request(r); int d_len = strlen(This->name), h_len;
if (host == NULL) { /* some error was logged already */ return0;
}
h_len = strlen(host);
/* @@@ do this within the setup? */ /* Ignore trailing dots in domain comparison: */ while (d_len > 0 && This->name[d_len - 1] == '.') {
--d_len;
} while (h_len > 0 && host[h_len - 1] == '.') {
--h_len;
} return h_len > d_len
&& strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
}
/* Return TRUE if host represents a host name */
PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
{ struct apr_sockaddr_t *addr; char *host = This->name; int i;
/* Host names must not start with a '.' */ if (host[0] == '.') { return0;
} /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
/* Return TRUE if addr is to be matched as a word */
PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
{ This->matcher = proxy_match_word; return1;
}
/* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ for (j = 0; j < conf->noproxies->nelts; j++) { struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; struct apr_sockaddr_t *conf_addr;
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "checking remote machine [%s] against [%s]",
hostname, npent[j].name); if (ap_strstr_c(hostname, npent[j].name) || npent[j].name[0] == '*') {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00916) "connect to remote machine %s blocked: name %s " "matched", hostname, npent[j].name); return HTTP_FORBIDDEN;
}
/* No IP address checks if no IP address was passed in, *i.e.theforwardaddressproxycase,wherethisserverdoes
* not resolve the hostname. */ if (!addr) continue;
/* Generate a pseudo-UUID from the PRNG to use as a nonce for *thelifetimeoftheprocess.uuid.dataisachararrayso
* this is an adequate substitute for apr_uuid_get(). */
ap_random_insecure_bytes(uuid.data, sizeof uuid.data);
apr_uuid_format(nonce, &uuid);
rv = PROXY_STRNCPY(balancer->s->nonce, nonce);
} return rv;
}
/* Process lbsets in order, only replacing unusable workers in a given lbset *withavailablesparesfromthesamelbset.Hotstandbyswillbeusedasa *lastresortwhenallotherworkersandsparesareunavailable.
*/ for (cur_lbset = 0; !best_worker && (cur_lbset <= max_lbset); cur_lbset++) {
unusable_workers = 0;
apr_array_clear(spares);
apr_array_clear(standbys);
for (i = 0; i < balancer->workers->nelts; i++) {
worker = APR_ARRAY_IDX(balancer->workers, i, proxy_worker *);
if (worker->s->lbset > max_lbset) {
max_lbset = worker->s->lbset;
}
if (worker->s->lbset != cur_lbset) { continue;
}
/* A draining worker that is neither a spare nor a standby should be *consideredunusabletobereplacedbyspares.
*/ if (PROXY_WORKER_IS_DRAINING(worker)) { if (!PROXY_WORKER_IS_SPARE(worker) && !PROXY_WORKER_IS_STANDBY(worker)) {
unusable_workers++;
}
continue;
}
/* If the worker is in error state run retry on that worker. It will *bemarkedasoperationaliftheretrytimeoutiselapsed.The *workermightstillbeunusable,butwetryanyway.
*/ if (!PROXY_WORKER_IS_USABLE(worker)) {
ap_proxy_retry_worker("BALANCER", worker, r->server);
}
/* Check if any spares are best. */ for (i = 0; (i < spares->nelts) && (i < unusable_workers); i++) {
worker = APR_ARRAY_IDX(spares, i, proxy_worker *);
if (is_best(worker, best_worker, baton)) {
best_worker = worker;
}
}
/* If no workers are available, use the standbys. */ if (!best_worker) { for (i = 0; i < standbys->nelts; i++) {
worker = APR_ARRAY_IDX(standbys, i, proxy_worker *);
static apr_status_t conn_pool_cleanup(void *theworker)
{ /* Signal that the child is exiting */
((proxy_worker *)theworker)->cp = NULL; return APR_SUCCESS;
}
/* When p is cleaning up the child is exiting, signal that to e.g. avoid *destroyingthesubpoolsexplicitelyinconnection_destructor()when *theyhavebeendestroyedalreadybythereslistcleanup.
*/
apr_pool_pre_cleanup_register(p, worker, conn_pool_cleanup);
}
/* Sanity check: Did we already return the pooled connection? */ if (conn->inreslist) {
ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923) "Pooled connection 0x%pp for worker %s has been" " already returned to the connection pool.", conn,
ap_proxy_worker_name(conn->pool, worker)); return;
}
if (conn->r) {
apr_pool_destroy(conn->r->pool);
conn->r = NULL;
}
/* determine if the connection should be cleared, closed or reused */ if (!worker->s->is_address_reusable) {
apr_pool_t *p = conn->pool;
apr_pool_clear(p);
conn = connection_make(p, worker);
} elseif (!conn->sock
|| (conn->connection
&& conn->connection->keepalive == AP_CONN_CLOSE)
|| !ap_proxy_connection_reusable(conn)) {
socket_cleanup(conn);
} elseif (conn->is_ssl) { /* The current ssl section/dir config of the conn is not necessarily *theoneitwillbereusedfor,sowhiletheconnisinthereslist *resetitssslconfigtotheworker's,untilanewusersetsitsown *sslconfigeventuallyinproxy_connection_create()andsoon.
*/
ap_proxy_ssl_engine(conn->connection, worker->section_config, 1);
}
for (x = 0, y = 0; expected[y]; ++y, ++x) { if (expected[y] == '$' && apr_isdigit(expected[y + 1])) { do {
y += 2;
} while (expected[y] == '$' && apr_isdigit(expected[y + 1])); if (!expected[y]) return0; while (str[x]) { int ret; if ((ret = ap_proxy_strcmp_ematch(&str[x++], &expected[y])) != 1) return ret;
} return -1;
} elseif (!str[x]) { return -1;
} elseif (expected[y] == '\\' && !expected[++y]) { /* NUL is an invalid char! */ return -2;
} if (str[x] != expected[y]) return1;
} /* We got all the way through the worker path without a difference */ return0;
}
staticint worker_matches(proxy_worker *worker, constchar *url, apr_size_t url_len,
apr_size_t min_match, apr_size_t *max_match, unsignedint mask)
{
apr_size_t name_len = strlen(worker->s->name_ex); if (name_len <= url_len
&& name_len > *max_match /* min_match is the length of the scheme://host part only of url, *soit'susedasafastpathtoavoidthematchwhenurlistoo *small,butit'sirrelevantwhentheworkerhostcontainsglobs *(i.e.->is_host_matchable).
*/
&& (worker->s->is_name_matchable
? ((mask & AP_PROXY_WORKER_IS_MATCH)
&& (worker->s->is_host_matchable || name_len >= min_match)
&& !ap_proxy_strcmp_ematch(url, worker->s->name_ex))
: ((mask & AP_PROXY_WORKER_IS_PREFIX)
&& (name_len >= min_match)
&& !strncmp(url, worker->s->name_ex, name_len)))) {
*max_match = name_len; return1;
} return0;
}
/* Default to lookup for both _PREFIX and _MATCH workers */ if (!(mask & (AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH))) {
mask |= AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH;
}
/* Normalize the url (worker name) */
rv = apr_uri_parse(p, ptr, &uri); if (rv != APR_SUCCESS) { return apr_pstrcat(p, "Unable to parse URL: ", url, NULL);
} if (!uri.scheme) { return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
} if (!uri.hostname || !*uri.hostname) { if (sockpath) { /* allow for unix:/path|http: */
uri.hostname = "localhost";
} else { return apr_pstrcat(p, "URL must be absolute!: ", url, NULL);
}
} else {
ap_str_tolower(uri.hostname);
}
ap_str_tolower(uri.scheme);
port_of_scheme = ap_proxy_port_of_scheme(uri.scheme); if (uri.port && uri.port == port_of_scheme) {
uri.port = 0;
} if (pdollars) { /* Restore/prepend pdollars into the path. */
uri.path = apr_pstrcat(p, pdollars, uri.path, NULL);
}
ptr = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD);
/* *Workerscanbeassociatedw/balancersorontheir *own;ie:thegenericreverse-proxyoraworker *inasimpleProxyPassstatement.eg: * *ProxyPass/http://www.example.com * *inwhichcasetheworkergoesintheconfslot.
*/ if (balancer) {
proxy_worker **runtime; /* recall that we get a ptr to the ptr here */
runtime = apr_array_push(balancer->workers);
*worker = *runtime = apr_palloc(p, sizeof(proxy_worker)); /* right to left baby */ /* we've updated the list of workers associated with
* this balancer *locally* */
balancer->wupdated = apr_time_now();
} elseif (conf) {
*worker = apr_array_push(conf->workers);
} else { /* we need to allocate space here */
*worker = apr_palloc(p, sizeof(proxy_worker));
}
memset(*worker, 0, sizeof(proxy_worker));
/* right here we just want to tuck away the worker info. *ifcalledduringconfig,wedon'thaveshmsetupyet,
* so just note the info for later. */ if (mask & AP_PROXY_WORKER_IS_MALLOCED)
wshared = ap_malloc(sizeof(proxy_worker_shared)); /* will be freed ap_proxy_share_worker */ else
wshared = apr_palloc(p, sizeof(proxy_worker_shared));
memset(wshared, 0, sizeof(proxy_worker_shared));
if (PROXY_STRNCPY(wshared->name_ex, ptr) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(10366) "Alert! worker name (%s) too long; truncated to: %s", ptr, wshared->name_ex);
} if (PROXY_STRNCPY(wshared->name, ptr) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(010118) "worker name (%s) too long; truncated for legacy modules that do not use " "proxy_worker_shared->name_ex: %s", ptr, wshared->name);
} if (PROXY_STRNCPY(wshared->scheme, uri.scheme) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(010117) "Alert! worker scheme (%s) too long; truncated to: %s", uri.scheme, wshared->scheme);
} if (PROXY_STRNCPY(wshared->hostname_ex, uri.hostname) != APR_SUCCESS) { return apr_psprintf(p, "worker hostname (%s) too long", uri.hostname);
} if (PROXY_STRNCPY(wshared->hostname, uri.hostname) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(010118) "worker hostname (%s) too long; truncated for legacy modules that do not use " "proxy_worker_shared->hostname_ex: %s", uri.hostname, wshared->hostname);
}
wshared->port = (uri.port) ? uri.port : port_of_scheme;
wshared->flush_packets = flush_off;
wshared->flush_wait = PROXY_FLUSH_WAIT;
wshared->address_ttl = (address_not_reusable) ? 0 : -1;
wshared->is_address_reusable = (address_not_reusable == 0);
wshared->disablereuse = (address_not_reusable != 0);
wshared->lbfactor = 100;
wshared->passes = 1;
wshared->fails = 1;
wshared->interval = apr_time_from_sec(HCHECK_WATHCHDOG_DEFAULT_INTERVAL);
wshared->smax = -1;
wshared->hash.def = ap_proxy_hashfunc(wshared->name_ex, PROXY_HASHFUNC_DEFAULT);
wshared->hash.fnv = ap_proxy_hashfunc(wshared->name_ex, PROXY_HASHFUNC_FNV);
wshared->was_malloced = (mask & AP_PROXY_WORKER_IS_MALLOCED) != 0; if (mask & AP_PROXY_WORKER_IS_MATCH) {
wshared->is_name_matchable = 1;
wshared->is_host_matchable = (address_not_reusable != 0);
/* Before AP_PROXY_WORKER_IS_MATCH (< 2.4.47), a regex worker with *dollarsubstitutionwasnevermatchedagainstanyactualURL,thus *therequestsfellthroughthegenericworker.NowifaProyPassMatch *matches,aworker(anditsparameters)isalwaysusedtodetermine *thepropertiesoftheconnectionwiththeoriginserver.Sofor *instancethesame"timeout="willbeenforcedforalltherequests *matchedbythesameProyPassMatchworker,whichisanimprovement *comparedtotheglobal/vhost[Proxy]Timeoutappliedbythegeneric *worker.Likewise,addressandconnectionreuseisthedefaultfor *aProyPassMatchworkerwithnodollarsubstitution,justlikea *"normal"worker.HowevertoavoidDNSandconnectionreusecompat *issues,connectionreuseisdisabledbydefaultifthereisany *substitutionintheuri-path(anexplicitenablereuse=oncanstill *opt-in),andreuseisevendisableddefinitivelyforsubstitutions *happeninginthehostname[:port](is_address_reusablewasunset *abovesoitwillpreventenablereuse=ontoapplyanyway).
*/ if (ap_strchr_c(wshared->name, '$')) {
wshared->disablereuse = 1;
}
} if (sockpath) { if (PROXY_STRNCPY(wshared->uds_path, sockpath) != APR_SUCCESS) { return apr_psprintf(p, "worker uds path (%s) too long", sockpath);
}
if (worker->s->status & PROXY_WORKER_INITIALIZED) { /* The worker is already initialized */
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00924) "worker %s shared already initialized",
ap_proxy_worker_name(p, worker));
} else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00925) "initializing worker %s shared",
ap_proxy_worker_name(p, worker)); /* Set default parameters */ if (!worker->s->retry_set) {
worker->s->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
} /* Consistently set address and connection reusabilty: when reuse *isdisabledbyconfiguration,orwhentheaddressisknownalready *tonotbereusableforthisworker(inanycase,thusignore/force *DisableReuse).
*/ if (!worker->s->address_ttl || (!worker->s->address_ttl_set
&& worker->s->disablereuse)) {
worker->s->is_address_reusable = 0;
} if (!worker->s->is_address_reusable && !worker->s->disablereuse) { /* Explicit enablereuse=on can't work in this case, warn user. */ if (worker->s->disablereuse_set) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10400) "enablereuse/disablereuse ignored for worker %s",
ap_proxy_worker_name(p, worker));
}
worker->s->disablereuse = 1;
}
/* *Whenmod_http2isloadedwemighthavemorethreadssinceithas *itsownpoolofprocessingthreads.
*/
ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads);
get_h2_num_workers = APR_RETRIEVE_OPTIONAL_FN(http2_get_num_workers); if (get_h2_num_workers) {
get_h2_num_workers(s, &minw, &maxw); /* So now the max is: *max_threads-1threadsforHTTP/1eachrequiringoneconnection *+onethreadforHTTP/2requiringmaxwconnections
*/
max_threads = max_threads - 1 + maxw;
} if (max_threads > 1) { /* Default hmax is max_threads to scale with the load and never *waitforanidleconnectiontoproceed.
*/ if (worker->s->hmax == 0) {
worker->s->hmax = max_threads;
} if (worker->s->smax == -1 || worker->s->smax > worker->s->hmax) {
worker->s->smax = worker->s->hmax;
} /* Set min to be lower than smax */ if (worker->s->min > worker->s->smax) {
worker->s->min = worker->s->smax;
}
} else { /* This will suppress the apr_reslist creation */
worker->s->min = worker->s->smax = worker->s->hmax = 0;
}
}
/* What if local is init'ed and shm isn't?? Even possible? */ if (worker->local_status & PROXY_WORKER_INITIALIZED) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00926) "worker %s local already initialized",
ap_proxy_worker_name(p, worker));
} else {
apr_global_mutex_lock(proxy_mutex); /* Check again after we got the lock if we are still uninitialized */ if (!(AP_VOLATILIZE_T(unsignedint, worker->local_status) & PROXY_WORKER_INITIALIZED)) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927) "initializing worker %s local",
ap_proxy_worker_name(p, worker)); /* Now init local worker data */ #if APR_HAS_THREADS if (worker->tmutex == NULL) {
rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p); if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00928) "can not create worker thread mutex");
apr_global_mutex_unlock(proxy_mutex); return rv;
}
} #endif if (worker->cp == NULL)
init_conn_pool(p, worker, s); if (worker->cp == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929) "can not create connection pool");
apr_global_mutex_unlock(proxy_mutex); return APR_EGENERAL;
}
start = ap_strstr_c(str, "${"); if (start == NULL) { return str;
}
end = ap_strchr_c(start+2, '}'); if (end == NULL) { return str;
} /* OK, this is syntax we want to interpolate. Is there such a var ? */
var = apr_pstrmemdup(r->pool, start+2, end-(start+2));
val = apr_table_get(r->subprocess_env, var);
firstpart = apr_pstrmemdup(r->pool, str, (start-str));
/* Put the UDS path appart if any (and not already stripped) */ if (r->proxyreq == PROXYREQ_REVERSE) {
access_status = fixup_uds_filename(r); if (ap_is_HTTP_ERROR(access_status)) { return access_status;
}
}
/* Keep this after fixup_uds_filename() */
url = apr_pstrdup(r->pool, r->filename + 6);
/* Set a timeout on the socket */ if (conf->timeout_set) {
apr_socket_timeout_set(*newsock, conf->timeout);
} else {
apr_socket_timeout_set(*newsock, r->server->timeout);
}
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "%s: fam %d socket created to connect to %s",
proxy_function, backend_addr->family, backend_name);
if (conf->source_address) {
apr_sockaddr_t *local_addr; /* Make a copy since apr_socket_bind() could change *conf->source_address,whichwedon'twant.
*/
local_addr = apr_pmemdup(r->pool, conf->source_address, sizeof(apr_sockaddr_t));
local_addr->pool = r->pool;
rv = apr_socket_bind(*newsock, local_addr); if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00938) "%s: failed to bind socket to local address",
proxy_function);
}
}
/* make the connection out of the socket */
rv = apr_socket_connect(*newsock, backend_addr);
/* if an error occurred, loop round and try again */ if (rv != APR_SUCCESS) {
apr_socket_close(*newsock);
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00939) "%s: attempt to connect to %pI (%s) failed",
proxy_function, backend_addr, backend_name);
backend_addr = backend_addr->next; continue;
}
connected = 1;
} return connected ? 0 : 1;
}
if (!PROXY_WORKER_IS_USABLE(worker)) { /* Retry the worker */
ap_proxy_retry_worker(proxy_function, worker, s);
if (!PROXY_WORKER_IS_USABLE(worker)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00940) "%s: disabled connection for (%s:%d)",
proxy_function, worker->s->hostname_ex,
(int)worker->s->port); return HTTP_SERVICE_UNAVAILABLE;
}
}
if (worker->s->hmax && worker->cp->res) {
rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
} else { /* create the new connection if the previous was destroyed */ if (!worker->cp->conn) {
rv = connection_constructor((void **)conn, worker, worker->cp->pool);
} else {
*conn = worker->cp->conn;
worker->cp->conn = NULL;
rv = APR_SUCCESS;
}
}
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00941) "%s: failed to acquire connection for (%s:%d)",
proxy_function, worker->s->hostname_ex,
(int)worker->s->port); return HTTP_SERVICE_UNAVAILABLE;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00942) "%s: has acquired connection for (%s:%d)",
proxy_function, worker->s->hostname_ex,
(int)worker->s->port);
(*conn)->worker = worker;
(*conn)->inreslist = 0;
return OK;
}
PROXY_DECLARE(int) ap_proxy_release_connection(constchar *proxy_function,
proxy_conn_rec *conn,
server_rec *s)
{
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00943) "%s: has released connection for (%s:%d)",
proxy_function, conn->worker->s->hostname_ex,
(int)conn->worker->s->port);
connection_cleanup(conn);
return OK;
}
static APR_INLINE void proxy_address_inc(proxy_address *address)
{
apr_uint32_t old = apr_atomic_inc32(&address->refcount);
ap_assert(old > 0 && old < APR_UINT32_MAX);
}
static APR_INLINE void proxy_address_dec(proxy_address *address)
{ /* Use _add32(, -1) since _dec32()'s returned value does not help */
apr_uint32_t old = apr_atomic_add32(&address->refcount, -1);
ap_assert(old > 0); if (old == 1) {
apr_pool_destroy(address->addr->pool);
}
}
if (ttl > 0) { /* We keep each worker's expiry date shared accross all the *childrensothattheyupdatetheiraddressatthesame *time,regardlessofwhetheraspecificchildforcedan *addresstoexpireatsomepoint(forconnect()issues).
*/
address->expiry = apr_atomic_read32(&worker->s->address_expiry); if (address->expiry <= now) {
apr_uint32_t prev, next = (now + ttl) - (now % ttl); do {
prev = apr_atomic_cas32(&worker->s->address_expiry,
next, address->expiry); if (prev == address->expiry) {
address->expiry = next; break;
}
address->expiry = prev;
} while (prev <= now);
}
} else { /* Never expires */
address->expiry = APR_UINT32_MAX;
}
/* One ref is for worker->address in any case */ if (worker->address || worker->cp->addr) {
apr_atomic_set32(&address->refcount, 1);
} else { /* Set worker->cp->addr once for compat with third-party *modules.Thisaddrneverchangedbeforeandcan'tchange *underneathusersnowbecauseofsomeTTLconfiguration. *Sowetakeonemorerefforworker->cp->addrtoremain *allocatedforever(thoughitmightnotbeuptodate..). *Modulesshoulduseconn->addrinsteadofworker->cp-addr *togettheactualaddressusedbyeachconn,determined *atconnect()time.
*/
apr_atomic_set32(&address->refcount, 2);
worker->cp->addr = address->addr;
}
/* Publish the changes. The old worker address (if any) is no *longerusedbythisworker,itwillbedestroyednowifthe *workeristhelastuser(refcount==1)orbythelastconn *usingit(refcount>1).
*/
worker_address_set(worker, address);
}
/* Take the ref for conn->address (before dropping the mutex so to *letnochanceforthisaddressbekilledbeforeit'sused!)
*/
proxy_address_inc(address);
PROXY_THREAD_UNLOCK(worker);
/* Release the old conn address */ if (conn->address) { /* On Windows and OS/2, apr_socket_connect() called from *ap_proxy_connect_backend()doesasimplepointercopyof *itsgivenconn->addr[->next]intoconn->sock->remote_addr. *Thusconn->addrcannotbefreediftheconn->sockshouldbe *keptalive(samenewandoldaddresses)andtheoldaddress *isstillinconn->sock->remote_addr.Inthiscasewerather *delaythereleaseoftheoldaddressbymovingthecleanup *toconn->scpoolsuchthatitrunswhenthesocketisclosed. *Inanyothercase,includingotherplatforms,justrelease *theoldaddressnowsinceconn->sock->remote_addriseither *obsolete(socketforciblyclosed)oracopyonconn->scpool *already(notadanglingpointer).
*/ int keep_addr_alive = 0,
keep_conn_alive = (conn->sock && conn->addr &&
proxy_addrs_equal(conn->addr,
address->addr)); if (keep_conn_alive) { #ifdefined(WIN32) || defined(OS2)
apr_sockaddr_t *remote_addr = NULL;
apr_socket_addr_get(&remote_addr, APR_REMOTE, conn->sock); for (addr = conn->addr; addr; addr = addr->next) { if (addr == remote_addr) {
keep_addr_alive = 1; break;
}
} #else /* Nothing to do, keep_addr_alive = 0 */ #endif
} elseif (conn->sock && (r ? APLOGrdebug(r) : APLOGdebug(s))) {
apr_sockaddr_t *local_addr = NULL;
apr_sockaddr_t *remote_addr = NULL;
apr_socket_addr_get(&local_addr, APR_LOCAL, conn->sock);
apr_socket_addr_get(&remote_addr, APR_REMOTE, conn->sock); if (r) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10481) "%s: closing connection to %s (%pI<>%pI) on " "address change", proxy_function, hostname,
local_addr, remote_addr);
} else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10482) "%s: closing connection to %s (%pI<>%pI) on " "address change", proxy_function, hostname,
local_addr, remote_addr);
}
} if (keep_addr_alive) {
apr_pool_cleanup_kill(conn->pool, conn->address,
proxy_address_cleanup);
apr_pool_cleanup_register(conn->scpool, conn->address,
proxy_address_cleanup,
apr_pool_cleanup_null);
} else {
apr_pool_cleanup_run(conn->pool, conn->address,
proxy_address_cleanup); if (!keep_conn_alive) {
conn_cleanup(conn);
}
}
}
/* Use the new address */
apr_pool_cleanup_register(conn->pool, address,
proxy_address_cleanup,
apr_pool_cleanup_null);
conn->address = address;
conn->hostname = address->hostname;
conn->port = address->hostport;
conn->addr = address->addr;
}
}
/* we break the URL into host, port, uri */ if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) { return ap_proxyerror(r, HTTP_BAD_REQUEST,
apr_pstrcat(p,"URI cannot be parsed: ", *url,
NULL));
}
if (!uri->hostname) { return ap_proxyerror(r, HTTP_BAD_REQUEST,
apr_pstrcat(p,"URI has no hostname: ", *url,
NULL));
}
if (!uri->port) {
uri->port = ap_proxy_port_of_scheme(uri->scheme);
}
if (proxyname) {
hostname = proxyname;
hostport = proxyport;
/* *IfwehavearemoteproxyandtheprotocolisHTTPS, *thenweneedtoprependaHTTPCONNECTrequestbefore *sendingouractualHTTPSrequests.
*/ if (conn->is_ssl) { constchar *proxy_auth;
/* Do we want to pass Proxy-Authorization along? *Ifwehaven'tusedit,thenYES *IfwehaveuseditthenMAYBE:RFC2616saysweMAYpropagateit. *Solet'smakeitconfigurablebyenv. *Thelogichereisthesameusedinmod_proxy_http.
*/
proxy_auth = apr_table_get(r->notes, "proxy-basic-creds"); if (proxy_auth == NULL
&& (r->user == NULL /* we haven't yet authenticated */
|| apr_table_get(r->subprocess_env, "Proxy-Chain-Auth"))) {
proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
} if (proxy_auth != NULL && proxy_auth[0] == '\0') {
proxy_auth = NULL;
}
/* Save our real backend data for using it later during HTTP CONNECT */
connect_info = conn->forward; if (!connect_info /* reset connect info if they changed */
|| connect_info->target_port != uri->port
|| ap_cstr_casecmp(connect_info->target_host, uri->hostname) != 0
|| (connect_info->proxy_auth != NULL) != (proxy_auth != NULL)
|| (connect_info->proxy_auth != NULL && proxy_auth != NULL &&
strcmp(connect_info->proxy_auth, proxy_auth) != 0)) {
apr_pool_t *fwd_pool = conn->pool; if (worker->s->is_address_reusable) { if (conn->fwd_pool) {
apr_pool_clear(conn->fwd_pool);
} else {
apr_pool_create(&conn->fwd_pool, conn->pool);
}
fwd_pool = conn->fwd_pool;
}
connect_info = apr_pcalloc(fwd_pool, sizeof(*connect_info));
connect_info->target_host = apr_pstrdup(fwd_pool, uri->hostname);
connect_info->target_port = uri->port; if (proxy_auth) {
connect_info->proxy_auth = apr_pstrdup(fwd_pool, proxy_auth);
}
conn->forward = NULL;
}
}
}
/* Close the connection if the remote proxy or origin server don't match */ if (conn->forward != connect_info
|| (conn->hostname
&& (conn->port != hostport
|| ap_cstr_casecmp(conn->hostname, hostname) != 0))) {
conn_cleanup(conn);
conn->forward = connect_info;
}
/* Resolve the connection address with the determined hostname/port */ if (ap_proxy_determine_address(uri->scheme, conn, hostname, hostport, 0, r, NULL)) { return HTTP_INTERNAL_SERVER_ERROR;
}
}
/* Get the server port for the Via headers */
server_port = ap_get_server_port(r);
AP_DEBUG_ASSERT(server_portstr_size > 0); if (ap_is_default_port(server_port, r)) {
server_portstr[0] = '\0';
} else {
apr_snprintf(server_portstr, server_portstr_size, ":%d",
server_port);
}
/* save timeout */
apr_socket_timeout_get(sock, ¤t_timeout); /* set no timeout */
apr_socket_timeout_set(sock, 0);
socket_status = apr_socket_recv(sock, test_buffer, &buffer_len); /* put back old timeout */
apr_socket_timeout_set(sock, current_timeout); if (APR_STATUS_IS_EOF(socket_status)
|| APR_STATUS_IS_ECONNRESET(socket_status)) { return0;
} else { return1;
}
} #endif/* USE_ALTERNATE_IS_CONNECTED */
/* *SendaHTTPCONNECTrequesttoaremoteproxy. *Theproxyisgivenby"backend",thetargetserver *iscontainedinthe"forward"memberof"backend".
*/ static apr_status_t send_http_connect(proxy_conn_rec *backend,
server_rec *s)
{ int status;
apr_size_t nbytes;
apr_size_t left; int complete = 0; char buffer[HUGE_STRING_LEN]; char drain_buffer[HUGE_STRING_LEN];
remote_connect_info *connect_info = backend->forward; int len = 0;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00948) "CONNECT: sending the CONNECT request for %s:%d " "to the remote proxy %pI (%s)",
connect_info->target_host, connect_info->target_port,
backend->addr, backend->hostname); /* Create the CONNECT request */
nbytes = apr_snprintf(buffer, sizeof(buffer), "CONNECT %s:%d HTTP/1.0" CRLF,
connect_info->target_host, connect_info->target_port); /* Add proxy authorization from the configuration, or initial
* request if necessary */ if (connect_info->proxy_auth != NULL) {
nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, "Proxy-Authorization: %s" CRLF,
connect_info->proxy_auth);
} /* Set a reasonable agent and send everything */
nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, "Proxy-agent: %s" CRLF CRLF,
ap_get_server_banner());
ap_xlate_proto_to_ascii(buffer, nbytes);
apr_socket_send(backend->sock, buffer, &nbytes);
/* Receive the whole CONNECT response */
left = sizeof(buffer) - 1; /* Read until we find the end of the headers or run out of buffer */ do {
nbytes = left;
status = apr_socket_recv(backend->sock, buffer + len, &nbytes);
len += nbytes;
left -= nbytes;
buffer[len] = '\0'; if (strstr(buffer + len - nbytes, CRLF_ASCII CRLF_ASCII) != NULL) {
ap_xlate_proto_from_ascii(buffer, len);
complete = 1; break;
}
} while (status == APR_SUCCESS && left > 0); /* Drain what's left */ if (!complete) {
nbytes = sizeof(drain_buffer) - 1; while (status == APR_SUCCESS && nbytes) {
status = apr_socket_recv(backend->sock, drain_buffer, &nbytes);
drain_buffer[nbytes] = '\0';
nbytes = sizeof(drain_buffer) - 1; if (strstr(drain_buffer, CRLF_ASCII CRLF_ASCII) != NULL) { break;
}
}
}
/* Check for HTTP_OK response status */ if (status == APR_SUCCESS) { unsignedint major, minor; /* Only scan for three character status code */ char code_str[4];
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00949) "send_http_connect: response from the remote proxy: %s",
buffer);
/* Extract the returned code */ if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) {
status = atoi(code_str); if (status == HTTP_OK) {
status = APR_SUCCESS;
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00950) "send_http_connect: the remote proxy returned code is '%s'",
code_str);
status = APR_INCOMPLETE;
}
}
}
return(status);
}
/* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */
PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock, constchar *uds_path,
apr_pool_t *p)
{ #if APR_HAVE_SYS_UN_H
apr_status_t rv;
apr_os_sock_t rawsock;
apr_interval_time_t t; struct sockaddr_un *sa;
apr_socklen_t addrlen, pathlen;
if (!PROXY_WORKER_IS_USABLE(worker)) { /* *Theworkerisinerrorlikelydonebyadifferentthread/process *e.g.foratimeoutorbadstatus.Weshouldrespectthisandshould *notcontinuewithaconnectionviathisworkerevenifwegotone.
*/
rv = APR_EINVAL;
} elseif (conn->connection) { /* We have a conn_rec, check the full filter stack for things like *SSLalert/shutdown,filtersasidedata...
*/
rv = ap_check_pipeline(conn->connection, conn->tmp_bb,
max_blank_lines);
apr_brigade_cleanup(conn->tmp_bb); if (rv == APR_SUCCESS) { /* Some data available, the caller might not want them. */ if (flags & PROXY_CHECK_CONN_EMPTY) {
rv = APR_ENOTEMPTY;
}
} elseif (APR_STATUS_IS_EAGAIN(rv)) { /* Filter chain is OK and empty, yet we can't determine from *ap_check_pipeline(actuallyap_core_input_filter)whether *anemptynon-blockingreadisEAGAINorEOFonthesocket *side(it'salwaysSUCCESS),socheckitexplicitlyhere.
*/ if (ap_proxy_is_socket_connected(conn->sock)) {
rv = APR_SUCCESS;
} else {
rv = APR_EPIPE;
}
}
} elseif (conn->sock) { /* For modules working with sockets directly, check it. */ if (!ap_proxy_is_socket_connected(conn->sock)) {
rv = APR_EPIPE;
}
} else {
rv = APR_ENOSOCKET;
}
/* We'll set conn->addr to the address actually connect()ed, so if the *networkconnectionisnotreused(perap_proxy_check_connection() *above)weneedtoresetconn->addrtothefirstresolvedaddress *andtrytoconnectitfirst.
*/ if (conn->address && rv != APR_SUCCESS) {
conn->addr = conn->address->addr;
}
backend_addr = conn->addr;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02824) "%s: connection established with %pI (%s:%hu)",
proxy_function, backend_addr,
conn->hostname, conn->port);
/* Set the actual sockaddr we are connected to */
conn->addr = backend_addr;
}
/* Set a timeout on the socket */ if (worker->s->timeout_set) {
apr_socket_timeout_set(newsock, worker->s->timeout);
} elseif (conf->timeout_set) {
apr_socket_timeout_set(newsock, conf->timeout);
} else {
apr_socket_timeout_set(newsock, s->timeout);
}
conn->sock = newsock;
/* *ForHTTPCONNECTweneedtoprependCONNECTrequestbefore *sendingouractualHTTPSrequests.
*/ if (connect_info) {
rv = send_http_connect(conn, s); /* If an error occurred, loop round and try again */ if (rv != APR_SUCCESS) {
conn->sock = NULL;
apr_socket_close(newsock);
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00958) "%s: attempt to connect to %s:%hu " "via http CONNECT through %pI (%s:%hu) failed",
proxy_function,
connect_info->target_host, connect_info->target_port,
backend_addr, conn->hostname, conn->port);
backend_addr = backend_addr->next; continue;
}
}
}
if (conn->connection) { if (conn->is_ssl) { /* on reuse, reinit the SSL connection dir config with the current *r->per_dir_config,thepreviousonewasresetonrelease.
*/
ap_proxy_ssl_engine(conn->connection, per_dir_config, 1);
} return OK;
}
/* For ssl connection to backend */ if (conn->is_ssl) { if (!ap_proxy_ssl_engine(conn->connection, per_dir_config, 1)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0,
s, APLOGNO(00961) "%s: failed to enable ssl support " "for %pI (%s)", proxy_function,
backend_addr, conn->hostname);
rc = HTTP_INTERNAL_SERVER_ERROR; goto cleanup;
} if (conn->ssl_hostname) { /* Set a note on the connection about what CN is requested, *suchthatmod_sslcancheckifitisrequestedtodoso.
*/
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, conn->connection, "%s: set SNI to %s for (%s)", proxy_function,
conn->ssl_hostname, conn->hostname);
apr_table_setn(conn->connection->notes, "proxy-request-hostname",
conn->ssl_hostname);
}
} else { /* TODO: See if this will break FTP */
ap_proxy_ssl_engine(conn->connection, per_dir_config, 0);
}
/* *savethetimeoutofthesocketbecausecore_pre_connection *willsetittobase_server->timeout *(coreTimeOutdirective).
*/
apr_socket_timeout_get(conn->sock, ¤t_timeout); /* set up the connection filters */
rc = ap_run_pre_connection(conn->connection, conn->sock); if (rc != OK && rc != DONE) {
conn->connection->aborted = 1;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00963) "%s: pre_connection setup failed (%d)",
proxy_function, rc); goto cleanup;
}
apr_socket_timeout_set(conn->sock, current_timeout);
/* Shutdown the connection before closing it (eg. SSL connections *needtobeclose-notify-ed).
*/
apr_pool_pre_cleanup_register(conn->scpool, conn, connection_shutdown);
apr_table_do(find_conn_headers, &x, headers, "Connection", NULL); if (x.first) { /* fast path - no memory allocated for one header */
apr_table_unset(headers, "Connection");
apr_table_unset(headers, x.first);
} if (x.array) { /* two or more headers */ while ((name = apr_array_pop(x.array))) {
apr_table_unset(headers, *name);
}
}
/* Return the original Transfer-Encoding and/or Content-Length values *thendroptheheaders,theymustbesetbytheproxyhandlerbased *ontheactualbodybeingforwarded.
*/ if ((*old_te_val = (char *)apr_table_get(r->headers_in, "Transfer-Encoding"))) {
apr_table_unset(r->headers_in, "Transfer-Encoding");
} if ((*old_cl_val = (char *)apr_table_get(r->headers_in, "Content-Length"))) {
apr_table_unset(r->headers_in, "Content-Length");
}
/* Clear out hop-by-hop request headers not to forward */ if (ap_proxy_clear_connection(r, r->headers_in) < 0) {
rc = HTTP_BAD_REQUEST; goto cleanup;
}
/* RFC2616 13.5.1 says we should strip these */
apr_table_unset(r->headers_in, "Keep-Alive");
apr_table_unset(r->headers_in, "Upgrade");
apr_table_unset(r->headers_in, "Trailer");
apr_table_unset(r->headers_in, "TE");
/* Compute Host header */ if (dconf->preserve_host == 0) { if (!uri->hostname) {
rc = HTTP_BAD_REQUEST; goto cleanup;
} if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
host = apr_pstrcat(r->pool, "[", uri->hostname, "]:",
uri->port_str, NULL);
} else {
host = apr_pstrcat(r->pool, "[", uri->hostname, "]", NULL);
}
} else { if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
host = apr_pstrcat(r->pool, uri->hostname, ":",
uri->port_str, NULL);
} else {
host = uri->hostname;
}
}
apr_table_setn(r->headers_in, "Host", host);
} else { /* don't want to use r->hostname as the incoming header might have a *portattached,let'susetheoriginalheader.
*/
host = saved_host; if (!host) {
host = r->server->server_hostname;
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092) "no HTTP 0.9 request (with no host line) " "on incoming request and preserve host set " "forcing hostname to be %s for uri %s",
host, r->uri);
apr_table_setn(r->headers_in, "Host", host);
}
}
/* handle Via */ if (conf->viaopt == via_block) { /* Block all outgoing Via: headers */
apr_table_unset(r->headers_in, "Via");
} elseif (conf->viaopt != via_off) { constchar *server_name = ap_get_server_name(r); /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, *thentheservernamereturnedbyap_get_server_name()isthe *originservername(whichdoesmaketoomuchsensewithVia:headers) *soweusetheproxyvhost'snameinstead.
*/ if (server_name == r->hostname)
server_name = r->server->server_hostname; /* Create a "Via:" request header entry and merge it */ /* Generate outgoing Via: header with/without server comment: */
apr_table_mergen(r->headers_in, "Via",
(conf->viaopt == via_full)
? apr_psprintf(p, "%d.%d %s%s (%s)",
HTTP_VERSION_MAJOR(r->proto_num),
HTTP_VERSION_MINOR(r->proto_num),
server_name, server_portstr,
AP_SERVER_BASEVERSION)
: apr_psprintf(p, "%d.%d %s%s",
HTTP_VERSION_MAJOR(r->proto_num),
HTTP_VERSION_MINOR(r->proto_num),
server_name, server_portstr)
);
}
/* Use HTTP/1.1 100-Continue as quick "HTTP ping" test *tobackend
*/ if (do_100_continue) { /* Add the Expect header if not already there. */ if (!(val = apr_table_get(r->headers_in, "Expect"))
|| (ap_cstr_casecmp(val, "100-Continue") != 0/* fast path */
&& !ap_find_token(r->pool, val, "100-Continue"))) {
apr_table_mergen(r->headers_in, "Expect", "100-Continue");
}
} else { /* XXX: we should strip the 100-continue token only from the *Expectheader,butarethereothersactuallyusedanywhere?
*/
apr_table_unset(r->headers_in, "Expect");
}
/* X-Forwarded-*: handling * *XXXPrivacyNote: *----------------- * *Theserequestheadersareonlyreallyusefulwhenthemod_proxy *isusedinareverseproxyconfiguration,sothatusefulinfo *abouttheclientcanbepassedthroughthereverseproxyandon *tothebackendserver,whichmayrequiretheinformationto *functionproperly. * *Inaforwardproxysituation,theseoptionsareapotential *privacyviolation,asinformationaboutclientsbehindtheproxy *arerevealedtoarbitraryserversoutthereontheinternet. * *TheHTTP/1.1Via:headerisdesignedforpassingclient *informationthroughproxiestoaserver,andshouldbeusedin *aforwardproxyconfigurationinsteadofX-Forwarded-*.Seethe *ProxyViaoptionfordetails.
*/ if (dconf->add_forwarded_headers) { if (PROXYREQ_REVERSE == r->proxyreq) { /* Add X-Forwarded-For: so that the upstream has a chance to *determine,wheretheoriginalrequestcamefrom.
*/
apr_table_mergen(r->headers_in, "X-Forwarded-For",
r->useragent_ip);
/* Add X-Forwarded-Host: so that upstream knows what the *originalrequesthostnamewas.
*/ if (saved_host) {
apr_table_mergen(r->headers_in, "X-Forwarded-Host",
saved_host);
}
/* Add X-Forwarded-Server: so that upstream knows what the *nameofthisproxyserveris(iftherearemorethanone) *XXX:ThisduplicatesVia:-dowestrictlyneedit?
*/
apr_table_mergen(r->headers_in, "X-Forwarded-Server",
r->server->server_hostname);
}
}
/* Do we want to strip Proxy-Authorization ? *Ifwehaven'tusedit,thenNO *IfwehaveuseditthenMAYBE:RFC2616saysweMAYpropagateit. *Solet'smakeitconfigurablebyenv.
*/ if (r->user != NULL /* we've authenticated */
&& !apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
apr_table_unset(r->headers_in, "Proxy-Authorization");
}
do { if (APR_BRIGADE_EMPTY(input_brigade)) {
rv = ap_proxy_read_input(r, backend, input_brigade,
HUGE_STRING_LEN); if (rv != OK) { return rv;
}
}
/* If this brigade contains EOS, either stop or remove it. */ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
seen_eos = 1;
}
apr_brigade_length(input_brigade, 1, &bytes);
if (*bytes_spooled + bytes > max_mem_spool) { /* can't spool any more in memory; write latest brigade to disk */ if (tmpfile == NULL) { constchar *temp_dir; char *template;
status = apr_temp_dir_get(&temp_dir, p); if (status != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01089) "search for temporary directory failed"); return HTTP_INTERNAL_SERVER_ERROR;
}
apr_filepath_merge(&template, temp_dir, "modproxy.tmp.XXXXXX",
APR_FILEPATH_NATIVE, p);
status = apr_file_mktemp(&tmpfile, template, 0, p); if (status != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01090) "creation of temporary file in directory " "%s failed", temp_dir); return HTTP_INTERNAL_SERVER_ERROR;
}
} for (e = APR_BRIGADE_FIRST(input_brigade);
e != APR_BRIGADE_SENTINEL(input_brigade);
e = APR_BUCKET_NEXT(e)) { constchar *data;
apr_size_t bytes_read, bytes_written;
apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written); if (status != APR_SUCCESS) { constchar *tmpfile_name;
if (flush) {
apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
}
apr_brigade_length(bb, 0, &transferred); if (transferred != -1)
p_conn->worker->s->transferred += transferred;
status = ap_pass_brigade(origin->output_filters, bb); /* Cleanup the brigade now to avoid buckets lifetime
* issues in case of error returned below. */
apr_brigade_cleanup(bb); if (status != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084) "pass request body failed to %pI (%s)",
p_conn->addr, p_conn->hostname); if (origin->aborted) { constchar *ssl_note;
apr_brigade_cleanup(to); for (e = APR_BRIGADE_FIRST(from);
e != APR_BRIGADE_SENTINEL(from);
e = APR_BUCKET_NEXT(e)) { if (!APR_BUCKET_IS_METADATA(e)) {
apr_bucket_read(e, &data, &bytes, APR_BLOCK_READ); new = apr_bucket_transient_create(data, bytes, bucket_alloc);
APR_BRIGADE_INSERT_TAIL(to, new);
} elseif (APR_BUCKET_IS_FLUSH(e)) { new = apr_bucket_flush_create(bucket_alloc);
APR_BRIGADE_INSERT_TAIL(to, new);
} elseif (APR_BUCKET_IS_EOS(e)) { new = apr_bucket_eos_create(bucket_alloc);
APR_BRIGADE_INSERT_TAIL(to, new);
} else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03304) "Unhandled bucket type of type %s in" " ap_proxy_buckets_lifetime_transform", e->type->name);
rv = APR_EGENERAL;
}
} return rv;
}
/* An arbitrary large value to address pathological case where we keep *readingfromonesideonly,withoutschedulingtheotherdirectionfor *toolong.ThiscanhappenwithlargeMTUandsmallreadbuffers,like *micro-benchmarkinghugefilesbidirectionaltransferwithclient,proxy *andbackendonlocalhostforinstance.Thoughwecouldjustignorethe *caseandletthesenderstopbyitselfatsomepointwhen/ifitneedsto *receivedata,orthereceiverstopwhen/ifitneedstosend...
*/ #define PROXY_TRANSFER_MAX_READS 10000
/* Yield if the output filters stack is full? This is to avoid *blockingandgivethecallerachancetoPOLLOUTasync.
*/ if ((flags & AP_PROXY_TRANSFER_YIELD_PENDING)
&& ap_filter_should_yield(c_o->output_filters)) { int rc = ap_filter_output_pending(c_o); if (rc == OK) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "ap_proxy_transfer_between_connections: " "yield (output pending)");
rv = APR_INCOMPLETE; break;
} if (rc != DECLINED) {
rv = AP_FILTER_ERROR; break;
}
}
/* Yield if we keep hold of the thread for too long? This gives *thecallerachancetoscheduletheotherdirectiontoo.
*/ if ((flags & AP_PROXY_TRANSFER_YIELD_MAX_READS)
&& ++num_reads > PROXY_TRANSFER_MAX_READS) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "ap_proxy_transfer_between_connections: " "yield (max reads)");
rv = APR_SUCCESS; break;
}
}
if (flags & AP_PROXY_TRANSFER_FLUSH_AFTER) {
ap_fflush(c_o->output_filters, bb_o);
apr_brigade_cleanup(bb_o);
}
apr_brigade_cleanup(bb_i);
/* Defaults to the largest timeout of both connections */
tunnel->timeout = (client_timeout >= 0 && client_timeout > origin_timeout ?
client_timeout : origin_timeout);
rv = ap_proxy_transfer_between_connections(tunnel->r,
in->c, out->c,
in->bb, out->bb,
in->name, &sent,
tunnel->read_buf_size,
AP_PROXY_TRANSFER_YIELD_PENDING |
AP_PROXY_TRANSFER_YIELD_MAX_READS); if (sent && out == tunnel->client) {
tunnel->replied = 1;
} if (rv != APR_SUCCESS) { if (APR_STATUS_IS_INCOMPLETE(rv)) { /* Pause POLLIN while waiting for POLLOUT on the other *side,henceavoidfillingtheoutputfilterseven *moretoavoidblockingthere.
*/
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, tunnel->r, "proxy: %s: %s wait writable",
tunnel->scheme, out->name);
} elseif (APR_STATUS_IS_EOF(rv)) { /* Stop POLLIN and wait for POLLOUT (flush) on the *othersidetoshutitdown.
*/
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, tunnel->r, "proxy: %s: %s read shutdown",
tunnel->scheme, in->name); if (tunnel->nohalfclose) { /* No half-close forwarding, we are done both ways as *soonasonesideshutsdown.
*/ return DONE;
}
in->down_in = 1;
} else { /* Real failure, bail out */ return HTTP_INTERNAL_SERVER_ERROR;
}
del_pollset(tunnel->pollset, in->pfd, APR_POLLIN); if (out->pfd->desc_type == APR_POLL_SOCKET) { /* if the output is a SOCKET, we can stop polling the input
* until the output signals POLLOUT again. */
add_pollset(tunnel->pollset, out->pfd, APR_POLLOUT);
} else { /* We can't use POLLOUT in this direction for the only *APR_POLL_FILEcasewehavesofar(mod_h2's"signal"pipe), *weassumethattheclient'souputfilterschainwillblock/flush *ifnecessary(i.e.nopendingdata),hencethattheorigin
* is EOF when reaching here. This direction is over. */
ap_assert(in->down_in && APR_STATUS_IS_EOF(rv));
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, tunnel->r, "proxy: %s: %s write shutdown",
tunnel->scheme, out->name);
out->down_out = 1;
}
}
¤ 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.404Bemerkung:
(vorverarbeitet am 2026-09-28)
¤
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.