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
next prev parent 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