From bd1ccf5f0a3c1d1161435d2b44d7e3009efcd13d Mon Sep 17 00:00:00 2001 From: Eric Covener Date: Wed, 11 Apr 2018 19:11:52 +0000 Subject: [PATCH] PR62199: add worker parameter ResponseFieldSize to mod_proxy Submitted By: Hank Ibell Committed By: covener git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1828926 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 4 +++ docs/manual/mod/mod_proxy.xml | 7 ++++ include/ap_mmn.h | 3 +- modules/proxy/mod_proxy.c | 8 +++++ modules/proxy/mod_proxy.h | 2 ++ modules/proxy/mod_proxy_http.c | 60 +++++++++++++++++++++++++++++----- 6 files changed, 75 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 3ce88899e4..4db111242f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.1 + *) mod_proxy_http: Add new worker parameter 'responsefieldsize' to + allow maximum HTTP response header size to be increased past 8192 + bytes. PR62199. [Hank Ibell ] + *) core: Preserve the original HTTP request method in the '%] diff --git a/docs/manual/mod/mod_proxy.xml b/docs/manual/mod/mod_proxy.xml index 418257fa6c..23deb30d66 100644 --- a/docs/manual/mod/mod_proxy.xml +++ b/docs/manual/mod/mod_proxy.xml @@ -1137,6 +1137,13 @@ ProxyPass "/example" "http://backend.example.com" max=20 ttl=120 retry=300 to override the ProxyIOBufferSize for a specific worker. This must be at least 512 or set to 0 for the system default of 8192. + responsefieldsize + 8192 + Adjust the size of the proxy response field buffer. The buffer size + should be at least the size of the largest expected header size from + a proxied response. Setting the value to 0 will use the system + default of 8192 bytes. + keepalive Off

This parameter should be used when you have a firewall between your diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 6393945ba6..4706d833a0 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -570,6 +570,7 @@ * ap_regcomp_default_cflag_by_name * 20171014.7 (2.5.1-dev) Add AP_CORE_DEFAULT macro * 20171014.8 (2.5.1-dev) Add CONN_STATE_NUM to enum conn_state_e + * 20171014.9 (2.5.1-dev) Add response_field_size to proxy_worker_shared */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -577,7 +578,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20171014 #endif -#define MODULE_MAGIC_NUMBER_MINOR 8 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 9 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index aeaea7c89e..ab252a3092 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -329,6 +329,14 @@ static const char *set_worker_param(apr_pool_t *p, (int)sizeof(worker->s->upgrade)); } } + else if (!strcasecmp(key, "responsefieldsize")) { + long s = atol(val); + if (s < 0) { + return "ResponseFieldSize must be greater than 0 bytes, or 0 for system default."; + } + worker->s->response_field_size = (s ? s : HUGE_STRING_LEN); + worker->s->response_field_size_set = 1; + } else { if (set_worker_hc_param_f) { return set_worker_hc_param_f(p, s, worker, key, val, NULL); diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 18127d527d..ae8be929bc 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -459,6 +459,8 @@ typedef struct { char secret[PROXY_WORKER_MAX_SECRET_SIZE]; /* authentication secret (e.g. AJP13) */ char upgrade[PROXY_WORKER_MAX_SCHEME_SIZE];/* upgrade protocol used by mod_proxy_wstunnel */ char hostname_ex[PROXY_RFC1035_HOSTNAME_SIZE]; /* RFC1035 compliant version of the remote backend address */ + apr_size_t response_field_size; /* Size of proxy response buffer in bytes. */ + unsigned int response_field_size_set:1; } proxy_worker_shared; #define ALIGNED_PROXY_WORKER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_worker_shared))) diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index fb95f4380c..115bf714e7 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -28,6 +28,13 @@ static apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r, proxy_conn_rec *backend); +static apr_status_t ap_proxygetline(apr_bucket_brigade *bb, + char *s, + int n, + request_rec *r, + int fold, + int *writen); + /* * Canonicalise http-like URLs. * scheme is the scheme for the URL @@ -1096,6 +1103,8 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr, void *sconf = r->server->module_config; proxy_server_conf *psc; proxy_dir_conf *dconf; + apr_status_t rc; + apr_bucket_brigade *tmp_bb; dconf = ap_get_module_config(r->per_dir_config, &proxy_module); psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); @@ -1110,7 +1119,14 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr, */ ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "Headers received from backend:"); - while ((len = ap_getline(buffer, size, rr, 1)) > 0) { + + tmp_bb = apr_brigade_create(r->pool, c->bucket_alloc); + while (1) { + rc = ap_proxygetline(tmp_bb, buffer, size, rr, 1, &len); + + if (len <= 0) + break; + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "%s", buffer); if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ @@ -1180,12 +1196,24 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr, process_proxy_header(r, dconf, buffer, value); saw_headers = 1; - /* the header was too long; at the least we should skip extra data */ - if (len >= size - 1) { + /* The header could not fit in the provided buffer. */ + if (rc == APR_ENOSPC) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO() + "header size is over the limit allowed by ResponseFieldSize (%d bytes). " + "Bad response header '%s': '%.*s'...", + size, buffer, 80, value); + + /* XXX: We overran the limit we passed to ap_rgetline_core, but if the length + * exceeded the limit by a small amount, it may have already been consumed + * by apr_brigade_split_line called from the core input filter. If that + * happens, this loop will throw away the next full line (header) instead of + * the remainder of the current long header. + */ while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1)) >= MAX_STRING_LEN - 1) { /* soak up the extra data */ } + if (len == 0) /* time to exit the larger loop as well */ break; } @@ -1246,7 +1274,8 @@ int ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, proxy_server_conf *conf, char *server_portstr) { conn_rec *c = r->connection; - char buffer[HUGE_STRING_LEN]; + char *buffer; + char fixed_buffer[HUGE_STRING_LEN]; const char *buf; char keepchar; apr_bucket *e; @@ -1255,6 +1284,7 @@ int ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, int len, backasswards; int interim_response = 0; /* non-zero whilst interim 1xx responses * are being read. */ + apr_size_t response_field_size = 0; int pread_len = 0; apr_table_t *save_table; int backend_broke = 0; @@ -1279,6 +1309,20 @@ int ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, bb = apr_brigade_create(p, c->bucket_alloc); pass_bb = apr_brigade_create(p, c->bucket_alloc); + /* Only use dynamically sized buffer if user specifies ResponseFieldSize */ + if(backend->worker->s->response_field_size_set) { + response_field_size = backend->worker->s->response_field_size; + + if (response_field_size != HUGE_STRING_LEN) + buffer = apr_pcalloc(p, response_field_size); + else + buffer = fixed_buffer; + } + else { + response_field_size = HUGE_STRING_LEN; + buffer = fixed_buffer; + } + /* Setup for 100-Continue timeout if appropriate */ if (do_100_continue) { apr_socket_timeout_get(backend->sock, &old_timeout); @@ -1308,11 +1352,11 @@ int ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, apr_brigade_cleanup(bb); - rc = ap_proxygetline(backend->tmp_bb, buffer, sizeof(buffer), + rc = ap_proxygetline(backend->tmp_bb, buffer, response_field_size, backend->r, 0, &len); if (len == 0) { /* handle one potential stray CRLF */ - rc = ap_proxygetline(backend->tmp_bb, buffer, sizeof(buffer), + rc = ap_proxygetline(backend->tmp_bb, buffer, response_field_size, backend->r, 0, &len); } if (len <= 0) { @@ -1386,7 +1430,7 @@ int ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, /* If not an HTTP/1 message or * if the status line was > 8192 bytes */ - if ((major != 1) || (len >= sizeof(buffer)-1)) { + if ((major != 1) || (len >= response_field_size - 1)) { proxy_run_detach_backend(r, backend); return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, "Corrupt status line returned by remote " @@ -1430,7 +1474,7 @@ int ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, "Set-Cookie", NULL); /* shove the headers direct into r->headers_out */ - ap_proxy_read_headers(r, backend->r, buffer, sizeof(buffer), origin, + ap_proxy_read_headers(r, backend->r, buffer, response_field_size, origin, &pread_len); if (r->headers_out == NULL) { -- 2.40.0