-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
api: make iOS Headers and HeadersBuilder case-insensitive #2383
Merged
Merged
Changes from 35 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
8ffc556
use insensitive key
Augustyniak c3c9015
Make HeadersBuilder case insensitive
Augustyniak 8137392
remove unnecessary call
Augustyniak 15f722b
Update code
Augustyniak 3576622
Fix formatting
Augustyniak f7538f2
update version history
Augustyniak 516a1fc
Lint fixes
Augustyniak 2ff4fb6
erge branch 'main' into make-headers-builder-case-insensitive
Augustyniak 42897f2
add doc
Augustyniak 0e328a0
fix casing
Augustyniak 8430f4f
fix typo
Augustyniak 86ce3fc
Improve version history
Augustyniak 37c6e5c
Make Headers case-insensitive too
Augustyniak 35c0536
lint fixes
Augustyniak 1fd9938
swiftlint fix
Augustyniak 6389322
doc string fix
Augustyniak 3ac922c
remove method
Augustyniak 6d6caf2
small tweaks to docs and description property
Augustyniak cc40134
simplify code
Augustyniak eea7eff
Merge branch 'main' into make-headers-builder-case-insensitive
Augustyniak eac2d9d
Doc updates
Augustyniak 302db00
get rid of todo
Augustyniak 9069668
use case-insensitive comparison
Augustyniak 15cb362
fix case insensitive comparison and add tests
Augustyniak ca4441a
lint fixes
Augustyniak fe79cb3
cr comments
Augustyniak a80ef6a
Update library/swift/HeadersBuilder.swift
Augustyniak 1a4d915
Update library/swift/HeadersContainer.swift
Augustyniak 2a5085a
Update library/swift/HeadersContainer.swift
Augustyniak b9d1dd9
Update library/swift/HeadersBuilder.swift
Augustyniak 75a586d
update version history
Augustyniak 7295362
change used method
Augustyniak 723c76f
update example app
Augustyniak 079a01a
update example app
Augustyniak 62885af
update example app
Augustyniak 702bc24
update example apps
Augustyniak d3bd1ea
update example apps
Augustyniak d45fa9f
update example apps
Augustyniak 42b38bf
update example apps
Augustyniak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/// The container which manages the underlying headers map. | ||
/// It maintains the original casing of passed header names. | ||
/// It treats headers names as case-insensitive for the purpose | ||
/// of header lookups and header name conflict resolutions. | ||
struct HeadersContainer: Equatable { | ||
private var headers: [String: Header] | ||
|
||
/// Represents a headers name together with all of its values. | ||
/// It preserves the original casing of the header name. | ||
struct Header: Equatable { | ||
private(set) var name: String | ||
private(set) var value: [String] | ||
|
||
init(name: String, value: [String] = []) { | ||
self.name = name | ||
self.value = value | ||
} | ||
|
||
mutating func addValue(_ value: [String]) { | ||
self.value.append(contentsOf: value) | ||
} | ||
|
||
mutating func addValue(_ value: String) { | ||
self.value.append(value) | ||
} | ||
} | ||
|
||
/// Initialize a new instance of the receiver using the provided headers map. | ||
/// | ||
/// - parameter headers: The headers map. | ||
init(headers: [String: [String]]) { | ||
var underlyingHeaders = [String: Header]() | ||
for (name, value) in headers { | ||
let lowercasedName = name.lowercased() | ||
/// Dictionaries in Swift are unordered collections. Process headers with names | ||
/// that are the same when lowercased in an alphabetical order to avoid a situation | ||
/// in which the result of the initialization is non-derministic i.e., we want | ||
/// "[A: ["1"]", "a: ["2"]]" headers to be always converted to ["A": ["1", "2"]] and | ||
/// never to "a": ["2", "1"]. | ||
/// | ||
/// If a given header name already exists in the processed headers map, check | ||
/// if the currently processed header name is before the existing header name as | ||
/// determined by an alphabetical order. | ||
guard let existingHeader = underlyingHeaders[lowercasedName] else { | ||
underlyingHeaders[lowercasedName] = Header(name: name, value: value) | ||
continue | ||
} | ||
|
||
if existingHeader.name > name { | ||
underlyingHeaders[lowercasedName] = | ||
Header(name: name, value: value + existingHeader.value) | ||
} else { | ||
underlyingHeaders[lowercasedName]?.addValue(value) | ||
} | ||
} | ||
self.headers = underlyingHeaders | ||
} | ||
|
||
/// Initialize an empty headers container. | ||
init() { | ||
self.headers = [:] | ||
} | ||
|
||
/// Add a value to a header with a given name. | ||
/// | ||
/// - parameter name: The name of the header. For the purpose of headers lookup | ||
/// and header name conflict resolution, the name of the header | ||
/// is considered to be case-insensitive. | ||
/// - parameter value: The value to add. | ||
mutating func add(name: String, value: String) { | ||
self.headers[name.lowercased(), default: Header(name: name)].addValue(value) | ||
} | ||
|
||
/// Set the value of a given header. | ||
/// | ||
/// - parameter name: The name of the header. | ||
/// - parameter value: The value to set the header value to. | ||
mutating func set(name: String, value: [String]?) { | ||
guard let value = value else { | ||
self.headers[name.lowercased()] = nil | ||
return | ||
} | ||
self.headers[name.lowercased()] = Header(name: name, value: value) | ||
} | ||
|
||
/// Get the value for the provided header name. | ||
/// | ||
/// - parameter name: The case-insensitive header name for which to | ||
/// get the current value. | ||
/// | ||
/// - returns: The value associated with a given header. | ||
func value(forName name: String) -> [String]? { | ||
return self.headers[name.lowercased()]?.value | ||
} | ||
|
||
/// Return all underlying headers. | ||
/// | ||
/// - returns: The underlying headers. | ||
func allHeaders() -> [String: [String]] { | ||
return Dictionary(uniqueKeysWithValues: self.headers.map { _, value in | ||
return (value.name, value.value) | ||
}) | ||
} | ||
} | ||
|
||
extension HeadersContainer: CustomStringConvertible { | ||
var description: String { | ||
return self.headers.description | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we merge the
Headers
type with theHeadersContainer
type? I don't see any reason to add that additional layer, but maybe I'm missing a reason why it's necessary?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of my previous solutions was to merge
Headers
andHeadersContainer
but I decided against it as:HeadersContainers
to be a struct - not possible withHeaders
as it has to inherit fromNSObject
. There are some negative of using structs tho - more copying.HeadersBuilder
depends on a lot of stuff that's currently inHeadersContainer
. If we mergeHeaders
andHeadersContainer
thenHeadersBuilder
would either have to have duplicated logic betweenHeaders
andHeadersBuilder
or makeHeadersBuilder
depend onHeaders
. I decided that having a separateHeadersContainer
class was a better approach to ensure that all of the case-insensitive lookup logic lives in one place and is shared betweenHeaders
andHeaderBuilder
.