From 2115a86a6d876d16310152ddda697eea5f294961 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 d2be032..a21db78 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() @@ -170,7 +174,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 1fe95fa..958410c 100644 --- a/src/Downloads.jl +++ b/src/Downloads.jl @@ -395,7 +395,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 interrupted = Threads.Atomic{Bool}(false) if interrupt !== nothing interrupt_task = @async begin