diff options
Diffstat (limited to 'modules/proxy/mod_proxy.c')
-rw-r--r-- | modules/proxy/mod_proxy.c | 704 |
1 files changed, 559 insertions, 145 deletions
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 69a35ce..c9cef7c 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -17,6 +17,7 @@ #include "mod_proxy.h" #include "mod_core.h" #include "apr_optional.h" +#include "apr_strings.h" #include "scoreboard.h" #include "mod_status.h" #include "proxy_util.h" @@ -29,10 +30,6 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *)); APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *, ap_conf_vector_t *, int proxy, int enable)); -APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *)); -APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup, - (apr_pool_t *, server_rec *, - conn_rec *, request_rec *, char *)); #endif #ifndef MAX @@ -55,6 +52,9 @@ proxy_hcmethods_t PROXY_DECLARE_DATA proxy_hcmethods[] = { {GET, "GET", 1}, {CPING, "CPING", 0}, {PROVIDER, "PROVIDER", 0}, + {OPTIONS11, "OPTIONS11", 1}, + {HEAD11, "HEAD11", 1}, + {GET11, "GET11", 1}, {EOT, NULL, 1} }; @@ -149,7 +149,7 @@ static const char *set_worker_param(apr_pool_t *p, return "Max must be a positive number"; worker->s->hmax = ival; } - /* XXX: More inteligent naming needed */ + /* XXX: More intelligent naming needed */ else if (!strcasecmp(key, "smax")) { /* Maximum number of connections to remote that * will not be destroyed @@ -224,6 +224,24 @@ static const char *set_worker_param(apr_pool_t *p, return "EnableReuse must be On|Off"; worker->s->disablereuse_set = 1; } + else if (!strcasecmp(key, "addressttl")) { + /* Address TTL in seconds + */ + apr_interval_time_t ttl; + if (strcmp(val, "-1") == 0) { + worker->s->address_ttl = -1; + } + else if (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; + } else if (!strcasecmp(key, "route")) { /* Worker route. */ @@ -308,13 +326,14 @@ static const char *set_worker_param(apr_pool_t *p, worker->s->conn_timeout_set = 1; } else if (!strcasecmp(key, "flusher")) { - if (strlen(val) >= sizeof(worker->s->flusher)) - apr_psprintf(p, "flusher name length must be < %d characters", - (int)sizeof(worker->s->flusher)); - PROXY_STRNCPY(worker->s->flusher, val); + 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)); + } } else if (!strcasecmp(key, "upgrade")) { - if (PROXY_STRNCPY(worker->s->upgrade, val) != APR_SUCCESS) { + 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)); } @@ -327,6 +346,12 @@ static const char *set_worker_param(apr_pool_t *p, worker->s->response_field_size = (s ? s : HUGE_STRING_LEN); worker->s->response_field_size_set = 1; } + else if (!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); @@ -557,6 +582,201 @@ static int alias_match(const char *uri, const char *alias_fakename) return urip - uri; } +/* + * Inspired by mod_jk's jk_servlet_normalize(). + */ +static int alias_match_servlet(apr_pool_t *p, + const char **urip, + const char *alias) +{ + char *map; + const char *uri = *urip; + apr_array_header_t *stack; + int map_pos, uri_pos, alias_pos, first_pos; + int alias_depth = 0, depth; + + /* Both uri and alias should start with '/' */ + if (uri[0] != '/' || alias[0] != '/') { + return 0; + } + + stack = apr_array_make(p, 5, sizeof(int)); + map = apr_palloc(p, strlen(uri) + 1); + map[0] = '/'; + map[1] = '\0'; + + map_pos = uri_pos = alias_pos = first_pos = 1; + while (uri[uri_pos] != '\0') { + /* Remove path parameters ;foo=bar/ from any path segment */ + if (uri[uri_pos] == ';') { + do { + uri_pos++; + } while (uri[uri_pos] != '/' && uri[uri_pos] != '\0'); + continue; + } + + if (map[map_pos - 1] == '/') { + /* Collapse ///// sequences to / */ + if (uri[uri_pos] == '/') { + do { + uri_pos++; + } while (uri[uri_pos] == '/'); + continue; + } + + if (uri[uri_pos] == '.') { + /* Remove /./ segments */ + if (uri[uri_pos + 1] == '/' + || uri[uri_pos + 1] == ';' + || uri[uri_pos + 1] == '\0') { + uri_pos++; + if (uri[uri_pos] == '/') { + uri_pos++; + } + continue; + } + + /* 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 */ + return 0; + } + 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; + } + else if (alias[alias_pos + 1] == '\0' + && alias[alias_pos] != '/') { + if (uri[uri_pos + 1] == ';') { + /* We'll preserve the parameters of the last + * segment if it does not end with '/', so mark + * the match as negative for below handling. + */ + *match = -(uri_pos + 1); + } + else if (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++; + } + } + + if (uri[uri_pos] == '/') { + first_pos = uri_pos + 1; + } + map[map_pos++] = uri[uri_pos++]; + map[map_pos] = '\0'; + } + + /* Can't reach the end of uri before the end of the alias, + * for example if uri is "/" and alias is "/examples" + */ + if (alias[alias_pos] != '\0') { + return 0; + } + + /* Check whether each alias segment matched */ + for (depth = 0; depth < alias_depth; ++depth) { + if (!APR_ARRAY_IDX(stack, depth, int)) { + return 0; + } + } + + /* If alias_depth == stack->nelts we have a full match, i.e. + * uri == alias so we can return uri_pos as is (the end of uri) + */ + 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 '/' + * and the corresponding segment of the uri has parameters, + * we want to forward those parameters (see above for the + * negative pos trick/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 * have to do this during this phase because later phases are * "short-circuiting"... i.e. translate_names will end when the first @@ -578,11 +798,12 @@ static int proxy_detect(request_rec *r) if (conf->req && r->parsed_uri.scheme) { /* but it might be something vhosted */ - if (!(r->parsed_uri.hostname - && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r)) - && ap_matches_request_vhost(r, r->parsed_uri.hostname, - (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port - : ap_default_port(r))))) { + 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); @@ -667,6 +888,7 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent, int mismatch = 0; unsigned int nocanon = ent->flags & PROXYPASS_NOCANON; const char *use_uri = nocanon ? r->unparsed_uri : r->uri; + const char *servlet_uri = NULL; if (dconf && (dconf->interpolate_env == 1) && (ent->flags & PROXYPASS_INTERPOLATE)) { fake = proxy_interpolate(r, ent->fake); @@ -727,7 +949,14 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent, } } else { - len = alias_match(r->uri, fake); + 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')) { @@ -736,7 +965,7 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent, "'%s'; declining", r->uri); return DECLINED; } - if (nocanon && len != alias_match(r->unparsed_uri, ent->fake)) { + if (nocanon && len != alias_match(r->unparsed_uri, fake)) { mismatch = 1; use_uri = r->uri; } @@ -752,6 +981,17 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent, } if (found) { + unsigned int encoded = ent->flags & PROXYPASS_MAP_ENCODED; + + /* A proxy module is assigned this URL, check whether it's interested + * in the request itself (e.g. proxy_wstunnel cares about Upgrade + * requests only, and could hand over to proxy_http otherwise). + */ + 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; @@ -762,29 +1002,67 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent, if (ent->flags & PROXYPASS_NOQUERY) { apr_table_setn(r->notes, "proxy-noquery", "1"); } + if (encoded) { + apr_table_setn(r->notes, "proxy-noencode", "1"); + } - ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03464) - "URI path '%s' matches proxy handler '%s'", r->uri, - found); - - return OK; + if (servlet_uri) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10248) + "Servlet path '%s' (%s) matches proxy handler '%s'", + r->uri, servlet_uri, found); + /* Apply servlet normalization to r->uri so that <Location> or any + * directory context match does not have to handle path parameters. + * We change r->uri in-place so that r->parsed_uri.path is updated + * too. Since normalized servlet_uri is necessarily shorter than + * the original r->uri, strcpy() is fine. + */ + AP_DEBUG_ASSERT(strlen(r->uri) >= strlen(servlet_uri)); + strcpy(r->uri, servlet_uri); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03464) + "URI path '%s' matches proxy handler '%s'", r->uri, + found); + } + return (encoded) ? DONE : OK; } - return DONE; + return HTTP_CONTINUE; } -static int proxy_trans(request_rec *r) +static int proxy_trans(request_rec *r, int pre_trans) { - int i; + 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 - * in proxy_detect + * in proxy_detect (DONE will prevent further decoding of r->uri, + * only if proxyreq is set before pre_trans already). */ - return OK; + return pre_trans ? DONE : OK; + } + + /* In early pre_trans hook, r->uri was not manipulated yet so we are + * compliant with RFC1945 at this point. Otherwise, it probably isn't + * an issue because this is a hybrid proxy/origin server. + */ + + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config, + &proxy_module); + + /* Always and only do PROXY_MAP_ENCODED mapping in pre_trans, when + * r->uri is still encoded, or we might consider for instance that + * a decoded sub-delim is now a delimiter (e.g. "%3B" => ';' for + * path parameters), which it's not. + */ + if ((pre_trans && !conf->map_encoded_one) + || (!pre_trans && conf->map_encoded_all)) { + /* Fast path, nothing at this stage */ + return DECLINED; } if ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0') @@ -796,37 +1074,42 @@ static int proxy_trans(request_rec *r) return DECLINED; } - /* XXX: since r->uri has been manipulated already we're not really - * compliant with RFC1945 at this point. But this probably isn't - * an issue because this is a hybrid proxy/origin server. - */ - - dconf = ap_get_module_config(r->per_dir_config, &proxy_module); - /* short way - this location is reverse proxied? */ if (dconf->alias) { - int rv = ap_proxy_trans_match(r, dconf->alias, dconf); - if (DONE != rv) { - return rv; + 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; + } } } - conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config, - &proxy_module); - /* long way - walk the list of aliases, find a match */ - if (conf->aliases->nelts) { - ent = (struct proxy_alias *) conf->aliases->elts; - for (i = 0; i < conf->aliases->nelts; i++) { - int rv = ap_proxy_trans_match(r, &ent[i], dconf); - if (DONE != rv) { + 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; } } } + return DECLINED; } +static int proxy_pre_translate_name(request_rec *r) +{ + return proxy_trans(r, 1); +} + +static int proxy_translate_name(request_rec *r) +{ + return proxy_trans(r, 0); +} + static int proxy_walk(request_rec *r) { proxy_server_conf *sconf = ap_get_module_config(r->server->module_config, @@ -857,6 +1140,7 @@ static int proxy_walk(request_rec *r) if (entry_proxy->refs && entry_proxy->refs->nelts) { if (!rxpool) { apr_pool_create(&rxpool, r->pool); + apr_pool_tag(rxpool, "proxy_rxpool"); } nmatch = entry_proxy->refs->nelts; pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t)); @@ -979,7 +1263,7 @@ static int proxy_needsdomain(request_rec *r, const char *url, const char *domain /* 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 */ - || strcasecmp(r->parsed_uri.hostname, "localhost") == 0) + || 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"); @@ -1049,9 +1333,10 @@ static int proxy_handler(request_rec *r) char *end; maxfwd = apr_strtoi64(str, &end, 10); if (maxfwd < 0 || maxfwd == APR_INT64_MAX || *end) { - return ap_proxyerror(r, HTTP_BAD_REQUEST, - apr_psprintf(r->pool, - "Max-Forwards value '%s' could not be parsed", str)); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10188) + "Max-Forwards value '%s' could not be parsed", str); + return ap_proxyerror(r, HTTP_BAD_REQUEST, + "Max-Forwards request header could not be parsed"); } else if (maxfwd == 0) { switch (r->method_number) { @@ -1185,23 +1470,31 @@ static int proxy_handler(request_rec *r) if (strcmp(ents[i].scheme, "*") == 0 || (ents[i].use_regex && ap_regexec(ents[i].regexp, url, 0, NULL, 0) == 0) || - (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) || + (p2 == NULL && ap_cstr_casecmp(scheme, ents[i].scheme) == 0) || (p2 != NULL && - strncasecmp(url, ents[i].scheme, + 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"); + + if (ents[i].creds) { + apr_table_set(r->notes, "proxy-basic-creds", ents[i].creds); + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "Using proxy auth creds %s", ents[i].creds); + } + access_status = proxy_run_scheme_handler(r, worker, conf, url, ents[i].hostname, ents[i].port); + if (ents[i].creds) apr_table_unset(r->notes, "proxy-basic-creds"); + /* Did the scheme handler process the request? */ if (access_status != DECLINED) { const char *cl_a; - char *end; apr_off_t cl; /* @@ -1211,18 +1504,17 @@ static int proxy_handler(request_rec *r) if (access_status != HTTP_BAD_GATEWAY) { goto cleanup; } + cl_a = apr_table_get(r->headers_in, "Content-Length"); - if (cl_a) { - apr_strtoff(&cl, cl_a, &end, 10); + if (cl_a && (!ap_parse_strict_length(&cl, cl_a) + || cl > 0)) { /* * The request body is of length > 0. We cannot * retry with a direct connection since we already * sent (parts of) the request body to the proxy * and do not have any longer. */ - if (cl > 0) { - goto cleanup; - } + goto cleanup; } /* * Transfer-Encoding was set as input header, so we had @@ -1347,6 +1639,8 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s) ps->forward = NULL; ps->reverse = NULL; ps->domain = NULL; + ps->map_encoded_one = 0; + ps->map_encoded_all = 1; ps->id = apr_psprintf(p, "p%x", 1); /* simply for storage size */ ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */ ps->viaopt_set = 0; /* 0 means default */ @@ -1373,6 +1667,7 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s) ps->source_address = NULL; ps->source_address_set = 0; apr_pool_create_ex(&ps->pool, p, NULL, NULL); + apr_pool_tag(ps->pool, "proxy_server_conf"); return ps; } @@ -1503,6 +1798,9 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv) ps->forward = overrides->forward ? overrides->forward : base->forward; ps->reverse = overrides->reverse ? overrides->reverse : base->reverse; + ps->map_encoded_one = overrides->map_encoded_one || base->map_encoded_one; + ps->map_encoded_all = overrides->map_encoded_all && base->map_encoded_all; + ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain; ps->id = (overrides->id == NULL) ? base->id : overrides->id; ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt; @@ -1560,6 +1858,7 @@ static void *create_proxy_dir_config(apr_pool_t *p, char *dummy) new->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); new->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias)); new->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias)); + new->error_override_codes = apr_array_make(p, 10, sizeof(int)); new->preserve_host_set = 0; new->preserve_host = 0; new->interpolate_env = -1; /* unset */ @@ -1567,10 +1866,17 @@ static void *create_proxy_dir_config(apr_pool_t *p, char *dummy) new->error_override_set = 0; new->add_forwarded_headers = 1; new->add_forwarded_headers_set = 0; + new->forward_100_continue = 1; + new->forward_100_continue_set = 0; return (void *) new; } +static int int_order(const void *i1, const void *i2) +{ + return *(const int *)i1 - *(const int *)i2; +} + static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) { proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf)); @@ -1588,6 +1894,17 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) = apr_array_append(p, base->cookie_paths, add->cookie_paths); new->cookie_domains = apr_array_append(p, base->cookie_domains, add->cookie_domains); + new->error_override_codes + = apr_array_append(p, base->error_override_codes, add->error_override_codes); + /* Keep the array sorted for binary search (since "base" and "add" are + * already sorted, it's only needed only if both are merged). + */ + if (base->error_override_codes->nelts + && add->error_override_codes->nelts) { + qsort(new->error_override_codes->elts, + new->error_override_codes->nelts, + sizeof(int), int_order); + } new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env : add->interpolate_env; new->preserve_host = (add->preserve_host_set == 0) ? base->preserve_host @@ -1603,12 +1920,17 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) : add->add_forwarded_headers; new->add_forwarded_headers_set = add->add_forwarded_headers_set || base->add_forwarded_headers_set; - + new->forward_100_continue = + (add->forward_100_continue_set == 0) ? base->forward_100_continue + : add->forward_100_continue; + new->forward_100_continue_set = add->forward_100_continue_set + || base->forward_100_continue_set; + return new; } -static const char * - add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex) +static const char *add_proxy(cmd_parms *cmd, void *dummy, const char *f1, + const char *r1, const char *creds, int regex) { server_rec *s = cmd->server; proxy_server_conf *conf = @@ -1666,19 +1988,24 @@ static const char * new->port = port; new->regexp = reg; new->use_regex = regex; + if (creds) { + new->creds = apr_pstrcat(cmd->pool, "Basic ", + ap_pbase64encode(cmd->pool, (char *)creds), + NULL); + } return NULL; } -static const char * - add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1) +static const char *add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, + const char *r1, const char *creds) { - return add_proxy(cmd, dummy, f1, r1, 0); + return add_proxy(cmd, dummy, f1, r1, creds, 0); } -static const char * - add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1) +static const char *add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, + const char *r1, const char *creds) { - return add_proxy(cmd, dummy, f1, r1, 1); + return add_proxy(cmd, dummy, f1, r1, creds, 1); } PROXY_DECLARE(const char *) ap_proxy_de_socketfy(apr_pool_t *p, const char *url) @@ -1688,8 +2015,8 @@ PROXY_DECLARE(const char *) ap_proxy_de_socketfy(apr_pool_t *p, const char *url) * We could be passed a URL during the config stage that contains * the UDS path... ignore it */ - if (!strncasecmp(url, "unix:", 5) && - ((ptr = ap_strchr_c(url, '|')) != NULL)) { + if (!ap_cstr_casecmpn(url, "unix:", 5) && + ((ptr = ap_strchr_c(url + 5, '|')) != NULL)) { /* move past the 'unix:...|' UDS path info */ const char *ret, *c; @@ -1721,12 +2048,14 @@ static const char * struct proxy_alias *new; char *f = cmd->path; char *r = NULL; + const char *real; char *word; apr_table_t *params = apr_table_make(cmd->pool, 5); const apr_array_header_t *arr; const apr_table_entry_t *elts; int i; - int use_regex = is_regex; + unsigned int worker_type = (is_regex) ? AP_PROXY_WORKER_IS_MATCH + : AP_PROXY_WORKER_IS_PREFIX; unsigned int flags = 0; const char *err; @@ -1742,7 +2071,7 @@ static const char * if (is_regex) { return "ProxyPassMatch invalid syntax ('~' usage)."; } - use_regex = 1; + worker_type = AP_PROXY_WORKER_IS_MATCH; continue; } f = word; @@ -1777,15 +2106,39 @@ static const char * "in the form 'key=value'."; } } - else + else { *val++ = '\0'; - apr_table_setn(params, word, val); + } + if (!strcasecmp(word, "mapping")) { + if (!strcasecmp(val, "encoded")) { + flags |= PROXYPASS_MAP_ENCODED; + } + else if (!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) { @@ -1793,7 +2146,7 @@ static const char * dconf->alias_set = 1; new = dconf->alias; if (apr_fnmatch_test(f)) { - use_regex = 1; + worker_type = AP_PROXY_WORKER_IS_MATCH; } } /* if per server, add to the alias array */ @@ -1802,9 +2155,9 @@ static const char * } new->fake = apr_pstrdup(cmd->pool, f); - new->real = apr_pstrdup(cmd->pool, ap_proxy_de_socketfy(cmd->pool, r)); + new->real = apr_pstrdup(cmd->pool, real); new->flags = flags; - if (use_regex) { + 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."; @@ -1828,7 +2181,7 @@ static const char * * cannot be parsed anyway with apr_uri_parse later on in * ap_proxy_define_balancer / ap_proxy_update_balancer */ - if (use_regex) { + if (worker_type & AP_PROXY_WORKER_IS_MATCH) { fake_copy = NULL; } else { @@ -1851,15 +2204,20 @@ static const char * new->balancer = balancer; } else { - proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, ap_proxy_de_socketfy(cmd->pool, r)); int reuse = 0; + proxy_worker *worker = ap_proxy_get_worker_ex(cmd->temp_pool, NULL, + conf, new->real, + worker_type); if (!worker) { - const char *err = ap_proxy_define_worker(cmd->pool, &worker, NULL, conf, r, 0); + const char *err; + err = ap_proxy_define_worker_ex(cmd->pool, &worker, NULL, + conf, r, worker_type); if (err) return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL); PROXY_COPY_CONF_PARAMS(worker, conf); - } else { + } + else { reuse = 1; ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, APLOGNO(01145) "Sharing worker '%s' instead of creating new worker '%s'", @@ -2078,14 +2436,50 @@ static const char * } static const char * - set_proxy_error_override(cmd_parms *parms, void *dconf, int flag) + set_proxy_error_override(cmd_parms *parms, void *dconf, const char *arg) { proxy_dir_conf *conf = dconf; - conf->error_override = flag; - conf->error_override_set = 1; + if (strcasecmp(arg, "Off") == 0) { + conf->error_override = 0; + conf->error_override_set = 1; + } + else if (strcasecmp(arg, "On") == 0) { + conf->error_override = 1; + conf->error_override_set = 1; + } + else if (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"; + + newcode = apr_array_push(conf->error_override_codes); + *newcode = argcode; + + /* 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"; + return NULL; } + static const char * add_proxy_http_headers(cmd_parms *parms, void *dconf, int flag) { @@ -2103,6 +2497,14 @@ static const char * conf->preserve_host_set = 1; return NULL; } +static const char * + forward_100_continue(cmd_parms *parms, void *dconf, int flag) +{ + proxy_dir_conf *conf = dconf; + conf->forward_100_continue = flag; + conf->forward_100_continue_set = 1; + return NULL; +} static const char * set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg) @@ -2279,6 +2681,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) proxy_worker *worker; char *path = cmd->path; char *name = NULL; + const char *real; char *word; apr_table_t *params = apr_table_make(cmd->pool, 5); const apr_array_header_t *arr; @@ -2319,6 +2722,9 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) 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"; + } ap_str_tolower(path); /* lowercase scheme://hostname */ @@ -2331,7 +2737,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) } /* Try to find existing worker */ - worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, ap_proxy_de_socketfy(cmd->temp_pool, name)); + worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, real); if (!worker) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01147) "Defining worker '%s' for balancer '%s'", @@ -2361,7 +2767,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker)); } else { err = set_worker_param(cmd->pool, cmd->server, worker, elts[i].key, - elts[i].val); + elts[i].val); if (err) return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); } @@ -2380,6 +2786,7 @@ static const char * char *word, *val; proxy_balancer *balancer = NULL; proxy_worker *worker = NULL; + unsigned int worker_type = 0; int in_proxy_section = 0; /* XXX: Should this be NOT_IN_DIRECTORY|NOT_IN_FILES? */ const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS); @@ -2396,6 +2803,13 @@ static const char * 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 { @@ -2420,11 +2834,18 @@ static const char * } } else { - worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, ap_proxy_de_socketfy(cmd->temp_pool, name)); + const char *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(cmd->pool, &worker, NULL, - conf, name, 0); + 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); @@ -2478,7 +2899,7 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) char *word, *val; proxy_balancer *balancer = NULL; proxy_worker *worker = NULL; - + unsigned int worker_type = AP_PROXY_WORKER_IS_PREFIX; const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_CONTEXT); proxy_server_conf *sconf = (proxy_server_conf *) ap_get_module_config(cmd->server->module_config, &proxy_module); @@ -2516,6 +2937,7 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) if (!r) { return "Regex could not be compiled"; } + worker_type = AP_PROXY_WORKER_IS_MATCH; } /* initialize our config and fetch it */ @@ -2562,11 +2984,17 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) } } else { - worker = ap_proxy_get_worker(cmd->temp_pool, NULL, sconf, - ap_proxy_de_socketfy(cmd->temp_pool, (char*)conf->p)); + const char *real; + + if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, conf->p))) { + return "<Proxy/ProxyMatch > uses an invalid \"unix:\" URL"; + } + + worker = ap_proxy_get_worker_ex(cmd->temp_pool, NULL, sconf, + real, worker_type); if (!worker) { - err = ap_proxy_define_worker(cmd->pool, &worker, NULL, - sconf, conf->p, 0); + err = ap_proxy_define_worker_ex(cmd->pool, &worker, NULL, sconf, + conf->p, worker_type); if (err) return apr_pstrcat(cmd->temp_pool, thiscmd->name, " ", err, NULL); @@ -2616,9 +3044,9 @@ static const command_rec proxy_cmds[] = "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_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF, + AP_INIT_TAKE23("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF, "a scheme, partial URL or '*' and a proxy server"), - AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF, + 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), @@ -2647,7 +3075,7 @@ static const command_rec proxy_cmds[] = "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_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF|ACCESS_CONF, + 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"), @@ -2676,14 +3104,15 @@ static const command_rec proxy_cmds[] = "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} }; static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL; static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL; static APR_OPTIONAL_FN_TYPE(ssl_engine_set) *proxy_ssl_engine = NULL; -static APR_OPTIONAL_FN_TYPE(ssl_is_https) *proxy_is_https = NULL; -static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *proxy_ssl_val = NULL; PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c) { @@ -2691,20 +3120,15 @@ PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c) * if c == NULL just check if the optional function was imported * else run the optional function so ssl filters are inserted */ - if (proxy_ssl_enable) { - return c ? proxy_ssl_enable(c) : 1; + if (c == NULL) { + return ap_ssl_has_outgoing_handlers(); } - - return 0; + return ap_ssl_bind_outgoing(c, NULL, 1) == OK; } PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c) { - if (proxy_ssl_disable) { - return proxy_ssl_disable(c); - } - - return 0; + return ap_ssl_bind_outgoing(c, NULL, 0) == OK; } PROXY_DECLARE(int) ap_proxy_ssl_engine(conn_rec *c, @@ -2715,41 +3139,22 @@ PROXY_DECLARE(int) ap_proxy_ssl_engine(conn_rec *c, * if c == NULL just check if the optional function was imported * else run the optional function so ssl filters are inserted */ - if (proxy_ssl_engine) { - return c ? proxy_ssl_engine(c, per_dir_config, 1, enable) : 1; + if (c == NULL) { + return ap_ssl_has_outgoing_handlers(); } - - if (!per_dir_config) { - if (enable) { - return ap_proxy_ssl_enable(c); - } - else { - return ap_proxy_ssl_disable(c); - } - } - - return 0; + return ap_ssl_bind_outgoing(c, per_dir_config, enable) == OK; } PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c) { - if (proxy_is_https) { - return proxy_is_https(c); - } - else - return 0; + return ap_ssl_conn_is_ssl(c); } PROXY_DECLARE(const char *) ap_proxy_ssl_val(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *var) { - if (proxy_ssl_val) { - /* XXX Perhaps the casting useless */ - return (const char *)proxy_ssl_val(p, s, c, r, (char *)var); - } - else - return NULL; + return ap_ssl_var_lookup(p, s, c, r, var); } static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog, @@ -2767,8 +3172,6 @@ static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog, proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable); proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable); proxy_ssl_engine = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_set); - proxy_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); - proxy_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); ap_proxy_strmatch_path = apr_strmatch_precompile(pconf, "path=", 0); ap_proxy_strmatch_domain = apr_strmatch_precompile(pconf, "domain=", 0); @@ -2867,7 +3270,7 @@ static int proxy_status_hook(request_rec *r, int flags) } else { ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Name: %s\n", - i, n, (*worker)->s->name); + 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: %" @@ -2939,45 +3342,49 @@ static void child_init(apr_pool_t *p, server_rec *s) */ worker = (proxy_worker *)conf->workers->elts; for (i = 0; i < conf->workers->nelts; i++, worker++) { - ap_proxy_initialize_worker(worker, s, conf->pool); + ap_proxy_initialize_worker(worker, s, p); } /* Create and initialize forward worker if defined */ if (conf->req_set && conf->req) { proxy_worker *forward; - ap_proxy_define_worker(p, &forward, NULL, NULL, "http://www.apache.org", 0); + ap_proxy_define_worker(conf->pool, &forward, NULL, NULL, + "http://www.apache.org", 0); conf->forward = forward; PROXY_STRNCPY(conf->forward->s->name, "proxy:forward"); + PROXY_STRNCPY(conf->forward->s->name_ex, "proxy:forward"); PROXY_STRNCPY(conf->forward->s->hostname, "*"); /* for compatibility */ PROXY_STRNCPY(conf->forward->s->hostname_ex, "*"); PROXY_STRNCPY(conf->forward->s->scheme, "*"); conf->forward->hash.def = conf->forward->s->hash.def = - ap_proxy_hashfunc(conf->forward->s->name, PROXY_HASHFUNC_DEFAULT); + ap_proxy_hashfunc(conf->forward->s->name_ex, PROXY_HASHFUNC_DEFAULT); conf->forward->hash.fnv = conf->forward->s->hash.fnv = - ap_proxy_hashfunc(conf->forward->s->name, PROXY_HASHFUNC_FNV); + ap_proxy_hashfunc(conf->forward->s->name_ex, PROXY_HASHFUNC_FNV); /* Do not disable worker in case of errors */ conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS; /* Mark as the "generic" worker */ conf->forward->s->status |= PROXY_WORKER_GENERIC; - ap_proxy_initialize_worker(conf->forward, s, conf->pool); + ap_proxy_initialize_worker(conf->forward, s, p); /* Disable address cache for generic forward worker */ conf->forward->s->is_address_reusable = 0; } if (!reverse) { - ap_proxy_define_worker(p, &reverse, NULL, NULL, "http://www.apache.org", 0); + ap_proxy_define_worker(conf->pool, &reverse, NULL, NULL, + "http://www.apache.org", 0); PROXY_STRNCPY(reverse->s->name, "proxy:reverse"); + PROXY_STRNCPY(reverse->s->name_ex, "proxy:reverse"); PROXY_STRNCPY(reverse->s->hostname, "*"); /* for compatibility */ PROXY_STRNCPY(reverse->s->hostname_ex, "*"); PROXY_STRNCPY(reverse->s->scheme, "*"); reverse->hash.def = reverse->s->hash.def = - ap_proxy_hashfunc(reverse->s->name, PROXY_HASHFUNC_DEFAULT); + ap_proxy_hashfunc(reverse->s->name_ex, PROXY_HASHFUNC_DEFAULT); reverse->hash.fnv = reverse->s->hash.fnv = - ap_proxy_hashfunc(reverse->s->name, PROXY_HASHFUNC_FNV); + ap_proxy_hashfunc(reverse->s->name_ex, PROXY_HASHFUNC_FNV); /* Do not disable worker in case of errors */ reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS; /* Mark as the "generic" worker */ reverse->s->status |= PROXY_WORKER_GENERIC; conf->reverse = reverse; - ap_proxy_initialize_worker(conf->reverse, s, conf->pool); + ap_proxy_initialize_worker(conf->reverse, s, p); /* Disable address cache for generic reverse worker */ reverse->s->is_address_reusable = 0; } @@ -3003,7 +3410,7 @@ static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog, APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL, APR_HOOK_MIDDLE); - /* Reset workers count on gracefull restart */ + /* Reset workers count on graceful restart */ proxy_lb_workers = 0; set_worker_hc_param_f = APR_RETRIEVE_OPTIONAL_FN(set_worker_hc_param); return OK; @@ -3023,7 +3430,10 @@ static void register_hooks(apr_pool_t *p) /* handler */ ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST); /* filename-to-URI translation */ - ap_hook_translate_name(proxy_trans, aszSucc, NULL, APR_HOOK_FIRST); + ap_hook_pre_translate_name(proxy_pre_translate_name, NULL, NULL, + APR_HOOK_MIDDLE); + ap_hook_translate_name(proxy_translate_name, aszSucc, NULL, + APR_HOOK_FIRST); /* walk <Proxy > entries and suppress default TRACE behavior */ ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST); /* fixups */ @@ -3058,6 +3468,7 @@ APR_HOOK_STRUCT( APR_HOOK_LINK(pre_request) APR_HOOK_LINK(post_request) APR_HOOK_LINK(request_status) + APR_HOOK_LINK(check_trans) ) APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, @@ -3066,6 +3477,9 @@ APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, char *url, const char *proxyhost, apr_port_t proxyport),(r,worker,conf, url,proxyhost,proxyport),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, check_trans, + (request_rec *r, const char *url), + (r, url), DECLINED) APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler, (request_rec *r, char *url),(r, url),DECLINED) |