Skip to content

Commit abdef03

Browse files
committed
Fix. Call multi callbacks with correct Lua state.
1 parent f1ebfad commit abdef03

File tree

7 files changed

+203
-21
lines changed

7 files changed

+203
-21
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ script:
4545
- lua -e "print(require 'cURL.utils'.find_ca_bundle())"
4646
- lunit.sh run.lua
4747
- lua test_pause02.c.lua
48+
- lua test_multi_callback.lua
4849
# - lunit.sh test_easy.lua
4950
# - lunit.sh test_safe.lua
5051
# - lunit.sh test_form.lua

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ test_script:
5656
- cd %APPVEYOR_BUILD_FOLDER%\test
5757
- lua run.lua
5858
- lua test_pause02.c.lua
59+
- lua test_multi_callback.lua
5960

6061
after_test:
6162
- cd %APPVEYOR_BUILD_FOLDER%

examples/cURLv3/uvwget.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ end
182182

183183
on_curl_action = function(easy, fd, action)
184184
local ok, err = pcall(function()
185-
trace("CURL::SOCKET", easy, s, ACTION_NAMES[action] or action)
185+
trace("CURL::SOCKET", easy, fd, ACTION_NAMES[action] or action)
186186

187187
local context = easy.data.context
188188

src/lceasy.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "lcutils.h"
1515
#include "lchttppost.h"
1616
#include "lcshare.h"
17+
#include "lcmulti.h"
1718
#include <memory.h>
1819

1920
static const char *LCURL_ERROR_TAG = "LCURL_ERROR_TAG";
@@ -45,6 +46,7 @@ int lcurl_easy_create(lua_State *L, int error_mode){
4546
p->magic = LCURL_EASY_MAGIC;
4647
p->L = NULL;
4748
p->post = NULL;
49+
p->multi = NULL;
4850
p->storage = lcurl_storage_init(L);
4951
p->wr.cb_ref = p->wr.ud_ref = LUA_NOREF;
5052
p->rd.cb_ref = p->rd.ud_ref = LUA_NOREF;
@@ -75,7 +77,19 @@ static int lcurl_easy_cleanup(lua_State *L){
7577
int i;
7678

7779
if(p->curl){
80+
p->L = L;
81+
if(p->post){
82+
p->post->L = L;
83+
}
84+
// In my tests when I cleanup some easy handle.
85+
// timerfunction called only for single multi handle.
86+
if(p->multi){
87+
p->multi->L = L;
88+
}
7889
curl_easy_cleanup(p->curl);
90+
if(p->multi){
91+
p->multi->L = NULL;
92+
}
7993
p->curl = NULL;
8094
}
8195

src/lceasy.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ enum {
3535

3636
#define LCURL_EASY_MAGIC 0xEA
3737

38+
typedef struct lcurl_multi_tag lcurl_multi_t;
39+
3840
typedef struct lcurl_easy_tag{
3941
unsigned char magic;
4042

@@ -44,6 +46,8 @@ typedef struct lcurl_easy_tag{
4446

4547
lcurl_hpost_t *post;
4648

49+
lcurl_multi_t *multi;
50+
4751
CURL *curl;
4852
int storage;
4953
int lists[LCURL_LIST_COUNT];

src/lcmulti.c

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,36 @@
2828
#define LCURL_MULTI_NAME LCURL_PREFIX" Multi"
2929
static const char *LCURL_MULTI = LCURL_MULTI_NAME;
3030

31+
static void lcurl__multi_assign_lua(lua_State *L, lcurl_multi_t *p, int assign_easy){
32+
p->L = L;
33+
34+
if(assign_easy){
35+
lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref);
36+
lua_pushnil(L);
37+
while(lua_next(L, -2)){
38+
lcurl_easy_t *e = lcurl_geteasy_at(L, -1);
39+
e->L = L;
40+
if(e->post){
41+
e->post->L = L;
42+
}
43+
lua_pop(L, 1);
44+
}
45+
lua_pop(L, 1);
46+
}
47+
}
48+
3149
//{
3250

3351
int lcurl_multi_create(lua_State *L, int error_mode){
3452
lcurl_multi_t *p;
35-
53+
3654
lua_settop(L, 1);
3755

3856
p = lutil_newudatap(L, lcurl_multi_t, LCURL_MULTI);
3957
p->curl = curl_multi_init();
4058
p->err_mode = error_mode;
4159
if(!p->curl) return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, CURLM_INTERNAL_ERROR);
42-
p->L = L;
60+
p->L = NULL;
4361
lcurl_util_new_weak_table(L, "v");
4462
p->h_ref = luaL_ref(L, LCURL_LUA_REGISTRY);
4563
p->tm.cb_ref = p->tm.ud_ref = LUA_NOREF;
@@ -89,24 +107,65 @@ static int lcurl_multi_cleanup(lua_State *L){
89107
static int lcurl_multi_add_handle(lua_State *L){
90108
lcurl_multi_t *p = lcurl_getmulti(L);
91109
lcurl_easy_t *e = lcurl_geteasy_at(L, 2);
92-
CURLMcode code = curl_multi_add_handle(p->curl, e->curl);
93-
if(code != CURLM_OK){
94-
lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code);
110+
CURLMcode code;
111+
112+
if(e->multi){
113+
return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, CURLM_ADDED_ALREADY);
95114
}
115+
116+
// From doc:
117+
// If you have CURLMOPT_TIMERFUNCTION set in the multi handle,
118+
// that callback will be called from within this function to ask
119+
// for an updated timer so that your main event loop will get
120+
// the activity on this handle to get started.
121+
//
122+
// So we should add easy before this call
123+
// call chain may be like => timerfunction->socket_action->socketfunction
124+
lua_settop(L, 2);
96125
lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref);
97126
lua_pushvalue(L, 2);
98127
lua_rawsetp(L, -2, e->curl);
99128
lua_settop(L, 1);
129+
130+
e->multi = p;
131+
132+
lcurl__multi_assign_lua(L, p, 0);
133+
code = curl_multi_add_handle(p->curl, e->curl);
134+
p->L = NULL;
135+
136+
if(code != CURLM_OK){
137+
// remove
138+
lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref);
139+
lua_pushnil(L);
140+
lua_rawsetp(L, -2, e->curl);
141+
e->multi = NULL;
142+
143+
return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code);
144+
}
100145
return 1;
101146
}
102147

103148
static int lcurl_multi_remove_handle(lua_State *L){
104149
lcurl_multi_t *p = lcurl_getmulti(L);
105150
lcurl_easy_t *e = lcurl_geteasy_at(L, 2);
106-
CURLMcode code = curl_multi_remove_handle(p->curl, e->curl);
151+
CURLMcode code;
152+
153+
if(e->multi != p){
154+
// cURL returns CURLM_OK for such call so we do the same.
155+
// tested on 7.37.1
156+
lua_settop(L, 1);
157+
return 1;
158+
}
159+
160+
lcurl__multi_assign_lua(L, p, 0);
161+
code = curl_multi_remove_handle(p->curl, e->curl);
162+
p->L = NULL;
163+
107164
if(code != CURLM_OK){
108165
lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code);
109166
}
167+
168+
e->multi = NULL;
110169
lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref);
111170
lua_pushnil(L);
112171
lua_rawsetp(L, -2, e->curl);
@@ -119,21 +178,10 @@ static int lcurl_multi_perform(lua_State *L){
119178
int running_handles = 0;
120179
CURLMcode code;
121180

122-
lua_settop(L, 1);
123-
lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref);
124-
lua_pushnil(L);
125-
while(lua_next(L, 2)){
126-
lcurl_easy_t *e = lcurl_geteasy_at(L, -1);
127-
e->L = L;
128-
if(e->post){
129-
e->post->L = L;
130-
}
131-
lua_pop(L, 1);
132-
}
133-
134-
lua_settop(L, 1);
135-
181+
lcurl__multi_assign_lua(L, p, 1);
136182
while((code = curl_multi_perform(p->curl, &running_handles)) == CURLM_CALL_MULTI_PERFORM);
183+
p->L = NULL;
184+
137185
if(code != CURLM_OK){
138186
lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code);
139187
}
@@ -256,7 +304,11 @@ static int lcurl_multi_socket_action(lua_State *L){
256304
CURLMcode code; int n, mask;
257305
if(s == CURL_SOCKET_TIMEOUT) mask = lutil_optint64(L, 3, 0);
258306
else mask = lutil_checkint64(L, 3);
307+
308+
lcurl__multi_assign_lua(L, p, 0);
259309
code = curl_multi_socket_action(p->curl, s, mask, &n);
310+
p->L = NULL;
311+
260312
if(code != CURLM_OK){
261313
lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code);
262314
}
@@ -412,6 +464,7 @@ static int lcurl_multi_socket_callback(CURL *easy, curl_socket_t s, int what, vo
412464
lutil_pushint64(L, s);
413465
lua_pushinteger(L, what);
414466

467+
e->L = L;
415468
if(lua_pcall(L, n+2, 0, 0)){
416469
assert(lua_gettop(L) >= top);
417470
lua_settop(L, top);

test/test_multi_callback.lua

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
local curl = require "lcurl"
2+
3+
local called, active_coroutine = 0
4+
5+
function on_timer()
6+
called = called + 1
7+
-- use `os.exit` because now Lua-cURL did not propogate error from callback
8+
if coroutine.running() ~= active_coroutine then os.exit(-1) end
9+
end
10+
11+
local function test_1()
12+
io.write('Test #1 - ')
13+
14+
called, active_coroutine = 0
15+
16+
local e = curl.easy()
17+
local m = curl.multi{ timerfunction = on_timer }
18+
19+
active_coroutine = coroutine.create(function()
20+
m:add_handle(e)
21+
end)
22+
23+
coroutine.resume(active_coroutine)
24+
assert(called == 1)
25+
26+
active_coroutine = nil
27+
m:remove_handle(e)
28+
assert(called == 2)
29+
30+
io.write('pass!\n')
31+
end
32+
33+
local function test_2()
34+
io.write('Test #2 - ')
35+
36+
called, active_coroutine = 0
37+
38+
local e = curl.easy()
39+
local m = curl.multi{ timerfunction = on_timer }
40+
41+
active_coroutine = coroutine.create(function()
42+
m:add_handle(e)
43+
end)
44+
45+
coroutine.resume(active_coroutine)
46+
assert(called == 1)
47+
48+
active_coroutine = coroutine.create(function()
49+
m:remove_handle(e)
50+
end)
51+
coroutine.resume(active_coroutine)
52+
assert(called == 2)
53+
54+
io.write('pass!\n')
55+
end
56+
57+
local function test_3()
58+
io.write('Test #3 - ')
59+
60+
called, active_coroutine = 0
61+
62+
local e = curl.easy()
63+
local m = curl.multi{ timerfunction = on_timer }
64+
65+
active_coroutine = coroutine.create(function()
66+
m:add_handle(e)
67+
end)
68+
69+
coroutine.resume(active_coroutine)
70+
assert(called == 1)
71+
72+
active_coroutine = nil
73+
e:close()
74+
assert(called == 2)
75+
76+
io.write('pass!\n')
77+
end
78+
79+
local function test_4()
80+
io.write('Test #4 - ')
81+
82+
called, active_coroutine = 0
83+
84+
local e = curl.easy()
85+
local m = curl.multi{ timerfunction = on_timer }
86+
87+
active_coroutine = coroutine.create(function()
88+
m:add_handle(e)
89+
end)
90+
91+
coroutine.resume(active_coroutine)
92+
assert(called == 1)
93+
94+
active_coroutine = coroutine.create(function()
95+
e:close()
96+
end)
97+
coroutine.resume(active_coroutine)
98+
assert(called == 2)
99+
100+
io.write('pass!\n')
101+
end
102+
103+
test_1()
104+
105+
test_2()
106+
107+
test_3()
108+
109+
test_4()

0 commit comments

Comments
 (0)