allHeaderFields is not case insensitive when looking up keys

Viewed 1020

I'm using Alamofire to make my API calls.

Depending on the server I'm using the response headers could be capitalized.

But as the documentation says for allHeaderFields :

var allHeaderFields: [AnyHashable : Any] { get }

A dictionary containing all the HTTP header fields received as part of the server’s response. By examining this dictionary clients can see the “raw” header information returned by the HTTP server.

The keys in this dictionary are the header field names, as received from the server. See RFC 2616 for a list of commonly used HTTP header fields.

HTTP headers are case insensitive. To simplify your code, certain header field names are canonicalized into their standard form. For example, if the server sends a content-length header, it is automatically adjusted to be Content-Length.

The returned dictionary of headers is configured to be case-preserving during the set operation (unless the key already exists with a different case), and case-insensitive when looking up keys.

For example, if you set the header X-foo, and then later set the header X-Foo, the dictionary’s key will be X-foo, but the value will taken from the X-Foo header.

But in my code if I'm doing this :

if let headers = response.response?.allHeaderFields {
   print("Access-Token: \(response.response?.allHeaderFields["Access-Token"])")
   print("access-token: \(response.response?.allHeaderFields["access-token"])")
   print("access-token: \(response.response?.allHeaderFields["Access-token"])")
}

In the console I have

Access-Token: nil
access-token: Optional(jdRtDzKHNs_i-jt3Lh3a3A)
access-token: nil

Am I missing something ?

3 Answers

Apple finally provided a way to do case-insensitive lookups for HTTP headers in Swift. We should call:

HTTPURLResponse.value(forHTTPHeaderField:)

for that instead of using allHeaderFields. You can find this info here (that's the bug report link provided by Eduardo Vital in his answer).

following extension works for all condition. as NSDictionary will avoid convert to swfit dictionary, and keep the case insensitive Dictionary.

extension HTTPURLResponse {
    /// get value for caseless header field
    public final func headerString(field: String) -> String? {
        return (self.allHeaderFields as NSDictionary)[field] as? String
    }
}

Interestingly, this castless feature disappears when using optional types.

so following all works:

(self.allHeaderFields as NSDictionary)["Access-Token"]
(self.allHeaderFields as NSDictionary)["access-token"]
(self.allHeaderFields as NSDictionary)["aCCESS-toKEN"]

but following not works:

(self.allHeaderFields as? NSDictionary)?["aCCESS-toKEN"]
Related