From 6b79d50af6fab6f0d6d85e5cc2ecf5e64e17a9a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A8=D0=B8=D0=BF=D0=B8=D1=86=D1=8B=D0=BD=20=D0=90=D0=BD?=
 =?UTF-8?q?=D0=B0=D1=82=D0=BE=D0=BB=D0=B8=D0=B9?= <avsh@get-net.ru>
Date: Mon, 4 Feb 2019 17:36:45 +0500
Subject: [PATCH] httpc: increase max outgoing header size to 8 KiB

The reason why the limit is so is that default Apache / nginx maximum
header size is 8 KiB.

Added a check to raise an error when a header is bigger then the limit.

Fixes #3959.
---
 src/httpc.c                       |  9 ++++++++-
 test/app-tap/http_client.test.lua | 21 ++++++++++++++++++++-
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/src/httpc.c b/src/httpc.c
index 950f8b32f6..e9f703ba1b 100644
--- a/src/httpc.c
+++ b/src/httpc.c
@@ -37,6 +37,8 @@
 #include "fiber.h"
 #include "errinj.h"
 
+#define MAX_HEADER_LEN 8192
+
 /**
  * libcurl callback for CURLOPT_WRITEFUNCTION
  * @see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
@@ -173,9 +175,14 @@ httpc_request_delete(struct httpc_request *req)
 int
 httpc_set_header(struct httpc_request *req, const char *fmt, ...)
 {
+	static __thread char header[MAX_HEADER_LEN + 1];
 	va_list ap;
 	va_start(ap, fmt);
-	const char *header = tt_vsprintf(fmt, ap);
+	int rc = vsnprintf(header, MAX_HEADER_LEN + 1, fmt, ap);
+	if (rc > MAX_HEADER_LEN) {
+		diag_set(IllegalParams, "header is too large");
+		return -1;
+	}
 	va_end(ap);
 
 	struct curl_slist *l = curl_slist_append(req->headers, header);
diff --git a/test/app-tap/http_client.test.lua b/test/app-tap/http_client.test.lua
index 10a3728f87..6a4ec91773 100755
--- a/test/app-tap/http_client.test.lua
+++ b/test/app-tap/http_client.test.lua
@@ -206,7 +206,7 @@ local function test_errors(test)
 end
 
 local function test_headers(test, url, opts)
-    test:plan(19)
+    test:plan(21)
     local http = client:new()
     local r = http:get(url .. 'headers', opts)
     test:is(type(r.headers["set-cookie"]), 'string', "set-cookie check")
@@ -231,6 +231,25 @@ local function test_headers(test, url, opts)
     local r = http:get(url .. 'headers', opts)
     test:is(r.headers["very_very_very_long_headers_name1"], "true", "truncated max_header_name_length")
     opts["max_header_name_length"] = nil
+
+    -- Send large headers.
+    local MAX_HEADER_NAME = 8192
+    local hname = 'largeheader'
+
+    -- "${hname}: ${hvalue}" is 8192 bytes length
+    local hvalue = string.rep('x', MAX_HEADER_NAME - hname:len() - 2)
+    local headers = {[hname] = hvalue}
+    local r = http:post(url, nil, merge(opts, {headers = headers}))
+    test:is(r.headers[hname], hvalue, '8192 bytes header: success')
+
+    -- "${hname}: ${hvalue}" is 8193 bytes length
+    local exp_err = 'header is too large'
+    local hvalue = string.rep('x', MAX_HEADER_NAME - hname:len() - 1)
+    local headers = {[hname] = hvalue}
+    local ok, err = pcall(http.post, http, url, nil,
+                          merge(opts, {headers = headers}))
+    test:is_deeply({ok, tostring(err)}, {false, exp_err},
+                   '8193 KiB header: error')
 end
 
 local function test_special_methods(test, url, opts)
-- 
GitLab