Helo, I revive this thread since headers in modfied by mod_proxy seems wrong to me, I have to take that into account when, say, analysing access logs (received X-Forwarded-* or Via headers vs the ones added by mod_proxy, see also PR 45387), or as said in the title, which is even worse when backend (recoverable) failures occur in load-balancing (same headers added multiple times).
I ended up with the following patch that copies r->headers_in before they are modified and restore them after they have been forwarded (mod_proxy_http and mod_proxy_wstunnel only seem to be concerned, but I may be missing something). Feedbacks welcome, if no one objects I'll commit it to trunk. Regards, Yann. Index: modules/proxy/mod_proxy_wstunnel.c =================================================================== --- modules/proxy/mod_proxy_wstunnel.c (revision 1584652) +++ modules/proxy/mod_proxy_wstunnel.c (working copy) @@ -320,15 +320,26 @@ static int ap_proxy_wstunnel_request(apr_pool_t *p apr_socket_t *client_socket = ap_get_conn_socket(c); ws_baton_t *baton = apr_pcalloc(r->pool, sizeof(ws_baton_t)); apr_socket_t *sockets[3] = {NULL, NULL, NULL}; + apr_table_t *saved_headers_in; int status; header_brigade = apr_brigade_create(p, backconn->bucket_alloc); ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "sending request"); + /* + * Save the original headers in before ap_proxy_create_hdrbrgd() and + * restore them after, since it will apply proxy purpose only modifications + * (eg. cleanup hop-by-hop headers, add Via or X-Forwarded-*...), whereas + * the original headers may be needed later to prepare the correct response + * or logging. + */ + saved_headers_in = r->headers_in; + r->headers_in = apr_table_copy(r->pool, saved_headers_in); rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, conn, worker, conf, uri, url, server_portstr, &old_cl_val, &old_te_val); + r->headers_in = saved_headers_in; if (rv != OK) { return rv; } Index: modules/proxy/mod_proxy_http.c =================================================================== --- modules/proxy/mod_proxy_http.c (revision 1584652) +++ modules/proxy/mod_proxy_http.c (working copy) @@ -717,24 +717,31 @@ int ap_proxy_http_request(apr_pool_t *p, request_r apr_off_t bytes; int force10, rv; conn_rec *origin = p_conn->connection; + apr_table_t *saved_headers_in; - if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { - if (r->expecting_100) { - return HTTP_EXPECTATION_FAILED; - } - force10 = 1; - } else { - force10 = 0; - } + /* + * Save the original headers in here and restore them when leaving, since + * we will apply proxy purpose only modifications (eg. cleanup hop-by-hop + * headers, add Via or X-Forwarded-* or Expect...), whereas the original + * headers may be needed later to prepare the correct response or logging. + */ + saved_headers_in = r->headers_in; + r->headers_in = apr_table_copy(r->pool, saved_headers_in); header_brigade = apr_brigade_create(p, bucket_alloc); rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, p_conn, worker, conf, uri, url, server_portstr, &old_cl_val, &old_te_val); if (rv != OK) { - return rv; + goto cleanup; } + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { + force10 = 1; + } else { + force10 = 0; + } + /* We have headers, let's figure out our request body... */ input_brigade = apr_brigade_create(p, bucket_alloc); @@ -775,7 +782,8 @@ int ap_proxy_http_request(apr_pool_t *p, request_r if (old_te_val && strcasecmp(old_te_val, "chunked") != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01093) "%s Transfer-Encoding is not supported", old_te_val); - return HTTP_INTERNAL_SERVER_ERROR; + rv = HTTP_INTERNAL_SERVER_ERROR; + goto cleanup; } if (old_cl_val && old_te_val) { @@ -808,7 +816,8 @@ int ap_proxy_http_request(apr_pool_t *p, request_r " from %s (%s)", p_conn->addr, p_conn->hostname ? p_conn->hostname: "", c->client_ip, c->remote_host ? c->remote_host: ""); - return HTTP_BAD_REQUEST; + rv = HTTP_BAD_REQUEST; + goto cleanup; } apr_brigade_length(temp_brigade, 1, &bytes); @@ -830,7 +839,8 @@ int ap_proxy_http_request(apr_pool_t *p, request_r " to %pI (%s) from %s (%s)", p_conn->addr, p_conn->hostname ? p_conn->hostname: "", c->client_ip, c->remote_host ? c->remote_host: ""); - return HTTP_INTERNAL_SERVER_ERROR; + rv = HTTP_INTERNAL_SERVER_ERROR; + goto cleanup; } /* Ensure we don't hit a wall where we have a buffer too small @@ -969,10 +979,13 @@ skip_body: "pass request body failed to %pI (%s) from %s (%s)", p_conn->addr, p_conn->hostname ? p_conn->hostname: "", c->client_ip, c->remote_host ? c->remote_host: ""); - return rv; } - - return OK; +cleanup: + /* Restore the original headers in (see comment above), + * we won't modify them anymore. + */ + r->headers_in = saved_headers_in; + return rv; } /* Index: modules/proxy/proxy_util.c =================================================================== --- modules/proxy/proxy_util.c (revision 1584652) +++ modules/proxy/proxy_util.c (working copy) @@ -3203,7 +3203,6 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_poo char *buf; const apr_array_header_t *headers_in_array; const apr_table_entry_t *headers_in; - apr_table_t *headers_in_copy; apr_bucket *e; int do_100_continue; conn_rec *origin = p_conn->connection; @@ -3363,21 +3362,10 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_poo } proxy_run_fixups(r); - /* - * Make a copy of the headers_in table before clearing the connection - * headers as we need the connection headers later in the http output - * filter to prepare the correct response headers. - * - * Note: We need to take r->pool for apr_table_copy as the key / value - * pairs in r->headers_in have been created out of r->pool and - * p might be (and actually is) a longer living pool. - * This would trigger the bad pool ancestry abort in apr_table_copy if - * apr is compiled with APR_POOL_DEBUG. - */ - headers_in_copy = apr_table_copy(r->pool, r->headers_in); - ap_proxy_clear_connection(r, headers_in_copy); + ap_proxy_clear_connection(r, r->headers_in); + /* send request headers */ - headers_in_array = apr_table_elts(headers_in_copy); + headers_in_array = apr_table_elts(r->headers_in); headers_in = (const apr_table_entry_t *) headers_in_array->elts; for (counter = 0; counter < headers_in_array->nelts; counter++) { if (headers_in[counter].key == NULL [EOS]