<div id="geary-body"><div><br></div></div><div id="geary-quote">Hi! Thanks for review.<br><br><div>On ÁÑ, äÕÒ 16, 2019 at 12:17 AM, Vladislav Shpilevoy <v.shpilevoy@tarantool.org> wrote:<br><blockquote type="cite"><div class="plaintext" style="white-space: pre-wrap;">Hi!

<blockquote><blockquote>        5. Out of 80 symbols. 
2. Still out of 80.
</blockquote>My text editor shows 70 symbols.

</blockquote>
Then there is something wrong with your editor, sorry.

<a href="https://github.com/tarantool/tarantool/blob/romanhabibov/gh-3679-httpc-request/src/lua/httpc.c#L180">https://github.com/tarantool/tarantool/blob/romanhabibov/gh-3679-httpc-request/src/lua/httpc.c#L180</a>

Lets count. Here you have 6 tabs - 6 * 8 = 48 symbols. Then you have 55 symbols of

    "headers must be string or table with \"__tostring\"");

48 + 55 = 103.

Also, the error string is not aligned by opening parenthesis.

In your email I see, that you have 4 symbols tab, what gives you 79 symbols, but
tab width in this file should be 8. So probably you mistakenly set tab width to
4 and the editor shows you < 80 width. Please, fix.

</div></blockquote><span style="white-space: pre-wrap;"><div>Ok. Done.</div><div><br></div></span><div><span style="white-space: pre-wrap;">commit c9802592b27cb6986e3e72eb948d8dbfb21baa7e</span></div><div><span style="white-space: pre-wrap;">Author: Roman Khabibov <roman.habibov@tarantool.org></span></div><div><span style="white-space: pre-wrap;">Date:   Wed Dec 26 17:49:34 2018 +0300</span></div><div><span style="white-space: pre-wrap;"><br></span></div><div><span style="white-space: pre-wrap;">    httpc: add checking of headers in httpc:request</span></div><div><span style="white-space: pre-wrap;">    </span></div><div><span style="white-space: pre-wrap;">    Add preprocessing of the request headers. Each header must be 'string' or 'table'</span></div><div><span style="white-space: pre-wrap;">    with '__tostring' metamethod.</span></div><div><span style="white-space: pre-wrap;">    </span></div><div><span style="white-space: pre-wrap;">    Closes #3679</span></div><div><span style="white-space: pre-wrap;"><br></span></div><div><span style="white-space: pre-wrap;">diff --git a/src/lua/httpc.c b/src/lua/httpc.c</span></div><div><span style="white-space: pre-wrap;">index 5f4e2e912..fb012f58b 100644</span></div><div><span style="white-space: pre-wrap;">--- a/src/lua/httpc.c</span></div><div><span style="white-space: pre-wrap;">+++ b/src/lua/httpc.c</span></div><div><span style="white-space: pre-wrap;">@@ -173,6 +173,18 @@ luaT_httpc_request(lua_State *L)</span></div><div><span style="white-space: pre-wrap;">    if (!lua_isnil(L, -1)) {</span></div><div><span style="white-space: pre-wrap;">               lua_pushnil(L);</span></div><div><span style="white-space: pre-wrap;">                while (lua_next(L, -2) != 0) {</span></div><div><span style="white-space: pre-wrap;">+                        int header_type = lua_type(L, -1);</span></div><div><span style="white-space: pre-wrap;">+                    if (header_type != LUA_TSTRING) {</span></div><div><span style="white-space: pre-wrap;">+                             char *err_msg = "headers must be " \</span></div><div><span style="white-space: pre-wrap;">+                                "string or table with \"__tostring\"";</span></div><div><span style="white-space: pre-wrap;">+                            if (header_type != LUA_TTABLE) {</span></div><div><span style="white-space: pre-wrap;">+                                      return luaL_error(L, err_msg);</span></div><div><span style="white-space: pre-wrap;">+                                } else if (!luaL_getmetafield(L, -1, </span></div><div><span style="white-space: pre-wrap;">+                                                       "__tostring")) {</span></div><div><span style="white-space: pre-wrap;">+                                      return luaL_error(L, err_msg);</span></div><div><span style="white-space: pre-wrap;">+                                }</span></div><div><span style="white-space: pre-wrap;">+                             lua_pop(L, 1);</span></div><div><span style="white-space: pre-wrap;">+                        }</span></div><div><span style="white-space: pre-wrap;">                      if (httpc_set_header(req, "%s: %s",</span></div><div><span style="white-space: pre-wrap;">                                       lua_tostring(L, -2),</span></div><div><span style="white-space: pre-wrap;">                                           lua_tostring(L, -1)) < 0) {</span></div><div><span style="white-space: pre-wrap;">diff --git a/test/app-tap/http_client.test.lua b/test/app-tap/http_client.test.lua</span></div><div><span style="white-space: pre-wrap;">index 10a3728f8..8a98d1e92 100755</span></div><div><span style="white-space: pre-wrap;">--- a/test/app-tap/http_client.test.lua</span></div><div><span style="white-space: pre-wrap;">+++ b/test/app-tap/http_client.test.lua</span></div><div><span style="white-space: pre-wrap;">@@ -205,6 +205,80 @@ local function test_errors(test)</span></div><div><span style="white-space: pre-wrap;">     test:is(r.status, 595, "GET: response on bad url")</span></div><div><span style="white-space: pre-wrap;"> end</span></div><div><span style="white-space: pre-wrap;"> </span></div><div><span style="white-space: pre-wrap;">+-- gh-3679 allow only headers can be converted to string</span></div><div><span style="white-space: pre-wrap;">+local function test_request_headers(test, url, opts)</span></div><div><span style="white-space: pre-wrap;">+    local exp_err = 'headers must be string or table with "__tostring"'</span></div><div><span style="white-space: pre-wrap;">+    local cases = {</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'string header',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = 'aaa'}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = nil,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'header with __tostring() metamethod',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = setmetatable({}, {</span></div><div><span style="white-space: pre-wrap;">+                __tostring = function(self)</span></div><div><span style="white-space: pre-wrap;">+                    return 'aaa'</span></div><div><span style="white-space: pre-wrap;">+                end})}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = nil,</span></div><div><span style="white-space: pre-wrap;">+            postrequest_check = function(opts)</span></div><div><span style="white-space: pre-wrap;">+                assert(type(opts.headers.aaa) == 'table',</span></div><div><span style="white-space: pre-wrap;">+                    '"aaa" header was modified in http_client')</span></div><div><span style="white-space: pre-wrap;">+            end,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'boolean header',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = true}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = exp_err,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'number header',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = 10}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = exp_err,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'cdata header (box.NULL)',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = box.NULL}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = exp_err,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'cdata<uint64_t> header',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = 10ULL}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = exp_err,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'table header w/o metatable',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = {}}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = exp_err,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+        {</span></div><div><span style="white-space: pre-wrap;">+            'table header w/o __tostring() metamethod',</span></div><div><span style="white-space: pre-wrap;">+            opts = {headers = {aaa = setmetatable({}, {})}},</span></div><div><span style="white-space: pre-wrap;">+            exp_err = exp_err,</span></div><div><span style="white-space: pre-wrap;">+        },</span></div><div><span style="white-space: pre-wrap;">+    }</span></div><div><span style="white-space: pre-wrap;">+    test:plan(#cases)</span></div><div><span style="white-space: pre-wrap;">+</span></div><div><span style="white-space: pre-wrap;">+    local http = client:new()</span></div><div><span style="white-space: pre-wrap;">+</span></div><div><span style="white-space: pre-wrap;">+    for _, case in ipairs(cases) do</span></div><div><span style="white-space: pre-wrap;">+        local opts = merge(table.copy(opts), case.opts)</span></div><div><span style="white-space: pre-wrap;">+        local ok, err = pcall(http.get, http, url, opts)</span></div><div><span style="white-space: pre-wrap;">+        if case.postrequest_check ~= nil then</span></div><div><span style="white-space: pre-wrap;">+            case.postrequest_check(opts)</span></div><div><span style="white-space: pre-wrap;">+        end</span></div><div><span style="white-space: pre-wrap;">+        if case.exp_err == nil then</span></div><div><span style="white-space: pre-wrap;">+            -- expect success</span></div><div><span style="white-space: pre-wrap;">+            test:ok(ok, case[1])</span></div><div><span style="white-space: pre-wrap;">+        else</span></div><div><span style="white-space: pre-wrap;">+            -- expect fail</span></div><div><span style="white-space: pre-wrap;">+            assert(type(err) == 'string')</span></div><div><span style="white-space: pre-wrap;">+            err = err:gsub('^builtin/[a-z._]+.lua:[0-9]+: ', '')</span></div><div><span style="white-space: pre-wrap;">+            test:is_deeply({ok, err}, {false, case.exp_err}, case[1])</span></div><div><span style="white-space: pre-wrap;">+        end</span></div><div><span style="white-space: pre-wrap;">+    end</span></div><div><span style="white-space: pre-wrap;">+end</span></div><div><span style="white-space: pre-wrap;">+</span></div><div><span style="white-space: pre-wrap;"> local function test_headers(test, url, opts)</span></div><div><span style="white-space: pre-wrap;">     test:plan(19)</span></div><div><span style="white-space: pre-wrap;">     local http = client:new()</span></div><div><span style="white-space: pre-wrap;">@@ -397,12 +471,13 @@ local function test_concurrent(test, url, opts)</span></div><div><span style="white-space: pre-wrap;"> end</span></div><div><span style="white-space: pre-wrap;"> </span></div><div><span style="white-space: pre-wrap;"> function run_tests(test, sock_family, sock_addr)</span></div><div><span style="white-space: pre-wrap;">-    test:plan(9)</span></div><div><span style="white-space: pre-wrap;">+    test:plan(10)</span></div><div><span style="white-space: pre-wrap;">     local server, url, opts = start_server(test, sock_family, sock_addr)</span></div><div><span style="white-space: pre-wrap;">     test:test("http.client", test_http_client, url, opts)</span></div><div><span style="white-space: pre-wrap;">     test:test("cancel and errinj", test_cancel_and_errinj, url .. 'long_query', opts)</span></div><div><span style="white-space: pre-wrap;">     test:test("basic http post/get", test_post_and_get, url, opts)</span></div><div><span style="white-space: pre-wrap;">     test:test("errors", test_errors)</span></div><div><span style="white-space: pre-wrap;">+    test:test("request_headers", test_request_headers, url, opts)</span></div><div><span style="white-space: pre-wrap;">     test:test("headers", test_headers, url, opts)</span></div><div><span style="white-space: pre-wrap;">     test:test("special methods", test_special_methods, url, opts)</span></div><div><span style="white-space: pre-wrap;">     if sock_family == 'AF_UNIX' and jit.os ~= "Linux" then</span></div><div><span style="white-space: pre-wrap;"><br></span></div></div></div>