@@ -476,9 +476,9 @@ internal class _HTTPURLProtocol: _NativeProtocol {
476
476
// We need this ugly cast in order to be able to support `URLSessionTask.init()`
477
477
session. delegateQueue. addOperation {
478
478
delegate. urlSession ( session, task: self . task!, willPerformHTTPRedirection: response as! HTTPURLResponse , newRequest: request) { [ weak self] ( request: URLRequest ? ) in
479
- guard let task = self else { return }
480
- self ? . task? . workQueue. async {
481
- task . didCompleteRedirectCallback ( request)
479
+ guard let self = self else { return }
480
+ self . task? . workQueue. async {
481
+ self . didCompleteRedirectCallback ( request)
482
482
}
483
483
}
484
484
}
@@ -651,7 +651,7 @@ internal extension _HTTPURLProtocol {
651
651
//TODO: Do we ever want to redirect for HEAD requests?
652
652
func methodAndURL( ) -> ( String , URL ) ? {
653
653
guard
654
- let location = response. value ( forHeaderField: . location, response : response ) ,
654
+ let location = response. value ( forHeaderField: . location) ,
655
655
let targetURL = URL ( string: location)
656
656
else {
657
657
// Can't redirect when there's no location to redirect to.
@@ -689,31 +689,33 @@ internal extension _HTTPURLProtocol {
689
689
return request
690
690
}
691
691
692
- let scheme = request. url? . scheme
693
- let host = request. url? . host
694
- let port = request. url? . port
692
+ guard
693
+ let fromUrl = fromRequest. url,
694
+ var components = URLComponents ( url: fromUrl, resolvingAgainstBaseURL: false )
695
+ else { return nil }
695
696
696
- var components = URLComponents ( )
697
- components. scheme = scheme
698
- components. host = host
699
- // Use the original port if the new URL does not contain a host
700
- // ie Location: /foo => <original host>:<original port>/Foo
701
- // but Location: newhost/foo will ignore the original port
702
- if targetURL. host == nil {
703
- components. port = port
697
+ // If the new URL contains a host, use the host and port from the new URL.
698
+ // Otherwise, the host and port from the original URL are used.
699
+ if targetURL. host != nil {
700
+ components. host = targetURL. host
701
+ components. port = targetURL. port
704
702
}
705
- //The path must either begin with "/" or be an empty string.
706
- if targetURL. relativeString. first != " / " {
707
- components. path = " / " + targetURL. relativeString
703
+
704
+ // The path must either begin with "/" or be an empty string.
705
+ if targetURL. path. hasPrefix ( " / " ) {
706
+ components. path = targetURL. path
708
707
} else {
709
- components. path = targetURL. relativeString
708
+ components. path = " / " + targetURL. path
710
709
}
710
+
711
+ // The query and fragment components are set separately to prevent them from being
712
+ // percent encoded again.
713
+ components. percentEncodedQuery = targetURL. query
714
+ components. percentEncodedFragment = targetURL. fragment
711
715
712
- guard let urlString = components. string else { fatalError ( " Invalid URL " ) }
713
- request. url = URL ( string : urlString )
716
+ guard let url = components. url else { fatalError ( " Invalid URL " ) }
717
+ request. url = url
714
718
715
- // Inherit the timeout from the previous request
716
- request. timeoutInterval = fromRequest. timeoutInterval
717
719
return request
718
720
}
719
721
}
@@ -726,10 +728,9 @@ fileprivate extension HTTPURLResponse {
726
728
case location = " Location "
727
729
}
728
730
729
- func value( forHeaderField field: _Field , response : HTTPURLResponse ? ) -> String ? {
731
+ func value( forHeaderField field: _Field ) -> String ? {
730
732
let value = field. rawValue
731
- guard let response = response else { fatalError ( " Response is nil " ) }
732
- if let location = response. allHeaderFields [ value] as? String {
733
+ if let location = self . allHeaderFields [ value] as? String {
733
734
return location
734
735
}
735
736
return nil
0 commit comments