From 5643a857244f209b98202096bf34404f1fa2f3c9 Mon Sep 17 00:00:00 2001 From: tan Date: Fri, 9 Aug 2024 14:50:05 +0530 Subject: [PATCH] fix: handle FDWatcher exception on closed sockets Adding checks to handle exception thrown while constructing FDWatcher if the socket handle is closed. Also checking the curl return code while adding handle to multi, and not proceeding if that failed. Should fix #253 --- src/Curl/Multi.jl | 23 +++++++++++++++++++++-- src/Downloads.jl | 6 +++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Curl/Multi.jl b/src/Curl/Multi.jl index c6ed079..290d090 100644 --- a/src/Curl/Multi.jl +++ b/src/Curl/Multi.jl @@ -43,7 +43,7 @@ end function add_handle(multi::Multi, easy::Easy) connect_semaphore_acquire(easy) - lock(multi.lock) do + added = lock(multi.lock) do if isempty(multi.easies) preserve_handle(multi) end @@ -51,6 +51,10 @@ function add_handle(multi::Multi, easy::Easy) init!(multi) @check curl_multi_add_handle(multi.handle, easy.handle) end + if added != 0 + connect_semaphore_release(easy) + end + return added end const MULTIS_LOCK = Base.ReentrantLock() @@ -179,7 +183,22 @@ function socket_callback( if action in (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT) readable = action in (CURL_POLL_IN, CURL_POLL_INOUT) writable = action in (CURL_POLL_OUT, CURL_POLL_INOUT) - watcher = FDWatcher(OS_HANDLE(sock), readable, writable) + watcher = try + FDWatcher(OS_HANDLE(sock), readable, writable) + catch watcher_ex + if watcher_ex isa Base.IOError + task = @async begin + lock(multi.lock) do + @check curl_multi_socket_action(multi.handle, sock, CURL_CSELECT_ERR) + check_multi_info(multi) + end + end + @isdefined(errormonitor) && errormonitor(task) + nothing + else + rethrow() + end + end preserve_handle(watcher) watcher_p = pointer_from_objref(watcher) @check curl_multi_assign(multi.handle, sock, watcher_p) diff --git a/src/Downloads.jl b/src/Downloads.jl index 5a3c214..28ae800 100644 --- a/src/Downloads.jl +++ b/src/Downloads.jl @@ -387,7 +387,11 @@ function request( easy_hook(downloader, easy, info) # do the request - add_handle(downloader.multi, easy) + add_handle_error = add_handle(downloader.multi, easy) + if add_handle_error != 0 + no_response = Response(nothing, "", 0, "", []) + throw(RequestError(url, add_handle_error, "", no_response)) + end try # ensure handle is removed @sync begin @async for buf in easy.output