Tarantool development patches archive
 help / color / mirror / Atom feed
From: Roman <roman.habibov@tarantool.org>
To: tarantool-patches@freelists.org,
	Alexander Turenko <alexander.turenko@tarantool.org>
Cc: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Subject: [tarantool-patches] Re: [PATCH] httpc: add checking of headers in httpc:request
Date: Wed, 26 Dec 2018 18:56:21 +0300	[thread overview]
Message-ID: <e4b4ca0d-3ce4-74d9-b045-e0f3ebf08a25@tarantool.org> (raw)
In-Reply-To: <20181223031929.hhmxdlsox2aon5ih@tkn_work_nb>


On 23.12.2018 6:19, Alexander Turenko wrote:
> Hi!
>
> See comments below and proposed fixup on the branch
> romanhabibov/gh-3679-httpc-request-fixups. Please, amend the patch and
> proceed with the next reviewer (Vlad, added to CC).
>
> WBR, Alexander Turenko.

Done. Vlad, review, please.


commit 253be70b02a700ef9936fbab98f80c79de3cf0fe
Author: Roman Khabibov <roman.habibov@tarantool.org>
Date:   Wed Dec 26 17:49:34 2018 +0300

     httpc: add checking of headers in httpc:request

     Add functions that preprocess the request headers. Each header must 
be nil, 'string' or 'table'
     with '__tostring' metamethod.

     Closes #3679

diff --git a/src/lua/httpc.lua b/src/lua/httpc.lua
index cd44b6054..21a7cd2fd 100644
--- a/src/lua/httpc.lua
+++ b/src/lua/httpc.lua
@@ -216,6 +216,42 @@ local function process_cookies(cookies)
      return result
  end

+local function bad_header_error(func_name, header_name)
+    error(('%s: cannot convert header "%s" to a string'):format(
+        func_name, header_name))
+end
+
+-- Verify and preprocess outcoming headers before send a request.
+--
+-- Return the new headers table (with all string values) or nil
+-- when no headers were provided.
+local function preprocess_request_headers(headers)
+    local func_name = 'preprocess_request_headers'
+
+    if headers == nil then
+        return nil
+    end
+
+    local res = {}
+
+    for name, value in pairs(headers) do
+        local lua_type = type(value)
+        if lua_type == 'string' then
+            res[name] = value
+        elseif lua_type == 'table' then
+            local mt = getmetatable(value)
+            if mt == nil or mt.__tostring == nil then
+                bad_header_error(func_name, name)
+            end
+            res[name] = tostring(value)
+        else
+            bad_header_error(func_name, name)
+        end
+    end
+
+    return res
+end
+
  local function process_headers(headers)
      for header, value in pairs(headers) do
          if type(value) == 'table' then
@@ -296,6 +332,12 @@ curl_mt = {
              if not method or not url then
                  error('request(method, url [, options]])')
              end
+            local headers = preprocess_request_headers(opts and 
opts.headers)
+            if headers ~= nil then
+                -- prevent changing of user provided options
+                opts = table.copy(opts)
+                opts.headers = headers
+            end
              local resp = self.curl:request(method, url, body, opts or {})
              if resp and resp.headers then
                  if resp.headers['set-cookie'] ~= nil then
diff --git a/test/app-tap/http_client.test.lua 
b/test/app-tap/http_client.test.lua
index 10a3728f8..4ae382fe7 100755
--- a/test/app-tap/http_client.test.lua
+++ b/test/app-tap/http_client.test.lua
@@ -205,6 +205,81 @@ local function test_errors(test)
      test:is(r.status, 595, "GET: response on bad url")
  end

+-- gh-3679 allow only headers can be converted to string
+local function test_request_headers(test, url, opts)
+    local exp_err = 'preprocess_request_headers: cannot convert header ' ..
+        '"aaa" to a string'
+    local cases = {
+        {
+            'string header',
+            opts = {headers = {aaa = 'aaa'}},
+            exp_err = nil,
+        },
+        {
+            'header with __tostring() metamethod',
+            opts = {headers = {aaa = setmetatable({}, {
+                __tostring = function(self)
+                    return 'aaa'
+                end})}},
+            exp_err = nil,
+            postrequest_check = function(opts)
+                assert(type(opts.headers.aaa) == 'table',
+                    '"aaa" header was modified in http_client')
+            end,
+        },
+        {
+            'boolean header',
+            opts = {headers = {aaa = true}},
+            exp_err = exp_err,
+        },
+        {
+            'number header',
+            opts = {headers = {aaa = 10}},
+            exp_err = exp_err,
+        },
+        {
+            'cdata header (box.NULL)',
+            opts = {headers = {aaa = box.NULL}},
+            exp_err = exp_err,
+        },
+        {
+            'cdata<uint64_t> header',
+            opts = {headers = {aaa = 10ULL}},
+            exp_err = exp_err,
+        },
+        {
+            'table header w/o metatable',
+            opts = {headers = {aaa = {}}},
+            exp_err = exp_err,
+        },
+        {
+            'table header w/o __tostring() metamethod',
+            opts = {headers = {aaa = setmetatable({}, {})}},
+            exp_err = exp_err,
+        },
+    }
+    test:plan(#cases)
+
+    local http = client:new()
+
+    for _, case in ipairs(cases) do
+        local opts = merge(table.copy(opts), case.opts)
+        local ok, err = pcall(http.get, http, url, opts)
+        if case.postrequest_check ~= nil then
+            case.postrequest_check(opts)
+        end
+        if case.exp_err == nil then
+            -- expect success
+            test:ok(ok, case[1])
+        else
+            -- expect fail
+            assert(type(err) == 'string')
+            err = err:gsub('^builtin/[a-z._]+.lua:[0-9]+: ', '')
+            test:is_deeply({ok, err}, {false, case.exp_err}, case[1])
+        end
+    end
+end
+
  local function test_headers(test, url, opts)
      test:plan(19)
      local http = client:new()
@@ -397,12 +472,13 @@ 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("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)
+    test:test("request_headers", test_request_headers, url, opts)
      test:test("headers", test_headers, url, opts)
      test:test("special methods", test_special_methods, url, opts)
      if sock_family == 'AF_UNIX' and jit.os ~= "Linux" then

  reply	other threads:[~2018-12-26 15:56 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-21 18:18 [tarantool-patches] " Roman Khabibov
2018-12-23  3:19 ` [tarantool-patches] " Alexander Turenko
2018-12-26 15:56   ` Roman [this message]
2018-12-29 10:30     ` Vladislav Shpilevoy
2019-01-09 13:29       ` Roman
2019-01-24 14:54         ` Roman
2019-01-29 19:44         ` Vladislav Shpilevoy
2019-01-29 20:32           ` Alexander Turenko
2019-01-31 23:47           ` Roman
2019-02-05 11:42             ` Vladislav Shpilevoy
2019-02-11 23:24               ` Roman
2019-02-15 21:17                 ` Vladislav Shpilevoy
2019-02-19 10:49                   ` Roman
2019-02-22 16:01                     ` Vladislav Shpilevoy
2019-02-23 21:38                       ` Roman Khabibov
2019-02-23 22:43                       ` Roman Khabibov
2019-02-25 13:04                         ` Vladislav Shpilevoy
2019-02-25 14:51 ` Kirill Yukhin

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=e4b4ca0d-3ce4-74d9-b045-e0f3ebf08a25@tarantool.org \
    --to=roman.habibov@tarantool.org \
    --cc=alexander.turenko@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='[tarantool-patches] Re: [PATCH] httpc: add checking of headers in httpc:request' \
    /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