Skip to content

Commit

Permalink
SR-12135: Data(contentsOf:) does not account for failed HTTP response…
Browse files Browse the repository at this point in the history
… codes.

- _NSNonfileURLContentLoader.contentsOf(url:) Only return the body
  response for certain HTTP codes, for others throw an error.
  • Loading branch information
spevans committed Mar 23, 2020
1 parent e1149d0 commit 820d280
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 11 deletions.
30 changes: 27 additions & 3 deletions Sources/FoundationNetworking/URLSession/NetworkingSpecific.swift
Expand Up @@ -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()
Expand All @@ -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()
}
}
6 changes: 5 additions & 1 deletion Tests/Foundation/HTTPServer.swift
Expand Up @@ -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)

}
}

Expand Down
45 changes: 38 additions & 7 deletions Tests/Foundation/Tests/TestNSData.swift
Expand Up @@ -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")
}
}
}
}

Expand Down

0 comments on commit 820d280

Please sign in to comment.