blob: b2347798829aeba80d72df15d8b848bdab6ae999 [file] [log] [blame]
// 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
//
class TestHTTPCookie: XCTestCase {
static var allTests: [(String, (TestHTTPCookie) -> () throws -> Void)] {
return [
("test_BasicConstruction", test_BasicConstruction),
("test_cookieDomainCanonicalization", test_cookieDomainCanonicalization),
("test_RequestHeaderFields", test_RequestHeaderFields),
("test_cookiesWithResponseHeader1cookie", test_cookiesWithResponseHeader1cookie),
("test_cookiesWithResponseHeader0cookies", test_cookiesWithResponseHeader0cookies),
("test_cookiesWithResponseHeader2cookies", test_cookiesWithResponseHeader2cookies),
("test_cookiesWithResponseHeaderNoDomain", test_cookiesWithResponseHeaderNoDomain),
("test_cookiesWithResponseHeaderNoPathNoDomain", test_cookiesWithResponseHeaderNoPathNoDomain),
("test_cookiesWithResponseHeaderNoNameValue", test_cookiesWithResponseHeaderNoNameValue),
("test_cookiesWithResponseHeaderNoName", test_cookiesWithResponseHeaderNoName),
("test_cookiesWithResponseHeaderEmptyName", test_cookiesWithResponseHeaderEmptyName),
("test_cookiesWithResponseHeaderNoValue", test_cookiesWithResponseHeaderNoValue),
("test_cookiesWithResponseHeaderAttributeWithoutNameIsIgnored", test_cookiesWithResponseHeaderAttributeWithoutNameIsIgnored),
("test_cookiesWithResponseHeaderValuelessAttributes", test_cookiesWithResponseHeaderValuelessAttributes),
("test_cookiesWithResponseHeaderValuedAttributes", test_cookiesWithResponseHeaderValuedAttributes),
("test_cookiesWithResponseHeaderInvalidPath", test_cookiesWithResponseHeaderInvalidPath),
("test_cookiesWithResponseHeaderWithEqualsInValue", test_cookiesWithResponseHeaderWithEqualsInValue),
("test_cookiesWithResponseHeaderSecondCookieInvalidToken", test_cookiesWithResponseHeaderSecondCookieInvalidToken),
("test_cookieExpiresDateFormats", test_cookieExpiresDateFormats),
("test_cookiesWithExpiresAsLastAttribute", test_cookiesWithExpiresAsLastAttribute),
("test_cookiesWithResponseHeaderTrimsNames", test_cookiesWithResponseHeaderTrimsNames),
("test_httpCookieWithSubstring", test_httpCookieWithSubstring),
]
}
func test_BasicConstruction() {
let invalidVersionZeroCookie = HTTPCookie(properties: [
.name: "TestCookie",
.value: "Test value @#$%^$&*",
.path: "/"
])
XCTAssertNil(invalidVersionZeroCookie)
let minimalVersionZeroCookie = HTTPCookie(properties: [
.name: "TestCookie",
.value: "Test value @#$%^$&*",
.path: "/",
.domain: "apple.com"
])
XCTAssertNotNil(minimalVersionZeroCookie)
XCTAssert(minimalVersionZeroCookie?.name == "TestCookie")
XCTAssert(minimalVersionZeroCookie?.value == "Test value @#$%^$&*")
XCTAssert(minimalVersionZeroCookie?.path == "/")
XCTAssert(minimalVersionZeroCookie?.domain == "apple.com")
let versionZeroCookieWithOriginURL = HTTPCookie(properties: [
.name: "TestCookie",
.value: "Test value @#$%^$&*",
.path: "/",
.originURL: URL(string: "https://apple.com")!
])
XCTAssert(versionZeroCookieWithOriginURL?.domain == "apple.com")
// Domain takes precedence over originURL inference
let versionZeroCookieWithDomainAndOriginURL = HTTPCookie(properties: [
.name: "TestCookie",
.value: "Test value @#$%^$&*",
.path: "/",
.domain: "apple.com",
.originURL: URL(string: "https://apple.com")!
])
XCTAssert(versionZeroCookieWithDomainAndOriginURL?.domain == "apple.com")
// This is implicitly a v0 cookie. Properties that aren't valid for v0 should fail.
let versionZeroCookieWithInvalidVersionOneProps = HTTPCookie(properties: [
.name: "TestCookie",
.value: "Test value @#$%^$&*",
.path: "/",
.domain: "apple.com",
.originURL: URL(string: "https://apple.com")!,
.comment: "This comment should be nil since this is a v0 cookie.",
.commentURL: "https://apple.com",
.discard: "TRUE",
.expires: Date(timeIntervalSince1970: 1000),
.maximumAge: "2000",
.port: "443,8443",
.secure: "YES"
])
XCTAssertEqual(versionZeroCookieWithInvalidVersionOneProps?.version, 0)
XCTAssertNotNil(versionZeroCookieWithInvalidVersionOneProps?.comment)
XCTAssertNotNil(versionZeroCookieWithInvalidVersionOneProps?.commentURL)
XCTAssert(versionZeroCookieWithInvalidVersionOneProps?.isSessionOnly == true)
// v0 should never use NSHTTPCookieMaximumAge
XCTAssertNil(versionZeroCookieWithInvalidVersionOneProps?.expiresDate?.timeIntervalSince1970)
XCTAssertEqual(versionZeroCookieWithInvalidVersionOneProps?.portList, [NSNumber(value: 443)])
XCTAssert(versionZeroCookieWithInvalidVersionOneProps?.isSecure == true)
XCTAssert(versionZeroCookieWithInvalidVersionOneProps?.version == 0)
}
func test_RequestHeaderFields() {
let noCookies: [HTTPCookie] = []
XCTAssertNil(HTTPCookie.requestHeaderFields(with: noCookies)["Cookie"])
let basicCookies: [HTTPCookie] = [
HTTPCookie(properties: [
.name: "TestCookie1",
.value: "testValue1",
.path: "/",
.originURL: URL(string: "https://apple.com")!
])!,
HTTPCookie(properties: [
.name: "TestCookie2",
.value: "testValue2",
.path: "/",
.originURL: URL(string: "https://apple.com")!
])!,
]
let basicCookieString = HTTPCookie.requestHeaderFields(with: basicCookies)["Cookie"]
XCTAssertEqual(basicCookieString, "TestCookie1=testValue1; TestCookie2=testValue2")
}
func test_cookiesWithResponseHeader1cookie() {
let header = ["header1":"value1",
"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: "https://example.com")!)
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].name, "fr")
XCTAssertEqual(cookies[0].value, "anjd&232")
}
func test_cookiesWithResponseHeader0cookies() {
let header = ["header1":"value1", "header2":"value2", "header3":"value3"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "http://example.com")!)
XCTAssertEqual(cookies.count, 0)
}
func test_cookiesWithResponseHeader2cookies() {
let header = ["header1":"value1",
"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: "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; path=/; secure; httponly",
"header2":"value2",
"header3":"value3"]
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()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss O"
formatter.timeZone = TimeZone(abbreviation: "GMT")
if let expiresDate = formatter.date(from: "Wed, 21 Sep 2016 05:33:00 GMT") {
XCTAssertTrue(expiresDate.compare(cookies[0].expiresDate!) == .orderedSame)
} else {
XCTFail("Unable to parse the given date from the formatter")
}
}
func test_cookiesWithResponseHeaderNoPathNoDomain() {
let header = ["header1":"value1",
"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: "https://example.com")!)
XCTAssertEqual(cookies[0].domain, "example.com")
XCTAssertEqual(cookies[0].path, "/")
}
func test_cookiesWithResponseHeaderNoNameValue() throws {
let header = ["Set-Cookie": ";attr1=value1"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 0)
}
func test_cookiesWithResponseHeaderNoName() throws {
let header = ["Set-Cookie": "=value1;attr2=value2"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 0)
}
func test_cookiesWithResponseHeaderEmptyName() throws {
let header = ["Set-Cookie": " =value1;attr2=value2"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 0)
}
func test_cookiesWithResponseHeaderNoValue() throws {
let header = ["Set-Cookie": "name;attr2=value2"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 0)
}
func test_cookiesWithResponseHeaderAttributeWithoutNameIsIgnored() throws {
let header = ["Set-Cookie": "name=value;Comment=value1; =value2;CommentURL=value3"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].name, "name")
XCTAssertEqual(cookies[0].value, "value")
XCTAssertEqual(cookies[0].comment, "value1")
XCTAssertEqual(cookies[0].commentURL, try XCTUnwrap(URL(string: "value3")))
}
func test_cookiesWithResponseHeaderValuelessAttributes() throws {
let header = ["Set-Cookie": "name=value;Secure;Comment;Discard;CommentURL;HttpOnly"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].name, "name")
XCTAssertEqual(cookies[0].value, "value")
XCTAssertTrue(cookies[0].isSecure)
XCTAssertTrue(cookies[0].isSessionOnly)
XCTAssertNil(cookies[0].comment)
XCTAssertNil(cookies[0].commentURL)
XCTAssertTrue(cookies[0].isHTTPOnly)
}
func test_cookiesWithResponseHeaderValuedAttributes() throws {
// The attributes that do not need value will be ignored if they have
// a value.
let header = ["Set-Cookie": "name=value;Secure=1;Discard=TRUE;HttpOnly=Yes"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].name, "name")
XCTAssertEqual(cookies[0].value, "value")
XCTAssertFalse(cookies[0].isSecure)
XCTAssertFalse(cookies[0].isSessionOnly)
XCTAssertFalse(cookies[0].isHTTPOnly)
}
func test_cookiesWithResponseHeaderInvalidPath() throws {
let header = ["Set-Cookie": "name=value;Path=This/is/not/a/valid/path"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].path, "/")
}
func test_cookiesWithResponseHeaderWithEqualsInValue() throws {
let header = ["Set-Cookie": "name=v=a=l=u=e;attr1=value1;attr2=value2"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].name, "name")
XCTAssertEqual(cookies[0].value, "v=a=l=u=e")
}
func test_cookiesWithResponseHeaderSecondCookieInvalidToken() throws {
let header = ["Set-Cookie": "n=v; Comment=real value, tok@en=second; CommentURL=https://example.com/second"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: try XCTUnwrap(URL(string: "https://example.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies[0].name, "n")
XCTAssertEqual(cookies[0].value, "v")
XCTAssertEqual(cookies[0].comment, "real value, tok@en=second")
XCTAssertEqual(cookies[0].commentURL, try XCTUnwrap(URL(string: "https://example.com/second")))
}
func test_cookiesWithExpiresAsLastAttribute() throws {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: [
"Set-Cookie": "AAA=111; path=/; domain=.example.com; expires=Sun, 16-Aug-2025 22:39:54 GMT, BBB=222; path=/; domain=.example.com; HttpOnly; expires=Sat, 15-Feb-2014 22:39:54 GMT"
], for: try XCTUnwrap(URL(string: "http://www.example.com/")))
XCTAssertEqual(cookies.count, 2)
XCTAssertEqual(cookies[0].name, "AAA")
XCTAssertEqual(cookies[0].value, "111")
XCTAssertEqual(cookies[1].name, "BBB")
XCTAssertEqual(cookies[1].value, "222")
}
func test_cookiesWithResponseHeaderTrimsNames() throws {
do {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: [
"Set-Cookie": "AAA =1; path=/; domain=.example.com; expires=Sun, 16-Aug-2025 22:39:54 GMT, BBB=2; path=/; domain=.example.com; HttpOnly; expires=Sat, 15-Feb-2014 22:39:54 GMT"
], for: try XCTUnwrap(URL(string: "http://www.example.com/")))
XCTAssertEqual(cookies.count, 2)
XCTAssertEqual(cookies[0].name, "AAA")
XCTAssertEqual(cookies[0].value, "1")
XCTAssertEqual(cookies[1].name, "BBB")
XCTAssertEqual(cookies[1].value, "2")
}
do {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: [
"Set-Cookie": " AAA=1; path=/; domain=.example.com; expires=Sun, 16-Aug-2025 22:39:54 GMT, BBB=2; path=/; domain=.example.com; HttpOnly; expires=Sat, 15-Feb-2014 22:39:54 GMT"
], for: try XCTUnwrap(URL(string: "http://www.example.com/")))
XCTAssertEqual(cookies.count, 2)
XCTAssertEqual(cookies[0].name, "AAA")
XCTAssertEqual(cookies[0].value, "1")
XCTAssertEqual(cookies[1].name, "BBB")
XCTAssertEqual(cookies[1].value, "2")
}
}
func test_cookieDomainCanonicalization() throws {
do {
let headers = [
"Set-Cookie": "PREF=a=b; expires=\(formattedCookieTime(sinceNow: 100))); path=/; domain=eXample.com"
]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: try XCTUnwrap(URL(string: "http://eXample.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies.first?.domain, ".example.com")
}
do {
let headers = [
"Set-Cookie": "PREF=a=b; expires=\(formattedCookieTime(sinceNow: 100))); path=/; domain=.eXample.com"
]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: try XCTUnwrap(URL(string: "http://eXample.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies.first?.domain, ".example.com")
}
do {
let headers = [
"Set-Cookie": "PREF=a=b; expires=\(formattedCookieTime(sinceNow: 100))); path=/; domain=a.eXample.com"
]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: try XCTUnwrap(URL(string: "http://a.eXample.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies.first?.domain, ".a.example.com")
}
do {
let headers = [
"Set-Cookie": "PREF=a=b; expires=\(formattedCookieTime(sinceNow: 100))); path=/"
]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: try XCTUnwrap(URL(string: "http://a.eXample.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies.first?.domain, "a.example.com")
}
do {
let headers = [
"Set-Cookie": "PREF=a=b; expires=\(formattedCookieTime(sinceNow: 100))); path=/; domain=1.2.3.4"
]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: try XCTUnwrap(URL(string: "http://eXample.com")))
XCTAssertEqual(cookies.count, 1)
XCTAssertEqual(cookies.first?.domain, "1.2.3.4")
}
}
func test_cookieExpiresDateFormats() {
let testDate = Date(timeIntervalSince1970: 1577881800)
let cookieString =
"""
format1=true; expires=Wed, 01 Jan 2020 12:30:00 GMT; path=/; domain=swift.org; secure; httponly,
format2=true; expires=Wed Jan 1 12:30:00 2020; path=/; domain=swift.org; secure; httponly,
format3=true; expires=Wed, 01-Jan-2020 12:30:00 GMT; path=/; domain=swift.org; secure; httponly
"""
let header = ["header1":"value1",
"Set-Cookie": cookieString,
"header2":"value2",
"header3":"value3"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: header, for: URL(string: "https://swift.org")!)
XCTAssertEqual(cookies.count, 3)
cookies.forEach { cookie in
XCTAssertEqual(cookie.expiresDate, testDate)
XCTAssertEqual(cookie.domain, ".swift.org")
XCTAssertEqual(cookie.path, "/")
}
}
func test_httpCookieWithSubstring() {
let cookie = HTTPCookie(properties: [.domain: ".", .path: "/", .name: "davesy".dropLast(), .value: "Jonesy".dropLast()])
if let cookie = cookie {
XCTAssertEqual(cookie.name, "daves")
} else {
XCTFail("Unable to create cookie with substring")
}
}
private func formattedCookieTime(sinceNow seconds: TimeInterval) -> String {
let f = DateFormatter()
f.timeZone = TimeZone(abbreviation: "GMT")
f.dateFormat = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z"
return f.string(from: Date(timeIntervalSinceNow: seconds))
}
}