Add support for retrieving headers in canonical form

This commit is contained in:
Cory Benfield 2017-10-13 14:13:45 -07:00
parent 2a7066873a
commit 0803012b7a
3 changed files with 59 additions and 0 deletions

View File

@ -192,6 +192,23 @@ public struct HTTPHeaders : Sequence, CustomStringConvertible {
public func makeIterator() -> AnyIterator<(name: String, value: String)> {
return AnyIterator(HTTPHeadersIterator(wrapping: storage.makeIterator()))
}
/// Retrieves the header values for the given header field in "canonical form": that is,
/// splitting them on commas as extensively as possible such that multiple values received on the
/// one line are returned as separate entries. Also respects the fact that Set-Cookie should not
/// be split in this way.
public func getCanonicalForm(_ name: String) -> [String] {
// It's not safe to split Set-Cookie on comma.
let queryName = name.lowercased()
if queryName == "set-cookie" {
return self[name]
}
if let result = storage[queryName] {
return result.map { tuple in tuple.1.split(separator: ",").map { substring in String(substring).trimmingCharacters(in: .whitespaces) } }.reduce([], +)
}
return []
}
}
extension HTTPHeaders : Equatable {

View File

@ -28,6 +28,9 @@ extension HTTPHeadersTest {
return [
("testCasePreservedButInsensitiveLookup", testCasePreservedButInsensitiveLookup),
("testWriteHeadersSeparately", testWriteHeadersSeparately),
("testRevealHeadersSeparately", testRevealHeadersSeparately),
("testSubscriptDoesntSplitHeaders", testSubscriptDoesntSplitHeaders),
("testCanonicalisationDoesntHappenForSetCookie", testCanonicalisationDoesntHappenForSetCookie),
]
}
}

View File

@ -70,4 +70,43 @@ class HTTPHeadersTest : XCTestCase {
XCTAssertTrue(writtenBytes.contains("set-cookie: foo=bar\r\n"))
XCTAssertTrue(writtenBytes.contains("set-cookie: buz=cux\r\n"))
}
func testRevealHeadersSeparately() {
let originalHeaders = [ ("User-Agent", "1"),
("host", "2"),
("X-SOMETHING", "3, 4"),
("X-Something", "5")]
let headers = HTTPHeaders(originalHeaders)
XCTAssertEqual(headers.getCanonicalForm("user-agent"), ["1"])
XCTAssertEqual(headers.getCanonicalForm("host"), ["2"])
XCTAssertEqual(headers.getCanonicalForm("x-something"), ["3", "4", "5"])
XCTAssertEqual(headers.getCanonicalForm("foo"), [])
}
func testSubscriptDoesntSplitHeaders() {
let originalHeaders = [ ("User-Agent", "1"),
("host", "2"),
("X-SOMETHING", "3, 4"),
("X-Something", "5")]
let headers = HTTPHeaders(originalHeaders)
XCTAssertEqual(headers["user-agent"], ["1"])
XCTAssertEqual(headers["host"], ["2"])
XCTAssertEqual(headers["x-something"], ["3, 4", "5"])
XCTAssertEqual(headers["foo"], [])
}
func testCanonicalisationDoesntHappenForSetCookie() {
let originalHeaders = [ ("User-Agent", "1"),
("host", "2"),
("Set-Cookie", "foo=bar; expires=Sun, 17-Mar-2013 13:49:50 GMT"),
("Set-Cookie", "buz=cux; expires=Fri, 13 Oct 2017 21:21:41 GMT")]
let headers = HTTPHeaders(originalHeaders)
XCTAssertEqual(headers.getCanonicalForm("user-agent"), ["1"])
XCTAssertEqual(headers.getCanonicalForm("host"), ["2"])
XCTAssertEqual(headers.getCanonicalForm("set-cookie"), ["foo=bar; expires=Sun, 17-Mar-2013 13:49:50 GMT",
"buz=cux; expires=Fri, 13 Oct 2017 21:21:41 GMT"])
}
}