diff --git a/Sources/FoundationNetworking/URLSession/NetworkingSpecific.swift b/Sources/FoundationNetworking/URLSession/NetworkingSpecific.swift index 4057cbab56..419c9ddb43 100644 --- a/Sources/FoundationNetworking/URLSession/NetworkingSpecific.swift +++ b/Sources/FoundationNetworking/URLSession/NetworkingSpecific.swift @@ -40,6 +40,15 @@ class _NSNonfileURLContentLoader: _NSNonfileURLContentLoading { @usableFromInline func contentsOf(url: URL) throws -> (result: NSData, textEncodingNameIfAvailable: String?) { + + func cocoaError(with error: Error? = nil) -> Error { + var userInfo: [String: Any] = [:] + if let error = error { + userInfo[NSUnderlyingErrorKey] = error + } + return CocoaError.error(.fileReadUnknown, userInfo: userInfo, url: url) + } + var urlResponse: URLResponse? let session = URLSession(configuration: URLSessionConfiguration.default) let cond = NSCondition() @@ -63,10 +72,25 @@ class _NSNonfileURLContentLoader: _NSNonfileURLContentLoading { cond.wait() } cond.unlock() - + + guard resError == nil else { + throw cocoaError(with: resError) + } + guard let data = resData else { - throw resError! + throw cocoaError() + } + + if let statusCode = (urlResponse as? HTTPURLResponse)?.statusCode { + switch statusCode { + // These are the only valid response codes that data will be returned for, all other codes will be treated as error. + case 101, 200...399, 401, 407: + return (NSData(bytes: UnsafeMutableRawPointer(mutating: (data as NSData).bytes), length: data.count), urlResponse?.textEncodingName) + + default: + break + } } - return (NSData(bytes: UnsafeMutableRawPointer(mutating: (data as NSData).bytes), length: data.count), urlResponse?.textEncodingName) + throw cocoaError() } } diff --git a/Tests/Foundation/HTTPServer.swift b/Tests/Foundation/HTTPServer.swift index 9b864f4c92..53d41d5ef9 100644 --- a/Tests/Foundation/HTTPServer.swift +++ b/Tests/Foundation/HTTPServer.swift @@ -716,7 +716,11 @@ public class TestURLSessionServer { bodyData: helloWorld) } - return _HTTPResponse(response: .OK, body: capitals[String(uri.dropFirst())]!) + guard let capital = capitals[String(uri.dropFirst())] else { + return _HTTPResponse(response: .NOTFOUND, body: "Not Found") + } + return _HTTPResponse(response: .OK, body: capital) + } } diff --git a/Tests/Foundation/Tests/TestNSData.swift b/Tests/Foundation/Tests/TestNSData.swift index fb8dba5aa0..133af6a95b 100644 --- a/Tests/Foundation/Tests/TestNSData.swift +++ b/Tests/Foundation/Tests/TestNSData.swift @@ -1517,13 +1517,44 @@ extension TestNSData { #endif } - func test_contentsOfURL() { - let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt" - let url = URL(string: urlString)! - let contents = NSData(contentsOf: url) - XCTAssertNotNil(contents) - if let contents = contents { - XCTAssertTrue(contents.length > 0) + func test_contentsOfURL() throws { + do { + let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/country.txt" + let url = try XCTUnwrap(URL(string: urlString)) + let contents = NSData(contentsOf: url) + XCTAssertNotNil(contents) + if let contents = contents { + XCTAssertTrue(contents.length > 0) + } + } + + do { + let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/NotFound" + let url = try XCTUnwrap(URL(string: urlString)) + XCTAssertNil(NSData(contentsOf: url)) + do { + _ = try NSData(contentsOf: url, options: []) + XCTFail("NSData(contentsOf:options: did not throw") + } catch let error as NSError { + if let nserror = error as? NSError { + XCTAssertEqual(NSCocoaErrorDomain, nserror.domain) + XCTAssertEqual(CocoaError.fileReadUnknown.rawValue, nserror.code) + } else { + XCTFail("Not an NSError") + } + } + + do { + _ = try Data(contentsOf: url) + XCTFail("Data(contentsOf:options: did not throw") + } catch let error as NSError { + if let nserror = error as? NSError { + XCTAssertEqual(NSCocoaErrorDomain, nserror.domain) + XCTAssertEqual(CocoaError.fileReadUnknown.rawValue, nserror.code) + } else { + XCTFail("Not an NSError") + } + } } }