@@ -106,6 +106,12 @@ open class URLSessionTask : NSObject, NSCopying {
106106
107107 /// May be nil if this is a stream task
108108 /*@NSCopying*/ open let originalRequest : URLRequest ?
109+
110+ /// If there's an authentication failure, we'd need to create a new request with the credentials supplied by the user
111+ var authRequest : URLRequest ? = nil
112+
113+ /// Authentication failure count
114+ fileprivate var previousFailureCount = 0
109115
110116 /// May differ from originalRequest due to http server redirection
111117 /*@NSCopying*/ open internal( set) var currentRequest : URLRequest ? {
@@ -528,9 +534,30 @@ extension _ProtocolClient : URLProtocolClient {
528534 }
529535 }
530536
537+ func createProtectionSpace( _ response: HTTPURLResponse ) -> URLProtectionSpace {
538+ let host = response. url? . host ?? " "
539+ let port = response. url? . port ?? 80 //we're doing http
540+ let _protocol = response. url? . scheme
541+ let wwwAuthHeader = response. allHeaderFields [ " WWW-Authenticate " ] as! String
542+ let authMethod = wwwAuthHeader. components ( separatedBy: " " ) [ 0 ]
543+ let realm = String ( String ( wwwAuthHeader. components ( separatedBy: " realm= " ) [ 1 ] . dropFirst ( ) ) . dropLast ( ) )
544+ return URLProtectionSpace ( host: host, port: port, protocol: _protocol, realm: realm, authenticationMethod: authMethod)
545+ }
546+
531547 func urlProtocolDidFinishLoading( _ protocol: URLProtocol ) {
532548 guard let task = `protocol`. task else { fatalError ( ) }
533549 guard let session = task. session as? URLSession else { fatalError ( ) }
550+ guard let response = task. response as? HTTPURLResponse else { fatalError ( " No response " ) }
551+ if response. statusCode == 401 {
552+ let protectionSpace = createProtectionSpace ( response)
553+ //TODO: Fetch and set proposed credentials if they exist
554+ let authenticationChallenge = URLAuthenticationChallenge ( protectionSpace: protectionSpace, proposedCredential: nil ,
555+ previousFailureCount: task. previousFailureCount, failureResponse: response, error: nil ,
556+ sender: `protocol` as! _HTTPURLProtocol )
557+ task. previousFailureCount += 1
558+ urlProtocol ( `protocol`, didReceive: authenticationChallenge)
559+ return
560+ }
534561 switch session. behaviour ( for: task) {
535562 case . taskDelegate( let delegate) :
536563 if let downloadDelegate = delegate as? URLSessionDownloadDelegate , let downloadTask = task as? URLSessionDownloadTask {
@@ -569,7 +596,24 @@ extension _ProtocolClient : URLProtocolClient {
569596 }
570597
571598 func urlProtocol( _ protocol: URLProtocol , didReceive challenge: URLAuthenticationChallenge ) {
572- NSUnimplemented ( )
599+ guard let task = `protocol`. task else { fatalError ( ) }
600+ guard let session = task. session as? URLSession else { fatalError ( ) }
601+ switch session. behaviour ( for: task) {
602+ case . taskDelegate( let delegate) :
603+ session. delegateQueue. addOperation {
604+ let authScheme = challenge. protectionSpace. authenticationMethod
605+ delegate. urlSession ( session, task: task, didReceive: challenge) { disposition, credential in
606+ task. suspend ( )
607+ guard let handler = URLSessionTask . authHandler ( for: authScheme) else {
608+ fatalError ( " \( authScheme) is not supported " )
609+ }
610+ handler ( task, disposition, credential)
611+ task. _protocol = _HTTPURLProtocol ( task: task, cachedResponse: nil , client: nil )
612+ task. resume ( )
613+ }
614+ }
615+ default : return
616+ }
573617 }
574618
575619 func urlProtocol( _ protocol: URLProtocol , didLoad data: Data ) {
@@ -632,6 +676,31 @@ extension _ProtocolClient : URLProtocolClient {
632676 NSUnimplemented ( )
633677 }
634678}
679+ extension URLSessionTask {
680+ typealias _AuthHandler = ( ( URLSessionTask , URLSession . AuthChallengeDisposition , URLCredential ? ) -> ( ) )
681+
682+ static func authHandler( for authScheme: String ) -> _AuthHandler ? {
683+ let handlers : [ String : _AuthHandler ] = [
684+ " Basic " : basicAuth,
685+ " Digest " : digestAuth
686+ ]
687+ return handlers [ authScheme]
688+ }
689+
690+ //Authentication handlers
691+ static func basicAuth( _ task: URLSessionTask , _ disposition: URLSession . AuthChallengeDisposition , _ credential: URLCredential ? ) {
692+ //TODO: Handle disposition. For now, we default to .useCredential
693+ let user = credential? . user ?? " "
694+ let password = credential? . password ?? " "
695+ let encodedString = " \( user) : \( password) " . data ( using: . utf8) ? . base64EncodedString ( )
696+ task. authRequest = task. originalRequest
697+ task. authRequest? . setValue ( " Basic \( encodedString!) " , forHTTPHeaderField: " Authorization " )
698+ }
699+
700+ static func digestAuth( _ task: URLSessionTask , _ disposition: URLSession . AuthChallengeDisposition , _ credential: URLCredential ? ) {
701+ NSUnimplemented ( )
702+ }
703+ }
635704
636705extension URLProtocol {
637706 enum _PropertyKey : String {
0 commit comments