Index: modules/proxy/mod_proxy.c =================================================================== --- modules/proxy/mod_proxy.c (revision 372568) +++ modules/proxy/mod_proxy.c (working copy) @@ -476,6 +476,10 @@ ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote)); ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->cookie_path_str = apr_strmatch_precompile(p, "path=", 0) ; + ps->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0) ; ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry)); ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry)); ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int)); @@ -511,6 +515,10 @@ ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy); ps->aliases = apr_array_append(p, base->aliases, overrides->aliases); ps->raliases = apr_array_append(p, base->raliases, overrides->raliases); + ps->cookie_paths = apr_array_append(p, base->cookie_paths, overrides->cookie_paths); + ps->cookie_domains = apr_array_append(p, base->cookie_domains, overrides->cookie_domains); + ps->cookie_path_str = base->cookie_path_str; + ps->cookie_domain_str = base->cookie_domain_str; ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies); ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn); ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports); @@ -677,6 +685,41 @@ return NULL; } +static const char * +cookie_path(cmd_parms *cmd, void *dummy, const char *f, const char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf; + struct proxy_alias *new; + + conf = (proxy_server_conf *)ap_get_module_config(s->module_config, + &proxy_module); + + new = apr_array_push(conf->cookie_paths); + new->fake = f; + new->real = r; + new->confpath = cmd->path; + + return NULL; +} + +static const char * +cookie_domain(cmd_parms *cmd, void *dummy, const char *f, const char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf; + struct proxy_alias *new; + + conf = (proxy_server_conf *)ap_get_module_config(s->module_config, &proxy_module); + + new = apr_array_push(conf->cookie_domains); + new->fake = f; + new->real = r; + new->confpath = cmd->path; + + return NULL; +} + static const char * set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg) { @@ -1042,6 +1085,10 @@ "a virtual path and a URL"), AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF, "a virtual path and a URL for reverse proxy behaviour"), + AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL, RSRC_CONF|ACCESS_CONF, + "Path rewrite rule for proxying cookies"), + AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL, RSRC_CONF|ACCESS_CONF, + "Domain rewrite rule for proxying cookies"), AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, "A list of names, hosts or domains to which the proxy will not connect"), AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, Index: modules/proxy/proxy_http.c =================================================================== --- modules/proxy/proxy_http.c (revision 372568) +++ modules/proxy/proxy_http.c (working copy) @@ -131,6 +131,134 @@ return url; } +/* cookies are a bit trickier to match: we've got two substrings to worry + * about, and we can't just find them with strstr 'cos of case. Regexp + * matching would be an easy fix, but for better consistency with all the + * other matches we'll refrain and use apr_strmatch to find path=/domain= + * and stick to plain strings for the config values. + */ +static const char * proxy_cookie_reverse_map(request_rec *r, + proxy_server_conf *conf, + const char *str) +{ + struct proxy_alias *ent; + size_t len = strlen(str); + const char *newpath = NULL; + const char *newdomain = NULL; + const char *pathp; + const char *domainp; + const char *pathe; + const char *domaine; + size_t l1, l2, i, poffs, doffs; + int ddiff = 0; + int pdiff = 0; + char *ret; + + /* + * find the match and replacement, but save replacing until we've done + * both path and domain so we know the new strlen + */ + if (pathp = apr_strmatch(conf->cookie_path_str, str, len) , pathp) { + pathp += 5; + poffs = pathp - str; + pathe = strchr(pathp, ';'); + l1 = pathe ? (pathe-pathp) : strlen(pathp); + pathe = pathp + l1; + ent = (struct proxy_alias *)conf->cookie_paths->elts; + for (i = 0; i < conf->cookie_paths->nelts; i++) { + l2 = strlen(ent[i].fake); + if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) { + if (ent[i].confpath) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: matched %s, location %s, uri %s %d", + ent[i].fake, ent[i].confpath, r->uri); + if (strncmp(ent[i].confpath, r->uri, + strlen(ent[i].confpath)) == 0) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: matched and uri, location match %s, location %s, uri %s", + ent[i].fake, ent[i].confpath, r->uri); + newpath = ent[i].real; + pdiff = strlen(newpath) - l1; + break; + } + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: matched %s no location", ent[i].fake); + newpath = ent[i].real; + pdiff = strlen(newpath) - l1; + break; + } + } + } + } + if (domainp = apr_strmatch(conf->cookie_domain_str, str, len), domainp) { + domainp += 7; + doffs = domainp - str; + domaine = strchr(domainp, ';'); + l1 = domaine ? (domaine-domainp) : strlen(domainp); + domaine = domainp + l1; + ent = (struct proxy_alias *)conf->cookie_domains->elts; + for (i = 0; i < conf->cookie_domains->nelts; i++) { + l2 = strlen(ent[i].fake); + if (l1 >= l2 && strncmp(ent[i].fake, domainp, l2) == 0) { + if (ent[i].confpath) { + if (strncmp(ent[i].confpath, r->uri, + strlen(ent[i].confpath)) == 0) { + newdomain = ent[i].real; + ddiff = strlen(newdomain) - l1; + break; + } + } + else { + newdomain = ent[i].real; + ddiff = strlen(newdomain) - l1; + break; + } + } + } + } + if (newpath) { + ret = apr_palloc(r->pool, len+pdiff+ddiff+1); + l1 = strlen(newpath); + if (newdomain) { + l2 = strlen(newdomain); + if (doffs > poffs) { + memcpy(ret, str, poffs); + memcpy(ret+poffs, newpath, l1); + memcpy(ret+poffs+l1, pathe, domainp-pathe); + memcpy(ret+doffs+pdiff, newdomain, l2); + strcpy(ret+doffs+pdiff+l2, domaine); + } + else { + memcpy(ret, str, doffs); + memcpy(ret+doffs, newdomain, l2); + memcpy(ret+doffs+l2, domaine, pathp-domaine); + memcpy(ret+poffs+ddiff, newpath, l1); + strcpy(ret+poffs+ddiff+l1, pathe); + } + } + else { + memcpy(ret, str, poffs); + memcpy(ret+poffs, newpath, l1); + strcpy(ret+poffs+l1, pathe); + } + } + else { + if (newdomain) { + ret = apr_palloc(r->pool, len+pdiff+ddiff+1); + l2 = strlen(newdomain); + memcpy(ret, str, doffs); + memcpy(ret+doffs, newdomain, l2); + strcpy(ret+doffs+l2, domaine); + } + else { + ret = (char*) str; /* no change */ + } + } + return ret; +} + /* Clear all connection-based headers from the incoming headers table */ static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers) { @@ -1274,6 +1402,46 @@ return APR_SUCCESS; } + +typedef struct { + request_rec* r; + proxy_server_conf* c; +} hdr_rec; + +static int process_proxy_header(void* CTX, const char* key, const char* value) +{ + hdr_rec* ctx = CTX ; + static const char* date_hdrs[] = {"Date", "Expires", "Last-Modified", NULL}; + static const struct { + const char* name; + const char* (*func)(request_rec*, proxy_server_conf*, const char*); + } transform_hdrs[] = { + { "Location", ap_proxy_location_reverse_map }, + { "Content-Location", ap_proxy_location_reverse_map }, + { "URI", ap_proxy_location_reverse_map }, + { "Set-Cookie", proxy_cookie_reverse_map }, + { NULL, NULL } + }; + int i ; + for (i = 0 ; date_hdrs[i] ; ++i) { + if (!strcasecmp(date_hdrs[i], key)) { + apr_table_add(ctx->r->headers_out, key, + ap_proxy_date_canon(ctx->r->pool, value)); + return 1; + } + } + for (i = 0 ; transform_hdrs[i].name ; ++i) { + if (!strcasecmp(transform_hdrs[i].name, key)) { + apr_table_add(ctx->r->headers_out, key, + (*transform_hdrs[i].func)(ctx->r, ctx->c, value)); + return 2; + } + } + apr_table_add(ctx->r->headers_out, key, value); + return 3; +} + + static apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, proxy_http_conn_t *p_conn, @@ -1282,6 +1450,8 @@ proxy_server_conf *conf, apr_bucket_brigade *bb, char *server_portstr) { + + apr_table_t* backend_headers; conn_rec *c = r->connection; char buffer[HUGE_STRING_LEN]; const char *buf; @@ -1294,6 +1464,9 @@ * in the case that the origin told us * to HTTP_CONTINUE */ + hdr_rec hdrctx; + hdrctx.r = r; + hdrctx.c = conf; /* Get response from the remote server, and pass it up the * filter chain @@ -1364,9 +1537,9 @@ /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/ /* Also, take care with headers with multiple occurences. */ - r->headers_out = ap_proxy_read_headers(r, rp, buffer, + backend_headers = ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin); - if (r->headers_out == NULL) { + if (backend_headers == NULL) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, "proxy: bad HTTP/%d.%d header " "returned by %s (%s)", major, minor, r->uri, @@ -1377,33 +1550,33 @@ * are in a bad position here.. so force everything we send out * to have nothing to do with the incoming packet */ - r->headers_out = apr_table_make(r->pool,1); + backend_headers = apr_table_make(r->pool,1); r->status = HTTP_BAD_GATEWAY; r->status_line = "bad gateway"; return r->status; } /* can't have both Content-Length and Transfer-Encoding */ - if (apr_table_get(r->headers_out, "Transfer-Encoding") - && apr_table_get(r->headers_out, "Content-Length")) { + if (apr_table_get(backend_headers, "Transfer-Encoding") + && apr_table_get(backend_headers, "Content-Length")) { /* 2616 section 4.4, point 3: "if both Transfer-Encoding * and Content-Length are received, the latter MUST be * ignored"; so unset it here to prevent any confusion * later. */ - apr_table_unset(r->headers_out, "Content-Length"); + apr_table_unset(backend_headers, "Content-Length"); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: server %s returned Transfer-Encoding and Content-Length", - p_conn->name); - p_conn->close += 1; + "proxy: server %s returned Transfer-Encoding and Content-Length", + p_conn->name); + p_conn->close += 1; } /* strip connection listed hop-by-hop headers from response */ - p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_out, + p_conn->close += ap_proxy_liststr(apr_table_get(backend_headers, "Connection"), "close"); - ap_proxy_clear_connection(p, r->headers_out); - if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { + ap_proxy_clear_connection(p, backend_headers); + if ((buf = apr_table_get(backend_headers, "Content-Type"))) { ap_set_content_type(r, apr_pstrdup(p, buf)); } ap_proxy_pre_http_request(origin,rp); @@ -1411,7 +1584,7 @@ /* handle Via header in response */ if (conf->viaopt != via_off && conf->viaopt != via_block) { /* create a "Via:" response header entry and merge it */ - apr_table_mergen(r->headers_out, "Via", + apr_table_mergen(backend_headers, "Via", (conf->viaopt == via_full) ? apr_psprintf(p, "%d.%d %s%s (%s)", HTTP_VERSION_MAJOR(r->proto_num), @@ -1432,7 +1605,8 @@ p_conn->close += 1; origin->keepalive = AP_CONN_CLOSE; } - } else { + } + else { /* an http/0.9 response */ backasswards = 1; r->status = 200; @@ -1442,40 +1616,24 @@ if ( r->status != HTTP_CONTINUE ) { received_continue = 0; - } else { + } + else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "proxy: HTTP: received 100 CONTINUE"); } - /* we must accept 3 kinds of date, but generate only 1 kind of date */ - if ((buf = apr_table_get(r->headers_out, "Date")) != NULL) { - apr_table_set(r->headers_out, "Date", - ap_proxy_date_canon(p, buf)); - } - if ((buf = apr_table_get(r->headers_out, "Expires")) != NULL) { - apr_table_set(r->headers_out, "Expires", - ap_proxy_date_canon(p, buf)); - } - if ((buf = apr_table_get(r->headers_out, "Last-Modified")) != NULL) { - apr_table_set(r->headers_out, "Last-Modified", - ap_proxy_date_canon(p, buf)); - } - - /* munge the Location and URI response headers according to - * ProxyPassReverse + /* transcribe backend headers to headers_out, rewritting any that need it. + * The old method based on if( apr_table_get(...) ) fails against + * duplicated headers, so we copy-and-transcribe by the entry instead. */ - if ((buf = apr_table_get(r->headers_out, "Location")) != NULL) { - apr_table_set(r->headers_out, "Location", - ap_proxy_location_reverse_map(r, conf, buf)); - } - if ((buf = apr_table_get(r->headers_out, "Content-Location")) != NULL) { - apr_table_set(r->headers_out, "Content-Location", - ap_proxy_location_reverse_map(r, conf, buf)); - } - if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) { - apr_table_set(r->headers_out, "URI", - ap_proxy_location_reverse_map(r, conf, buf)); - } + if (r->headers_out) { + apr_table_clear(r->headers_out); + } + else { + r->headers_out = apr_table_make(r->pool, 10); + } + apr_table_do(process_proxy_header, &hdrctx, backend_headers, NULL); + apr_table_clear(backend_headers); if ((r->status == 401) && (conf->error_override != 0)) { const char *wa = "WWW-Authenticate"; Index: modules/proxy/mod_proxy.h =================================================================== --- modules/proxy/mod_proxy.h (revision 372568) +++ modules/proxy/mod_proxy.h (working copy) @@ -51,6 +51,7 @@ #include "apr_uri.h" #include "apr_date.h" #include "apr_fnmatch.h" +#include "apr_strmatch.h" #define APR_WANT_STRFUNC #include "apr_want.h" @@ -101,6 +102,7 @@ struct proxy_alias { const char *real; const char *fake; + const char *confpath; }; struct dirconn_entry { @@ -160,6 +162,15 @@ } badopt; /* how to deal with bad headers */ char badopt_set; +/* new stuff on the end maximises binary back-compatibility. + the strmatch_patterns are really a const just to have a + case-independent strstr. + */ + apr_array_header_t *cookie_paths; + apr_array_header_t *cookie_domains; + const apr_strmatch_pattern *cookie_path_str; + const apr_strmatch_pattern *cookie_domain_str; + } proxy_server_conf; typedef struct {