-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Create URL Protection Space from HTTPURLResponse #2761
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
Conversation
@swift-ci test linux |
1 similar comment
@swift-ci test linux |
let port = response.url?.port ?? 80 //HTTP | ||
let _protocol = response.url?.scheme | ||
guard let wwwAuthHeader = response.allHeaderFields["Www-Authenticate"] as? String else { | ||
fatalError("Authentication failed but no Www-Authenticate header in response") |
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.
It might be better to be stricter on the input validation, it can alway be loosened later on. Rather than defaulting values I would suggest:
guard let url = response.url, let host = url.host, let _protocol = url.scheme, _protocol == "http" || _protocol == "https" else {
return nil
}
let port = url.port ?? (_protocol == "http" ? 80 : 443)
to tighten up the validation.
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.
Makes sense. And less code. I like it. Will do.
// Typical Www-Authenticate header is something like | ||
// Www-Authenticate: Digest realm="test", domain="/HTTP/Digest", nonce="e3d002b9b2080453fdacea2d89f2d102" | ||
|
||
let components = authenticateValue.components(separatedBy: " ") |
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.
Will this split correctly if there are multiple spaces between each part?
I think split(separator: Character, maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true) -> [Substring]
might be a better choice as by default it will not add empty sequences to the output array.
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.
This is the grammar:
WWW-Authenticate = *( "," OWS ) challenge *( OWS "," [ OWS challenge
] )
auth-param = token BWS "=" BWS ( token / quoted-string )
auth-scheme = token
challenge = auth-scheme [ 1*SP ( token68 / [ ( "," / auth-param ) *(
OWS "," [ OWS auth-param ] ) ] ) ]
credentials = auth-scheme [ 1*SP ( token68 / [ ( "," / auth-param )
*( OWS "," [ OWS auth-param ] ) ] ) ]
where:
OWS = *( SP / HTAB )
; optional whitespace
…
BWS = OWS
; "bad" whitespace
so it looks like we need to split for " " or "\t". Use the CharacterSet variants maybe?
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.
return NSURLAuthenticationMethodNTLM | ||
case "negotiate": | ||
return NSURLAuthenticationMethodNegotiate | ||
default: |
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.
Will it cause problems to create a URLProtectionSpace
for methods that are not currently implemented (ie everything except Basic)? Could always return nil
for these other ones for now
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.
I actually thought about this helper point as parsing as much info as possible (and reasonable). Hmm, but on the other side it could take responsibility to cut off unsupported things. Agree, this should return nil for everything other than Basic auth.
// Basic implementation. | ||
// Doesn't follow complete spec of quoted strings (RFC-7230, p. 3.2.6). | ||
// Handles escaping of quotes only. | ||
|
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.
Give that this is concerned with authentication I think that the complete spec should be followed. Maybe it could be broken out into a separate function that fully parses a String
into its constituent parts. If any percent decoding needs to be done on the input string then there is DataURLProtocol._PercentDecoder
that could possibly be used.
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.
Thanks for hint. I guess I will also look into curl implementation of similar task. AFAIK it is pretty simple. I like the idea of separate parsing, could be also tested separately.
Good by me once the feedback is looked at. |
return port | ||
} | ||
|
||
switch scheme { |
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.
I forget if scheme
is normalized?
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.
This should probably be scheme.lowercased().
// Typical Www-Authenticate header is something like | ||
// Www-Authenticate: Digest realm="test", domain="/HTTP/Digest", nonce="e3d002b9b2080453fdacea2d89f2d102" | ||
|
||
let components = authenticateValue.components(separatedBy: " ") |
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.
This is the grammar:
WWW-Authenticate = *( "," OWS ) challenge *( OWS "," [ OWS challenge
] )
auth-param = token BWS "=" BWS ( token / quoted-string )
auth-scheme = token
challenge = auth-scheme [ 1*SP ( token68 / [ ( "," / auth-param ) *(
OWS "," [ OWS auth-param ] ) ] ) ]
credentials = auth-scheme [ 1*SP ( token68 / [ ( "," / auth-param )
*( OWS "," [ OWS auth-param ] ) ] ) ]
where:
OWS = *( SP / HTAB )
; optional whitespace
…
BWS = OWS
; "bad" whitespace
so it looks like we need to split for " " or "\t". Use the CharacterSet variants maybe?
// Typical Www-Authenticate header is something like | ||
// Www-Authenticate: Digest realm="test", domain="/HTTP/Digest", nonce="e3d002b9b2080453fdacea2d89f2d102" | ||
|
||
let components = authenticateValue.components(separatedBy: " ") |
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.
I'm so sorry, the solution is really delayed. It is still WIP. Had done about 3/4. I am thinking to convert this to draft, to not to look like stale PR. |
@lxbndr are you still unable to finish this patch? |
@millenomi |
Thank you! I'll be here to facilitate. |
923c868
to
507a169
Compare
This all started as an attempt to parse "realm" parameter and not to crash. New implementation is RFC-compliant. I think I found perfect place for parsing code, and it is in All necessary string comparisons are case-insensitive, but internally data stored as is, so Some simplifications made assuming we are supporting only "Basic" scheme. But in general this code could be used to adopt other schemes. I reused as much existing code as seemed reasonable (like token parsing, whitespace trimming, character constants). It seems like we could refactor existing code a bit to improve consistency and streamline some logic. E.g. existing code references RFC-2616, which is obsoleted by RFCs 7230-7235. To keep this patch focused I used old code as is. The general flow of WWW-Authenticate parsing was inspired by CURL's approach, as it seems pretty robust. |
@millenomi Could you please take a look? I think I took all comments into account. But as changes are much bigger than first time, I am expecting everything. |
Nothing immediately jumps at me. At this point, this looks large and useful enough that we can start taking bug fixes as separate patches if needed. |
@swift-ci please test |
507a169
to
51938c9
Compare
I found that one line I've changed was unnecessary. Reverted HTTPURLProtocol.swift:137 to original. @millenomi Is there anything else I can do here? I am not in a rush, just thought maybe I missed some requirement. |
@@ -111,6 +111,10 @@ struct _Delimiters { | |||
static let Space = UnicodeScalar(0x20) | |||
static let HorizontalTab = UnicodeScalar(0x09) | |||
static let Colon = UnicodeScalar(0x3a) | |||
static let Backslash = UnicodeScalar(0x5c)! | |||
static let Comma = UnicodeScalar(0x2c)! | |||
static let Dquote = UnicodeScalar(0x22)! |
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.
Nit: Mind if we call this DoubleQuote
? We already spell HTAB HorizontalTab
instead of Htab
, and it just reads a little more clearly, I think, in the context where we have comments referring to "dequoted" strings and then code that refers to "dquote".
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.
Sure! Anything to reduce confusion. Change pushed.
51938c9
to
686675c
Compare
This comment has been minimized.
This comment has been minimized.
Build failure on Linux looks unrelated. @MaxDesiatov Could re-run fix that, WDYT? |
This comment has been minimized.
This comment has been minimized.
Basic implementation of authentication challenge parsing.
686675c
to
ca2fe94
Compare
Rebased to latest main. |
This comment has been minimized.
This comment has been minimized.
1 similar comment
@swift-ci please test |
Would anyone mind if this is merged? |
@swift-ci please test |
@MaxDesiatov No I dont mind, lets get this in. I think have some URLSession fixes that rely on this so would be good to progress those. |
Linux failure seems to be unrelated |
@swift-ci please test Linux platform |
1 similar comment
@swift-ci please test Linux platform |
Basic implementation of "Www-Authenticate" parsing. Some real-life responses could crash previous implementation, as it doesn't work with realm in case insensitive way. We decided to modify existing (but unused) internal helper, considering that a good step in iterative improvement of FoundationNetworking.
This also fixes access to "Www-Authenticate" header in some places,
as HTTPURLResponse does capitalize headers after #2728.