diff --git a/Sources/FoundationNetworking/URLSession/libcurl/MultiHandle.swift b/Sources/FoundationNetworking/URLSession/libcurl/MultiHandle.swift index f1b3ad6a21..29bdac44d3 100644 --- a/Sources/FoundationNetworking/URLSession/libcurl/MultiHandle.swift +++ b/Sources/FoundationNetworking/URLSession/libcurl/MultiHandle.swift @@ -195,7 +195,7 @@ private extension URLSession._MultiHandle { } /// Removes socket reference from the shared store. If there is work item scheduled, - /// executes it on the current thread. + /// executes it on the current thread. Then, schedules the socket for closure. func endOperation(for socketReference: _SocketReference) { precondition(socketReferences.removeValue(forKey: socketReference.socket) != nil, "No operation associated with the socket") if let workItem = socketReference.workItem, !workItem.isCancelled { @@ -203,14 +203,11 @@ private extension URLSession._MultiHandle { // we should cancel pending work when unregister action is requested. precondition(!socketReference.shouldClose, "Socket close was scheduled, but there is some pending work left") workItem.perform() - } - } - /// Marks this reference to close socket on deinit. This allows us - /// to extend socket lifecycle by keeping the reference alive. - func scheduleClose(for socket: CFURLSession_socket_t) { - let reference = socketReferences[socket] ?? _SocketReference(socket: socket) - reference.shouldClose = true + /// Marks this reference to close socket on deinit. This allows us + /// to extend socket lifecycle by keeping the reference alive. + socketReference.shouldClose = true + } } /// Schedules work to be performed when an operation ends for the socket, @@ -253,10 +250,9 @@ internal extension URLSession._MultiHandle { // the connection cache is managed by CURL multi_handle, and sockets can actually // outlive easy_handle (even after curl_easy_cleanup call). That's why // socket management lives in _MultiHandle. - try! CFURLSession_easy_setopt_ptr(handle.rawHandle, CFURLSessionOptionCLOSESOCKETDATA, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())).asError() try! CFURLSession_easy_setopt_scl(handle.rawHandle, CFURLSessionOptionCLOSESOCKETFUNCTION) { (clientp: UnsafeMutableRawPointer?, item: CFURLSession_socket_t) in - guard let handle = URLSession._MultiHandle.from(callbackUserData: clientp) else { fatalError() } - handle.scheduleClose(for: item) + // Override close(3)/closesocket(3) to keep socket open. + // CFURLSession_socket_t will be scheduled for closure when endOperation(: _SocketReference) is called. return 0 }.asError()