Skip to content

Commit

Permalink
use insensitive key
Browse files Browse the repository at this point in the history
  • Loading branch information
Augustyniak committed Jun 23, 2022
1 parent c2ded27 commit 768ddd2
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 8 deletions.
39 changes: 31 additions & 8 deletions library/swift/HeadersBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,37 @@ import Foundation
private let kRestrictedPrefixes = [":", "x-envoy-mobile"]

private func isRestrictedHeader(name: String) -> Bool {
return name == "host" || kRestrictedPrefixes.contains { name.hasPrefix($0) }
return name.lowercased() == "host" || kRestrictedPrefixes.contains { name.lowercased().hasPrefix($0) }
}

/// Base builder class used to construct `Headers` instances.
/// See `{Request|Response}HeadersBuilder` for usage.
@objcMembers
public class HeadersBuilder: NSObject {
private(set) var headers: [String: [String]]

struct CaseInsensitiveKey: Hashable {
let name: String
lazy var lowercasedName: String = { self.name.lowercased() }()

static func == (lhs: CaseInsensitiveKey, rhs: CaseInsensitiveKey) -> Bool {
var lhs = lhs, rhs = rhs
return lhs.lowercasedName == rhs.lowercasedName
}

func hash(into hasher: inout Hasher) -> Int {
hasher.combine(self.name.hashValue)
return hasher.finalize()
}
}

private(set) var _headers: [CaseInsensitiveKey: [String]]

var headers: [String: [String]] {
return Dictionary(uniqueKeysWithValues: _headers.map { key, value in
var k = key
return (k.lowercasedName, value)
})
}

/// Append a value to the header name.
///
Expand All @@ -24,7 +47,7 @@ public class HeadersBuilder: NSObject {
return self
}

self.headers[name, default: []].append(value)
self._headers[CaseInsensitiveKey(name: name), default: []].append(value)
return self
}

Expand All @@ -40,7 +63,7 @@ public class HeadersBuilder: NSObject {
return self
}

self.headers[name] = value
self._headers[CaseInsensitiveKey(name: name)] = value
return self
}

Expand All @@ -55,7 +78,7 @@ public class HeadersBuilder: NSObject {
return self
}

self.headers[name] = nil
self._headers[CaseInsensitiveKey(name: name)] = nil
return self
}

Expand All @@ -69,22 +92,22 @@ public class HeadersBuilder: NSObject {
/// - returns: This builder.
@discardableResult
func internalSet(name: String, value: [String]) -> Self {
self.headers[name] = value
self._headers[CaseInsensitiveKey(name: name)] = value
return self
}

// Only explicitly implemented to work around a swiftinterface issue in Swift 5.1. This can be
// removed once envoy is only built with Swift 5.2+
public override init() {
self.headers = [:]
self._headers = [:]
super.init()
}

/// Initialize a new builder. Subclasses should provide their own public convenience initializers.
///
/// - parameter headers: The headers with which to start.
required init(headers: [String: [String]]) {
self.headers = headers
self._headers = Dictionary(uniqueKeysWithValues: headers.map { key, value in (CaseInsensitiveKey(name: key), value) })
super.init()
}
}
Expand Down
12 changes: 12 additions & 0 deletions test/swift/HeadersBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,16 @@ final class HeadersBuilderTests: XCTestCase {
let headers2 = RequestHeadersBuilder(headers: ["x-foo": ["123"], "x-bar": ["abc"]]).build()
XCTAssert(headers1 !== headers2)
}

// func testInitializationIsCaseSensitiveButDoesIgnoresKeysCasingWhenLookingForDuplicates() {
// let headers = HeadersBuilder(headers: ["fOo": ["abc"], "foo": ["123"]])
// XCTAssertEqual(["fOo": ["abc", "123"]], headers.headers)
// }

func testInitializationIsCaseInsensitiveOperation() {
let headers = HeadersBuilder(headers: ["foo": ["123"], "fOo": ["abc"]])
headers.set(name: "fOo", value: ["abd"])
headers.set(name: "foo", value: ["123"])
XCTAssertEqual(["foo": ["abc", "123"]], headers.headers)
}
}

0 comments on commit 768ddd2

Please sign in to comment.