Skip to content

Commit 11b6bb7

Browse files
add try/catch to all callback functions (never unwind through them) (#206)
1 parent ac80772 commit 11b6bb7

File tree

3 files changed

+132
-92
lines changed

3 files changed

+132
-92
lines changed

src/Curl/Easy.jl

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,16 @@ function header_callback(
354354
count :: Csize_t,
355355
easy_p :: Ptr{Cvoid},
356356
)::Csize_t
357-
easy = unsafe_pointer_to_objref(easy_p)::Easy
358-
n = size * count
359-
hdr = unsafe_string(data, n)
360-
push!(easy.res_hdrs, hdr)
361-
return n
357+
try
358+
easy = unsafe_pointer_to_objref(easy_p)::Easy
359+
n = size * count
360+
hdr = unsafe_string(data, n)
361+
push!(easy.res_hdrs, hdr)
362+
return n
363+
catch err
364+
@async @error("header_callback: unexpected error", err=err, maxlog=1_000)
365+
return typemax(Csize_t)
366+
end
362367
end
363368

364369
# feed data to read_callback
@@ -380,39 +385,49 @@ function read_callback(
380385
count :: Csize_t,
381386
easy_p :: Ptr{Cvoid},
382387
)::Csize_t
383-
easy = unsafe_pointer_to_objref(easy_p)::Easy
384-
buf = easy.input
385-
if buf === nothing
386-
notify(easy.ready)
387-
return 0 # done uploading
388-
end
389-
if isempty(buf)
390-
notify(easy.ready)
391-
return CURL_READFUNC_PAUSE # wait for more data
388+
try
389+
easy = unsafe_pointer_to_objref(easy_p)::Easy
390+
buf = easy.input
391+
if buf === nothing
392+
notify(easy.ready)
393+
return 0 # done uploading
394+
end
395+
if isempty(buf)
396+
notify(easy.ready)
397+
return CURL_READFUNC_PAUSE # wait for more data
398+
end
399+
n = min(size * count, length(buf))
400+
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), data, buf, n)
401+
deleteat!(buf, 1:n)
402+
return n
403+
catch err
404+
@async @error("read_callback: unexpected error", err=err, maxlog=1_000)
405+
return CURL_READFUNC_ABORT
392406
end
393-
n = min(size * count, length(buf))
394-
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), data, buf, n)
395-
deleteat!(buf, 1:n)
396-
return n
397407
end
398408

399409
function seek_callback(
400410
easy_p :: Ptr{Cvoid},
401411
offset :: curl_off_t,
402412
origin :: Cint,
403413
)::Cint
404-
if origin != 0
405-
@async @error("seek_callback: unsupported seek origin", origin, maxlog=1_000)
406-
return CURL_SEEKFUNC_CANTSEEK
407-
end
408-
easy = unsafe_pointer_to_objref(easy_p)::Easy
409-
easy.seeker === nothing && return CURL_SEEKFUNC_CANTSEEK
410-
try easy.seeker(offset)
414+
try
415+
if origin != 0
416+
@async @error("seek_callback: unsupported seek origin", origin, maxlog=1_000)
417+
return CURL_SEEKFUNC_CANTSEEK
418+
end
419+
easy = unsafe_pointer_to_objref(easy_p)::Easy
420+
easy.seeker === nothing && return CURL_SEEKFUNC_CANTSEEK
421+
try easy.seeker(offset)
422+
catch err
423+
@async @error("seek_callback: seeker failed", err, maxlog=1_000)
424+
return CURL_SEEKFUNC_FAIL
425+
end
426+
return CURL_SEEKFUNC_OK
411427
catch err
412-
@async @error("seek_callback: seeker failed", err, maxlog=1_000)
428+
@async @error("seek_callback: unexpected error", err=err, maxlog=1_000)
413429
return CURL_SEEKFUNC_FAIL
414430
end
415-
return CURL_SEEKFUNC_OK
416431
end
417432

418433
function write_callback(
@@ -421,12 +436,17 @@ function write_callback(
421436
count :: Csize_t,
422437
easy_p :: Ptr{Cvoid},
423438
)::Csize_t
424-
easy = unsafe_pointer_to_objref(easy_p)::Easy
425-
n = size * count
426-
buf = Array{UInt8}(undef, n)
427-
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), buf, data, n)
428-
put!(easy.output, buf)
429-
return n
439+
try
440+
easy = unsafe_pointer_to_objref(easy_p)::Easy
441+
n = size * count
442+
buf = Array{UInt8}(undef, n)
443+
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), buf, data, n)
444+
put!(easy.output, buf)
445+
return n
446+
catch err
447+
@async @error("write_callback: unexpected error", err=err, maxlog=1_000)
448+
return typemax(Csize_t)
449+
end
430450
end
431451

432452
function progress_callback(
@@ -436,9 +456,14 @@ function progress_callback(
436456
ul_total :: curl_off_t,
437457
ul_now :: curl_off_t,
438458
)::Cint
439-
easy = unsafe_pointer_to_objref(easy_p)::Easy
440-
put!(easy.progress, (dl_total, dl_now, ul_total, ul_now))
441-
return 0
459+
try
460+
easy = unsafe_pointer_to_objref(easy_p)::Easy
461+
put!(easy.progress, (dl_total, dl_now, ul_total, ul_now))
462+
return 0
463+
catch err
464+
@async @error("progress_callback: unexpected error", err=err, maxlog=1_000)
465+
return -1
466+
end
442467
end
443468

444469
function debug_callback(
@@ -448,10 +473,15 @@ function debug_callback(
448473
size :: Csize_t,
449474
easy_p :: Ptr{Cvoid},
450475
)::Cint
451-
easy = unsafe_pointer_to_objref(easy_p)::Easy
452-
@assert easy.handle == handle
453-
easy.debug(info_type(type), unsafe_string(data, size))
454-
return 0
476+
try
477+
easy = unsafe_pointer_to_objref(easy_p)::Easy
478+
@assert easy.handle == handle
479+
easy.debug(info_type(type), unsafe_string(data, size))
480+
return 0
481+
catch err
482+
@async @error("debug_callback: unexpected error", err=err, maxlog=1_000)
483+
return -1
484+
end
455485
end
456486

457487
function add_callbacks(easy::Easy)

src/Curl/Multi.jl

Lines changed: 57 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -122,22 +122,27 @@ function timer_callback(
122122
timeout_ms :: Clong,
123123
multi_p :: Ptr{Cvoid},
124124
)::Cint
125-
multi = unsafe_pointer_to_objref(multi_p)::Multi
126-
@assert multi_h == multi.handle
127-
stoptimer!(multi)
128-
if timeout_ms >= 0
129-
multi.timer = Timer(timeout_ms/1000) do timer
130-
lock(multi.lock) do
131-
multi.timer === timer || return
132-
multi.timer = nothing
133-
do_multi(multi)
125+
try
126+
multi = unsafe_pointer_to_objref(multi_p)::Multi
127+
@assert multi_h == multi.handle
128+
stoptimer!(multi)
129+
if timeout_ms >= 0
130+
multi.timer = Timer(timeout_ms/1000) do timer
131+
lock(multi.lock) do
132+
multi.timer === timer || return
133+
multi.timer = nothing
134+
do_multi(multi)
135+
end
134136
end
137+
elseif timeout_ms != -1
138+
@async @error("timer_callback: invalid timeout value", timeout_ms, maxlog=1_000)
139+
return -1
135140
end
136-
elseif timeout_ms != -1
137-
@async @error("timer_callback: invalid timeout value", timeout_ms, maxlog=1_000)
141+
return 0
142+
catch err
143+
@async @error("timer_callback: unexpected error", err=err, maxlog=1_000)
138144
return -1
139145
end
140-
return 0
141146
end
142147

143148
function socket_callback(
@@ -147,44 +152,49 @@ function socket_callback(
147152
multi_p :: Ptr{Cvoid},
148153
watcher_p :: Ptr{Cvoid},
149154
)::Cint
150-
if action (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT, CURL_POLL_REMOVE)
151-
@async @error("socket_callback: unexpected action", action, maxlog=1_000)
152-
return -1
153-
end
154-
multi = unsafe_pointer_to_objref(multi_p)::Multi
155-
if watcher_p != C_NULL
156-
old_watcher = unsafe_pointer_to_objref(watcher_p)::FDWatcher
157-
@check curl_multi_assign(multi.handle, sock, C_NULL)
158-
unpreserve_handle(old_watcher)
159-
end
160-
if action in (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT)
161-
readable = action in (CURL_POLL_IN, CURL_POLL_INOUT)
162-
writable = action in (CURL_POLL_OUT, CURL_POLL_INOUT)
163-
watcher = FDWatcher(OS_HANDLE(sock), readable, writable)
164-
preserve_handle(watcher)
165-
watcher_p = pointer_from_objref(watcher)
166-
@check curl_multi_assign(multi.handle, sock, watcher_p)
167-
task = @async while watcher.readable || watcher.writable # isopen(watcher)
168-
events = try
169-
wait(watcher)
170-
catch err
171-
err isa EOFError && return
172-
err isa Base.IOError || rethrow()
173-
FileWatching.FDEvent()
174-
end
175-
flags = CURL_CSELECT_IN * isreadable(events) +
176-
CURL_CSELECT_OUT * iswritable(events) +
177-
CURL_CSELECT_ERR * (events.disconnect || events.timedout)
178-
lock(multi.lock) do
179-
watcher.readable || watcher.writable || return # !isopen
180-
@check curl_multi_socket_action(multi.handle, sock, flags)
181-
check_multi_info(multi)
155+
try
156+
if action (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT, CURL_POLL_REMOVE)
157+
@async @error("socket_callback: unexpected action", action, maxlog=1_000)
158+
return -1
159+
end
160+
multi = unsafe_pointer_to_objref(multi_p)::Multi
161+
if watcher_p != C_NULL
162+
old_watcher = unsafe_pointer_to_objref(watcher_p)::FDWatcher
163+
@check curl_multi_assign(multi.handle, sock, C_NULL)
164+
unpreserve_handle(old_watcher)
165+
end
166+
if action in (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT)
167+
readable = action in (CURL_POLL_IN, CURL_POLL_INOUT)
168+
writable = action in (CURL_POLL_OUT, CURL_POLL_INOUT)
169+
watcher = FDWatcher(OS_HANDLE(sock), readable, writable)
170+
preserve_handle(watcher)
171+
watcher_p = pointer_from_objref(watcher)
172+
@check curl_multi_assign(multi.handle, sock, watcher_p)
173+
task = @async while watcher.readable || watcher.writable # isopen(watcher)
174+
events = try
175+
wait(watcher)
176+
catch err
177+
err isa EOFError && return
178+
err isa Base.IOError || rethrow()
179+
FileWatching.FDEvent()
180+
end
181+
flags = CURL_CSELECT_IN * isreadable(events) +
182+
CURL_CSELECT_OUT * iswritable(events) +
183+
CURL_CSELECT_ERR * (events.disconnect || events.timedout)
184+
lock(multi.lock) do
185+
watcher.readable || watcher.writable || return # !isopen
186+
@check curl_multi_socket_action(multi.handle, sock, flags)
187+
check_multi_info(multi)
188+
end
182189
end
190+
@isdefined(errormonitor) && errormonitor(task)
183191
end
184-
@isdefined(errormonitor) && errormonitor(task)
192+
@isdefined(old_watcher) && close(old_watcher)
193+
return 0
194+
catch
195+
@async @error("socket_callback: unexpected error", err=err, maxlog=1_000)
196+
return -1
185197
end
186-
@isdefined(old_watcher) && close(old_watcher)
187-
return 0
188198
end
189199

190200
function add_callbacks(multi::Multi)

test/runtests.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,11 @@ include("setup.jl")
201201
end
202202

203203
@testset "delete default header" begin
204-
headers = [
205-
"Accept" => nothing
206-
"User-Agent" => nothing
207-
]
208-
json = download_json(url, headers = headers)
204+
headers = [
205+
"Accept" => nothing
206+
"User-Agent" => nothing
207+
]
208+
json = download_json(url, headers = headers)
209209
@test !("Accept" in keys(json["headers"]))
210210
@test !("User-Agent" in keys(json["headers"]))
211211
end

0 commit comments

Comments
 (0)