Skip to content

Commit b834853

Browse files
authored
Merge pull request #2424 from gmittert/DontStealTheSources
[URL Session] Avoid Closing Sockets Before Cancelling Event Sources
2 parents ea46535 + 02656bf commit b834853

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)