diff --git a/CoreFoundation/URL.subproj/CFURLSessionInterface.c b/CoreFoundation/URL.subproj/CFURLSessionInterface.c index 32f09c4c37..20cb0aab88 100644 --- a/CoreFoundation/URL.subproj/CFURLSessionInterface.c +++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.c @@ -19,7 +19,6 @@ //===----------------------------------------------------------------------===// #include "CFURLSessionInterface.h" -#include #include #include @@ -140,61 +139,6 @@ CFURLSessionEasyCode CFURLSessionInit(void) { return MakeEasyCode(curl_global_init(CURL_GLOBAL_SSL)); } -#if LIBCURL_VERSION_MAJOR > 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 86) - -Boolean CFURLSessionWebSocketsSupported(void) { - curl_version_info_data *info = curl_version_info(CURLVERSION_NOW); - for (int i = 0; ; i++) { - const char * const protocol = info->protocols[i]; - if (protocol == NULL) { - break; - } - if ((0 == strncmp(protocol, "ws", 2)) || - (0 == strncmp(protocol, "wss", 3))) { - return true; - } - } - return false; -} - -CFURLSessionEasyCode CFURLSessionEasyHandleWebSocketsReceive(CFURLSessionEasyHandle _Nonnull handle, char *_Nonnull data, size_t dataLen, size_t * _Nonnull receivedDataLen, CFURLSessionWebSocketsFrame * _Nullable receivedFrame) { - CURLcode retVal = curl_ws_recv(handle, data, dataLen, receivedDataLen, (struct curl_ws_frame **)receivedFrame); - return MakeEasyCode(retVal); -} - -CFURLSessionEasyCode CFURLSessionEasyHandleWebSocketsSend(CFURLSessionEasyHandle _Nonnull handle, const char *_Nonnull data, size_t dataLen, size_t * _Nonnull writtenDataLen, long long frameSize, CFURLSessionWebSocketsMessageFlag messageFlags) { - CURLcode retVal = curl_ws_send(handle, data, dataLen, writtenDataLen, frameSize, messageFlags); - return MakeEasyCode(retVal); -} - -CFURLSessionWebSocketsFrame * _Nonnull CFURLSessionEasyHandleWebSocketsMetadata(CFURLSessionEasyHandle _Nonnull handle) { - return (CFURLSessionWebSocketsFrame *)curl_ws_meta(handle); -} - -#else - -Boolean CFURLSessionWebSocketsSupported(void) { - return false; -} - -CFURLSessionEasyCode CFURLSessionEasyHandleWebSocketsReceive(CFURLSessionEasyHandle _Nonnull handle, char *_Nonnull data, size_t dataLen, size_t * _Nonnull receivedDataLen, CFURLSessionWebSocketsFrame * _Nullable receivedFrame) { - CFAssert(false, __kCFLogAssertion, "Cannot use WebSockets functions without libcurl >= 7.86.0"); - return CFURLSessionEasyCodeNOT_BUILT_IN; -} -CFURLSessionEasyCode CFURLSessionEasyHandleWebSocketsSend(CFURLSessionEasyHandle _Nonnull handle, const char *_Nonnull data, size_t dataLen, size_t * _Nonnull writtenDataLen, long long frameSize, CFURLSessionWebSocketsMessageFlag messageFlags) { - CFAssert(false, __kCFLogAssertion, "Cannot use WebSockets functions without libcurl >= 7.86.0"); - return CFURLSessionEasyCodeNOT_BUILT_IN; -} - -struct CFURLSessionWebSocketsFrame emptyFrame = { 0, 0, 0, 0 }; - -CFURLSessionWebSocketsFrame * _Nonnull CFURLSessionEasyHandleWebSocketsMetadata(CFURLSessionEasyHandle _Nonnull handle) { - CFAssert(false, __kCFLogAssertion, "Cannot use WebSockets functions without libcurl >= 7.86.0"); - return &emptyFrame; -} - -#endif - int const CFURLSessionEasyErrorSize = { CURL_ERROR_SIZE + 1 }; CFURLSessionEasyCode const CFURLSessionEasyCodeOK = { CURLE_OK }; @@ -318,27 +262,6 @@ CFURLSessionProtocol const CFURLSessionProtocolGOPHER = CURLPROTO_GOPHER; CFURLSessionProtocol const CFURLSessionProtocolALL = CURLPROTO_ALL; -#if LIBCURL_VERSION_MAJOR > 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 86) -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsText = CURLWS_TEXT; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsBinary = CURLWS_BINARY; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsCont = CURLWS_CONT; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsClose = CURLWS_CLOSE; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsPing = CURLWS_PING; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsPong = CURLWS_PONG; - -CFURLSessionOption const CFURLSessionWebSocketsRawMode = { CURLWS_RAW_MODE }; -#else -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsText = -1; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsBinary = -1; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsCont = -1; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsClose = -1; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsPing = -1; -CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsPong = -1; - -CFURLSessionOption const CFURLSessionWebSocketsRawMode = { -1 }; -#endif - - size_t const CFURLSessionMaxWriteSize = CURL_MAX_WRITE_SIZE; diff --git a/CoreFoundation/URL.subproj/CFURLSessionInterface.h b/CoreFoundation/URL.subproj/CFURLSessionInterface.h index 574250a88d..dbc79080e1 100644 --- a/CoreFoundation/URL.subproj/CFURLSessionInterface.h +++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.h @@ -541,35 +541,6 @@ CF_EXPORT CFURLSessionProtocol const CFURLSessionProtocolGOPHER; // CURLPROTO_GO CF_EXPORT CFURLSessionProtocol const CFURLSessionProtocolALL; // CURLPROTO_ALL -// The following WebSockets symbols are -1 on libcurl < 7.86.0, or when WebSockets are disabled - -typedef unsigned int CFURLSessionWebSocketsMessageFlag; - -CF_EXPORT CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsText; // CURLWS_TEXT -CF_EXPORT CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsBinary; // CURLWS_BINARY -CF_EXPORT CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsCont; // CURLWS_CONT -CF_EXPORT CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsClose; // CURLWS_CLOSE -CF_EXPORT CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsPing; // CURLWS_PING -CF_EXPORT CFURLSessionWebSocketsMessageFlag const CFURLSessionWebSocketsPong; // CURLWS_PONG - -CF_EXPORT CFURLSessionOption const CFURLSessionWebSocketsRawMode; // CURLWS_RAW_MODE - -// The following WebSockets functions are functional with libcurl 7.86.0 or later, when WebSockets support is enabled. On libcurl versions without WebSockets support, they'll trap on use. Consult CFURLSessionWebSocketsSupported() to get a runtime signal whether they're functional. -CF_EXPORT Boolean CFURLSessionWebSocketsSupported(void); - -typedef struct CFURLSessionWebSocketsFrame { - int age; /* always zero */ - CFURLSessionWebSocketsMessageFlag flags; - long long offset; - long long bytesLeft; -} CFURLSessionWebSocketsFrame; - -CF_EXPORT CFURLSessionEasyCode CFURLSessionEasyHandleWebSocketsReceive(CFURLSessionEasyHandle _Nonnull handle, char *_Nonnull data, size_t dataLen, size_t * _Nonnull receivedDataLen, CFURLSessionWebSocketsFrame * _Nullable receivedFrame); -CF_EXPORT CFURLSessionEasyCode CFURLSessionEasyHandleWebSocketsSend(CFURLSessionEasyHandle _Nonnull handle, const char *_Nonnull data, size_t dataLen, size_t * _Nonnull writtenDataLen, long long frameSize, CFURLSessionWebSocketsMessageFlag messageFlags); - -CF_EXPORT CFURLSessionWebSocketsFrame * _Nonnull CFURLSessionEasyHandleWebSocketsMetadata(CFURLSessionEasyHandle _Nonnull handle); - - CF_EXPORT size_t const CFURLSessionMaxWriteSize; // CURL_MAX_WRITE_SIZE CF_EXPORT char * _Nonnull CFURLSessionCurlVersionString(void); diff --git a/Docs/API Surface.tasks b/Docs/API Surface.tasks index 0ffc0f38a1..ead758aeb8 100644 --- a/Docs/API Surface.tasks +++ b/Docs/API Surface.tasks @@ -9901,9 +9901,9 @@ API Surface: - uploadTask(with:fromFile:) @done - uploadTask(with:fromFile:completionHandler:) @done - uploadTask(withStreamedRequest:) @done - - webSocketTask(with:) @done - - webSocketTask(with:) @done - - webSocketTask(with:protocols:) @done + - webSocketTask(with:) + - webSocketTask(with:) + - webSocketTask(with:protocols:) - URLSessionConfiguration - allowsCellularAccess @done - allowsConstrainedNetworkAccess @@ -10104,12 +10104,12 @@ API Surface: - URLSessionUploadTask @done - init() - new() @done @unsupported @useSwiftForMemoryManagement - - URLSessionWebSocketDelegate @done + - URLSessionWebSocketDelegate - urlSession(_:webSocketTask:didCloseWith:reason:) - Swift.Void - urlSession(_:webSocketTask:didOpenWithProtocol:) - Swift.Void - - URLSessionWebSocketTask @done + - URLSessionWebSocketTask - CloseCode - RawValue - abnormalClosure diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj index 4f3e98bd42..c2e4fbc3ac 100644 --- a/Foundation.xcodeproj/project.pbxproj +++ b/Foundation.xcodeproj/project.pbxproj @@ -59,7 +59,7 @@ 159884921DCC877700E3314C /* TestHTTPCookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159884911DCC877700E3314C /* TestHTTPCookieStorage.swift */; }; 15A619DC245A2895003C8C62 /* libCFXMLInterface.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1550106A22EA24D10088F082 /* libCFXMLInterface.a */; }; 15A619E0245A298C003C8C62 /* CFXMLInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 15A619DF245A298C003C8C62 /* CFXMLInterface.c */; }; - 15B80388228F376000B30FF6 /* libcurl.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1FD9E01D6D178E0080E83C /* libcurl.4.dylib */; }; + 15B80388228F376000B30FF6 /* libcurl.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1FD9E01D6D178E0080E83C /* libcurl.3.dylib */; }; 15B8039E228F376000B30FF6 /* URLProtectionSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADE0B821BD15DFF00C49C64 /* URLProtectionSpace.swift */; }; 15B803B4228F376000B30FF6 /* URLCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADE0B7F1BD15DFF00C49C64 /* URLCredential.swift */; }; 15B803CF228F376000B30FF6 /* URLAuthenticationChallenge.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADE0B7D1BD15DFF00C49C64 /* URLAuthenticationChallenge.swift */; }; @@ -108,7 +108,6 @@ 528776191BF27D9500CB0090 /* Test.plist in Resources */ = {isa = PBXBuildFile; fileRef = 528776181BF27D9500CB0090 /* Test.plist */; }; 555683BD1C1250E70041D4C6 /* TestUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555683BC1C1250E70041D4C6 /* TestUserDefaults.swift */; }; 559451EC1F706BFA002807FB /* CFXMLPreferencesDomain.c in Sources */ = {isa = PBXBuildFile; fileRef = 559451EA1F706BF5002807FB /* CFXMLPreferencesDomain.c */; }; - 5A6AC80C28E7BC8F00A22FA7 /* WebSocketURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A6AC80A28E7652D00A22FA7 /* WebSocketURLProtocol.swift */; }; 5B0163BB1D024EB7003CCD96 /* DateComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0163BA1D024EB7003CCD96 /* DateComponents.swift */; }; 5B13B3251C582D4700651CE2 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA66F6381BF1619600136161 /* main.swift */; }; 5B13B3261C582D4C00651CE2 /* TestAffineTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = C93559281C12C49F009FD6A9 /* TestAffineTransform.swift */; }; @@ -155,7 +154,7 @@ 5B13B3511C582D4C00651CE2 /* TestByteCountFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A34B551C18C85D00FD972B /* TestByteCountFormatter.swift */; }; 5B13B3521C582D4C00651CE2 /* TestNSValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3047AEB1C38BC3300295652 /* TestNSValue.swift */; }; 5B1FD9C51D6D16150080E83C /* CFURLSessionInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B1FD9C11D6D160F0080E83C /* CFURLSessionInterface.c */; }; - 5B1FD9E11D6D178E0080E83C /* libcurl.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1FD9E01D6D178E0080E83C /* libcurl.4.dylib */; }; + 5B1FD9E11D6D178E0080E83C /* libcurl.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1FD9E01D6D178E0080E83C /* libcurl.3.dylib */; }; 5B1FD9E31D6D17B80080E83C /* TestURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1FD9E21D6D17B80080E83C /* TestURLSession.swift */; }; 5B23AB891CE62D4D000DB898 /* ReferenceConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B23AB881CE62D4D000DB898 /* ReferenceConvertible.swift */; }; 5B23AB8B1CE62F9B000DB898 /* PersonNameComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B23AB8A1CE62F9B000DB898 /* PersonNameComponents.swift */; }; @@ -852,12 +851,11 @@ 528776181BF27D9500CB0090 /* Test.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Test.plist; sourceTree = ""; }; 555683BC1C1250E70041D4C6 /* TestUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUserDefaults.swift; sourceTree = ""; usesTabs = 1; }; 559451EA1F706BF5002807FB /* CFXMLPreferencesDomain.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CFXMLPreferencesDomain.c; sourceTree = ""; }; - 5A6AC80A28E7652D00A22FA7 /* WebSocketURLProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocketURLProtocol.swift; path = URLSession/WebSocket/WebSocketURLProtocol.swift; sourceTree = ""; }; 5B0163BA1D024EB7003CCD96 /* DateComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateComponents.swift; sourceTree = ""; }; 5B0C6C211C1E07E600705A0E /* TestNSRegularExpression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSRegularExpression.swift; sourceTree = ""; }; 5B1FD9C11D6D160F0080E83C /* CFURLSessionInterface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CFURLSessionInterface.c; sourceTree = ""; }; 5B1FD9C21D6D160F0080E83C /* CFURLSessionInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFURLSessionInterface.h; sourceTree = ""; }; - 5B1FD9E01D6D178E0080E83C /* libcurl.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcurl.4.dylib; path = usr/lib/libcurl.4.dylib; sourceTree = SDKROOT; }; + 5B1FD9E01D6D178E0080E83C /* libcurl.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcurl.3.dylib; path = usr/lib/libcurl.3.dylib; sourceTree = SDKROOT; }; 5B1FD9E21D6D17B80080E83C /* TestURLSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestURLSession.swift; sourceTree = ""; }; 5B23AB861CE62D17000DB898 /* Boxing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Boxing.swift; sourceTree = ""; }; 5B23AB881CE62D4D000DB898 /* ReferenceConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReferenceConvertible.swift; sourceTree = ""; }; @@ -1354,7 +1352,7 @@ buildActionMask = 2147483647; files = ( 15FF00CC22934AD7004AD205 /* libCFURLSessionInterface.a in Frameworks */, - 15B80388228F376000B30FF6 /* libcurl.4.dylib in Frameworks */, + 15B80388228F376000B30FF6 /* libcurl.3.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1369,7 +1367,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5B1FD9E11D6D178E0080E83C /* libcurl.4.dylib in Frameworks */, + 5B1FD9E11D6D178E0080E83C /* libcurl.3.dylib in Frameworks */, 5B40F9F41C12524C000E72E3 /* libxml2.dylib in Frameworks */, 5B7C8B031BEA86A900C5B690 /* libCoreFoundation.a in Frameworks */, 5B5D89781BBDADDB00234F36 /* libz.dylib in Frameworks */, @@ -1437,14 +1435,6 @@ path = AttributedString; sourceTree = ""; }; - 5A6AC80728E7649D00A22FA7 /* WebSocket */ = { - isa = PBXGroup; - children = ( - 5A6AC80A28E7652D00A22FA7 /* WebSocketURLProtocol.swift */, - ); - name = WebSocket; - sourceTree = ""; - }; 5B5D88531BBC938800234F36 = { isa = PBXGroup; children = ( @@ -1794,7 +1784,7 @@ 5B5D89AB1BBDCD0B00234F36 /* Frameworks */ = { isa = PBXGroup; children = ( - 5B1FD9E01D6D178E0080E83C /* libcurl.4.dylib */, + 5B1FD9E01D6D178E0080E83C /* libcurl.3.dylib */, 5B40F9F31C12524C000E72E3 /* libxml2.dylib */, 5B5D89751BBDADD300234F36 /* libicucore.dylib */, 5B5D89791BBDADDF00234F36 /* libobjc.dylib */, @@ -2276,7 +2266,6 @@ F023072D23F0B6D70023DBEC /* URLSession */ = { isa = PBXGroup; children = ( - 5A6AC80728E7649D00A22FA7 /* WebSocket */, F023073A23F0B7060023DBEC /* libcurl */, F023073523F0B6F60023DBEC /* HTTP */, F023073223F0B6E90023DBEC /* FTP */, @@ -2725,7 +2714,7 @@ }; 5B7C8A6D1BEA7F8F00C5B690 = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 1410; + LastSwiftMigration = 1150; ProvisioningStyle = Manual; }; 5BDC405B1BD6D83B00ED97BB = { @@ -2885,7 +2874,6 @@ buildActionMask = 2147483647; files = ( B91161AA2429860900BD2907 /* DataURLProtocol.swift in Sources */, - 5A6AC80C28E7BC8F00A22FA7 /* WebSocketURLProtocol.swift in Sources */, F023073823F0B6FE0023DBEC /* HTTPMessage.swift in Sources */, 15B8043D228F38A600B30FF6 /* URLCredentialStorage.swift in Sources */, F023074023F0B7100023DBEC /* libcurlHelpers.swift in Sources */, diff --git a/Sources/FoundationNetworking/CMakeLists.txt b/Sources/FoundationNetworking/CMakeLists.txt index 2dec76980a..c1462d1dc0 100644 --- a/Sources/FoundationNetworking/CMakeLists.txt +++ b/Sources/FoundationNetworking/CMakeLists.txt @@ -36,7 +36,6 @@ add_library(FoundationNetworking URLSession/FTP/FTPURLProtocol.swift URLSession/HTTP/HTTPMessage.swift URLSession/HTTP/HTTPURLProtocol.swift - URLSession/WebSocket/WebSocketURLProtocol.swift URLSession/Message.swift URLSession/NativeProtocol.swift URLSession/NetworkingSpecific.swift diff --git a/Sources/FoundationNetworking/Resources/Info.plist b/Sources/FoundationNetworking/Resources/Info.plist index cc715d0504..7258076d92 100644 --- a/Sources/FoundationNetworking/Resources/Info.plist +++ b/Sources/FoundationNetworking/Resources/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleExecutable - SwiftFoundationNetworking + SwiftFoundation CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion diff --git a/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift b/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift index 9695be004e..8afe22db3f 100644 --- a/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift +++ b/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift @@ -499,106 +499,9 @@ internal class _HTTPURLProtocol: _NativeProtocol { } return nil } - - /// Whenever we receive a response (i.e. a complete header) from libcurl, - /// this method gets called. - func didReceiveResponse() { - guard let _ = task as? URLSessionDataTask else { return } - guard case .transferInProgress(let ts) = self.internalState else { fatalError("Transfer not in progress.") } - guard let response = ts.response as? HTTPURLResponse else { fatalError("Header complete, but not URL response.") } - guard let session = task?.session as? URLSession else { fatalError() } - switch session.behaviour(for: self.task!) { - case .noDelegate: - break - case .taskDelegate: - //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 - // - // For now, we'll notify the delegate, but won't pause the transfer, - // and we'll disregard the completion handler: - switch response.statusCode { - case 301, 302, 303, 305...308: - break - default: - self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) - } - case .dataCompletionHandler: - break - case .downloadCompletionHandler: - break - } - } - - /// If the response is a redirect, return the new request - /// - /// RFC 7231 section 6.4 defines redirection behavior for HTTP/1.1 - /// - /// - SeeAlso: - func redirectRequest(for response: HTTPURLResponse, fromRequest: URLRequest) -> URLRequest? { - //TODO: Do we ever want to redirect for HEAD requests? - - guard - let location = response.value(forHeaderField: .location), - let targetURL = URL(string: location) - else { - // Can't redirect when there's no location to redirect to. - return nil - } - - var request = fromRequest - - // Check for a redirect: - switch response.statusCode { - case 301...302 where request.httpMethod == "POST", 303: - // Change "POST" into "GET" but leave other methods unchanged: - request.httpMethod = "GET" - request.httpBody = nil - - case 301...302, 305...308: - // Re-use existing method: - break - - default: - return nil - } - - // If targetURL has only relative path of url, create a new valid url with relative path - // Otherwise, return request with targetURL ie.url from location field - guard targetURL.scheme == nil || targetURL.host == nil else { - request.url = targetURL - return request - } - - guard - let fromUrl = fromRequest.url, - var components = URLComponents(url: fromUrl, resolvingAgainstBaseURL: false) - else { return nil } - - // If the new URL contains a host, use the host and port from the new URL. - // Otherwise, the host and port from the original URL are used. - if targetURL.host != nil { - components.host = targetURL.host - components.port = targetURL.port - } - - // The path must either begin with "/" or be an empty string. - if targetURL.path.hasPrefix("/") { - components.path = targetURL.path - } else { - components.path = "/" + targetURL.path - } - - // The query and fragment components are set separately to prevent them from being - // percent encoded again. - components.percentEncodedQuery = targetURL.query - components.percentEncodedFragment = targetURL.fragment - - guard let url = components.url else { fatalError("Invalid URL") } - request.url = url +} - return request - } +fileprivate extension _HTTPURLProtocol { /// These are a list of headers that should be passed to libcurl. /// @@ -708,6 +611,109 @@ extension _HTTPURLProtocol { } } +/// Response processing +internal extension _HTTPURLProtocol { + /// Whenever we receive a response (i.e. a complete header) from libcurl, + /// this method gets called. + func didReceiveResponse() { + guard let _ = task as? URLSessionDataTask else { return } + guard case .transferInProgress(let ts) = self.internalState else { fatalError("Transfer not in progress.") } + guard let response = ts.response as? HTTPURLResponse else { fatalError("Header complete, but not URL response.") } + guard let session = task?.session as? URLSession else { fatalError() } + switch session.behaviour(for: self.task!) { + case .noDelegate: + break + case .taskDelegate: + //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 + // + // For now, we'll notify the delegate, but won't pause the transfer, + // and we'll disregard the completion handler: + switch response.statusCode { + case 301, 302, 303, 305...308: + break + default: + self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + } + case .dataCompletionHandler: + break + case .downloadCompletionHandler: + break + } + } + + /// If the response is a redirect, return the new request + /// + /// RFC 7231 section 6.4 defines redirection behavior for HTTP/1.1 + /// + /// - SeeAlso: + func redirectRequest(for response: HTTPURLResponse, fromRequest: URLRequest) -> URLRequest? { + //TODO: Do we ever want to redirect for HEAD requests? + + guard + let location = response.value(forHeaderField: .location), + let targetURL = URL(string: location) + else { + // Can't redirect when there's no location to redirect to. + return nil + } + + var request = fromRequest + + // Check for a redirect: + switch response.statusCode { + case 301...302 where request.httpMethod == "POST", 303: + // Change "POST" into "GET" but leave other methods unchanged: + request.httpMethod = "GET" + request.httpBody = nil + + case 301...302, 305...308: + // Re-use existing method: + break + + default: + return nil + } + + // If targetURL has only relative path of url, create a new valid url with relative path + // Otherwise, return request with targetURL ie.url from location field + guard targetURL.scheme == nil || targetURL.host == nil else { + request.url = targetURL + return request + } + + guard + let fromUrl = fromRequest.url, + var components = URLComponents(url: fromUrl, resolvingAgainstBaseURL: false) + else { return nil } + + // If the new URL contains a host, use the host and port from the new URL. + // Otherwise, the host and port from the original URL are used. + if targetURL.host != nil { + components.host = targetURL.host + components.port = targetURL.port + } + + // The path must either begin with "/" or be an empty string. + if targetURL.path.hasPrefix("/") { + components.path = targetURL.path + } else { + components.path = "/" + targetURL.path + } + + // The query and fragment components are set separately to prevent them from being + // percent encoded again. + components.percentEncodedQuery = targetURL.query + components.percentEncodedFragment = targetURL.fragment + + guard let url = components.url else { fatalError("Invalid URL") } + request.url = url + + return request + } +} + fileprivate extension HTTPURLResponse { /// Type safe HTTP header field name(s) enum _Field: String { diff --git a/Sources/FoundationNetworking/URLSession/URLSession.swift b/Sources/FoundationNetworking/URLSession/URLSession.swift index 2dcb000a22..ad64b4095d 100644 --- a/Sources/FoundationNetworking/URLSession/URLSession.swift +++ b/Sources/FoundationNetworking/URLSession/URLSession.swift @@ -200,7 +200,6 @@ open class URLSession : NSObject { _ = URLProtocol.registerClass(_HTTPURLProtocol.self) _ = URLProtocol.registerClass(_FTPURLProtocol.self) _ = URLProtocol.registerClass(_DataURLProtocol.self) - _ = URLProtocol.registerClass(_WebSocketURLProtocol.self) }() /* @@ -511,20 +510,6 @@ open class URLSession : NSObject { */ @available(*, unavailable, message: "URLSessionStreamTask is not available in swift-corelibs-foundation") open func streamTask(withHostName hostname: String, port: Int) -> URLSessionStreamTask { NSUnsupported() } - - open func webSocketTask(with url: URL) -> URLSessionWebSocketTask { - return webSocketTask(with: _Request(url), behavior: .callDelegate) - } - - open func webSocketTask(with url: URL, protocols: [String]) -> URLSessionWebSocketTask { - var request = URLRequest(url: url) - request.setValue(protocols.joined(separator: ", "), forHTTPHeaderField: "Sec-WebSocket-Protocol") - return webSocketTask(with: request) - } - - open func webSocketTask(with request: URLRequest) -> URLSessionWebSocketTask { - return webSocketTask(with: _Request(request), behavior: .callDelegate) - } } @@ -606,19 +591,7 @@ fileprivate extension URLSession { } return task } - - /// Create a web socket task - func webSocketTask(with request: _Request, behavior: _TaskRegistry._Behaviour) -> URLSessionWebSocketTask { - guard !self.invalidated else { fatalError("Session invalidated") } - let r = createConfiguredRequest(from: request) - let i = createNextTaskIdentifier() - let task = URLSessionWebSocketTask(session: self, request: r, taskIdentifier: i, body: URLSessionTask._Body.none) - workQueue.async { - self.taskRegistry.add(task, behaviour: behavior) - } - return task - } - + /// Create a download task that is marked invalid. func invalidDownloadTask(behavior: _TaskRegistry._Behaviour) -> URLSessionDownloadTask { /* We do not support resume data in swift-corelibs-foundation, so whatever we are passed, we should just behave as Darwin does in the presence of invalid data. */ diff --git a/Sources/FoundationNetworking/URLSession/URLSessionConfiguration.swift b/Sources/FoundationNetworking/URLSession/URLSessionConfiguration.swift index 1282b70135..f788206822 100644 --- a/Sources/FoundationNetworking/URLSession/URLSessionConfiguration.swift +++ b/Sources/FoundationNetworking/URLSession/URLSessionConfiguration.swift @@ -75,7 +75,7 @@ open class URLSessionConfiguration : NSObject, NSCopying { urlCredentialStorage: .shared, urlCache: .shared, shouldUseExtendedBackgroundIdleMode: false, - protocolClasses: [_HTTPURLProtocol.self, _FTPURLProtocol.self, _WebSocketURLProtocol.self]) + protocolClasses: [_HTTPURLProtocol.self, _FTPURLProtocol.self]) } private init(identifier: String?, diff --git a/Sources/FoundationNetworking/URLSession/URLSessionTask.swift b/Sources/FoundationNetworking/URLSession/URLSessionTask.swift index b3a731b791..699b799887 100644 --- a/Sources/FoundationNetworking/URLSession/URLSessionTask.swift +++ b/Sources/FoundationNetworking/URLSession/URLSessionTask.swift @@ -663,269 +663,6 @@ open class URLSessionDownloadTask : URLSessionTask { } } -/* - * A URLSessionWebSocketTask is a task that allows clients to connect to servers supporting - * WebSocket. The task will perform the HTTP handshake to upgrade the connection - * and once the WebSocket handshake is successful, the client can read and write - * messages that will be framed using the WebSocket protocol by the framework. - */ -open class URLSessionWebSocketTask : URLSessionTask { - public enum CloseCode : Int, @unchecked Sendable { - case invalid = 0 - case normalClosure = 1000 - case goingAway = 1001 - case protocolError = 1002 - case unsupportedData = 1003 - case noStatusReceived = 1005 - case abnormalClosure = 1006 - case invalidFramePayloadData = 1007 - case policyViolation = 1008 - case messageTooBig = 1009 - case mandatoryExtensionMissing = 1010 - case internalServerError = 1011 - case tlsHandshakeFailure = 1015 - } - - public enum Message { - case data(Data) - case string(String) - } - - internal var handshakeCompleted = false { - didSet { - doPendingWork() - } - } - - private var taskError: Error? = nil { - didSet { - doPendingWork() - } - } - - private var sendBuffer = [(Message, (Error?) -> Void)]() - private var receiveBuffer = [Message]() - private var receiveCompletionHandlers = [(Result) -> Void]() - private var pongCompletionHandlers = [(Error?) -> Void]() - private var closeMessage: (CloseCode, Data)? = nil - - internal var protocolPicked: String? = nil - - func appendReceivedMessage(_ message: Message) { - workQueue.async { - self.receiveBuffer.append(message) - self.doPendingWork() - } - } - - func noteReceivedPong() { - workQueue.async { - guard !self.pongCompletionHandlers.isEmpty else { - self.close(code: .protocolError, reason: nil) - return - } - let completionHandler = self.pongCompletionHandlers.removeFirst() - completionHandler(nil) - } - } - - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - open func sendPing() async throws { - let _: Void = try await withCheckedThrowingContinuation { continuation in - sendPing { error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: ()) - } - } - } - } - - open func sendPing(pongReceiveHandler: @escaping (Error?) -> Void) { - self.workQueue.async { - self._getProtocol { urlProtocol in - self.workQueue.async { - if let webSocketProtocol = urlProtocol as? _WebSocketURLProtocol { - do { - try webSocketProtocol.sendWebSocketData(Data(), flags: [.ping]) - self.pongCompletionHandlers.append(pongReceiveHandler) - } catch { - pongReceiveHandler(error) - } - } else { - pongReceiveHandler(POSIXError(.ENOTCONN)) - } - } - } - } - } - - override open func cancel() { - cancel(with: .invalid, reason: nil) - } - - open func cancel(with closeCode: CloseCode, reason: Data?) { - close(code: closeCode, reason: reason) - } - - open var maximumMessageSize: Int = 1 * 1024 * 1024 - - open private(set) var closeCode: CloseCode = .invalid - - open private(set) var closeReason: Data? = nil - - internal func close(code: CloseCode, reason: Data?) { - workQueue.async { - // If we've already errored out in some way, no need to re-close. - if self.taskError != nil { return } - - self.closeCode = code - self.closeReason = reason - self.taskError = POSIXError(.ENOTCONN) - self.closeMessage = (code, reason ?? Data()) - self.doPendingWork() - } - } - - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public func send(_ message: Message) async throws -> Void { - let _: Void = try await withCheckedThrowingContinuation { continuation in - send(message) { error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: ()) - } - } - } - } - - private func send(_ message: Message, completionHandler: @escaping (Error?) -> Void) { - self.workQueue.async { - self.sendBuffer.append((message, completionHandler)) - self.doPendingWork() - } - } - - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public func receive() async throws -> Message { - try await withCheckedThrowingContinuation { continuation in - receive() { result in - continuation.resume(with: result) - } - } - } - - private func receive(completionHandler: @escaping (Result) -> Void) { - self.workQueue.async { - self.receiveCompletionHandlers.append(completionHandler) - self.doPendingWork() - } - } - - private func doPendingWork() { - self.workQueue.async { - let session = self.session as! URLSession - if let taskError = self.taskError ?? self.error { - for (_, handler) in self.sendBuffer { - session.delegateQueue.addOperation { - handler(taskError) - } - } - self.sendBuffer.removeAll() - for handler in self.receiveCompletionHandlers { - session.delegateQueue.addOperation { - handler(.failure(taskError)) - } - } - self.receiveCompletionHandlers.removeAll() - self._getProtocol { urlProtocol in - self.workQueue.async { - if self.handshakeCompleted && self.state != .completed { - if let webSocketProtocol = urlProtocol as? _WebSocketURLProtocol { - if let closeMessage = self.closeMessage { - self.closeMessage = nil - var closeData = Data([UInt8(closeMessage.0.rawValue >> 8), UInt8(closeMessage.0.rawValue & 0xFF)]) - closeData.append(contentsOf: closeMessage.1) - try? webSocketProtocol.sendWebSocketData(closeData, flags: [.close]) - } - } - } - } - } - } else { - self._getProtocol { urlProtocol in - self.workQueue.async { - if self.handshakeCompleted { - if let webSocketProtocol = urlProtocol as? _WebSocketURLProtocol { - while !self.sendBuffer.isEmpty { - let (message, completionHandler) = self.sendBuffer.removeFirst() - do { - switch message { - case .data(let data): - try webSocketProtocol.sendWebSocketData(data, flags: [.binary]) - case .string(let str): - try webSocketProtocol.sendWebSocketData(str.data(using: .utf8)!, flags: [.text]) - } - completionHandler(nil) - } catch { - completionHandler(error) - } - } - if let closeMessage = self.closeMessage { - self.closeMessage = nil - var closeData = Data([UInt8(closeMessage.0.rawValue >> 8), UInt8(closeMessage.0.rawValue & 0xFF)]) - closeData.append(contentsOf: closeMessage.1) - try? webSocketProtocol.sendWebSocketData(closeData, flags: [.close]) - } - } - } - while !self.receiveBuffer.isEmpty && !self.receiveCompletionHandlers.isEmpty { - let message = self.receiveBuffer.removeFirst() - let handler = self.receiveCompletionHandlers.removeFirst() - handler(.success(message)) - } - } - } - } - } - } - - override open func resume() { - guard _EasyHandle.supportsWebSockets else { - workQueue.async { - var userInfo: [String: Any] = [NSLocalizedDescriptionKey: "WebSockets not supported by libcurl"] - if let url = self.originalRequest?.url { - userInfo[NSURLErrorFailingURLErrorKey] = url - userInfo[NSURLErrorFailingURLStringErrorKey] = url.absoluteString - } - let urlError = URLError(_nsError: NSError(domain: NSURLErrorDomain, - code: NSURLErrorUnsupportedURL, - userInfo: userInfo)) - self.error = urlError - _ProtocolClient().urlProtocol(task: self, didFailWithError: urlError) - } - return - } - super.resume() - } - - internal static var supportsWebSockets: Bool { - _EasyHandle.supportsWebSockets - } -} - -public protocol URLSessionWebSocketDelegate : URLSessionTaskDelegate { - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) -} - -extension URLSessionWebSocketDelegate { - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) {} - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {} -} - /* * An URLSessionStreamTask provides an interface to perform reads * and writes to a TCP/IP stream created via URLSession. This task @@ -1018,6 +755,7 @@ extension _ProtocolClient : URLProtocolClient { guard let task = `protocol`.task else { fatalError("Received response, but there's no task.") } task.response = response let session = task.session as! URLSession + guard let dataTask = task as? URLSessionDataTask else { return } // Only cache data tasks: self.cachePolicy = policy @@ -1035,21 +773,13 @@ extension _ProtocolClient : URLProtocolClient { } switch session.behaviour(for: task) { - case .taskDelegate(let delegate): - if let dataDelegate = delegate as? URLSessionDataDelegate, - let dataTask = task as? URLSessionDataTask { - session.delegateQueue.addOperation { - dataDelegate.urlSession(session, dataTask: dataTask, didReceive: response, completionHandler: { _ in - URLSession.printDebug("warning: Ignoring disposition from completion handler.") - }) - } - } else if let webSocketDelegate = delegate as? URLSessionWebSocketDelegate, - let webSocketTask = task as? URLSessionWebSocketTask { - session.delegateQueue.addOperation { - webSocketDelegate.urlSession(session, webSocketTask: webSocketTask, didOpenWithProtocol: webSocketTask.protocolPicked) - } + case .taskDelegate(let delegate as URLSessionDataDelegate): + session.delegateQueue.addOperation { + delegate.urlSession(session, dataTask: dataTask, didReceive: response, completionHandler: { _ in + URLSession.printDebug("warning: Ignoring disposition from completion handler.") + }) } - case .noDelegate, .dataCompletionHandler, .downloadCompletionHandler: + case .noDelegate, .taskDelegate, .dataCompletionHandler, .downloadCompletionHandler: break } } @@ -1128,11 +858,6 @@ extension _ProtocolClient : URLProtocolClient { session.delegateQueue.addOperation { downloadDelegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: urlProtocol.properties[URLProtocol._PropertyKey.temporaryFileURL] as! URL) } - } else if let webSocketDelegate = delegate as? URLSessionWebSocketDelegate, - let webSocketTask = task as? URLSessionWebSocketTask { - session.delegateQueue.addOperation { - webSocketDelegate.urlSession(session, webSocketTask: webSocketTask, didCloseWith: webSocketTask.closeCode, reason: webSocketTask.closeReason) - } } session.delegateQueue.addOperation { guard task.state != .completed else { return } diff --git a/Sources/FoundationNetworking/URLSession/WebSocket/WebSocketURLProtocol.swift b/Sources/FoundationNetworking/URLSession/WebSocket/WebSocketURLProtocol.swift deleted file mode 100644 index 4a51ed3a91..0000000000 --- a/Sources/FoundationNetworking/URLSession/WebSocket/WebSocketURLProtocol.swift +++ /dev/null @@ -1,176 +0,0 @@ -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// - -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) -import SwiftFoundation -#else -import Foundation -#endif - -@_implementationOnly import CoreFoundation -@_implementationOnly import CFURLSessionInterface -import Dispatch - -internal class _WebSocketURLProtocol: _HTTPURLProtocol { - public required init(task: URLSessionTask, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) { - super.init(task: task, cachedResponse: nil, client: client) - } - - public required init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) { - super.init(request: request, cachedResponse: nil, client: client) - } - - override class func canInit(with request: URLRequest) -> Bool { - switch request.url?.scheme { - case "ws", "wss": return true - default: return false - } - } - - override func canCache(_ response: CachedURLResponse) -> Bool { - false - } - - override func canRespondFromCache(using response: CachedURLResponse) -> Bool { false } - - override func didReceiveResponse() { - guard let webSocketTask = task as? URLSessionWebSocketTask else { return } - guard case .transferInProgress(let ts) = self.internalState else { fatalError("Transfer not in progress.") } - guard let response = ts.response as? HTTPURLResponse else { fatalError("Header complete, but not URL response.") } - - webSocketTask.protocolPicked = response.value(forHTTPHeaderField: "Sec-WebSocket-Protocol") - - easyHandle.timeoutTimer = nil - - webSocketTask.handshakeCompleted = true - - self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) - } - - /// Set options on the easy handle to match the given request. - /// - /// This performs a series of `curl_easy_setopt()` calls. - override func configureEasyHandle(for request: URLRequest, body: _Body) { - guard request.httpMethod == "GET" else { - NSLog("WebSocket tasks must use GET") - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorUnsupportedURL, - userInfo: [ - NSLocalizedDescriptionKey: "websocket task must use GET httpMethod", - NSURLErrorFailingURLStringErrorKey: request.url?.description ?? "" - ]) - internalState = .transferFailed - transferCompleted(withError: error) - return - } - - super.configureEasyHandle(for: request, body: body) - - easyHandle.setAllowedProtocolsToAll() - - guard let webSocketTask = task as? URLSessionWebSocketTask else { return } - easyHandle.set(preferredReceiveBufferSize: webSocketTask.maximumMessageSize) - } - - override func completionAction(forCompletedRequest request: URLRequest, response: URLResponse) -> _CompletionAction { - // Redirect: - guard let httpURLResponse = response as? HTTPURLResponse else { - fatalError("Response was not HTTPURLResponse") - } - if let request = redirectRequest(for: httpURLResponse, fromRequest: request) { - return .redirectWithRequest(request) - } - return .completeTask - } - - func sendWebSocketData(_ data: Data, flags: _EasyHandle.WebSocketFlags) throws { - try easyHandle.sendWebSocketsData(data, flags: flags) - } - - override func didReceive(data: Data) -> _EasyHandle._Action { - guard case .transferInProgress(var ts) = internalState else { - fatalError("Received web socket data, but no transfer in progress.") - } - - if let response = validateHeaderComplete(transferState:ts) { - ts.response = response - } - - // Note this excludes code 300 which should return the response of the redirect and not follow it. - // For other redirect codes dont notify the delegate of the data received in the redirect response. - if let httpResponse = ts.response as? HTTPURLResponse, - 301...308 ~= httpResponse.statusCode { - // Save the response body in case the delegate does not perform a redirect and the 3xx response - // including its body needs to be returned to the client. - var redirectBody = lastRedirectBody ?? Data() - redirectBody.append(data) - lastRedirectBody = redirectBody - } - - let flags = easyHandle.getWebSocketFlags() - - notifyTask(aboutReceivedData: data, flags: flags) - internalState = .transferInProgress(ts) - return .proceed - } - - fileprivate func notifyTask(aboutReceivedData data: Data, flags: _EasyHandle.WebSocketFlags) { - guard let t = self.task else { - fatalError("Cannot notify") - } - guard case .taskDelegate = t.session.behaviour(for: self.task!), - let task = self.task as? URLSessionWebSocketTask else { - fatalError("WebSocket internal invariant violated") - } - - // Buffer the response message in the task - if flags.contains(.close) { - let closeCode: URLSessionWebSocketTask.CloseCode - let reasonData: Data - if data.count >= 2 { - closeCode = data.withUnsafeBytes { - let codeInt = UInt16(bigEndian: $0.load(as: UInt16.self)) - return URLSessionWebSocketTask.CloseCode(rawValue: Int(codeInt)) ?? .unsupportedData - } - reasonData = Data(data[2...]) - } else { - closeCode = .normalClosure - reasonData = Data() - } - task.close(code: closeCode, reason: reasonData) - } else if flags.contains(.pong) { - task.noteReceivedPong() - } else if flags.contains(.binary) { - let message = URLSessionWebSocketTask.Message.data(data) - task.appendReceivedMessage(message) - } else if flags.contains(.text) { - guard let utf8 = String(data: data, encoding: .utf8) else { - NSLog("Invalid utf8 message received from server \(data)") - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorBadServerResponse, - userInfo: [ - NSLocalizedDescriptionKey: "Invalid message received from server", - NSURLErrorFailingURLStringErrorKey: request.url?.description ?? "" - ]) - internalState = .transferFailed - transferCompleted(withError: error) - return - } - let message = URLSessionWebSocketTask.Message.string(utf8) - task.appendReceivedMessage(message) - } else { - NSLog("Unexpected message received from server \(data) \(flags)") - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorBadServerResponse, - userInfo: [ - NSLocalizedDescriptionKey: "Unexpected message received from server", - NSURLErrorFailingURLStringErrorKey: request.url?.description ?? "" - ]) - internalState = .transferFailed - transferCompleted(withError: error) - } - } -} diff --git a/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift b/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift index d6e50554ac..dfdd0c9a2e 100644 --- a/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift +++ b/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift @@ -206,13 +206,6 @@ extension _EasyHandle { //CURLOPT_DEFAULT_PROTOCOL available only in libcurl 7.45.0 } - func setAllowedProtocolsToAll() { - let protocols = (CFURLSessionProtocolALL) - let redirectProtocols = (CFURLSessionProtocolHTTP | CFURLSessionProtocolHTTPS) - try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionPROTOCOLS, protocols).asError() - try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionREDIR_PROTOCOLS, redirectProtocols).asError() - } - //TODO: Proxy setting, namely CFURLSessionOptionPROXY, CFURLSessionOptionPROXYPORT, // CFURLSessionOptionPROXYTYPE, CFURLSessionOptionNOPROXY, CFURLSessionOptionHTTPPROXYTUNNEL, CFURLSessionOptionPROXYHEADER, // CFURLSessionOptionHEADEROPT, etc. @@ -281,7 +274,7 @@ extension _EasyHandle { } func set(timeout value: Int) { - try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionTIMEOUT, numericCast(value)).asError() + try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionTIMEOUT, numericCast(value)).asError() } func getTimeoutIntervalSpent() -> Double { @@ -289,64 +282,7 @@ extension _EasyHandle { CFURLSession_easy_getinfo_double(rawHandle, CFURLSessionInfoTOTAL_TIME, &timeSpent) return timeSpent / 1000 } -} -/// WebSocket support -extension _EasyHandle { - struct WebSocketFlags: OptionSet { - internal private(set) var rawValue: UInt32 - - static let text = WebSocketFlags(rawValue: CFURLSessionWebSocketsText) - static let binary = WebSocketFlags(rawValue: CFURLSessionWebSocketsBinary) - static let cont = WebSocketFlags(rawValue: CFURLSessionWebSocketsCont) - static let close = WebSocketFlags(rawValue: CFURLSessionWebSocketsClose) - static let ping = WebSocketFlags(rawValue: CFURLSessionWebSocketsPing) - static let pong = WebSocketFlags(rawValue: CFURLSessionWebSocketsPong) - } - - // Only valid to call within a didReceive(data:size:nmemb:) call - func getWebSocketFlags() -> WebSocketFlags { - let metadataPointer = CFURLSessionEasyHandleWebSocketsMetadata(rawHandle) - let flags = WebSocketFlags(rawValue: metadataPointer.pointee.flags) - return flags - } - - func receiveWebSocketsData() throws -> (Data, WebSocketFlags) { - let len = 16 * 1024 // pulled out of a hat - var data = Data.init(capacity: len) - var bytesRead: Int = 0 - var frameMetadata = CFURLSessionWebSocketsFrame() - try data.withUnsafeMutableBytes { bytes in - try bytes.baseAddress!.withMemoryRebound(to: CChar.self, capacity: len) { bytesPtr in - try withUnsafeMutablePointer(to: &bytesRead) { bytesReadPtr in - try withUnsafeMutablePointer(to: &frameMetadata) { metadataPtr in - try CFURLSessionEasyHandleWebSocketsReceive(rawHandle, bytesPtr, len, bytesReadPtr, metadataPtr).asError() - } - } - } - } - let flags = WebSocketFlags(rawValue: frameMetadata.flags) - return (data, flags) - } - - func sendWebSocketsData(_ data: Data, flags: WebSocketFlags) throws { - let cfurlSessionFlags = flags.rawValue as CFURLSessionWebSocketsMessageFlag - - try data.withUnsafeBytes { bytes in - try bytes.baseAddress!.withMemoryRebound(to: CChar.self, capacity: data.count) { bytesPtr in - var offset = 0 - repeat { - var amountWritten = 0 - try CFURLSessionEasyHandleWebSocketsSend(rawHandle, bytesPtr.advanced(by: offset), data.count, &amountWritten, 0, cfurlSessionFlags).asError() - offset += amountWritten - } while offset < data.count - } - } - } - - static var supportsWebSockets: Bool { - return CFURLSessionWebSocketsSupported() - } } fileprivate func printLibcurlDebug(handle: CFURLSessionEasyHandle, type: CInt, data: UnsafeMutablePointer, size: Int, userInfo: UnsafeMutableRawPointer?) -> CInt { @@ -504,9 +440,7 @@ fileprivate extension _EasyHandle { func resetTimer() { //simply create a new timer with the same queue, timeout and handler //this must cancel the old handler and reset the timer - if let timeoutTimer { - self.timeoutTimer = _TimeoutSource(queue: timeoutTimer.queue, milliseconds: timeoutTimer.milliseconds, handler: timeoutTimer.handler) - } + timeoutTimer = _TimeoutSource(queue: timeoutTimer.queue, milliseconds: timeoutTimer.milliseconds, handler: timeoutTimer.handler) } /// Forward the libcurl callbacks into Swift methods diff --git a/Sources/FoundationXML/Resources/Info.plist b/Sources/FoundationXML/Resources/Info.plist index 7053d71976..7258076d92 100644 --- a/Sources/FoundationXML/Resources/Info.plist +++ b/Sources/FoundationXML/Resources/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleExecutable - SwiftFoundationXML + SwiftFoundation CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion diff --git a/Tests/Foundation/HTTPServer.swift b/Tests/Foundation/HTTPServer.swift index 9349874b09..412bb54656 100644 --- a/Tests/Foundation/HTTPServer.swift +++ b/Tests/Foundation/HTTPServer.swift @@ -577,7 +577,6 @@ struct _HTTPRequest: CustomStringConvertible { struct _HTTPResponse { enum Response: Int { - case SWITCHING_PROTOCOLS = 101 case OK = 200 case FOUND = 302 case BAD_REQUEST = 400 @@ -686,8 +685,6 @@ public class TestURLSessionServer: CustomStringConvertible { try httpServer.respondWithAuthResponse(request: req) } else if req.uri.hasPrefix("/unauthorized") { try httpServer.respondWithUnauthorizedHeader() - } else if req.uri.hasPrefix("/web-socket") { - try handleWebSocketRequest(req) } else { let response = try getResponse(request: req) try httpServer.respond(with: response) @@ -867,154 +864,6 @@ public class TestURLSessionServer: CustomStringConvertible { return try _HTTPResponse(response: .OK, body: capital) } - private func unmaskedPayload(from masked: Data) throws -> Data { - if masked.count < 6 { - throw InternalServerError.badBody - } - if masked.count == 6 { - return Data() - } - var maskingKey: UInt32 = 0 - _ = withUnsafeMutableBytes(of: &maskingKey) { buffer in - masked.subdata(in: 2..<6).copyBytes(to: buffer) - } - var paddedMasked = masked - var padCount = 0 - while paddedMasked.count % 4 != 2 { - paddedMasked.append(0x00) - padCount += 1 - } - let maskedPayload = paddedMasked.suffix(from: 6) - let unmaskedPayload = maskedPayload.enumerated().map { i, byte in - let maskByte: UInt8 - switch i % 4 { - case 3: maskByte = UInt8(maskingKey >> 24) - case 2: maskByte = UInt8((maskingKey >> 16) & 0xFF) - case 1: maskByte = UInt8((maskingKey >> 8) & 0xFF) - case 0: maskByte = UInt8(maskingKey & 0xFF) - default: fatalError() - } - return maskByte ^ byte - } - return Data(unmaskedPayload.dropLast(padCount)) - } - - func handleWebSocketRequest(_ request: _HTTPRequest) throws { - guard request.method == .GET, - "websocket" == request.getHeader(for: "upgrade"), - let connectionHeader = request.getHeader(for: "connection"), - connectionHeader.lowercased().contains("upgrade") else { - try httpServer.respond(with: _HTTPResponse(response: .NOT_FOUND)) - return - } - - var responseHeaders = ["Upgrade: websocket", - "Connection: Upgrade"] - - let expectFullRequestResponseTests: Bool - let uri = request.uri - if uri.count > "/web-socket/".count { - let expectedProtocol = String(uri.suffix(from: uri.index(uri.startIndex, offsetBy: "/web-socket/".count))) - guard let receivedProtocolStr = request.getHeader(for: "Sec-WebSocket-Protocol"), - expectedProtocol == receivedProtocolStr.components(separatedBy: ", ")[0] else { - NSLog("Expected Sec-WebSocket-Protocol") - throw InternalServerError.badHeaders - } - responseHeaders.append("Sec-WebSocket-Protocol: \(expectedProtocol)") - expectFullRequestResponseTests = false - } else { - expectFullRequestResponseTests = true - } - - var upgradeResponse = _HTTPResponse(response: .SWITCHING_PROTOCOLS, headers: responseHeaders) - // Lacking an available SHA1 implementation, we'll only include this response for a well-known key - if "dGhlIHNhbXBsZSBub25jZQ==" == request.getHeader(for: "sec-websocket-key") { - upgradeResponse.addHeader("Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") - } - - try httpServer.respond(with: upgradeResponse) - - do { - let closeCode = 1000 - let closeReason = "BuhBye".data(using: .utf8)! - let closePayload = Data([UInt8(closeCode >> 8), - UInt8(closeCode & 0xFF)]) + closeReason - - if expectFullRequestResponseTests { - let stringPayload = "Hello".data(using: .utf8)! - let dataPayload = Data([0x20, 0x22, 0x10, 0x03]) - let pingPayload = "Hi".data(using: .utf8)! - - // Receive a string message - guard let stringFrame = try httpServer.tcpSocket.readData(), - stringFrame.count == (2 + 4 + stringPayload.count), - Data(stringFrame.prefix(2)) == Data([0x81, (0x80 | UInt8(stringPayload.count))]), - try unmaskedPayload(from: stringFrame) == stringPayload else { - NSLog("Invalid string frame") - throw InternalServerError.badBody - } - - // Send a string message - let sendStringFrame = Data([0x81, UInt8(stringPayload.count)]) + stringPayload - try httpServer.tcpSocket.writeRawData(sendStringFrame) - - // Receive a data message - guard let dataFrame = try httpServer.tcpSocket.readData(), - dataFrame.count == (2 + 4 + dataPayload.count), - Data(dataFrame.prefix(2)) == Data([0x82, (0x80 | UInt8(dataPayload.count))]), - try unmaskedPayload(from: dataFrame) == dataPayload else { - NSLog("Invalid data frame") - throw InternalServerError.badBody - } - - // Send a data message - let sendDataFrame = Data([0x82, UInt8(dataPayload.count)]) + dataPayload - try httpServer.tcpSocket.writeRawData(sendDataFrame) - - // Receive a ping - guard let pingFrame = try httpServer.tcpSocket.readData(), - pingFrame.count == (2 + 4 + 0), - Data(pingFrame.prefix(2)) == Data([0x89, 0x80]), - try unmaskedPayload(from: pingFrame) == Data() else { - NSLog("Invalid ping frame") - throw InternalServerError.badBody - } - // ... and pong it - try httpServer.tcpSocket.writeRawData(Data([0x8a, 0x00])) - - // Send a ping - let sendPingFrame = Data([0x89, UInt8(pingPayload.count)]) + pingPayload - try httpServer.tcpSocket.writeRawData(sendPingFrame) - // ... and receive its pong - guard let pongFrame = try httpServer.tcpSocket.readData(), - pongFrame.count == (2 + 4 + pingPayload.count), - Data(pongFrame.prefix(2)) == Data([0x8a, (0x80 | UInt8(pingPayload.count))]), - try unmaskedPayload(from: pongFrame) == pingPayload else { - NSLog("Invalid pong frame") - throw InternalServerError.badBody - } - - // Send a close - let sendCloseFrame = Data([0x88, UInt8(closePayload.count)]) + closePayload - try httpServer.tcpSocket.writeRawData(sendCloseFrame) - } - - // Receive a close message - guard let closeFrame = try httpServer.tcpSocket.readData(), - closeFrame.count == (2 + 4 + closePayload.count), - Data(closeFrame.prefix(2)) == Data([0x88, (0x80 | UInt8(closePayload.count))]), - try unmaskedPayload(from: closeFrame) == closePayload else { - NSLog("Invalid close payload") - throw InternalServerError.badBody - } - - } catch { - let badBodyCloseFrame = Data([0x88, 0x08, 0x03, 0xEA, 0x42, 0x75, 0x68, 0x42, 0x79, 0x65]) - try httpServer.tcpSocket.writeRawData(badBodyCloseFrame) - throw error - } - } - private func statusCodeResponse(forRequest request: _HTTPRequest, statusCode: Int) throws -> _HTTPResponse { guard let bodyData = try? request.headersAsJSON() else { return try _HTTPResponse(response: .SERVER_ERROR, body: "Cant convert headers to JSON object") @@ -1064,7 +913,6 @@ enum InternalServerError : Error { case socketAlreadyClosed case requestTooShort case badBody - case badHeaders } @@ -1110,7 +958,7 @@ class LoopbackServerTest : XCTestCase { do { try subServer.readAndRespond() } catch { - NSLog("readAndRespond: \(error)") + NSLog("reandAndRespond: \(error)") } } } catch { diff --git a/Tests/Foundation/Tests/TestAffineTransform.swift b/Tests/Foundation/Tests/TestAffineTransform.swift index ccf7076e96..e322bc66db 100644 --- a/Tests/Foundation/Tests/TestAffineTransform.swift +++ b/Tests/Foundation/Tests/TestAffineTransform.swift @@ -26,7 +26,7 @@ public struct Vector { // MARK: - Tests class TestAffineTransform: XCTestCase { - private let accuracyThreshold: CGFloat = 0.001 + private let accuracyThreshold = 0.001 static var allTests: [(String, (TestAffineTransform) -> () throws -> Void)] { return [ diff --git a/Tests/Foundation/Tests/TestPropertyListSerialization.swift b/Tests/Foundation/Tests/TestPropertyListSerialization.swift index 37b51c7411..3a6acaed6e 100644 --- a/Tests/Foundation/Tests/TestPropertyListSerialization.swift +++ b/Tests/Foundation/Tests/TestPropertyListSerialization.swift @@ -83,7 +83,7 @@ class TestPropertyListSerialization : XCTestCase { func test_decodeEmptyData() { XCTAssertThrowsError(try PropertyListSerialization.propertyList(from: Data(), format: nil)) { error in - let nserror = error as! NSError + let nserror = error as NSError XCTAssertEqual(nserror.domain, NSCocoaErrorDomain) XCTAssertEqual(CocoaError(_nsError: nserror).code, .propertyListReadCorrupt) XCTAssertEqual(nserror.userInfo[NSDebugDescriptionErrorKey] as? String, "Cannot parse a NULL or zero-length data") diff --git a/Tests/Foundation/Tests/TestURLSession.swift b/Tests/Foundation/Tests/TestURLSession.swift index 1fee167704..901ccea721 100644 --- a/Tests/Foundation/Tests/TestURLSession.swift +++ b/Tests/Foundation/Tests/TestURLSession.swift @@ -7,14 +7,6 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT - #if canImport(SwiftFoundationNetworking) && !DEPLOYMENT_RUNTIME_OBJC - @testable import SwiftFoundationNetworking - #else - @testable import FoundationNetworking - #endif -#endif - class TestURLSession: LoopbackServerTest { let httpMethods = ["HEAD", "GET", "PUT", "POST", "DELETE"] @@ -1857,85 +1849,8 @@ class TestURLSession: LoopbackServerTest { } } -#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT - func test_webSocket() async throws { - guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return } - guard URLSessionWebSocketTask.supportsWebSockets else { - print("libcurl lacks WebSockets support, skipping \(#function)") - return - } - - let urlString = "ws://127.0.0.1:\(TestURLSession.serverPort)/web-socket" - let url = try XCTUnwrap(URL(string: urlString)) - let request = URLRequest(url: url) - - let delegate = SessionDelegate(with: expectation(description: "\(urlString): Connect")) - let task = delegate.runWebSocketTask(with: request, timeoutInterval: 4) - - // We interleave sending and receiving, as the test HTTPServer implementation is barebones, and can't handle receiving more than one frame at a time. So, this back-and-forth acts as a gating mechanism - try await task.send(.string("Hello")) - - let stringMessage = try await task.receive() - switch stringMessage { - case .string(let str): - XCTAssert(str == "Hello") - default: - XCTFail("Unexpected String Message") - } - - try await task.send(.data(Data([0x20, 0x22, 0x10, 0x03]))) - - let dataMessage = try await task.receive() - switch dataMessage { - case .data(let data): - XCTAssert(data == Data([0x20, 0x22, 0x10, 0x03])) - default: - XCTFail("Unexpected Data Message") - } - - try await task.sendPing() - - wait(for: [delegate.expectation], timeout: 50) - - let callbacks = [ "urlSession(_:webSocketTask:didOpenWithProtocol:)", - "urlSession(_:webSocketTask:didCloseWith:reason:)", - "urlSession(_:task:didCompleteWithError:)" ] - XCTAssertEqual(delegate.callbacks.count, callbacks.count) - XCTAssertEqual(delegate.callbacks, callbacks, "Callbacks for \(#function)") - } - - func test_webSocketSpecificProtocol() async throws { - guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return } - guard URLSessionWebSocketTask.supportsWebSockets else { - print("libcurl lacks WebSockets support, skipping \(#function)") - return - } - - let urlString = "ws://127.0.0.1:\(TestURLSession.serverPort)/web-socket/chatbot" - let url = try XCTUnwrap(URL(string: urlString)) - let request = URLRequest(url: url) - - let delegate = SessionDelegate(with: expectation(description: "\(urlString): Connect")) - let task = delegate.runWebSocketTask(with: request, timeoutInterval: 4, protocols: ["chatbot", "IRC", "BulletinBoard"]) - - DispatchQueue.global(qos: .default).asyncAfter(wallDeadline: .now() + 1) { - task.cancel(with: .normalClosure, reason: "BuhBye".data(using: .utf8)) - } - - wait(for: [delegate.expectation], timeout: 50) - - let callbacks = [ "urlSession(_:webSocketTask:didOpenWithProtocol:)", - "urlSession(_:task:didCompleteWithError:)" ] - XCTAssertEqual(delegate.callbacks.count, callbacks.count) - XCTAssertEqual(delegate.callbacks, callbacks, "Callbacks for \(#function)") - - XCTAssertEqual(task.closeCode, .normalClosure) - XCTAssertEqual(task.closeReason, "BuhBye".data(using: .utf8)) - } -#endif - static var allTests: [(String, (TestURLSession) -> () throws -> Void)] { - var retVal = [ + return [ ("test_dataTaskWithURL", test_dataTaskWithURL), ("test_dataTaskWithURLRequest", test_dataTaskWithURLRequest), ("test_dataTaskWithURLCompletionHandler", test_dataTaskWithURLCompletionHandler), @@ -2007,13 +1922,6 @@ class TestURLSession: LoopbackServerTest { /* ⚠️ */ testExpectedToFail(test_noDoubleCallbackWhenCancellingAndProtocolFailsFast, "This test crashes nondeterministically: https://bugs.swift.org/browse/SR-11310")), /* ⚠️ */ ("test_cancelledTasksCannotBeResumed", testExpectedToFail(test_cancelledTasksCannotBeResumed, "Breaks on Ubuntu 18.04")), ] - if #available(macOS 12.0, *) { - retVal.append(contentsOf: [ - ("test_webSocket", asyncTest(test_webSocket)), - ("test_webSocketSpecificProtocol", asyncTest(test_webSocketSpecificProtocol)), - ]) - } - return retVal } } @@ -2034,10 +1942,10 @@ extension SharedDelegate: URLSessionDownloadDelegate { } -class SessionDelegate: NSObject, URLSessionDelegate, URLSessionWebSocketDelegate { +class SessionDelegate: NSObject, URLSessionDelegate { var expectation: XCTestExpectation! = nil var session: URLSession! = nil - var task: URLSessionTask! = nil + var task: URLSessionDataTask! = nil var cancelExpectation: XCTestExpectation? = nil var invalidateExpectation: XCTestExpectation? = nil @@ -2098,22 +2006,7 @@ class SessionDelegate: NSObject, URLSessionDelegate, URLSessionWebSocketDelegate task = session.uploadTask(withStreamedRequest: request) task.resume() } - - func runWebSocketTask(with request: URLRequest, timeoutInterval: Double = 3, protocols: [String] = []) -> URLSessionWebSocketTask { - let config = URLSessionConfiguration.default - config.timeoutIntervalForRequest = timeoutInterval - session = URLSession(configuration: config, delegate: self, delegateQueue: nil) - let webSocketTask: URLSessionWebSocketTask - if protocols.isEmpty { - webSocketTask = session.webSocketTask(with: request) - } else { - webSocketTask = session.webSocketTask(with: request.url!, protocols: protocols) - } - task = webSocketTask - task.resume() - return webSocketTask - } - + func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { callbacks.append(#function) self.error = error @@ -2123,13 +2016,6 @@ class SessionDelegate: NSObject, URLSessionDelegate, URLSessionWebSocketDelegate func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { callbacks.append(#function) } - - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { - callbacks.append(#function) - } - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { - callbacks.append(#function) - } } extension SessionDelegate: URLSessionTaskDelegate { diff --git a/Tests/Foundation/Utilities.swift b/Tests/Foundation/Utilities.swift index 14d6eee3b0..486a79c047 100644 --- a/Tests/Foundation/Utilities.swift +++ b/Tests/Foundation/Utilities.swift @@ -545,14 +545,6 @@ func shouldAttemptXFailTests(_ reason: String) -> Bool { } } -func shouldAttemptDarwinXFailTests(_ reason: String) -> Bool { - #if canImport(Darwin) - return shouldAttemptXFailTests(reason) - #else - return true - #endif -} - func shouldAttemptWindowsXFailTests(_ reason: String) -> Bool { #if os(Windows) return shouldAttemptXFailTests(reason) @@ -593,10 +585,6 @@ func testExpectedToFail(_ test: @escaping (T) -> () throws -> Void, _ reason testExpectedToFailWithCheck(check: shouldAttemptXFailTests(_:), test, reason) } -func testExpectedToFailOnDarwin(_ test: @escaping (T) -> () throws -> Void, _ reason: String) -> (T) -> () throws -> Void { - testExpectedToFailWithCheck(check: shouldAttemptDarwinXFailTests(_:), test, reason) -} - func testExpectedToFailOnWindows(_ test: @escaping (T) -> () throws -> Void, _ reason: String) -> (T) -> () throws -> Void { testExpectedToFailWithCheck(check: shouldAttemptWindowsXFailTests(_:), test, reason) }