Skip to content
80 changes: 74 additions & 6 deletions Sources/CoreFoundation/CFSocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,30 @@ CF_INLINE int __CFSocketLastError(void) {
}

CF_INLINE CFIndex __CFSocketFdGetSize(CFDataRef fdSet) {
#if TARGET_OS_WIN32
if (CFDataGetLength(fdSet) == 0) {
return 0;
}
return FD_SETSIZE;
#else
return NBBY * CFDataGetLength(fdSet);
#endif
}

CF_INLINE Boolean __CFSocketFdSet(CFSocketNativeHandle sock, CFMutableDataRef fdSet) {
/* returns true if a change occurred, false otherwise */
Boolean retval = false;
if (INVALID_SOCKET != sock && 0 <= sock) {
fd_set *fds;
#if TARGET_OS_WIN32
if (CFDataGetLength(fdSet) == 0) {
CFDataIncreaseLength(fdSet, sizeof(fd_set));
fds = (fd_set *)CFDataGetMutableBytePtr(fdSet);
FD_ZERO(fds);
} else {
fds = (fd_set *)CFDataGetMutableBytePtr(fdSet);
}
#else
CFIndex numFds = NBBY * CFDataGetLength(fdSet);
fd_mask *fds_bits;
if (sock >= numFds) {
Expand All @@ -232,9 +249,11 @@ CF_INLINE Boolean __CFSocketFdSet(CFSocketNativeHandle sock, CFMutableDataRef fd
} else {
fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet);
}
if (!FD_ISSET(sock, (fd_set *)fds_bits)) {
fds = (fd_set *)fds_bits;
#endif
if (!FD_ISSET(sock, fds)) {
retval = true;
FD_SET(sock, (fd_set *)fds_bits);
FD_SET(sock, fds);
}
}
return retval;
Expand Down Expand Up @@ -418,6 +437,15 @@ CF_INLINE Boolean __CFSocketFdClr(CFSocketNativeHandle sock, CFMutableDataRef fd
/* returns true if a change occurred, false otherwise */
Boolean retval = false;
if (INVALID_SOCKET != sock && 0 <= sock) {
#if TARGET_OS_WIN32
if (CFDataGetLength(fdSet) > 0) {
fd_set *fds = (fd_set *)CFDataGetMutableBytePtr(fdSet);
if (FD_ISSET(sock, fds)) {
retval = true;
FD_CLR(sock, fds);
}
}
#else
CFIndex numFds = NBBY * CFDataGetLength(fdSet);
fd_mask *fds_bits;
if (sock < numFds) {
Expand All @@ -427,6 +455,7 @@ CF_INLINE Boolean __CFSocketFdClr(CFSocketNativeHandle sock, CFMutableDataRef fd
FD_CLR(sock, (fd_set *)fds_bits);
}
}
#endif
}
return retval;
}
Expand Down Expand Up @@ -1190,6 +1219,27 @@ static void
clearInvalidFileDescriptors(CFMutableDataRef d)
{
if (d) {
#if TARGET_OS_WIN32
if (CFDataGetLength(d) == 0) {
return;
}

fd_set *fds = (fd_set *)CFDataGetMutableBytePtr(d);
fd_set invalidFds;
FD_ZERO(&invalidFds);
// Gather all invalid sockets into invalidFds set
for (u_int idx = 0; idx < fds->fd_count; idx++) {
SOCKET socket = fds->fd_array[idx];
if (! __CFNativeSocketIsValid(socket)) {
FD_SET(socket, &invalidFds);
}
}
// Remove invalid sockets from source set
for (u_int idx = 0; idx < invalidFds.fd_count; idx++) {
SOCKET socket = invalidFds.fd_array[idx];
FD_CLR(socket, fds);
}
#else
SInt32 count = __CFSocketFdGetSize(d);
fd_set* s = (fd_set*) CFDataGetMutableBytePtr(d);
for (SInt32 idx = 0; idx < count; idx++) {
Expand All @@ -1198,14 +1248,13 @@ clearInvalidFileDescriptors(CFMutableDataRef d)
FD_CLR(idx, s);
}
}
#endif
}
}

static void
manageSelectError()
manageSelectError(SInt32 selectError)
{
SInt32 selectError = __CFSocketLastError();

__CFSOCKETLOG("socket manager received error %ld from select", (long)selectError);

if (EBADF == selectError) {
Expand Down Expand Up @@ -1265,8 +1314,15 @@ static void *__CFSocketManager(void * arg)
SInt32 nrfds, maxnrfds, fdentries = 1;
SInt32 rfds, wfds;
fd_set *exceptfds = NULL;
#if TARGET_OS_WIN32
fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(fd_set), 0);
fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(fd_set), 0);
FD_ZERO(writefds);
FD_ZERO(readfds);
#else
fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0);
fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0);
#endif
fd_set *tempfds;
SInt32 idx, cnt;
uint8_t buffer[256];
Expand All @@ -1292,6 +1348,11 @@ static void *__CFSocketManager(void * arg)
free(readBuffer);
free(writeBuffer);
#endif

#if TARGET_OS_WIN32
// This parameter is ignored by `select` from Winsock2 API
maxnrfds = INT_MAX;
#else
rfds = __CFSocketFdGetSize(__CFReadSocketsFds);
wfds = __CFSocketFdGetSize(__CFWriteSocketsFds);
maxnrfds = __CFMax(rfds, wfds);
Expand All @@ -1302,6 +1363,7 @@ static void *__CFSocketManager(void * arg)
}
memset(writefds, 0, fdentries * sizeof(fd_mask));
memset(readfds, 0, fdentries * sizeof(fd_mask));
#endif
CFDataGetBytes(__CFWriteSocketsFds, CFRangeMake(0, CFDataGetLength(__CFWriteSocketsFds)), (UInt8 *)writefds);
CFDataGetBytes(__CFReadSocketsFds, CFRangeMake(0, CFDataGetLength(__CFReadSocketsFds)), (UInt8 *)readfds);

Expand Down Expand Up @@ -1347,7 +1409,13 @@ static void *__CFSocketManager(void * arg)
}
#endif

SInt32 error = 0;
nrfds = select(maxnrfds, readfds, writefds, exceptfds, pTimeout);
if (nrfds < 0) {
// Store error as early as possible, as the code below could
// reset it and make late check unreliable.
error = __CFSocketLastError();
}

#if defined(LOG_CFSOCKET) && defined(DEBUG_POLLING_SELECT)
__CFSOCKETLOG("socket manager woke from select, ret=%ld", (long)nrfds);
Expand Down Expand Up @@ -1436,7 +1504,7 @@ static void *__CFSocketManager(void * arg)
}

if (0 > nrfds) {
manageSelectError();
manageSelectError(error);
continue;
}
if (FD_ISSET(__CFWakeupSocketPair[1], readfds)) {
Expand Down
6 changes: 5 additions & 1 deletion Sources/Foundation/NSData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,12 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
}

let fm = FileManager.default
#if os(WASI)
// WASI does not have permission concept
let permissions: Int? = nil
#else
let permissions = try? fm.attributesOfItem(atPath: path)[.posixPermissions] as? Int

#endif
if writeOptionsMask.contains(.atomic) {
let (newFD, auxFilePath) = try _NSCreateTemporaryFile(path)
let fh = FileHandle(fileDescriptor: newFD, closeOnDealloc: true)
Expand Down
3 changes: 1 addition & 2 deletions Sources/FoundationNetworking/DataURLProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ internal class _DataURLProtocol: URLProtocol {
urlClient.urlProtocolDidFinishLoading(self)
} else {
let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorBadURL)
if let session = self.task?.session as? URLSession, let delegate = session.delegate as? URLSessionTaskDelegate,
let task = self.task {
if let task = self.task, let session = task.actualSession, let delegate = task.delegate {
delegate.urlSession(session, task: task, didCompleteWithError: error)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ internal extension _FTPURLProtocol {
switch session.behaviour(for: self.task!) {
case .noDelegate:
break
case .taskDelegate:
case .taskDelegate, .dataCompletionHandlerWithTaskDelegate, .downloadCompletionHandlerWithTaskDelegate:
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
case .dataCompletionHandler:
break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ internal class _HTTPURLProtocol: _NativeProtocol {

guard let session = task?.session as? URLSession else { fatalError() }

if let delegate = session.delegate as? URLSessionTaskDelegate {
if let delegate = task?.delegate {
// At this point we need to change the internal state to note
// that we're waiting for the delegate to call the completion
// handler. Then we'll call the delegate callback
Expand Down Expand Up @@ -524,7 +524,9 @@ internal class _HTTPURLProtocol: _NativeProtocol {
switch session.behaviour(for: self.task!) {
case .noDelegate:
break
case .taskDelegate:
case .taskDelegate,
.dataCompletionHandlerWithTaskDelegate,
.downloadCompletionHandlerWithTaskDelegate:
//TODO: There's a problem with libcurl / with how we're using it.
// We're currently unable to pause the transfer / the easy handle:
// https://curl.haxx.se/mail/lib-2016-03/0222.html
Expand Down
84 changes: 51 additions & 33 deletions Sources/FoundationNetworking/URLSession/NativeProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,43 +129,59 @@ internal class _NativeProtocol: URLProtocol, _EasyHandleDelegate {
}

fileprivate func notifyDelegate(aboutReceivedData data: Data) {
guard let t = self.task else {
guard let task = self.task, let session = task.session as? URLSession else {
fatalError("Cannot notify")
}
if case .taskDelegate(let delegate) = t.session.behaviour(for: self.task!),
let dataDelegate = delegate as? URLSessionDataDelegate,
let task = self.task as? URLSessionDataTask {
// Forward to the delegate:
guard let s = self.task?.session as? URLSession else {
fatalError()
}
s.delegateQueue.addOperation {
dataDelegate.urlSession(s, dataTask: task, didReceive: data)
}
} else if case .taskDelegate(let delegate) = t.session.behaviour(for: self.task!),
let downloadDelegate = delegate as? URLSessionDownloadDelegate,
let task = self.task as? URLSessionDownloadTask {
guard let s = self.task?.session as? URLSession else {
fatalError()
}
let fileHandle = try! FileHandle(forWritingTo: self.tempFileURL)
_ = fileHandle.seekToEndOfFile()
fileHandle.write(data)
task.countOfBytesReceived += Int64(data.count)
s.delegateQueue.addOperation {
downloadDelegate.urlSession(s, downloadTask: task, didWriteData: Int64(data.count), totalBytesWritten: task.countOfBytesReceived,
totalBytesExpectedToWrite: task.countOfBytesExpectedToReceive)
switch task.session.behaviour(for: task) {
case .taskDelegate(let delegate),
.dataCompletionHandlerWithTaskDelegate(_, let delegate),
.downloadCompletionHandlerWithTaskDelegate(_, let delegate):
if let dataDelegate = delegate as? URLSessionDataDelegate,
let dataTask = task as? URLSessionDataTask {
session.delegateQueue.addOperation {
dataDelegate.urlSession(session, dataTask: dataTask, didReceive: data)
}
} else if let downloadDelegate = delegate as? URLSessionDownloadDelegate,
let downloadTask = task as? URLSessionDownloadTask {
let fileHandle = try! FileHandle(forWritingTo: self.tempFileURL)
_ = fileHandle.seekToEndOfFile()
fileHandle.write(data)
task.countOfBytesReceived += Int64(data.count)
session.delegateQueue.addOperation {
downloadDelegate.urlSession(
session,
downloadTask: downloadTask,
didWriteData: Int64(data.count),
totalBytesWritten: task.countOfBytesReceived,
totalBytesExpectedToWrite: task.countOfBytesExpectedToReceive
)
}
}
default:
break
}
}

fileprivate func notifyDelegate(aboutUploadedData count: Int64) {
guard let task = self.task, let session = task.session as? URLSession,
case .taskDelegate(let delegate) = session.behaviour(for: task) else { return }
task.countOfBytesSent += count
session.delegateQueue.addOperation {
delegate.urlSession(session, task: task, didSendBodyData: count,
totalBytesSent: task.countOfBytesSent, totalBytesExpectedToSend: task.countOfBytesExpectedToSend)
guard let task = self.task, let session = task.session as? URLSession else {
return
}
switch session.behaviour(for: task) {
case .taskDelegate(let delegate),
.dataCompletionHandlerWithTaskDelegate(_, let delegate),
.downloadCompletionHandlerWithTaskDelegate(_, let delegate):
task.countOfBytesSent += count
session.delegateQueue.addOperation {
delegate.urlSession(
session,
task: task,
didSendBodyData: count,
totalBytesSent: task.countOfBytesSent,
totalBytesExpectedToSend: task.countOfBytesExpectedToSend
)
}
default:
break
}
}

Expand Down Expand Up @@ -284,7 +300,7 @@ internal class _NativeProtocol: URLProtocol, _EasyHandleDelegate {

var currentInputStream: InputStream?

if let delegate = session.delegate as? URLSessionTaskDelegate {
if let delegate = task?.delegate {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()

Expand Down Expand Up @@ -338,11 +354,13 @@ internal class _NativeProtocol: URLProtocol, _EasyHandleDelegate {
// Data will be forwarded to the delegate as we receive it, we don't
// need to do anything about it.
return .ignore
case .dataCompletionHandler:
case .dataCompletionHandler,
.dataCompletionHandlerWithTaskDelegate:
// Data needs to be concatenated in-memory such that we can pass it
// to the completion handler upon completion.
return .inMemory(nil)
case .downloadCompletionHandler:
case .downloadCompletionHandler,
.downloadCompletionHandlerWithTaskDelegate:
// Data needs to be written to a file (i.e. a download task).
let fileHandle = try! FileHandle(forWritingTo: self.tempFileURL)
return .toFile(self.tempFileURL, fileHandle)
Expand Down
4 changes: 4 additions & 0 deletions Sources/FoundationNetworking/URLSession/TaskRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ extension URLSession {
case callDelegate
/// Default action for all events, except for completion.
case dataCompletionHandler(DataTaskCompletion)
/// Default action for all asynchronous events.
case dataCompletionHandlerWithTaskDelegate(DataTaskCompletion, URLSessionTaskDelegate?)
/// Default action for all events, except for completion.
case downloadCompletionHandler(DownloadTaskCompletion)
/// Default action for all asynchronous events.
case downloadCompletionHandlerWithTaskDelegate(DownloadTaskCompletion, URLSessionTaskDelegate?)
}

fileprivate var tasks: [Int: URLSessionTask] = [:]
Expand Down
Loading