Skip to content

Commit 02656bf

Browse files
committed
[URL Session] Avoid Closing Sockets Before Cancelling Event Sources
When you create an event source for a handle in dispatch, dispatch assumes it owns it, however, when curl finishes with the socket, it closes it, which leaves dispatch with an invalid handle. This sets the curl close callback to do nothing which allows us to close the socket after cancelling the dispatch sources.
1 parent 40574fe commit 02656bf

File tree

4 files changed

+38
-3
lines changed

4 files changed

+38
-3
lines changed

CoreFoundation/URL.subproj/CFURLSessionInterface.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ CFURLSessionEasyCode CFURLSession_easy_setopt_tc(CFURLSessionEasyHandle _Nonnull
110110
return MakeEasyCode(curl_easy_setopt(curl, option.value, a));
111111
}
112112

113+
CFURLSessionEasyCode CFURLSession_easy_setopt_csf(CFURLSessionEasyHandle _Nonnull curl, CFURLSessionOption option, int (*_Nonnull a)(void *_Nullable clientp, CFURLSession_socket_t)) {
114+
return MakeEasyCode(curl_easy_setopt(curl, option.value, a));
115+
}
116+
113117
CFURLSessionEasyCode CFURLSession_easy_getinfo_long(CFURLSessionEasyHandle _Nonnull curl, CFURLSessionInfo info, long *_Nonnull a) {
114118
return MakeEasyCode(curl_easy_getinfo(curl, info.value, a));
115119
}

CoreFoundation/URL.subproj/CFURLSessionInterface.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ CF_EXPORT CFURLSessionMultiCode CFURLSession_multi_setopt_ptr(CFURLSessionMultiH
601601
CF_EXPORT CFURLSessionMultiCode CFURLSession_multi_setopt_l(CFURLSessionMultiHandle _Nonnull multi_handle, CFURLSessionMultiOption option, long a);
602602
CF_EXPORT CFURLSessionMultiCode CFURLSession_multi_setopt_sf(CFURLSessionMultiHandle _Nonnull multi_handle, CFURLSessionMultiOption option, int (*_Nonnull a)(CFURLSessionEasyHandle _Nonnull, CFURLSession_socket_t, int, void *_Nullable, void *_Nullable));
603603
CF_EXPORT CFURLSessionMultiCode CFURLSession_multi_setopt_tf(CFURLSessionMultiHandle _Nonnull multi_handle, CFURLSessionMultiOption option, int (*_Nonnull a)(CFURLSessionMultiHandle _Nonnull, long, void *_Nullable));
604-
604+
CF_EXPORT CFURLSessionEasyCode CFURLSession_easy_setopt_csf(CFURLSessionEasyHandle _Nonnull curl, CFURLSessionOption option, int (*_Nonnull a)(void* _Nullable clientp, CFURLSession_socket_t));
605605
CF_EXPORT CFURLSessionEasyCode CFURLSessionInit(void);
606606

607607

Foundation/URLSession/libcurl/EasyHandle.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,14 @@ fileprivate extension _EasyHandle {
486486
return 1
487487
}
488488
}.asError()
489+
490+
try! CFURLSession_easy_setopt_csf(rawHandle, CFURLSessionOptionCLOSESOCKETFUNCTION) { (clientp: UnsafeMutableRawPointer?, socket: CFURLSession_socket_t) -> Int32 in
491+
// Don't let CURL close the socket here because the
492+
// dispatch sources are associated with it and we need to
493+
// cancel them before closing the file descriptor.
494+
return 0
495+
}.asError()
496+
489497
// seeking in input stream
490498
try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionSEEKDATA, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())).asError()
491499
try! CFURLSession_easy_setopt_seek(rawHandle, CFURLSessionOptionSEEKFUNCTION, { (userdata, offset, origin) -> Int32 in

Foundation/URLSession/libcurl/MultiHandle.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ fileprivate extension URLSession._MultiHandle {
122122
} else if socketSources != nil && action == .unregister {
123123
// We need to release the stored pointer:
124124
if let opaque = socketSourcePtr {
125-
Unmanaged<_SocketSources>.fromOpaque(opaque).release()
125+
let s: Unmanaged<_SocketSources> = Unmanaged<_SocketSources>.fromOpaque(opaque)
126+
s.takeUnretainedValue().tearDown(socket: socket, queue: queue)
127+
s.release()
126128
}
127129
socketSources = nil
128130
}
@@ -407,6 +409,7 @@ fileprivate extension URLSession._MultiHandle._Timeout {
407409
fileprivate class _SocketSources {
408410
var readSource: DispatchSource?
409411
var writeSource: DispatchSource?
412+
let activeSockets = DispatchGroup()
410413

411414
func createReadSource(socket: CFURLSession_socket_t, queue: DispatchQueue, handler: DispatchWorkItem) {
412415
guard readSource == nil else { return }
@@ -415,7 +418,13 @@ fileprivate class _SocketSources {
415418
#else
416419
let s = DispatchSource.makeReadSource(fileDescriptor: socket, queue: queue)
417420
#endif
421+
activeSockets.enter()
418422
s.setEventHandler(handler: handler)
423+
s.setCancelHandler(handler: DispatchWorkItem { [weak self] in
424+
guard let self = self else { return }
425+
self.activeSockets.leave()
426+
})
427+
419428
readSource = s as? DispatchSource
420429
s.resume()
421430
}
@@ -427,12 +436,26 @@ fileprivate class _SocketSources {
427436
#else
428437
let s = DispatchSource.makeWriteSource(fileDescriptor: socket, queue: queue)
429438
#endif
439+
activeSockets.enter()
440+
s.setCancelHandler(handler: DispatchWorkItem { [weak self] in
441+
guard let self = self else { return }
442+
self.activeSockets.leave()
443+
})
430444
s.setEventHandler(handler: handler)
431445
writeSource = s as? DispatchSource
432446
s.resume()
433447
}
434448

435-
func tearDown() {
449+
func tearDown(socket: CFURLSession_socket_t, queue: DispatchQueue) {
450+
activeSockets.notify(queue: queue) {
451+
withExtendedLifetime(self) {
452+
#if os(Windows)
453+
closesocket(socket)
454+
#else
455+
close(socket)
456+
#endif
457+
}
458+
}
436459
if let s = readSource {
437460
s.cancel()
438461
}

0 commit comments

Comments
 (0)