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