From: Ilya Markov <imarkov@tarantool.org> To: georgy@tarantool.org Cc: tarantool-patches@freelists.org Subject: [tarantool-patches] [PATCH] http.client: Fix waiting after received result Date: Tue, 19 Jun 2018 18:08:02 +0300 [thread overview] Message-ID: <1529420882-24447-1-git-send-email-imarkov@tarantool.org> (raw) Current implementation of http.client relies on fiber_cond which is set after the request was registered and doesn't consider the fact that response may be handled before the set of fiber_cond. So we may have the following situation: 1. Register request in libcurl(curl_multi_add_handle in curl_execute). 2. Receive and process response, fiber_cond_signal on cond_var which no one waits. 3. fiber_cond_wait on cond which is already signaled. Wait until timeout is fired. In this case user have to wait timeout, though data was received earlier. Fix this with adding extra flag in_progress to curl_request struct. Set this flag true before registering request in libcurl and set it false when request is finished before fiber_cond_signal. When in_progress flag is false, don't wait on cond variable. Add 1 error injection. Closes #3452 --- The patch is based on 1.9 https://github.com/tarantool/tarantool/issues/3452 https://github.com/tarantool/tarantool/tree/gh-3452-timeout-http-client src/curl.c | 22 ++++++++++++++++++---- src/curl.h | 2 ++ src/errinj.h | 1 + test/app-tap/http_client.test.lua | 21 +++++++++++++++------ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/curl.c b/src/curl.c index df95713..b85a455 100644 --- a/src/curl.c +++ b/src/curl.c @@ -35,6 +35,7 @@ #include <curl/curl.h> #include "fiber.h" +#include "errinj.h" /** * Process events @@ -82,6 +83,13 @@ curl_multi_process(CURLM *multi, curl_socket_t sockfd, int events) struct curl_request *request = NULL; curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *) &request); request->code = (int) code; + request->in_progress = false; +#ifndef NDEBUG + struct errinj *errinj = errinj(ERRINJ_HTTP_RESPONSE_ADD_WAIT, + ERRINJ_BOOL); + if (errinj != NULL) + errinj->bparam = false; +#endif fiber_cond_signal(&request->cond); } } @@ -270,6 +278,7 @@ curl_request_create(struct curl_request *curl_request) diag_set(OutOfMemory, 0, "curl", "easy"); return -1; } + curl_request->in_progress = false; curl_request->code = CURLE_OK; fiber_cond_create(&curl_request->cond); return 0; @@ -288,13 +297,18 @@ curl_execute(struct curl_request *curl_request, struct curl_env *env, double timeout) { CURLMcode mcode; - + curl_request->in_progress = true; mcode = curl_multi_add_handle(env->multi, curl_request->easy); if (mcode != CURLM_OK) goto curl_merror; - - /* Don't wait on a cond if request has already failed */ - if (curl_request->code == CURLE_OK) { +#ifndef NDEBUG + struct errinj *errinj = errinj(ERRINJ_HTTP_RESPONSE_ADD_WAIT, + ERRINJ_BOOL); + while (errinj != NULL && errinj->bparam) + fiber_sleep(0.001); +#endif + /* Don't wait on a cond if request has already failed or finished. */ + if (curl_request->code == CURLE_OK && curl_request->in_progress) { ++env->stat.active_requests; int rc = fiber_cond_wait_timeout(&curl_request->cond, timeout); if (rc < 0 || fiber_is_cancelled()) diff --git a/src/curl.h b/src/curl.h index 21ba475..cf91636 100644 --- a/src/curl.h +++ b/src/curl.h @@ -68,6 +68,8 @@ struct curl_env { struct curl_request { /** Internal libcurl status code. */ int code; + /** States that request is running. */ + bool in_progress; /** Information associated with a specific easy handle. */ CURL *easy; /** diff --git a/src/errinj.h b/src/errinj.h index 4998fdc..5694d3f 100644 --- a/src/errinj.h +++ b/src/errinj.h @@ -113,6 +113,7 @@ struct errinj { _(ERRINJ_LOG_ROTATE, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_SNAP_COMMIT_DELAY, ERRINJ_BOOL, {.bparam = 0}) \ _(ERRINJ_SNAP_WRITE_ROW_TIMEOUT, ERRINJ_DOUBLE, {.dparam = 0}) \ + _(ERRINJ_HTTP_RESPONSE_ADD_WAIT, ERRINJ_BOOL, {.bparam = false}) \ ENUM0(errinj_id, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/app-tap/http_client.test.lua b/test/app-tap/http_client.test.lua index 887395d..37ba1ae 100755 --- a/test/app-tap/http_client.test.lua +++ b/test/app-tap/http_client.test.lua @@ -79,19 +79,28 @@ local function test_http_client(test, url, opts) test:is(r.status, 200, 'request') end -local function test_cancel_and_timeout(test, url, opts) - test:plan(2) +local function test_cancel_and_errinj(test, url, opts) + test:plan(3) local ch = fiber.channel(1) local http = client:new() - local f = fiber.create(function() - ch:put(http:get(url, opts)) end) + local func = function(fopts) + ch:put(http:get(url, fopts)) + end + local f = fiber.create(func, opts) f:cancel() local r = ch:get() test:ok(r.status == 408 and string.find(r.reason, "Timeout"), "After cancel fiber timeout is returned") - local r = http:get(url, merge(opts, {timeout = 0.0001})) + r = http:get(url, merge(opts, {timeout = 0.0001})) test:ok(r.status == 408 and string.find(r.reason, "Timeout"), "Timeout check") + local errinj = box.error.injection + errinj.set('ERRINJ_HTTP_RESPONSE_ADD_WAIT', true) + local topts = merge(opts, {timeout = 1200}) + f = fiber.create(func, topts) + r = ch:get() + test:is(r.status, 200, "No hangs in errinj") + errinj.set('ERRINJ_HTTP_RESPONSE_ADD_WAIT', false) end local function test_post_and_get(test, url, opts) @@ -384,7 +393,7 @@ function run_tests(test, sock_family, sock_addr) test:plan(9) local server, url, opts = start_server(test, sock_family, sock_addr) test:test("http.client", test_http_client, url, opts) - test:test("cancel and timeout", test_cancel_and_timeout, url, opts) + test:test("cancel and errinj", test_cancel_and_errinj, url, opts) test:test("basic http post/get", test_post_and_get, url, opts) test:test("errors", test_errors) test:test("headers", test_headers, url, opts) -- 2.7.4
reply other threads:[~2018-06-19 15:08 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=1529420882-24447-1-git-send-email-imarkov@tarantool.org \ --to=imarkov@tarantool.org \ --cc=georgy@tarantool.org \ --cc=tarantool-patches@freelists.org \ --subject='Re: [tarantool-patches] [PATCH] http.client: Fix waiting after received result' \ /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