Skip to content

Commit e7712b9

Browse files
committed
Fix. Init socket callback with nil.
Fix. `multi.setopt` supports socket callback. Change. `socket_acttion` without args means timeout action. Change. cURLv3 multi supports options in callback. Add. `multi-uv` example.
1 parent 619fb1b commit e7712b9

3 files changed

Lines changed: 172 additions & 5 deletions

File tree

examples/cURLv3/multi-uv.lua

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
local curl = require "cURL"
2+
local uv = require "lluv"
3+
4+
local fprintf = function(f, ...) f:write((string.format(...))) end
5+
local printf = function(...) fprintf(io.stdout, ...) end
6+
7+
local stderr = io.stderr
8+
9+
local timeout, curl_handle
10+
11+
local ACTION_NAMES = {
12+
[curl.POLL_IN ] = "POLL_IN";
13+
[curl.POLL_INOUT ] = "POLL_INOUT";
14+
[curl.POLL_OUT ] = "POLL_OUT";
15+
[curl.POLL_NONE ] = "POLL_NONE";
16+
[curl.POLL_REMOVE ] = "POLL_REMOVE";
17+
}
18+
local EVENT_NAMES = {
19+
[ uv.READABLE ] = "READABLE";
20+
[ uv.WRITABLE ] = "WRITABLE";
21+
[ uv.READABLE + uv.WRITABLE ] = "READABLE + WRITABLE";
22+
}
23+
local FLAGS = {
24+
[ uv.READABLE ] = curl.CSELECT_IN;
25+
[ uv.WRITABLE ] = curl.CSELECT_OUT;
26+
[ uv.READABLE + uv.WRITABLE ] = curl.CSELECT_IN + curl.CSELECT_OUT;
27+
28+
}
29+
30+
local trace = function() end or print
31+
32+
local FILES, CONTEXT = {}, {}
33+
34+
function create_curl_context(sockfd)
35+
local context = {
36+
sockfd = sockfd;
37+
poll_handle = uv.poll_socket(sockfd);
38+
}
39+
context.poll_handle.data = context
40+
41+
return context
42+
end
43+
44+
function destroy_curl_context(context)
45+
context.poll_handle:close()
46+
end
47+
48+
function add_download(url, num)
49+
local filename = tostring(num) .. ".download"
50+
local file = io.open(filename, "w")
51+
if not file then
52+
fprintf(stderr, "Error opening %s\n", filename)
53+
return
54+
end
55+
56+
local handle = curl.easy{
57+
url = url;
58+
writefunction = file;
59+
}
60+
61+
FILES[handle] = file
62+
63+
curl_handle:add_handle(handle)
64+
fprintf(stderr, "Added download %s -> %s\n", url, filename);
65+
end
66+
67+
function check_multi_info()
68+
while true do
69+
local easy, ok, err = curl_handle:info_read()
70+
if not easy then curl_handle:close() error(err) end
71+
if easy == 0 then break end
72+
73+
local context = CONTEXT[e]
74+
if context then destroy_curl_context(context) end
75+
local file = FILES[easy]
76+
if file then FILES[easy] = nil, file:close() end
77+
local done_url = easy:getinfo_effective_url()
78+
easy:close()
79+
if ok then
80+
printf("%s DONE\n", done_url);
81+
elseif data == "error" then
82+
printf("%s ERROR - %s\n", done_url, tostring(err));
83+
end
84+
end
85+
end
86+
87+
function curl_perform(handle, err, events)
88+
-- calls by libuv --
89+
trace("UV::POLL", handle, err, EVENT_NAMES[events] or events)
90+
91+
local flags = assert(FLAGS[events], ("unknown event:" .. events))
92+
93+
context = handle.data
94+
95+
curl_handle:socket_action(context.sockfd, flags)
96+
97+
check_multi_info()
98+
end
99+
100+
function on_timeout(timer)
101+
-- calls by libuv --
102+
trace("UV::TIMEOUT", timer)
103+
104+
local running_handles, err = curl_handle:socket_action()
105+
106+
check_multi_info()
107+
end
108+
109+
function start_timeout(timeout_ms)
110+
-- calls by curl --
111+
trace("CURL::TIMEOUT", timeout_ms)
112+
113+
-- 0 means directly call socket_action, but we'll do it in a bit
114+
if timeout_ms <= 0 then timeout_ms = 1 end
115+
116+
timeout:stop():start(timeout_ms, 0, on_timeout)
117+
end
118+
119+
local handle_socket = function(...)
120+
local ok, err = pcall(handle_socket_impl, ...)
121+
if not ok then uv.defer(function() error(err) end) end
122+
end
123+
124+
function handle_socket_impl(easy, s, action)
125+
-- calls by curl --
126+
127+
trace("CURL::SOCKET", easy, s, ACTION_NAMES[action] or action)
128+
129+
local curl_context = CONTEXT[easy] or create_curl_context(s)
130+
CONTEXT[easy] = curl_context
131+
132+
assert(curl_context.sockfd == s)
133+
134+
if action == curl.POLL_IN then
135+
curl_context.poll_handle:start(uv.READABLE, curl_perform)
136+
elseif action == curl.POLL_OUT then
137+
curl_context.poll_handle:start(uv.WRITABLE, curl_perform)
138+
elseif action == curl.POLL_REMOVE then
139+
CONTEXT[easy] = nil
140+
destroy_curl_context(curl_context)
141+
end
142+
end
143+
144+
timeout = uv.timer()
145+
146+
curl_handle = curl.multi{
147+
socketfunction = handle_socket;
148+
timerfunction = start_timeout;
149+
}
150+
151+
curl_handle = curl.multi{
152+
socketfunction = handle_socket;
153+
timerfunction = start_timeout;
154+
}
155+
156+
for i = 1, math.huge do
157+
local url = arg[i]
158+
if not url then break end
159+
add_download(url, i)
160+
end
161+
162+
uv.run(loop, UV_RUN_DEFAULT)

src/lcmulti.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ int lcurl_multi_create(lua_State *L, int error_mode){
4343
lcurl_util_new_weak_table(L, "v");
4444
p->h_ref = luaL_ref(L, LCURL_LUA_REGISTRY);
4545
p->tm.cb_ref = p->tm.ud_ref = LUA_NOREF;
46+
p->sc.cb_ref = p->sc.ud_ref = LUA_NOREF;
4647

4748
if(lua_type(L, 1) == LUA_TTABLE){
4849
int ret = lcurl_utils_apply_options(L, 1, 2, 1, p->err_mode, LCURL_ERROR_MULTI, CURLM_UNKNOWN_OPTION);
@@ -228,9 +229,11 @@ static int lcurl_multi_timeout(lua_State *L){
228229

229230
static int lcurl_multi_socket_action(lua_State *L){
230231
lcurl_multi_t *p = lcurl_getmulti(L);
231-
curl_socket_t s = lutil_checkint64(L, 2);
232-
int n, mask = lutil_checkint64(L, 3);
233-
CURLMcode code = curl_multi_socket_action(p->curl, s, mask, &n);
232+
curl_socket_t s = lutil_optint64(L, 2, CURL_SOCKET_TIMEOUT);
233+
CURLMcode code; int n, mask;
234+
if(s == CURL_SOCKET_TIMEOUT) mask = 0;
235+
else mask = lutil_checkint64(L, 3);
236+
code = curl_multi_socket_action(p->curl, s, mask, &n);
234237
if(code != CURLM_OK){
235238
lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code);
236239
}
@@ -427,7 +430,8 @@ static int lcurl_multi_setopt(lua_State *L){
427430
#define OPT_ENTRY(l, N, T, S) case CURLMOPT_##N: return lcurl_multi_set_##N(L);
428431
switch(opt){
429432
#include "lcoptmulti.h"
430-
OPT_ENTRY(timerfunction, TIMERFUNCTION, TTT, 0)
433+
OPT_ENTRY(timerfunction, TIMERFUNCTION, TTT, 0)
434+
OPT_ENTRY(socketfunction, SOCKETFUNCTION, TTT, 0)
431435
}
432436
#undef OPT_ENTRY
433437

src/lua/cURL/impl/cURL.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,9 @@ local Multi = class(curl.multi) do
520520
local add_handle = wrap_function("add_handle")
521521
local remove_handle = wrap_function("remove_handle")
522522

523-
function Multi:__init()
523+
function Multi:__init(opt)
524524
self._easy = {n = 0}
525+
if opt then self:setopt(opt) end
525526
return self
526527
end
527528

0 commit comments

Comments
 (0)