2828#define LCURL_MULTI_NAME LCURL_PREFIX" Multi"
2929static 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
3351int 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){
89107static 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
103148static 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 );
0 commit comments