then</p>
<example>
- ProxyPass /mirror/foo/ http://backend.example.com/
+ <Location /mirror/foo/><br />
+ <indent>
+ ProxyPass http://backend.example.com/<br />
+ </indent>
+ </Location><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
<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>
+ <Location /mirror/foo/><br />
+ <indent>
+ ProxyPass http://backend.example.com/<br />
+ </indent>
+ </Location><br />
+ <Location /mirror/foo/i><br />
+ <indent>
+ ProxyPass !<br />
+ </indent>
+ </Location><br />
+ </example>
+
<example>
ProxyPass /mirror/foo/i !<br />
ProxyPass /mirror/foo http://backend.example.com
<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>
}
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
* 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;
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;
}
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);
}
};
- 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;