Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 33 additions & 28 deletions Foundation/HTTPCookie.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,54 +266,55 @@ open class HTTPCookie : NSObject {
}
_version = version

if let portString = properties[.port] as? String, _version == 1 {
_portList = portString.split(separator: ",")
if let portString = properties[.port] as? String {
let portList = portString.split(separator: ",")
.compactMap { Int(String($0)) }
.map { NSNumber(value: $0) }
if version == 1 {
_portList = portList
} else {
// Version 0 only stores a single port number
_portList = portList.count > 0 ? [portList[0]] : nil
}
} else {
_portList = nil
}

// TODO: factor into a utility function
if version == 0 {
var expDate: Date? = nil
// Maximum-Age is prefered over expires-Date but only version 1 cookies use Maximum-Age
if let maximumAge = properties[.maximumAge] as? String,
let secondsFromNow = Int(maximumAge) {
if version == 1 {
expDate = Date(timeIntervalSinceNow: Double(secondsFromNow))
}
} else {
let expiresProperty = properties[.expires]
if let date = expiresProperty as? Date {
_expiresDate = date
expDate = date
} else if let dateString = expiresProperty as? String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss O" // per RFC 6265 '<rfc1123-date, defined in [RFC2616], Section 3.3.1>'
let timeZone = TimeZone(abbreviation: "GMT")
formatter.timeZone = timeZone
_expiresDate = formatter.date(from: dateString)
} else {
_expiresDate = nil
expDate = formatter.date(from: dateString)
}
} else if
let maximumAge = properties[.maximumAge] as? String,
let secondsFromNow = Int(maximumAge), _version == 1 {
_expiresDate = Date(timeIntervalSinceNow: Double(secondsFromNow))
} else {
_expiresDate = nil
}
_expiresDate = expDate

if let discardString = properties[.discard] as? String {
_sessionOnly = discardString == "TRUE"
} else {
_sessionOnly = properties[.maximumAge] == nil && version >= 1
}
if version == 0 {
_comment = nil
_commentURL = nil

_comment = properties[.comment] as? String
if let commentURL = properties[.commentURL] as? URL {
_commentURL = commentURL
} else if let commentURL = properties[.commentURL] as? String {
_commentURL = URL(string: commentURL)
} else {
_comment = properties[.comment] as? String
if let commentURL = properties[.commentURL] as? URL {
_commentURL = commentURL
} else if let commentURL = properties[.commentURL] as? String {
_commentURL = URL(string: commentURL)
} else {
_commentURL = nil
}
_commentURL = nil
}
_HTTPOnly = false

Expand Down Expand Up @@ -363,7 +364,11 @@ open class HTTPCookie : NSObject {
cookieString.removeLast()
cookieString.removeLast()
}
return ["Cookie": cookieString]
if cookieString == "" {
return [:]
} else {
return ["Cookie": cookieString]
}
}

/// Return an array of cookies parsed from the specified response header fields and URL.
Expand Down Expand Up @@ -418,9 +423,9 @@ open class HTTPCookie : NSObject {
properties[canonicalize(name)] = value
}

//if domain wasn't provided use the URL
// If domain wasn't provided, extract it from the URL
if properties[.domain] == nil {
properties[.domain] = url.absoluteString
properties[.domain] = url.host
}

//the default Path is "/"
Expand Down
4 changes: 1 addition & 3 deletions Foundation/NSURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying {
guard isFileURL,
let path = path else {
throw NSError(domain: NSCocoaErrorDomain,
code: CocoaError.Code.fileNoSuchFile.rawValue)
//return false
code: CocoaError.Code.fileReadUnsupportedScheme.rawValue)
}

guard FileManager.default.fileExists(atPath: path) else {
Expand All @@ -621,7 +620,6 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying {
userInfo: [
"NSURL" : self,
"NSFilePath" : path])
//return false
}

return true
Expand Down
12 changes: 8 additions & 4 deletions Foundation/NSURLRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,13 @@ open class NSMutableURLRequest : NSURLRequest {
/// - Parameter value: the header field value.
/// - Parameter field: the header field name (case-insensitive).
open func setValue(_ value: String?, forHTTPHeaderField field: String) {
// Store the field name capitalized to match native Foundation
let capitalizedFieldName = field.capitalized
var f: [String : String] = allHTTPHeaderFields ?? [:]
if let old = existingHeaderField(field, inHeaderFields: f) {
if let old = existingHeaderField(capitalizedFieldName, inHeaderFields: f) {
f.removeValue(forKey: old.0)
}
f[field] = value
f[capitalizedFieldName] = value
allHTTPHeaderFields = f
}

Expand All @@ -474,11 +476,13 @@ open class NSMutableURLRequest : NSURLRequest {
/// - Parameter value: the header field value.
/// - Parameter field: the header field name (case-insensitive).
open func addValue(_ value: String, forHTTPHeaderField field: String) {
// Store the field name capitalized to match native Foundation
let capitalizedFieldName = field.capitalized
var f: [String : String] = allHTTPHeaderFields ?? [:]
if let old = existingHeaderField(field, inHeaderFields: f) {
if let old = existingHeaderField(capitalizedFieldName, inHeaderFields: f) {
f[old.0] = old.1 + "," + value
} else {
f[field] = value
f[capitalizedFieldName] = value
}
allHTTPHeaderFields = f
}
Expand Down
4 changes: 0 additions & 4 deletions Foundation/URLSession/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,6 @@ internal extension URLSession._Configuration {
internal extension URLSession._Configuration {
func configure(request: URLRequest) -> URLRequest {
var request = request
httpAdditionalHeaders?.forEach {
guard request.value(forHTTPHeaderField: $0.0) == nil else { return }
request.setValue($0.1, forHTTPHeaderField: $0.0)
}
return setCookies(on: request)
}

Expand Down
8 changes: 8 additions & 0 deletions Foundation/URLSession/http/HTTPURLProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ internal class _HTTPURLProtocol: _NativeProtocol {
httpHeaders = hh
} else {
hh.forEach {
// When adding a header, remove any current entry with the same header name regardless of case
let newKey = $0.lowercased()
for key in httpHeaders!.keys {
if newKey == (key as! String).lowercased() {
httpHeaders?.removeValue(forKey: key)
break
}
}
httpHeaders![$0] = $1
}
}
Expand Down
6 changes: 4 additions & 2 deletions TestFoundation/TestBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -432,20 +432,22 @@ class TestBundle : XCTestCase {
XCTAssertNotNil(bundle.executableURL)
}
}

func test_bundleFindAuxiliaryExecutables() {
_withEachPlaygroundLayout { (playground) in
let bundle = Bundle(path: playground.bundlePath)!
XCTAssertNotNil(bundle.url(forAuxiliaryExecutable: _auxiliaryExecutable))
XCTAssertNil(bundle.url(forAuxiliaryExecutable: "does_not_exist_at_all"))
}
}

func test_mainBundleExecutableURL() {
#if !DARWIN_COMPATIBILITY_TESTS // _CFProcessPath() is unavailable on native Foundation
let maybeURL = Bundle.main.executableURL
XCTAssertNotNil(maybeURL)
guard let url = maybeURL else { return }

XCTAssertEqual(url.path, String(cString: _CFProcessPath()))
#endif
}
}
31 changes: 15 additions & 16 deletions TestFoundation/TestHTTPCookie.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,31 +67,29 @@ class TestHTTPCookie: XCTestCase {
.domain: "apple.com",
.originURL: URL(string: "https://apple.com")!,
.comment: "This comment should be nil since this is a v0 cookie.",
.commentURL: URL(string: "https://apple.com")!,
.commentURL: "https://apple.com",
.discard: "TRUE",
.expires: Date(timeIntervalSince1970: 1000),
.maximumAge: "2000",
.port: "443,8443",
.secure: "YES"
])
XCTAssertNil(versionZeroCookieWithInvalidVersionOneProps?.comment)
XCTAssertNil(versionZeroCookieWithInvalidVersionOneProps?.commentURL)
XCTAssertEqual(versionZeroCookieWithInvalidVersionOneProps?.version, 0)
XCTAssertNotNil(versionZeroCookieWithInvalidVersionOneProps?.comment)
XCTAssertNotNil(versionZeroCookieWithInvalidVersionOneProps?.commentURL)
XCTAssert(versionZeroCookieWithInvalidVersionOneProps?.isSessionOnly == true)

// v0 should never use NSHTTPCookieMaximumAge
XCTAssert(
versionZeroCookieWithInvalidVersionOneProps?.expiresDate?.timeIntervalSince1970 ==
Date(timeIntervalSince1970: 1000).timeIntervalSince1970
)
XCTAssertNil(versionZeroCookieWithInvalidVersionOneProps?.expiresDate?.timeIntervalSince1970)

XCTAssertNil(versionZeroCookieWithInvalidVersionOneProps?.portList)
XCTAssertEqual(versionZeroCookieWithInvalidVersionOneProps?.portList, [NSNumber(value: 443)])
XCTAssert(versionZeroCookieWithInvalidVersionOneProps?.isSecure == true)
XCTAssert(versionZeroCookieWithInvalidVersionOneProps?.version == 0)
}

func test_RequestHeaderFields() {
let noCookies: [HTTPCookie] = []
XCTAssertEqual(HTTPCookie.requestHeaderFields(with: noCookies)["Cookie"], "")
XCTAssertNil(HTTPCookie.requestHeaderFields(with: noCookies)["Cookie"])

let basicCookies: [HTTPCookie] = [
HTTPCookie(properties: [
Expand All @@ -117,7 +115,7 @@ class TestHTTPCookie: XCTestCase {
"Set-Cookie": "fr=anjd&232; Max-Age=7776000; path=/; domain=.example.com; secure; httponly",
"header2":"value2",
"header3":"value3"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "http://example.com")!)
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "https://example.com")!)
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].name, "fr")
XCTAssertEqual(cookies[0].value, "anjd&232")
Expand All @@ -134,19 +132,20 @@ class TestHTTPCookie: XCTestCase {
"Set-Cookie": "fr=a&2@#; Max-Age=1186000; path=/; domain=.example.com; secure, xd=plm!@#;path=/;domain=.example2.com",
"header2":"value2",
"header3":"value3"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "http://example.com")!)
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "https://example.com")!)
XCTAssertEqual(cookies.count, 2)
XCTAssertTrue(cookies[0].isSecure)
XCTAssertFalse(cookies[1].isSecure)
}

func test_cookiesWithResponseHeaderNoDomain() {
let header = ["header1":"value1",
"Set-Cookie": "fr=anjd&232; expires=Wed, 21 Sep 2016 05:33:00 GMT; Max-Age=7776000; path=/; secure; httponly",
"Set-Cookie": "fr=anjd&232; expires=Wed, 21 Sep 2016 05:33:00 GMT; path=/; secure; httponly",
"header2":"value2",
"header3":"value3"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "http://example.com")!)
XCTAssertEqual(cookies[0].domain, "http://example.com")
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "https://example.com")!)
XCTAssertEqual(cookies[0].version, 0)
XCTAssertEqual(cookies[0].domain, "example.com")
XCTAssertNotNil(cookies[0].expiresDate)

let formatter = DateFormatter()
Expand All @@ -165,8 +164,8 @@ class TestHTTPCookie: XCTestCase {
"Set-Cookie": "fr=tx; expires=Wed, 21-Sep-2016 05:33:00 GMT; Max-Age=7776000; secure; httponly",
"header2":"value2",
"header3":"value3"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "http://example.com")!)
XCTAssertEqual(cookies[0].domain, "http://example.com")
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "https://example.com")!)
XCTAssertEqual(cookies[0].domain, "example.com")
XCTAssertEqual(cookies[0].path, "/")
}
}
2 changes: 1 addition & 1 deletion TestFoundation/TestHTTPCookieStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ class TestHTTPCookieStorage: XCTestCase {
}

func test_cookieInXDGSpecPath() {
#if !os(Android)
#if !os(Android) && !DARWIN_COMPATIBILITY_TESTS // No XDG on native Foundation
//Test without setting the environment variable
let testCookie = HTTPCookie(properties: [
.name: "TestCookie0",
Expand Down
10 changes: 5 additions & 5 deletions TestFoundation/TestNSURLRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ class TestNSURLRequest : XCTestCase {
XCTAssertNotNil(request.allHTTPHeaderFields)
XCTAssertEqual(request.allHTTPHeaderFields?["Accept"], "application/json")

// Setting "accept" should remove "Accept"
// Setting "accept" should update "Accept"
request.setValue("application/xml", forHTTPHeaderField: "accept")
XCTAssertNil(request.allHTTPHeaderFields?["Accept"])
XCTAssertEqual(request.allHTTPHeaderFields?["accept"], "application/xml")
XCTAssertNil(request.allHTTPHeaderFields?["accept"])
XCTAssertEqual(request.allHTTPHeaderFields?["Accept"], "application/xml")

// Adding to "Accept" should add to "accept"
// Adding to "Accept" should add to "Accept"
request.addValue("text/html", forHTTPHeaderField: "Accept")
XCTAssertEqual(request.allHTTPHeaderFields?["accept"], "application/xml,text/html")
XCTAssertEqual(request.allHTTPHeaderFields?["Accept"], "application/xml,text/html")
}

func test_copy() {
Expand Down
4 changes: 2 additions & 2 deletions TestFoundation/TestURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ class TestURL : XCTestCase {
XCTFail()
} catch let error as NSError {
XCTAssertEqual(NSCocoaErrorDomain, error.domain)
XCTAssertEqual(CocoaError.Code.fileNoSuchFile.rawValue, error.code)
XCTAssertEqual(CocoaError.Code.fileReadUnsupportedScheme.rawValue, error.code)
} catch {
XCTFail()
}
Expand Down Expand Up @@ -461,7 +461,7 @@ class TestURL : XCTestCase {
XCTFail()
} catch let error as NSError {
XCTAssertEqual(NSCocoaErrorDomain, error.domain)
XCTAssertEqual(CocoaError.Code.fileNoSuchFile.rawValue, error.code)
XCTAssertEqual(CocoaError.Code.fileReadUnsupportedScheme.rawValue, error.code)
} catch {
XCTFail()
}
Expand Down
11 changes: 6 additions & 5 deletions TestFoundation/TestURLRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,17 @@ class TestURLRequest : XCTestCase {

request.setValue("application/json", forHTTPHeaderField: "Accept")
XCTAssertNotNil(request.allHTTPHeaderFields)
XCTAssertNil(request.allHTTPHeaderFields?["accept"])
XCTAssertEqual(request.allHTTPHeaderFields?["Accept"], "application/json")

// Setting "accept" should remove "Accept"
// Setting "accept" should update "Accept"
request.setValue("application/xml", forHTTPHeaderField: "accept")
XCTAssertNil(request.allHTTPHeaderFields?["Accept"])
XCTAssertEqual(request.allHTTPHeaderFields?["accept"], "application/xml")
XCTAssertNil(request.allHTTPHeaderFields?["accept"])
XCTAssertEqual(request.allHTTPHeaderFields?["Accept"], "application/xml")

// Adding to "Accept" should add to "accept"
// Adding to "Accept" should add to "Accept"
request.addValue("text/html", forHTTPHeaderField: "Accept")
XCTAssertEqual(request.allHTTPHeaderFields?["accept"], "application/xml,text/html")
XCTAssertEqual(request.allHTTPHeaderFields?["Accept"], "application/xml,text/html")
}

func test_copy() {
Expand Down
6 changes: 4 additions & 2 deletions TestFoundation/TestURLSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,12 @@ class TestURLSession : LoopbackServerTest {
func test_verifyHttpAdditionalHeaders() {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
config.httpAdditionalHeaders = ["header2": "svalue2", "header3": "svalue3"]
config.httpAdditionalHeaders = ["header2": "svalue2", "header3": "svalue3", "header4": "svalue4"]
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestHeaders"
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "POST \(urlString) with additional headers")
var req = URLRequest(url: URL(string: urlString)!)
let headers = ["header1": "rvalue1", "header2": "rvalue2"]
let headers = ["header1": "rvalue1", "header2": "rvalue2", "Header4": "rvalue4"]
req.httpMethod = "POST"
req.allHTTPHeaderFields = headers
var task = session.dataTask(with: req) { (data, _, error) -> Void in
Expand All @@ -284,6 +284,8 @@ class TestURLSession : LoopbackServerTest {
XCTAssertNotNil(headers.range(of: "header1: rvalue1"))
XCTAssertNotNil(headers.range(of: "header2: rvalue2"))
XCTAssertNotNil(headers.range(of: "header3: svalue3"))
XCTAssertNotNil(headers.range(of: "Header4: rvalue4"))
XCTAssertNil(headers.range(of: "header4: svalue"))
}
task.resume()

Expand Down