From: Graham Leggett Date: Fri, 22 Oct 2010 00:13:45 +0000 (+0000) Subject: mod_proxy: Optimise ProxyPass within a Location so that it is stored X-Git-Tag: 2.3.9~243 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=13fec746f9d2eda64afb61f97f443fd82e164ddb;p=apache mod_proxy: Optimise ProxyPass within a Location so that it is stored per-directory, and chosen during the location walk. Make ProxyPass work correctly from within a LocationMatch. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1026184 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 1ed0f63c83..c61a187804 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,10 @@ Changes with Apache 2.3.9 Fix a denial of service attack against mod_reqtimeout. [Stefan Fritsch] + *) mod_proxy: Optimise ProxyPass within a Location so that it is stored + per-directory, and chosen during the location walk. Make ProxyPass + work correctly from within a LocationMatch. [Graham Leggett] + *) core: Fix segfault if per-module LogLevel is on virtual host scope. PR 50117. [Stefan Fritsch] diff --git a/docs/manual/mod/mod_proxy.xml b/docs/manual/mod/mod_proxy.xml index 18539f5323..2f276f1b4a 100644 --- a/docs/manual/mod/mod_proxy.xml +++ b/docs/manual/mod/mod_proxy.xml @@ -714,13 +714,24 @@ expressions then

- ProxyPass /mirror/foo/ http://backend.example.com/ + <Location /mirror/foo/>
+ + ProxyPass http://backend.example.com/
+
+ </Location>

will cause a local request for http://example.com/mirror/foo/bar to be internally converted into a proxy request to http://backend.example.com/bar.

+

The following alternative syntax is possible, however carries a + performance penalty when present in large numbers:

+ + + ProxyPass /mirror/foo/ http://backend.example.com/ + +

If the first argument ends with a trailing /, the second argument should also end with a trailing / and vice @@ -732,6 +743,19 @@ expressions

The ! directive is useful in situations where you don't want to reverse-proxy a subdirectory, e.g.

+ + <Location /mirror/foo/>
+ + ProxyPass http://backend.example.com/
+
+ </Location>
+ <Location /mirror/foo/i>
+ + ProxyPass !
+
+ </Location>
+
+ ProxyPass /mirror/foo/i !
ProxyPass /mirror/foo http://backend.example.com @@ -749,7 +773,10 @@ expressions ProxyPass rules starting with the longest URLs first. Otherwise later rules for longer URLS will be hidden by any earlier rule which uses a leading substring of the URL. Note that - there is some relation with worker sharing.

+ there is some relation with worker sharing. In contrast, only one + ProxyPass directive can be placed + in a Location block, and the most + specific location will take precedence.

For the same reasons exclusions must come before the general ProxyPass directives.

diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index a2e99b339f..ac89447365 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -559,21 +559,104 @@ static apr_array_header_t *proxy_vars(request_rec *r, } return ret; } -static int proxy_trans(request_rec *r) + +static int proxy_trans_match(request_rec *r, struct proxy_alias *ent, + proxy_dir_conf *dconf) { - void *sconf = r->server->module_config; - proxy_server_conf *conf = - (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); - int i, len; - struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts; - proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, - &proxy_module); + int len; const char *fake; const char *real; ap_regmatch_t regm[AP_MAX_REG_MATCH]; ap_regmatch_t reg1[AP_MAX_REG_MATCH]; char *found = NULL; int mismatch = 0; + unsigned int nocanon = ent->flags & PROXYPASS_NOCANON; + const char *use_uri = nocanon ? r->unparsed_uri : r->uri; + + if ((dconf->interpolate_env == 1) && (ent->flags & PROXYPASS_INTERPOLATE)) { + fake = proxy_interpolate(r, ent->fake); + real = proxy_interpolate(r, ent->real); + } + else { + fake = ent->fake; + real = ent->real; + } + if (ent->regex) { + if (!ap_regexec(ent->regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) { + if ((real[0] == '!') && (real[1] == '\0')) { + 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); + /* Note: The strcmp() below catches cases where there + * was no regex substitution. This is so cases like: + * + * ProxyPassMatch \.gif balancer://foo + * + * will work "as expected". The upshot is that the 2 + * directives below act the exact same way (ie: $1 is implied): + * + * ProxyPassMatch ^(/.*\.gif)$ balancer://foo + * ProxyPassMatch ^(/.*\.gif)$ balancer://foo$1 + * + * which may be confusing. + */ + if (found && strcmp(found, real)) { + found = apr_pstrcat(r->pool, "proxy:", found, NULL); + } + else { + found = apr_pstrcat(r->pool, "proxy:", real, use_uri, NULL); + } + } + } + else { + len = alias_match(r->uri, fake); + + if (len != 0) { + if ((real[0] == '!') && (real[1] == '\0')) { + return DECLINED; + } + if (nocanon && len != alias_match(r->unparsed_uri, ent->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. Safe fallback is to ignore nocanon. + */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon"); + } + + if (found) { + 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"); + } + return OK; + } + + return DONE; +} + +static int proxy_trans(request_rec *r) +{ + int i; + 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 @@ -587,86 +670,27 @@ static int proxy_trans(request_rec *r) * an issue because this is a hybrid proxy/origin server. */ - for (i = 0; i < conf->aliases->nelts; i++) { - unsigned int nocanon = ent[i].flags & PROXYPASS_NOCANON; - const char *use_uri = nocanon ? r->unparsed_uri : r->uri; - if ((dconf->interpolate_env == 1) - && (ent[i].flags & PROXYPASS_INTERPOLATE)) { - fake = proxy_interpolate(r, ent[i].fake); - real = proxy_interpolate(r, ent[i].real); - } - else { - fake = ent[i].fake; - real = ent[i].real; - } - if (ent[i].regex) { - if (!ap_regexec(ent[i].regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) { - if ((real[0] == '!') && (real[1] == '\0')) { - return DECLINED; - } - /* test that we haven't reduced the URI */ - if (nocanon && ap_regexec(ent[i].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); - /* Note: The strcmp() below catches cases where there - * was no regex substitution. This is so cases like: - * - * ProxyPassMatch \.gif balancer://foo - * - * will work "as expected". The upshot is that the 2 - * directives below act the exact same way (ie: $1 is implied): - * - * ProxyPassMatch ^(/.*\.gif)$ balancer://foo - * ProxyPassMatch ^(/.*\.gif)$ balancer://foo$1 - * - * which may be confusing. - */ - if (found && strcmp(found, real)) { - found = apr_pstrcat(r->pool, "proxy:", found, NULL); - } - else { - found = apr_pstrcat(r->pool, "proxy:", real, - use_uri, NULL); - } - } - } - else { - len = alias_match(r->uri, fake); + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); - if (len != 0) { - if ((real[0] == '!') && (real[1] == '\0')) { - return DECLINED; - } - if (nocanon - && len != alias_match(r->unparsed_uri, ent[i].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. Safe fallback is to ignore nocanon. - */ - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon"); + /* short way - this location is reverse proxied? */ + if (dconf->alias) { + int rv = proxy_trans_match(r, dconf->alias, dconf); + if (DONE != rv) { + return rv; } + } - if (found) { - 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"); + 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 = proxy_trans_match(r, &ent[i], dconf); + if (DONE != rv) { + return rv; } - return OK; } } return DECLINED; @@ -1216,6 +1240,8 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) new->error_override = (add->error_override_set == 0) ? base->error_override : add->error_override; new->error_override_set = add->error_override_set || base->error_override_set; + new->alias = (add->alias_set == 0) ? base->alias : add->alias; + new->alias_set = add->alias_set || base->alias_set; return new; } @@ -1297,6 +1323,7 @@ static const char * static const char * add_pass(cmd_parms *cmd, void *dummy, const char *arg, int is_regex) { + proxy_dir_conf *dconf = (proxy_dir_conf *)dummy; server_rec *s = cmd->server; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); @@ -1356,10 +1383,27 @@ static const char * } }; - if (r == NULL) + if (r == NULL) { return "ProxyPass|ProxyPassMatch needs a path when not defined in a location"; + } + + /* if per directory, save away the single alias */ + if (cmd->path) { + if (dconf->alias_set) { + return "ProxyPass may be defined just once within a specific location block"; + } + dconf->alias = apr_pcalloc(cmd->pool, sizeof(struct proxy_alias)); + dconf->alias_set = 1; + new = dconf->alias; + if (apr_fnmatch_test(f)) { + use_regex = 1; + } + } + /* if per server, add to the alias array */ + else { + new = apr_array_push(conf->aliases); + } - new = apr_array_push(conf->aliases); new->fake = apr_pstrdup(cmd->pool, f); new->real = apr_pstrdup(cmd->pool, r); new->flags = flags; diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index b8c4a94f1f..021ccec8f9 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -186,6 +186,7 @@ typedef struct { const apr_strmatch_pattern* cookie_domain_str; signed char p_is_fnmatch; /* Is the path an fnmatch candidate? */ signed char interpolate_env; + struct proxy_alias *alias; /** * the following setting masks the error page @@ -199,6 +200,7 @@ typedef struct { int preserve_host:1; int preserve_host_set:1; int error_override_set:1; + int alias_set:1; } proxy_dir_conf; /* if we interpolate env vars per-request, we'll need a per-request