]> granicus.if.org Git - apache/commitdiff
mod_proxy: Optimise ProxyPass within a Location so that it is stored
authorGraham Leggett <minfrin@apache.org>
Fri, 22 Oct 2010 00:13:45 +0000 (00:13 +0000)
committerGraham Leggett <minfrin@apache.org>
Fri, 22 Oct 2010 00:13:45 +0000 (00:13 +0000)
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

CHANGES
docs/manual/mod/mod_proxy.xml
modules/proxy/mod_proxy.c
modules/proxy/mod_proxy.h

diff --git a/CHANGES b/CHANGES
index 1ed0f63c8383549369554c800753fc07eb569700..c61a1878045343b7c188762bdb9091df83dc265f 100644 (file)
--- 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]
 
index 18539f5323a86c75292c183967bc79492842e359..2f276f1b4a518036fdf7b714f164a2f504dd2c58 100644 (file)
@@ -714,13 +714,24 @@ expressions</description>
     then</p>
 
     <example>
-      ProxyPass /mirror/foo/ http://backend.example.com/
+        &lt;Location /mirror/foo/&gt;<br />
+        <indent>
+          ProxyPass http://backend.example.com/<br />
+        </indent>
+        &lt;/Location&gt;<br />
     </example>
 
     <p>will cause a local request for
     <code>http://example.com/mirror/foo/bar</code> to be internally converted
     into a proxy request to <code>http://backend.example.com/bar</code>.</p>
 
+    <p>The following alternative syntax is possible, however carries a
+    performance penalty when present in large numbers:</p>
+
+    <example>
+        ProxyPass /mirror/foo/ http://backend.example.com/
+    </example>
+    
     <note type="warning">
     <p>If the first argument ends with a trailing <strong>/</strong>, the second
        argument should also end with a trailing <strong>/</strong> and vice
@@ -732,6 +743,19 @@ expressions</description>
     <p>The <code>!</code> directive is useful in situations where you don't want
     to reverse-proxy a subdirectory, <em>e.g.</em></p>
 
+    <example>
+        &lt;Location /mirror/foo/&gt;<br />
+        <indent>
+          ProxyPass http://backend.example.com/<br />
+        </indent>
+        &lt;/Location&gt;<br />
+        &lt;Location /mirror/foo/i&gt;<br />
+        <indent>
+            ProxyPass !<br />
+        </indent>
+        &lt;/Location&gt;<br />
+    </example>
+
     <example>
       ProxyPass /mirror/foo/i !<br />
       ProxyPass /mirror/foo http://backend.example.com
@@ -749,7 +773,10 @@ expressions</description>
       <directive module="mod_proxy">ProxyPass</directive> 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.</p>
+      there is some relation with worker sharing. In contrast, only one
+      <directive module="mod_proxy">ProxyPass</directive> directive can be placed
+      in a <directive module="core">Location</directive> block, and the most
+      specific location will take precedence.</p>
 
       <p>For the same reasons exclusions must come <em>before</em> the
       general <directive>ProxyPass</directive> directives.</p>
index a2e99b339f33c55462b121b41bd74be90a9ddd25..ac89447365b02b8e212cf4dccd241f5c6c41f355 100644 (file)
@@ -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;
index b8c4a94f1fe566da5b474cfc671727fc9924f5b2..021ccec8f9314f2ab2dcf546e0d8082ae680ae88 100644 (file)
@@ -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