PR62199: add worker parameter ResponseFieldSize to mod_proxy
authorEric Covener <covener@apache.org>
Wed, 11 Apr 2018 19:11:52 +0000 (19:11 +0000)
committerEric Covener <covener@apache.org>
Wed, 11 Apr 2018 19:11:52 +0000 (19:11 +0000)
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
docs/manual/mod/mod_proxy.xml
include/ap_mmn.h
modules/proxy/mod_proxy.c
modules/proxy/mod_proxy.h
modules/proxy/mod_proxy_http.c

diff --git a/CHANGES b/CHANGES
index 3ce88899e4d9df16f9a0f2965153d8f163893a06..4db111242f0a12cf081f1371f7ea53a13378d59d 100644 (file)
--- 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 <hwibell gmail.com>]
+
   *) core: Preserve the original HTTP request method in the '%<m' LogFormat
      when an path-based ErrorDocument is used.  PR 62186. 
      [Micha Lenk <micha lenk.info>]
index 418257fa6c8b1f40b64dc407a28d58c5c0b468c5..23deb30d665f8bb4b6326671539052ea23f86839 100644 (file)
@@ -1137,6 +1137,13 @@ ProxyPass "/example" "http://backend.example.com" max=20 ttl=120 retry=300
         to override the <directive>ProxyIOBufferSize</directive> for a specific worker.
         This must be at least 512 or set to 0 for the system default of 8192.
     </td></tr>
+    <tr><td>responsefieldsize</td>
+        <td>8192</td>
+        <td>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.
+    </td></tr>
     <tr><td>keepalive</td>
         <td>Off</td>
         <td><p>This parameter should be used when you have a firewall between your
index 6393945ba687aa933071f39cdbe60791609e156c..4706d833a058f43e6ab2863d2109494eba790eb1 100644 (file)
  *                         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" */
 #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
index aeaea7c89e7003d681cdcf8b45aad92902c8d76e..ab252a309266352d3c313161cfc46db362b2c027 100644 (file)
@@ -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);
index 18127d527de930b81d010d3bf9e2f486ad457cb9..ae8be929bccf8d043dfdc355be3323b253c418b6 100644 (file)
@@ -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)))
index fb95f4380c8fbf1b8664de6687cfc83bd4e339d0..115bf714e7f4f6a0d052729bb98911e768ad5b75 100644 (file)
@@ -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) {