Skip to content

Commit 670323e

Browse files
author
Pushkar N Kulkarni
authored
Merge pull request #1001 from nethraravindran/httpRedirection-branch
[SR-2679] Fix for URLSession delivers redirection delegate methods in wrong order
2 parents ea68075 + bbc4671 commit 670323e

File tree

3 files changed

+127
-11
lines changed

3 files changed

+127
-11
lines changed

Foundation/NSURLSession/http/HTTPURLProtocol.swift

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -776,10 +776,15 @@ internal extension _HTTPURLProtocol {
776776
// For now, we'll notify the delegate, but won't pause the transfer,
777777
// and we'll disregard the completion handler:
778778
guard let s = session as? URLSession else { fatalError() }
779-
s.delegateQueue.addOperation {
780-
delegate.urlSession(s, dataTask: dt, didReceive: response, completionHandler: { _ in
781-
URLSession.printDebug("warning: Ignoring disposition from completion handler.")
782-
})
779+
switch response.statusCode {
780+
case 301, 302, 303, 307:
781+
break
782+
default:
783+
s.delegateQueue.addOperation {
784+
delegate.urlSession(s, dataTask: dt, didReceive: response, completionHandler: { _ in
785+
URLSession.printDebug("warning: Ignoring disposition from completion handler.")
786+
})
787+
}
783788
}
784789
case .taskDelegate:
785790
break
@@ -859,7 +864,7 @@ internal extension _HTTPURLProtocol {
859864
//TODO: Do we ever want to redirect for HEAD requests?
860865
func methodAndURL() -> (String, URL)? {
861866
guard
862-
let location = response.value(forHeaderField: .location),
867+
let location = response.value(forHeaderField: .location, response: response),
863868
let targetURL = URL(string: location)
864869
else {
865870
// Can't redirect when there's no location to redirect to.
@@ -882,8 +887,24 @@ internal extension _HTTPURLProtocol {
882887
guard let (method, targetURL) = methodAndURL() else { return nil }
883888
var request = fromRequest
884889
request.httpMethod = method
885-
request.url = targetURL
886-
return request
890+
891+
// If targetURL has only relative path of url, create a new valid url with relative path
892+
// Otherwise, return request with targetURL ie.url from location field
893+
guard targetURL.scheme == nil || targetURL.host == nil else {
894+
request.url = targetURL
895+
return request
896+
}
897+
898+
let scheme = request.url?.scheme
899+
let host = request.url?.host
900+
901+
var components = URLComponents()
902+
components.scheme = scheme
903+
components.host = host
904+
components.path = targetURL.relativeString
905+
guard let urlString = components.string else { fatalError("Invalid URL") }
906+
request.url = URL(string: urlString)
907+
return request
887908
}
888909
}
889910

@@ -894,7 +915,12 @@ fileprivate extension HTTPURLResponse {
894915
/// - SeeAlso: RFC 2616 section 14.30 <https://tools.ietf.org/html/rfc2616#section-14.30>
895916
case location = "Location"
896917
}
897-
func value(forHeaderField field: _Field) -> String? {
898-
return field.rawValue
918+
func value(forHeaderField field: _Field, response: HTTPURLResponse?) -> String? {
919+
let value = field.rawValue
920+
guard let response = response else { fatalError("Response is nil") }
921+
if let location = response.allHeaderFields[value] as? String {
922+
return location
923+
}
924+
return nil
899925
}
900926
}

TestFoundation/HTTPServer.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ struct _HTTPRequest {
207207
struct _HTTPResponse {
208208
enum Response : Int {
209209
case OK = 200
210+
case REDIRECT = 302
210211
}
211212
private let responseCode: Response
212213
private let headers: String
@@ -229,6 +230,7 @@ public class TestURLSessionServer {
229230
"Peru":"Lima",
230231
"Italy":"Rome",
231232
"USA":"Washington, D.C.",
233+
"UnitedStates": "USA",
232234
"country.txt": "A country is a region that is identified as a distinct national entity in political geography"]
233235
let httpServer: _HTTPServer
234236
let startDelay: TimeInterval?
@@ -269,6 +271,18 @@ public class TestURLSessionServer {
269271
let text = request.getCommaSeparatedHeaders()
270272
return _HTTPResponse(response: .OK, headers: "Content-Length: \(text.characters.count)", body: text)
271273
}
274+
275+
if uri == "/UnitedStates" {
276+
let value = capitals[String(uri.characters.dropFirst())]!
277+
let text = request.getCommaSeparatedHeaders()
278+
let host = request.headers[1].components(separatedBy: " ")[1]
279+
let ip = host.components(separatedBy: ":")[0]
280+
let port = host.components(separatedBy: ":")[1]
281+
let newPort = Int(port)! + 1
282+
let newHost = ip + ":" + String(newPort)
283+
let httpResponse = _HTTPResponse(response: .REDIRECT, headers: "Location: http://\(newHost + "/" + value)", body: text)
284+
return httpResponse
285+
}
272286
return _HTTPResponse(response: .OK, body: capitals[String(uri.characters.dropFirst())]!)
273287
}
274288

TestFoundation/TestNSURLSession.swift

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class TestURLSession : XCTestCase {
3939
("test_verifyHttpAdditionalHeaders", test_verifyHttpAdditionalHeaders),
4040
("test_timeoutInterval", test_timeoutInterval),
4141
("test_customProtocol", test_customProtocol),
42+
("test_httpRedirection", test_httpRedirection),
4243
]
4344
}
4445

@@ -467,6 +468,24 @@ class TestURLSession : XCTestCase {
467468
task.resume()
468469
waitForExpectations(timeout: 12)
469470
}
471+
472+
func test_httpRedirection() {
473+
let serverReady = ServerSemaphore()
474+
globalDispatchQueue.async {
475+
do {
476+
try self.runServer(with: serverReady)
477+
} catch {
478+
XCTAssertTrue(true)
479+
return
480+
}
481+
}
482+
serverReady.wait()
483+
let urlString = "http://127.0.0.1:\(serverPort)/UnitedStates"
484+
let url = URL(string: urlString)!
485+
let d = HTTPRedirectionDataTask(with: expectation(description: "data task"))
486+
d.run(with: url)
487+
waitForExpectations(timeout: 12)
488+
}
470489
}
471490

472491
class SessionDelegate: NSObject, URLSessionDelegate {
@@ -516,14 +535,13 @@ class DataTask : NSObject {
516535
extension DataTask : URLSessionDataDelegate {
517536
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
518537
capital = String(data: data, encoding: String.Encoding.utf8)!
519-
dataTaskExpectation.fulfill()
520538
}
521539
}
522540

523541
extension DataTask : URLSessionTaskDelegate {
524542
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
525-
guard (error as? URLError) != nil else { return }
526543
dataTaskExpectation.fulfill()
544+
guard (error as? URLError) != nil else { return }
527545
if let cancellation = cancelExpectation {
528546
cancellation.fulfill()
529547
}
@@ -608,3 +626,61 @@ class CustomProtocol : URLProtocol {
608626
return
609627
}
610628
}
629+
630+
class HTTPRedirectionDataTask : NSObject {
631+
let dataTaskExpectation: XCTestExpectation!
632+
var session: URLSession! = nil
633+
var task: URLSessionDataTask! = nil
634+
var cancelExpectation: XCTestExpectation?
635+
636+
public var error = false
637+
638+
init(with expectation: XCTestExpectation) {
639+
dataTaskExpectation = expectation
640+
}
641+
642+
func run(with request: URLRequest) {
643+
let config = URLSessionConfiguration.default
644+
config.timeoutIntervalForRequest = 8
645+
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
646+
task = session.dataTask(with: request)
647+
task.resume()
648+
}
649+
650+
func run(with url: URL) {
651+
let config = URLSessionConfiguration.default
652+
config.timeoutIntervalForRequest = 8
653+
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
654+
task = session.dataTask(with: url)
655+
task.resume()
656+
}
657+
658+
func cancel() {
659+
task.cancel()
660+
}
661+
}
662+
663+
extension HTTPRedirectionDataTask : URLSessionDataDelegate {
664+
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
665+
guard let httpresponse = response as? HTTPURLResponse else { fatalError() }
666+
XCTAssertNotNil(response)
667+
XCTAssertEqual(200, httpresponse.statusCode, "HTTP response code is not 200")
668+
}
669+
}
670+
671+
extension HTTPRedirectionDataTask : URLSessionTaskDelegate {
672+
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
673+
dataTaskExpectation.fulfill()
674+
guard (error as? URLError) != nil else { return }
675+
if let cancellation = cancelExpectation {
676+
cancellation.fulfill()
677+
}
678+
self.error = true
679+
}
680+
681+
public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
682+
XCTAssertNotNil(response)
683+
XCTAssertEqual(302, response.statusCode, "HTTP response code is not 302")
684+
completionHandler(request)
685+
}
686+
}

0 commit comments

Comments
 (0)