Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
To: tarantool-patches@freelists.org,
	Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Subject: [tarantool-patches] Re: [PATCH v1 1/1] http: fix httpc auto-managed headers
Date: Fri, 22 Feb 2019 10:39:51 +0300	[thread overview]
Message-ID: <80130a1b-84ad-5f90-9422-95bb0a1e67cb@tarantool.org> (raw)
In-Reply-To: <9099f8a339c278e3a5dac683923d13c7ee470ce9.1550761676.git.kshcherbatov@tarantool.org>

Perhaps even better here is so:

diff --git a/src/httpc.c b/src/httpc.c
index b4ca6cfce..f2df069e5 100644
--- a/src/httpc.c
+++ b/src/httpc.c
@@ -184,23 +184,24 @@ httpc_managed_headers_mask_update(uint8_t *auto_headers_mask,
 	static struct {
 		const char *header_name;
 		uint32_t header_name_len;
-		int8_t bit_index;
 	} httpc_managed_headers[] = {
-		{"Connection",		10,	1},
-		{"Keep-Alive",		10,	2},
-		{"Content-Length",	14,	4},
-		{"Accept",		6, 	8},
+		{"Connection",		10},
+		{"Keep-Alive",		10},
+		{"Content-Length",	14},
+		{"Accept",		6},
 	};
 	uint32_t httpc_managed_headers_cnt =
 		sizeof(httpc_managed_headers)/sizeof(httpc_managed_headers[0]);
+	assert(httpc_managed_headers_cnt <
+	       sizeof(*auto_headers_mask) * CHAR_BIT);
 	for (uint32_t i = 0; i < httpc_managed_headers_cnt; i++) {
 		if (strncasecmp(header, httpc_managed_headers[i].header_name,
 				httpc_managed_headers[i].header_name_len) != 0)
 			continue;
-		if ((*auto_headers_mask &
-		     httpc_managed_headers[i].bit_index) != 0)
+		int8_t bit_index = 1 << i;
+		if ((*auto_headers_mask & bit_index) != 0)
 			return -1;
-		*auto_headers_mask |= httpc_managed_headers[i].bit_index;
+		*auto_headers_mask |= bit_index;
 		return 0;
 	}
 	return 0;


===========================================

ntent-Length", "Connection", "Keep-Alive: timeout".
However, when the user explicitly specified them in the header
options section of the call argument, they could be written to
the HTTP request twice.
Was introduced service http c_managed_header_mask mask in which
the bits corresponding to the http auto-manager headers, set 1
when referenced. When the bit is set, the new value is not
applied. In this way, we prefer user-defined value for all
httpc-managed headers.

Closes #3955
---
 src/httpc.c                       | 42 +++++++++++++++++++++++++++++++
 src/httpc.h                       |  8 ++++++
 test/app-tap/http_client.test.lua | 12 ++++++++-
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/src/httpc.c b/src/httpc.c
index 950f8b32f..f2df069e5 100644
--- a/src/httpc.c
+++ b/src/httpc.c
@@ -170,6 +170,43 @@ httpc_request_delete(struct httpc_request *req)
 	mempool_free(&req->env->req_pool, req);
 }
 
+/**
+ * Update bitmask of the http request headers that httpc may set
+ * automatically. In case of reserved pattern is found in header,
+ * routine sets corresponding bit in auto_headers_mask.
+ * Returns -1 when header is reserved and it's bit is already set
+ * in auto_headers_mask; 0 otherwise,
+*/
+static int
+httpc_managed_headers_mask_update(uint8_t *auto_headers_mask,
+				  const char *header)
+{
+	static struct {
+		const char *header_name;
+		uint32_t header_name_len;
+	} httpc_managed_headers[] = {
+		{"Connection",		10},
+		{"Keep-Alive",		10},
+		{"Content-Length",	14},
+		{"Accept",		6},
+	};
+	uint32_t httpc_managed_headers_cnt =
+		sizeof(httpc_managed_headers)/sizeof(httpc_managed_headers[0]);
+	assert(httpc_managed_headers_cnt <
+	       sizeof(*auto_headers_mask) * CHAR_BIT);
+	for (uint32_t i = 0; i < httpc_managed_headers_cnt; i++) {
+		if (strncasecmp(header, httpc_managed_headers[i].header_name,
+				httpc_managed_headers[i].header_name_len) != 0)
+			continue;
+		int8_t bit_index = 1 << i;
+		if ((*auto_headers_mask & bit_index) != 0)
+			return -1;
+		*auto_headers_mask |= bit_index;
+		return 0;
+	}
+	return 0;
+}
+
 int
 httpc_set_header(struct httpc_request *req, const char *fmt, ...)
 {
@@ -178,6 +215,11 @@ httpc_set_header(struct httpc_request *req, const char *fmt, ...)
 	const char *header = tt_vsprintf(fmt, ap);
 	va_end(ap);
 
+	if (httpc_managed_headers_mask_update(&req->auto_headers_mask,
+					      header) != 0) {
+		/* Do not add extra header to list. */
+		return 0;
+	}
 	struct curl_slist *l = curl_slist_append(req->headers, header);
 	if (l == NULL) {
 		diag_set(OutOfMemory, strlen(header), "curl", "http header");
diff --git a/src/httpc.h b/src/httpc.h
index dc709e6fa..d30343be1 100644
--- a/src/httpc.h
+++ b/src/httpc.h
@@ -109,6 +109,14 @@ struct httpc_request {
 	struct region resp_headers;
 	/** buffer of body */
 	struct region resp_body;
+	/**
+	 * A bitmask of the http request headers from among
+	 * the headers that httpc can set automatically.
+	 * This allows to prevent settings such headers twice.
+	 * Read httpc_managed_headers_mask_update definition for
+	 * more details.
+	 */
+	uint8_t auto_headers_mask;
 };
 
 /**
diff --git a/test/app-tap/http_client.test.lua b/test/app-tap/http_client.test.lua
index 10a3728f8..8d1c50549 100755
--- a/test/app-tap/http_client.test.lua
+++ b/test/app-tap/http_client.test.lua
@@ -79,6 +79,14 @@ local function test_http_client(test, url, opts)
     test:is(r.status, 200, 'request')
 end
 
+local function test_http_client_headers_redefine(test, url, opts)
+    test:plan(2)
+    opts.headers={['Connection'] = 'Keep-Alive'}
+    local r = client.get(url, opts)
+    test:is(r.status, 200, 'simple 200')
+    test:is(r.headers["connection"], 'Keep-Alive', 'redefined Connection header')
+end
+
 local function test_cancel_and_errinj(test, url, opts)
     test:plan(3)
     local ch = fiber.channel(1)
@@ -397,9 +405,11 @@ local function test_concurrent(test, url, opts)
 end
 
 function run_tests(test, sock_family, sock_addr)
-    test:plan(9)
+    test:plan(10)
     local server, url, opts = start_server(test, sock_family, sock_addr)
     test:test("http.client", test_http_client, url, opts)
+    test:test("http.client headers redefine", test_http_client_headers_redefine,
+              url, opts)
     test:test("cancel and errinj", test_cancel_and_errinj, url .. 'long_query', opts)
     test:test("basic http post/get", test_post_and_get, url, opts)
     test:test("errors", test_errors)
-- 
2.20.1

  reply	other threads:[~2019-02-22  7:39 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-21 15:09 [tarantool-patches] " Kirill Shcherbatov
2019-02-22  7:39 ` Kirill Shcherbatov [this message]
2019-02-25  7:52   ` [tarantool-patches] " Kirill Shcherbatov
2019-02-25 14:17     ` Vladislav Shpilevoy
2019-02-25 15:21       ` Kirill Shcherbatov
2019-03-07 13:51         ` Vladimir Davydov
2019-03-07 14:07           ` Kirill Shcherbatov
2019-03-07 14:32             ` Vladimir Davydov
2019-03-07 14:48           ` Kirill Shcherbatov
2019-03-07 14:53             ` Vladimir Davydov
2019-03-11  9:23               ` [tarantool-patches] Re: [PATCH v2 " Kirill Shcherbatov
2019-03-11 10:02                 ` Vladimir Davydov
2019-03-11 15:57                   ` Kirill Shcherbatov
2019-03-12  8:54                     ` Vladimir Davydov
2019-03-07 14:07         ` [tarantool-patches] Re: [PATCH v1 " Vladislav Shpilevoy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=80130a1b-84ad-5f90-9422-95bb0a1e67cb@tarantool.org \
    --to=kshcherbatov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='[tarantool-patches] Re: [PATCH v1 1/1] http: fix httpc auto-managed headers' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox