From abdef03e987a9e388be72253efa32cb6f3f295a2 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 27 Sep 2016 17:52:49 +0300 Subject: [PATCH 01/10] Fix. Call `multi` callbacks with correct Lua state. --- .travis.yml | 1 + appveyor.yml | 1 + examples/cURLv3/uvwget.lua | 2 +- src/lceasy.c | 14 +++++ src/lceasy.h | 4 ++ src/lcmulti.c | 93 +++++++++++++++++++++++------- test/test_multi_callback.lua | 109 +++++++++++++++++++++++++++++++++++ 7 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 test/test_multi_callback.lua diff --git a/.travis.yml b/.travis.yml index db03623..0967a9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,7 @@ script: - lua -e "print(require 'cURL.utils'.find_ca_bundle())" - lunit.sh run.lua - lua test_pause02.c.lua + - lua test_multi_callback.lua # - lunit.sh test_easy.lua # - lunit.sh test_safe.lua # - lunit.sh test_form.lua diff --git a/appveyor.yml b/appveyor.yml index e805dde..92a1427 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -56,6 +56,7 @@ test_script: - cd %APPVEYOR_BUILD_FOLDER%\test - lua run.lua - lua test_pause02.c.lua + - lua test_multi_callback.lua after_test: - cd %APPVEYOR_BUILD_FOLDER% diff --git a/examples/cURLv3/uvwget.lua b/examples/cURLv3/uvwget.lua index 4119ae8..8341e1d 100644 --- a/examples/cURLv3/uvwget.lua +++ b/examples/cURLv3/uvwget.lua @@ -182,7 +182,7 @@ end on_curl_action = function(easy, fd, action) local ok, err = pcall(function() - trace("CURL::SOCKET", easy, s, ACTION_NAMES[action] or action) + trace("CURL::SOCKET", easy, fd, ACTION_NAMES[action] or action) local context = easy.data.context diff --git a/src/lceasy.c b/src/lceasy.c index a0f89ad..82ef5ee 100644 --- a/src/lceasy.c +++ b/src/lceasy.c @@ -14,6 +14,7 @@ #include "lcutils.h" #include "lchttppost.h" #include "lcshare.h" +#include "lcmulti.h" #include static const char *LCURL_ERROR_TAG = "LCURL_ERROR_TAG"; @@ -45,6 +46,7 @@ int lcurl_easy_create(lua_State *L, int error_mode){ p->magic = LCURL_EASY_MAGIC; p->L = NULL; p->post = NULL; + p->multi = NULL; p->storage = lcurl_storage_init(L); p->wr.cb_ref = p->wr.ud_ref = LUA_NOREF; p->rd.cb_ref = p->rd.ud_ref = LUA_NOREF; @@ -75,7 +77,19 @@ static int lcurl_easy_cleanup(lua_State *L){ int i; if(p->curl){ + p->L = L; + if(p->post){ + p->post->L = L; + } + // In my tests when I cleanup some easy handle. + // timerfunction called only for single multi handle. + if(p->multi){ + p->multi->L = L; + } curl_easy_cleanup(p->curl); + if(p->multi){ + p->multi->L = NULL; + } p->curl = NULL; } diff --git a/src/lceasy.h b/src/lceasy.h index e64f2b4..1093a65 100644 --- a/src/lceasy.h +++ b/src/lceasy.h @@ -35,6 +35,8 @@ enum { #define LCURL_EASY_MAGIC 0xEA +typedef struct lcurl_multi_tag lcurl_multi_t; + typedef struct lcurl_easy_tag{ unsigned char magic; @@ -44,6 +46,8 @@ typedef struct lcurl_easy_tag{ lcurl_hpost_t *post; + lcurl_multi_t *multi; + CURL *curl; int storage; int lists[LCURL_LIST_COUNT]; diff --git a/src/lcmulti.c b/src/lcmulti.c index 7aee895..2213f33 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -28,18 +28,36 @@ #define LCURL_MULTI_NAME LCURL_PREFIX" Multi" static const char *LCURL_MULTI = LCURL_MULTI_NAME; +static void lcurl__multi_assign_lua(lua_State *L, lcurl_multi_t *p, int assign_easy){ + p->L = L; + + if(assign_easy){ + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_pushnil(L); + while(lua_next(L, -2)){ + lcurl_easy_t *e = lcurl_geteasy_at(L, -1); + e->L = L; + if(e->post){ + e->post->L = L; + } + lua_pop(L, 1); + } + lua_pop(L, 1); + } +} + //{ int lcurl_multi_create(lua_State *L, int error_mode){ lcurl_multi_t *p; - + lua_settop(L, 1); p = lutil_newudatap(L, lcurl_multi_t, LCURL_MULTI); p->curl = curl_multi_init(); p->err_mode = error_mode; if(!p->curl) return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, CURLM_INTERNAL_ERROR); - p->L = L; + p->L = NULL; lcurl_util_new_weak_table(L, "v"); p->h_ref = luaL_ref(L, LCURL_LUA_REGISTRY); p->tm.cb_ref = p->tm.ud_ref = LUA_NOREF; @@ -89,24 +107,65 @@ static int lcurl_multi_cleanup(lua_State *L){ static int lcurl_multi_add_handle(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); lcurl_easy_t *e = lcurl_geteasy_at(L, 2); - CURLMcode code = curl_multi_add_handle(p->curl, e->curl); - if(code != CURLM_OK){ - lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); + CURLMcode code; + + if(e->multi){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, CURLM_ADDED_ALREADY); } + + // From doc: + // If you have CURLMOPT_TIMERFUNCTION set in the multi handle, + // that callback will be called from within this function to ask + // for an updated timer so that your main event loop will get + // the activity on this handle to get started. + // + // So we should add easy before this call + // call chain may be like => timerfunction->socket_action->socketfunction + lua_settop(L, 2); lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); lua_pushvalue(L, 2); lua_rawsetp(L, -2, e->curl); lua_settop(L, 1); + + e->multi = p; + + lcurl__multi_assign_lua(L, p, 0); + code = curl_multi_add_handle(p->curl, e->curl); + p->L = NULL; + + if(code != CURLM_OK){ + // remove + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_pushnil(L); + lua_rawsetp(L, -2, e->curl); + e->multi = NULL; + + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); + } return 1; } static int lcurl_multi_remove_handle(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); lcurl_easy_t *e = lcurl_geteasy_at(L, 2); - CURLMcode code = curl_multi_remove_handle(p->curl, e->curl); + CURLMcode code; + + if(e->multi != p){ + // cURL returns CURLM_OK for such call so we do the same. + // tested on 7.37.1 + lua_settop(L, 1); + return 1; + } + + lcurl__multi_assign_lua(L, p, 0); + code = curl_multi_remove_handle(p->curl, e->curl); + p->L = NULL; + if(code != CURLM_OK){ lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); } + + e->multi = NULL; lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); lua_pushnil(L); lua_rawsetp(L, -2, e->curl); @@ -119,21 +178,10 @@ static int lcurl_multi_perform(lua_State *L){ int running_handles = 0; CURLMcode code; - lua_settop(L, 1); - lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); - lua_pushnil(L); - while(lua_next(L, 2)){ - lcurl_easy_t *e = lcurl_geteasy_at(L, -1); - e->L = L; - if(e->post){ - e->post->L = L; - } - lua_pop(L, 1); - } - - lua_settop(L, 1); - + lcurl__multi_assign_lua(L, p, 1); while((code = curl_multi_perform(p->curl, &running_handles)) == CURLM_CALL_MULTI_PERFORM); + p->L = NULL; + if(code != CURLM_OK){ lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); } @@ -256,7 +304,11 @@ static int lcurl_multi_socket_action(lua_State *L){ CURLMcode code; int n, mask; if(s == CURL_SOCKET_TIMEOUT) mask = lutil_optint64(L, 3, 0); else mask = lutil_checkint64(L, 3); + + lcurl__multi_assign_lua(L, p, 0); code = curl_multi_socket_action(p->curl, s, mask, &n); + p->L = NULL; + if(code != CURLM_OK){ lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); } @@ -412,6 +464,7 @@ static int lcurl_multi_socket_callback(CURL *easy, curl_socket_t s, int what, vo lutil_pushint64(L, s); lua_pushinteger(L, what); + e->L = L; if(lua_pcall(L, n+2, 0, 0)){ assert(lua_gettop(L) >= top); lua_settop(L, top); diff --git a/test/test_multi_callback.lua b/test/test_multi_callback.lua new file mode 100644 index 0000000..3a81b00 --- /dev/null +++ b/test/test_multi_callback.lua @@ -0,0 +1,109 @@ +local curl = require "lcurl" + +local called, active_coroutine = 0 + +function on_timer() + called = called + 1 + -- use `os.exit` because now Lua-cURL did not propogate error from callback + if coroutine.running() ~= active_coroutine then os.exit(-1) end +end + +local function test_1() + io.write('Test #1 - ') + + called, active_coroutine = 0 + + local e = curl.easy() + local m = curl.multi{ timerfunction = on_timer } + + active_coroutine = coroutine.create(function() + m:add_handle(e) + end) + + coroutine.resume(active_coroutine) + assert(called == 1) + + active_coroutine = nil + m:remove_handle(e) + assert(called == 2) + + io.write('pass!\n') +end + +local function test_2() + io.write('Test #2 - ') + + called, active_coroutine = 0 + + local e = curl.easy() + local m = curl.multi{ timerfunction = on_timer } + + active_coroutine = coroutine.create(function() + m:add_handle(e) + end) + + coroutine.resume(active_coroutine) + assert(called == 1) + + active_coroutine = coroutine.create(function() + m:remove_handle(e) + end) + coroutine.resume(active_coroutine) + assert(called == 2) + + io.write('pass!\n') +end + +local function test_3() + io.write('Test #3 - ') + + called, active_coroutine = 0 + + local e = curl.easy() + local m = curl.multi{ timerfunction = on_timer } + + active_coroutine = coroutine.create(function() + m:add_handle(e) + end) + + coroutine.resume(active_coroutine) + assert(called == 1) + + active_coroutine = nil + e:close() + assert(called == 2) + + io.write('pass!\n') +end + +local function test_4() + io.write('Test #4 - ') + + called, active_coroutine = 0 + + local e = curl.easy() + local m = curl.multi{ timerfunction = on_timer } + + active_coroutine = coroutine.create(function() + m:add_handle(e) + end) + + coroutine.resume(active_coroutine) + assert(called == 1) + + active_coroutine = coroutine.create(function() + e:close() + end) + coroutine.resume(active_coroutine) + assert(called == 2) + + io.write('pass!\n') +end + +test_1() + +test_2() + +test_3() + +test_4() From e7f83e7aa631e437db7d1d98f5182cb20fa22657 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 27 Sep 2016 18:06:17 +0300 Subject: [PATCH 02/10] Fix. `CURLM_ADDED_ALREADY` defined only in cURL 7.32.1 --- src/lcmulti.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lcmulti.c b/src/lcmulti.c index 2213f33..f35beee 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -110,7 +110,13 @@ static int lcurl_multi_add_handle(lua_State *L){ CURLMcode code; if(e->multi){ - return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, CURLM_ADDED_ALREADY); + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, +#if LCURL_CURL_VER_GE(7,32,1) + CURLM_ADDED_ALREADY +#else + CURLM_BAD_EASY_HANDLE +#endif + ); } // From doc: From a836734bfc02b695aa102b257b22c0f0f13e4a34 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 27 Sep 2016 18:20:34 +0300 Subject: [PATCH 03/10] Fix. Test for Lua > 5.1 --- test/test_multi_callback.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/test_multi_callback.lua b/test/test_multi_callback.lua index 3a81b00..4240557 100644 --- a/test/test_multi_callback.lua +++ b/test/test_multi_callback.lua @@ -2,10 +2,17 @@ local curl = require "lcurl" local called, active_coroutine = 0 +-- for Lua 5.1 compat +local function co_running() + local co, main = coroutine.running() + if main == true then return nil end + return co +end + function on_timer() called = called + 1 -- use `os.exit` because now Lua-cURL did not propogate error from callback - if coroutine.running() ~= active_coroutine then os.exit(-1) end + if co_running() ~= active_coroutine then os.exit(-1) end end local function test_1() From 9a1270fde6a143fd100a306853ce26df5da7ac59 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 27 Sep 2016 19:01:15 +0300 Subject: [PATCH 04/10] Fix. `curl_multi_socket_action` may invoke `easy` callbacks before `socket` callback. --- src/lcmulti.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lcmulti.c b/src/lcmulti.c index f35beee..cdc80dc 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -311,7 +311,7 @@ static int lcurl_multi_socket_action(lua_State *L){ if(s == CURL_SOCKET_TIMEOUT) mask = lutil_optint64(L, 3, 0); else mask = lutil_checkint64(L, 3); - lcurl__multi_assign_lua(L, p, 0); + lcurl__multi_assign_lua(L, p, 1); code = curl_multi_socket_action(p->curl, s, mask, &n); p->L = NULL; @@ -470,7 +470,6 @@ static int lcurl_multi_socket_callback(CURL *easy, curl_socket_t s, int what, vo lutil_pushint64(L, s); lua_pushinteger(L, what); - e->L = L; if(lua_pcall(L, n+2, 0, 0)){ assert(lua_gettop(L) >= top); lua_settop(L, top); From b2e9474c6bf975a0f1813a387e2c0ec21d3064f3 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 27 Sep 2016 19:26:13 +0300 Subject: [PATCH 05/10] Fix. reset `lcurl_easy_t::multi` pointer when `info_read` function remove easy handle. --- src/lcmulti.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lcmulti.c b/src/lcmulti.c index cdc80dc..61f78b8 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -214,6 +214,7 @@ static int lcurl_multi_info_read(lua_State *L){ if(remove){ //! @fixme We ignore any errors if(CURLM_OK == curl_multi_remove_handle(p->curl, e->curl)){ + e->multi = NULL; lua_pushnil(L); lua_rawsetp(L, -3, e->curl); } From 56b4d05c17b406d790721b1921314a5da7da2c58 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 28 Sep 2016 12:53:40 +0300 Subject: [PATCH 06/10] Fix. Set Lua in callback context in `info_read` function. Fix. Support nested callbacks. Fix. Cleanup easy references when calls multi::close. Add. `__tostring` method for handles. --- .travis.yml | 1 + appveyor.yml | 1 + src/lceasy.c | 102 +++++++++++++++++++--------- src/lceasy.h | 2 + src/lcmulti.c | 71 +++++++++++++------ src/lcmulti.h | 2 + src/lcshare.c | 7 ++ test/test_multi_nested_callback.lua | 47 +++++++++++++ 8 files changed, 180 insertions(+), 53 deletions(-) create mode 100644 test/test_multi_nested_callback.lua diff --git a/.travis.yml b/.travis.yml index 0967a9a..53c5d2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,7 @@ script: - lunit.sh run.lua - lua test_pause02.c.lua - lua test_multi_callback.lua + - lua test_multi_nested_callback.lua.lua # - lunit.sh test_easy.lua # - lunit.sh test_safe.lua # - lunit.sh test_form.lua diff --git a/appveyor.yml b/appveyor.yml index 92a1427..3de24b5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -57,6 +57,7 @@ test_script: - lua run.lua - lua test_pause02.c.lua - lua test_multi_callback.lua + - lua test_multi_nested_callback.lua after_test: - cd %APPVEYOR_BUILD_FOLDER% diff --git a/src/lceasy.c b/src/lceasy.c index 82ef5ee..de84c15 100644 --- a/src/lceasy.c +++ b/src/lceasy.c @@ -28,6 +28,31 @@ static const char *LCURL_EASY = LCURL_EASY_NAME; # define LCURL_E_UNKNOWN_OPTION CURLE_UNKNOWN_TELNET_OPTION #endif +/* Before call curl_XXX function which can call any callback + * need set Curren Lua thread pointer in easy/multi contexts. + * But it also possible that we already in callback call. + * E.g. `curl_easy_pause` function may be called from write callback. + * and it even may be called in different thread. + * ```Lua + * multi:add_handle(easy) + * easy:setopt_writefunction(function(...) + * coroutine.wrap(function() multi:add_handle(easy2) end)() + * end) + * ``` + * So we have to restore previews Lua state in callback contexts. + */ +void lcurl__easy_assign_lua(lua_State *L, lcurl_easy_t *p, lua_State *value, int assign_multi){ + if(p->multi && assign_multi){ + lcurl__multi_assign_lua(L, p->multi, value, 1); + } + else{ + p->L = value; + if(p->post){ + p->post->L = value; + } + } +} + //{ int lcurl_easy_create(lua_State *L, int error_mode){ @@ -72,24 +97,25 @@ lcurl_easy_t *lcurl_geteasy_at(lua_State *L, int i){ return p; } +static int lcurl_easy_to_s(lua_State *L){ + lcurl_easy_t *p = (lcurl_easy_t *)lutil_checkudatap (L, 1, LCURL_EASY); + lua_pushfstring(L, LCURL_PREFIX " Easy (%p)", (void*)p); + return 1; +} + static int lcurl_easy_cleanup(lua_State *L){ lcurl_easy_t *p = lcurl_geteasy(L); int i; if(p->curl){ - p->L = L; - if(p->post){ - p->post->L = L; - } + lua_State *curL; + // In my tests when I cleanup some easy handle. // timerfunction called only for single multi handle. - if(p->multi){ - p->multi->L = L; - } + curL = p->L; lcurl__easy_assign_lua(L, p, L, 1); curl_easy_cleanup(p->curl); - if(p->multi){ - p->multi->L = NULL; - } + lcurl__easy_assign_lua(L, p, curL, 1); + p->curl = NULL; } @@ -127,18 +153,17 @@ static int lcurl_easy_cleanup(lua_State *L){ static int lcurl_easy_perform(lua_State *L){ lcurl_easy_t *p = lcurl_geteasy(L); CURLcode code; + lua_State *curL; int top = 1; lua_settop(L, top); assert(p->rbuffer.ref == LUA_NOREF); // store reference to current coroutine to callbacks - p->L = L; - if(p->post){ - p->post->L = L; - } - + // User should not call `perform` if handle assign to multi + curL = p->L; lcurl__easy_assign_lua(L, p, L, 0); code = curl_easy_perform(p->curl); + lcurl__easy_assign_lua(L, p, curL, 0); if(p->rbuffer.ref != LUA_NOREF){ luaL_unref(L, LCURL_LUA_REGISTRY, p->rbuffer.ref); @@ -744,6 +769,7 @@ static size_t lcurl_write_callback_(lua_State*L, static size_t lcurl_write_callback(char *ptr, size_t size, size_t nmemb, void *arg){ lcurl_easy_t *p = arg; + assert(NULL != p->L); return lcurl_write_callback_(p->L, p, &p->wr, ptr, size, nmemb); } @@ -847,11 +873,13 @@ static size_t lcurl_easy_read_callback(char *buffer, size_t size, size_t nitems, if(p->magic == LCURL_HPOST_STREAM_MAGIC){ return lcurl_hpost_read_callback(buffer, size, nitems, arg); } + assert(NULL != p->L); return lcurl_read_callback(p->L, &p->rd, &p->rbuffer, buffer, size, nitems); } static size_t lcurl_hpost_read_callback(char *buffer, size_t size, size_t nitems, void *arg){ lcurl_hpost_stream_t *p = arg; + assert(NULL != p->L); return lcurl_read_callback(*p->L, &p->rd, &p->rbuffer, buffer, size, nitems); } @@ -869,6 +897,7 @@ static int lcurl_easy_set_READFUNCTION(lua_State *L){ static size_t lcurl_header_callback(char *ptr, size_t size, size_t nmemb, void *arg){ lcurl_easy_t *p = arg; + assert(NULL != p->L); return lcurl_write_callback_(p->L, p, &p->hd, ptr, size, nmemb); } @@ -889,10 +918,12 @@ static int lcurl_xferinfo_callback(void *arg, curl_off_t dltotal, curl_off_t dln { lcurl_easy_t *p = arg; lua_State *L = p->L; + int n, top, ret = 0; + + assert(NULL != p->L); - int ret = 0; - int top = lua_gettop(L); - int n = lcurl_util_push_cb(L, &p->pr); + top = lua_gettop(L); + n = lcurl_util_push_cb(L, &p->pr); lua_pushnumber( L, (lua_Number)dltotal ); lua_pushnumber( L, (lua_Number)dlnow ); @@ -1033,8 +1064,14 @@ static int lcurl_easy_getinfo(lua_State *L){ static int lcurl_easy_pause(lua_State *L){ lcurl_easy_t *p = lcurl_geteasy(L); + lua_State *curL; int mask = luaL_checkint(L, 2); - CURLcode code = curl_easy_pause(p->curl, mask); + CURLcode code; + + curL = p->L; lcurl__easy_assign_lua(L, p, L, 1); + code = curl_easy_pause(p->curl, mask); + lcurl__easy_assign_lua(L, p, curL, 1); + if(code != CURLE_OK){ return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); } @@ -1096,19 +1133,20 @@ static const struct luaL_Reg lcurl_easy_methods[] = { #include "lcinfoeasy.h" #undef OPT_ENTRY - { "pause", lcurl_easy_pause }, - { "reset", lcurl_easy_reset }, - { "setopt", lcurl_easy_setopt }, - { "getinfo", lcurl_easy_getinfo }, - { "unsetopt", lcurl_easy_unsetopt }, - { "escape", lcurl_easy_escape }, - { "unescape", lcurl_easy_unescape }, - { "perform", lcurl_easy_perform }, - { "close", lcurl_easy_cleanup }, - { "__gc", lcurl_easy_cleanup }, - - { "setdata", lcurl_easy_setdata }, - { "getdata", lcurl_easy_getdata }, + { "pause", lcurl_easy_pause }, + { "reset", lcurl_easy_reset }, + { "setopt", lcurl_easy_setopt }, + { "getinfo", lcurl_easy_getinfo }, + { "unsetopt", lcurl_easy_unsetopt }, + { "escape", lcurl_easy_escape }, + { "unescape", lcurl_easy_unescape }, + { "perform", lcurl_easy_perform }, + { "close", lcurl_easy_cleanup }, + { "__gc", lcurl_easy_cleanup }, + { "__tostring", lcurl_easy_to_s }, + + { "setdata", lcurl_easy_setdata }, + { "getdata", lcurl_easy_getdata }, {NULL,NULL} }; diff --git a/src/lceasy.h b/src/lceasy.h index 1093a65..96cdc1b 100644 --- a/src/lceasy.h +++ b/src/lceasy.h @@ -65,4 +65,6 @@ lcurl_easy_t *lcurl_geteasy_at(lua_State *L, int i); void lcurl_easy_initlib(lua_State *L, int nup); +void lcurl__easy_assign_lua(lua_State *L, lcurl_easy_t *p, lua_State *value, int assign_multi); + #endif diff --git a/src/lcmulti.c b/src/lcmulti.c index 61f78b8..08a0dd3 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -28,22 +28,19 @@ #define LCURL_MULTI_NAME LCURL_PREFIX" Multi" static const char *LCURL_MULTI = LCURL_MULTI_NAME; -static void lcurl__multi_assign_lua(lua_State *L, lcurl_multi_t *p, int assign_easy){ - p->L = L; - - if(assign_easy){ +void lcurl__multi_assign_lua(lua_State *L, lcurl_multi_t *p, lua_State *value, int assign_easy){ + if((assign_easy)&&(p->L != value)){ lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); lua_pushnil(L); while(lua_next(L, -2)){ lcurl_easy_t *e = lcurl_geteasy_at(L, -1); - e->L = L; - if(e->post){ - e->post->L = L; - } + lcurl__easy_assign_lua(L, e, value, 0); lua_pop(L, 1); } lua_pop(L, 1); } + + p->L = value; } //{ @@ -78,6 +75,12 @@ lcurl_multi_t *lcurl_getmulti_at(lua_State *L, int i){ return p; } +static int lcurl_multi_to_s(lua_State *L){ + lcurl_multi_t *p = (lcurl_multi_t *)lutil_checkudatap (L, 1, LCURL_MULTI); + lua_pushfstring(L, LCURL_PREFIX " Multi (%p)", (void*)p); + return 1; +} + static int lcurl_multi_cleanup(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); if(p->curl){ @@ -86,6 +89,14 @@ static int lcurl_multi_cleanup(lua_State *L){ } if(p->h_ref != LUA_NOREF){ + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_pushnil(L); + while(lua_next(L, -2)){ + lcurl_easy_t *e = lcurl_geteasy_at(L, -1); + e->multi = NULL; + lua_pop(L, 1); + } + lua_pop(L, 1); luaL_unref(L, LCURL_LUA_REGISTRY, p->h_ref); p->h_ref = LUA_NOREF; } @@ -108,6 +119,7 @@ static int lcurl_multi_add_handle(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); lcurl_easy_t *e = lcurl_geteasy_at(L, 2); CURLMcode code; + lua_State *curL; if(e->multi){ return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, @@ -135,9 +147,9 @@ static int lcurl_multi_add_handle(lua_State *L){ e->multi = p; - lcurl__multi_assign_lua(L, p, 0); + curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); code = curl_multi_add_handle(p->curl, e->curl); - p->L = NULL; + lcurl__multi_assign_lua(L, p, curL, 1); if(code != CURLM_OK){ // remove @@ -155,6 +167,7 @@ static int lcurl_multi_remove_handle(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); lcurl_easy_t *e = lcurl_geteasy_at(L, 2); CURLMcode code; + lua_State *curL; if(e->multi != p){ // cURL returns CURLM_OK for such call so we do the same. @@ -163,9 +176,9 @@ static int lcurl_multi_remove_handle(lua_State *L){ return 1; } - lcurl__multi_assign_lua(L, p, 0); + curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); code = curl_multi_remove_handle(p->curl, e->curl); - p->L = NULL; + lcurl__multi_assign_lua(L, p, curL, 1); if(code != CURLM_OK){ lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); @@ -183,10 +196,11 @@ static int lcurl_multi_perform(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); int running_handles = 0; CURLMcode code; + lua_State *curL; - lcurl__multi_assign_lua(L, p, 1); + curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); while((code = curl_multi_perform(p->curl, &running_handles)) == CURLM_CALL_MULTI_PERFORM); - p->L = NULL; + lcurl__multi_assign_lua(L, p, curL, 1); if(code != CURLM_OK){ lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); @@ -213,7 +227,14 @@ static int lcurl_multi_info_read(lua_State *L){ e = lcurl_geteasy_at(L, -1); if(remove){ //! @fixme We ignore any errors - if(CURLM_OK == curl_multi_remove_handle(p->curl, e->curl)){ + CURLMcode code; + lua_State *curL; + + curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); + code = curl_multi_remove_handle(p->curl, e->curl); + lcurl__multi_assign_lua(L, p, curL, 1); + + if(CURLM_OK == code){ e->multi = NULL; lua_pushnil(L); lua_rawsetp(L, -3, e->curl); @@ -309,12 +330,14 @@ static int lcurl_multi_socket_action(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); curl_socket_t s = lutil_optint64(L, 2, CURL_SOCKET_TIMEOUT); CURLMcode code; int n, mask; + lua_State *curL; + if(s == CURL_SOCKET_TIMEOUT) mask = lutil_optint64(L, 3, 0); else mask = lutil_checkint64(L, 3); - lcurl__multi_assign_lua(L, p, 1); + curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); code = curl_multi_socket_action(p->curl, s, mask, &n); - p->L = NULL; + lcurl__multi_assign_lua(L, p, curL, 1); if(code != CURLM_OK){ lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); @@ -415,10 +438,12 @@ static int lcurl_multi_set_callback(lua_State *L, int lcurl_multi_timer_callback(CURLM *multi, long ms, void *arg){ lcurl_multi_t *p = arg; lua_State *L = p->L; + int n, top, ret = 0; - int ret = 0; - int top = lua_gettop(L); - int n = lcurl_util_push_cb(L, &p->tm); + assert(NULL != p->L); + + top = lua_gettop(L); + n = lcurl_util_push_cb(L, &p->tm); lua_pushnumber(L, ms); if(lua_pcall(L, n, LUA_MULTRET, 0)){ @@ -460,8 +485,11 @@ static int lcurl_multi_socket_callback(CURL *easy, curl_socket_t s, int what, vo lcurl_multi_t *p = arg; lua_State *L = p->L; lcurl_easy_t *e; - int n, top = lua_gettop(L); + int n, top; + + assert(NULL != p->L); + top = lua_gettop(L); n = lcurl_util_push_cb(L, &p->sc); lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); @@ -547,6 +575,7 @@ static const struct luaL_Reg lcurl_multi_methods[] = { {"wait", lcurl_multi_wait }, {"timeout", lcurl_multi_timeout }, {"socket_action", lcurl_multi_socket_action }, + { "__tostring", lcurl_multi_to_s }, #define OPT_ENTRY(L, N, T, S) { "setopt_"#L, lcurl_multi_set_##N }, #include "lcoptmulti.h" diff --git a/src/lcmulti.h b/src/lcmulti.h index 73d767f..16e6d7c 100644 --- a/src/lcmulti.h +++ b/src/lcmulti.h @@ -31,4 +31,6 @@ lcurl_multi_t *lcurl_getmulti_at(lua_State *L, int i); void lcurl_multi_initlib(lua_State *L, int nup); +void lcurl__multi_assign_lua(lua_State *L, lcurl_multi_t *p, lua_State *value, int assign_easy); + #endif diff --git a/src/lcshare.c b/src/lcshare.c index e310603..604111a 100644 --- a/src/lcshare.c +++ b/src/lcshare.c @@ -43,6 +43,12 @@ lcurl_share_t *lcurl_getshare_at(lua_State *L, int i){ return p; } +static int lcurl_easy_to_s(lua_State *L){ + lcurl_share_t *p = (lcurl_share_t *)lutil_checkudatap (L, 1, LCURL_SHARE); + lua_pushfstring(L, LCURL_PREFIX " Share (%p)", (void*)p); + return 1; +} + static int lcurl_share_cleanup(lua_State *L){ lcurl_share_t *p = lcurl_getshare(L); if(p->curl){ @@ -113,6 +119,7 @@ static int lcurl_share_setopt(lua_State *L){ //} static const struct luaL_Reg lcurl_share_methods[] = { + { "__tostring", lcurl_easy_to_s }, {"setopt", lcurl_share_setopt }, #define OPT_ENTRY(L, N, T, S) { "setopt_"#L, lcurl_share_set_##N }, diff --git a/test/test_multi_nested_callback.lua b/test/test_multi_nested_callback.lua new file mode 100644 index 0000000..31d8fd9 --- /dev/null +++ b/test/test_multi_nested_callback.lua @@ -0,0 +1,47 @@ +local curl = require "lcurl" + +-- for Lua 5.1 compat +local function co_running() + local co, main = coroutine.running() + if main == true then return nil end + return co +end + +local state, called = true, 0 +local thread, msg + +local function check_thread() + if thread ~= co_running() then + print(msg) + os.exit(-1) + end +end + +local m; m = curl.multi{ + timerfunction = function() + check_thread() + called = called + 1 + + if state then state = false + thread = coroutine.create(function() + local e = curl.easy() + m:add_handle(e) + end) + + msg = 'add from coroutine' + coroutine.resume(thread) + assert(called == 2) + + msg, thread = 'add from main' + local e = curl.easy() + m:add_handle(e) + assert(called == 3) + end + end +} + +e = curl.easy() + +m:add_handle(e) + +assert(called == 3) From 54a75bd25ee899bcacd92dde9200461dd5a5e5bc Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 28 Sep 2016 13:01:49 +0300 Subject: [PATCH 07/10] Fix. Unify `tostring` methods --- src/lceasy.c | 4 ++-- src/lchttppost.c | 8 +++++++- src/lcmulti.c | 4 ++-- src/lcshare.c | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/lceasy.c b/src/lceasy.c index de84c15..f02aeca 100644 --- a/src/lceasy.c +++ b/src/lceasy.c @@ -93,13 +93,13 @@ int lcurl_easy_create(lua_State *L, int error_mode){ lcurl_easy_t *lcurl_geteasy_at(lua_State *L, int i){ lcurl_easy_t *p = (lcurl_easy_t *)lutil_checkudatap (L, i, LCURL_EASY); - luaL_argcheck (L, p != NULL, 1, LCURL_EASY_NAME" expected"); + luaL_argcheck (L, p != NULL, 1, LCURL_EASY_NAME" object expected"); return p; } static int lcurl_easy_to_s(lua_State *L){ lcurl_easy_t *p = (lcurl_easy_t *)lutil_checkudatap (L, 1, LCURL_EASY); - lua_pushfstring(L, LCURL_PREFIX " Easy (%p)", (void*)p); + lua_pushfstring(L, LCURL_EASY_NAME" (%p)", (void*)p); return 1; } diff --git a/src/lchttppost.c b/src/lchttppost.c index e9e1ecf..65c4ff0 100644 --- a/src/lchttppost.c +++ b/src/lchttppost.c @@ -112,10 +112,16 @@ int lcurl_hpost_create(lua_State *L, int error_mode){ lcurl_hpost_t *lcurl_gethpost_at(lua_State *L, int i){ lcurl_hpost_t *p = (lcurl_hpost_t *)lutil_checkudatap (L, i, LCURL_HTTPPOST); - luaL_argcheck (L, p != NULL, 1, LCURL_PREFIX"HTTPPost object expected"); + luaL_argcheck (L, p != NULL, 1, LCURL_HTTPPOST_NAME" object expected"); return p; } +static int lcurl_hpost_to_s(lua_State *L){ + lcurl_hpost_t *p = (lcurl_hpost_t *)lutil_checkudatap (L, 1, LCURL_HTTPPOST); + lua_pushfstring(L, LCURL_HTTPPOST_NAME" (%p)", (void*)p); + return 1; +} + static int lcurl_hpost_add_content(lua_State *L){ // add_buffer(name, data, [type,] [headers]) lcurl_hpost_t *p = lcurl_gethpost(L); diff --git a/src/lcmulti.c b/src/lcmulti.c index 08a0dd3..925535c 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -71,13 +71,13 @@ int lcurl_multi_create(lua_State *L, int error_mode){ lcurl_multi_t *lcurl_getmulti_at(lua_State *L, int i){ lcurl_multi_t *p = (lcurl_multi_t *)lutil_checkudatap (L, i, LCURL_MULTI); - luaL_argcheck (L, p != NULL, 1, LCURL_MULTI_NAME" expected"); + luaL_argcheck (L, p != NULL, 1, LCURL_MULTI_NAME" object expected"); return p; } static int lcurl_multi_to_s(lua_State *L){ lcurl_multi_t *p = (lcurl_multi_t *)lutil_checkudatap (L, 1, LCURL_MULTI); - lua_pushfstring(L, LCURL_PREFIX " Multi (%p)", (void*)p); + lua_pushfstring(L, LCURL_MULTI_NAME" (%p)", (void*)p); return 1; } diff --git a/src/lcshare.c b/src/lcshare.c index 604111a..88e93f1 100644 --- a/src/lcshare.c +++ b/src/lcshare.c @@ -39,13 +39,13 @@ int lcurl_share_create(lua_State *L, int error_mode){ lcurl_share_t *lcurl_getshare_at(lua_State *L, int i){ lcurl_share_t *p = (lcurl_share_t *)lutil_checkudatap (L, i, LCURL_SHARE); - luaL_argcheck (L, p != NULL, 1, LCURL_SHARE_NAME" expected"); + luaL_argcheck (L, p != NULL, 1, LCURL_SHARE_NAME" object expected"); return p; } static int lcurl_easy_to_s(lua_State *L){ lcurl_share_t *p = (lcurl_share_t *)lutil_checkudatap (L, 1, LCURL_SHARE); - lua_pushfstring(L, LCURL_PREFIX " Share (%p)", (void*)p); + lua_pushfstring(L, LCURL_SHARE_NAME" (%p)", (void*)p); return 1; } From 367eb1482f651504a03b11ea4225e30f6115df29 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 28 Sep 2016 13:03:02 +0300 Subject: [PATCH 08/10] Fix. Travis test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 53c5d2e..197f214 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,7 @@ script: - lunit.sh run.lua - lua test_pause02.c.lua - lua test_multi_callback.lua - - lua test_multi_nested_callback.lua.lua + - lua test_multi_nested_callback.lua # - lunit.sh test_easy.lua # - lunit.sh test_safe.lua # - lunit.sh test_form.lua From 78a4a03e23329178d5b81d903d9279a8d1565b30 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 28 Sep 2016 15:52:22 +0300 Subject: [PATCH 09/10] Fix. `easy:close` removes self from `multi` handle. Change. Do not reset Lua state back to NULL. --- lakefile | 7 +++-- src/lceasy.c | 17 ++++++++++++ src/lcmulti.c | 77 +++++++++++++++++++++++++++++++++++++++++++-------- src/lcmulti.h | 2 ++ 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/lakefile b/lakefile index 1a2530b..3421276 100644 --- a/lakefile +++ b/lakefile @@ -2,8 +2,9 @@ PROJECT = 'cURL' INITLAKEFILE() -DEFINES = L{DEFINES, +DEFINES = L{DEFINES, IF(WINDOWS, 'DLL_EXPORT', ''); + IF(DEBUG, 'LCURL_RESET_NULL_LUA', ''); } cURL = c.shared{'lcurl', @@ -18,7 +19,7 @@ cURL = c.shared{'lcurl', target('build', cURL) install = target('install', { - file.group{odir=LIBDIR; src = cURL }; + file.group{odir=LIBDIR; src = cURL }; file.group{odir=LIBDIR; src = J("src", "lua") ; recurse = true }; file.group{odir=J(ROOT, 'examples'); src = 'examples'; recurse = true }; file.group{odir=TESTDIR; src = 'test'; recurse = true }; @@ -30,6 +31,8 @@ target('test', install, function() run_test('test_form.lua') run_test('test_pause02.c.lua') run_test('test_curl.lua') + run_test('test_multi_callback.lua') + run_test('test_multi_nested_callback.lua') if not test_summary() then quit("test fail") diff --git a/src/lceasy.c b/src/lceasy.c index f02aeca..b237884 100644 --- a/src/lceasy.c +++ b/src/lceasy.c @@ -40,6 +40,8 @@ static const char *LCURL_EASY = LCURL_EASY_NAME; * end) * ``` * So we have to restore previews Lua state in callback contexts. + * But if previews Lua state is NULL then we can just do not set it back. + * But set it to NULL make esier debug code. */ void lcurl__easy_assign_lua(lua_State *L, lcurl_easy_t *p, lua_State *value, int assign_multi){ if(p->multi && assign_multi){ @@ -107,6 +109,12 @@ static int lcurl_easy_cleanup(lua_State *L){ lcurl_easy_t *p = lcurl_geteasy(L); int i; + if(p->multi){ + CURLMcode code = lcurl__multi_remove_handle(L, p->multi, p); + + //! @todo what I can do if I can not remove it??? + } + if(p->curl){ lua_State *curL; @@ -114,6 +122,9 @@ static int lcurl_easy_cleanup(lua_State *L){ // timerfunction called only for single multi handle. curL = p->L; lcurl__easy_assign_lua(L, p, L, 1); curl_easy_cleanup(p->curl); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__easy_assign_lua(L, p, curL, 1); p->curl = NULL; @@ -163,6 +174,9 @@ static int lcurl_easy_perform(lua_State *L){ // User should not call `perform` if handle assign to multi curL = p->L; lcurl__easy_assign_lua(L, p, L, 0); code = curl_easy_perform(p->curl); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__easy_assign_lua(L, p, curL, 0); if(p->rbuffer.ref != LUA_NOREF){ @@ -1070,6 +1084,9 @@ static int lcurl_easy_pause(lua_State *L){ curL = p->L; lcurl__easy_assign_lua(L, p, L, 1); code = curl_easy_pause(p->curl, mask); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__easy_assign_lua(L, p, curL, 1); if(code != CURLE_OK){ diff --git a/src/lcmulti.c b/src/lcmulti.c index 925535c..1e86e4a 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -28,7 +28,35 @@ #define LCURL_MULTI_NAME LCURL_PREFIX" Multi" static const char *LCURL_MULTI = LCURL_MULTI_NAME; +#if defined(DEBUG) || defined(_DEBUG) +static void lcurl__multi_validate_sate(lua_State *L, lcurl_multi_t *p){ + int top = lua_gettop(L); + + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + assert(lua_istable(L, -1)); + + lua_pushnil(L); + while(lua_next(L, -2)){ + lcurl_easy_t *e = lcurl_geteasy_at(L, -1); + void *ptr = lua_touserdata(L, -2); + + assert(e->curl == ptr); + assert(e->multi == p); + assert(e->L == p->L); + + lua_pop(L, 1); + } + + lua_pop(L, 1); + assert(lua_gettop(L) == top); +} +#else +# define lcurl__multi_validate_sate(L, p) (void*)(0) +#endif + void lcurl__multi_assign_lua(lua_State *L, lcurl_multi_t *p, lua_State *value, int assign_easy){ + lcurl__multi_validate_sate(L, p); + if((assign_easy)&&(p->L != value)){ lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); lua_pushnil(L); @@ -145,10 +173,16 @@ static int lcurl_multi_add_handle(lua_State *L){ lua_rawsetp(L, -2, e->curl); lua_settop(L, 1); + // all `esay` handles have to have same L + lcurl__easy_assign_lua(L, e, p->L, 0); + e->multi = p; curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); code = curl_multi_add_handle(p->curl, e->curl); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__multi_assign_lua(L, p, curL, 1); if(code != CURLM_OK){ @@ -166,30 +200,42 @@ static int lcurl_multi_add_handle(lua_State *L){ static int lcurl_multi_remove_handle(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); lcurl_easy_t *e = lcurl_geteasy_at(L, 2); + CURLMcode code = lcurl__multi_remove_handle(L, p, e); + + if(code != CURLM_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); + } + + lua_settop(L, 1); + return 1; +} + +CURLMcode lcurl__multi_remove_handle(lua_State *L, lcurl_multi_t *p, lcurl_easy_t *e){ CURLMcode code; lua_State *curL; if(e->multi != p){ // cURL returns CURLM_OK for such call so we do the same. // tested on 7.37.1 - lua_settop(L, 1); - return 1; + return CURLM_OK; } curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); code = curl_multi_remove_handle(p->curl, e->curl); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__multi_assign_lua(L, p, curL, 1); - if(code != CURLM_OK){ - lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); + if(code == CURLM_OK){ + e->multi = NULL; + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_pushnil(L); + lua_rawsetp(L, -2, e->curl); + lua_pop(L, 1); } - - e->multi = NULL; - lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); - lua_pushnil(L); - lua_rawsetp(L, -2, e->curl); - lua_settop(L, 1); - return 1; + + return code; } static int lcurl_multi_perform(lua_State *L){ @@ -200,6 +246,9 @@ static int lcurl_multi_perform(lua_State *L){ curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); while((code = curl_multi_perform(p->curl, &running_handles)) == CURLM_CALL_MULTI_PERFORM); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__multi_assign_lua(L, p, curL, 1); if(code != CURLM_OK){ @@ -232,6 +281,9 @@ static int lcurl_multi_info_read(lua_State *L){ curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); code = curl_multi_remove_handle(p->curl, e->curl); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__multi_assign_lua(L, p, curL, 1); if(CURLM_OK == code){ @@ -337,6 +389,9 @@ static int lcurl_multi_socket_action(lua_State *L){ curL = p->L; lcurl__multi_assign_lua(L, p, L, 1); code = curl_multi_socket_action(p->curl, s, mask, &n); +#ifndef LCURL_RESET_NULL_LUA + if(curL != NULL) +#endif lcurl__multi_assign_lua(L, p, curL, 1); if(code != CURLM_OK){ diff --git a/src/lcmulti.h b/src/lcmulti.h index 16e6d7c..a49f832 100644 --- a/src/lcmulti.h +++ b/src/lcmulti.h @@ -33,4 +33,6 @@ void lcurl_multi_initlib(lua_State *L, int nup); void lcurl__multi_assign_lua(lua_State *L, lcurl_multi_t *p, lua_State *value, int assign_easy); +CURLMcode lcurl__multi_remove_handle(lua_State *L, lcurl_multi_t *p, lcurl_easy_t *e); + #endif From c5e4734f63d20efb5c792b39818a28f5d002cd7c Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 28 Sep 2016 16:18:56 +0300 Subject: [PATCH 10/10] Update test. --- test/test_easy.lua | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/test_easy.lua b/test/test_easy.lua index e0cf64a..3c0a3a3 100644 --- a/test/test_easy.lua +++ b/test/test_easy.lua @@ -998,4 +998,38 @@ end end +local _ENV = TEST_CASE'multi_add_remove' if ENABLE then + +local m, c + +function setup() + m = assert(scurl.multi()) +end + +function teardown() + if c then c:close() end + if m then m:close() end + m, c = nil +end + +function test_remove_unknow_easy() + c = assert(scurl.easy()) + assert_equal(m, m:remove_handle(c)) +end + +function test_double_remove_easy() + c = assert(scurl.easy()) + assert_equal(m, m:add_handle(c)) + assert_equal(m, m:remove_handle(c)) + assert_equal(m, m:remove_handle(c)) +end + +function test_double_add_easy() + c = assert(scurl.easy()) + assert_equal(m, m:add_handle(c)) + assert_nil(m:add_handle(c)) +end + +end + RUN()