Inconsistent Handling of HTTP Redirects in URLSession

Originator:johnbrayton
Number:rdar://31284156 Date Originated:2017-03-27
Status:Open Resolved:
Product:iOS + SDK Product Version:iOS 10.3 (14E277)
Classification:Serious Bug Reproducible:Always
 
I have a URLSession delegate that blocks redirects as follows:

func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Swift.Void) {
        
	completionHandler(nil)
        
}

I begin a task with:

let dataTask = session.dataTask(with: URL(string: self.initialUrlString)!)
dataTask.resume()

If a server returns a redirect, I expect the following to happen after my "willPerformHTTPRedirection" method calls completionHandler(nil):

1.  The "didReceive response" method of the session delegate be called with the HTTP response containing the redirect.

2.  The "didReceive data" method be called as many times as necessary to send the body of the redirect response to the delegate.

3.  The "didCompleteWithError" method be called with a null error.

Sometimes this happens, but other times nothing happens until one minute (depending on timeoutIntervalForRequest) passes. Then didCompleteWithError is called with an error indicating that the request timed out. I believe the pattern is that it works as expected the first time that code is executed on a device or simulator with a specific URL. Every time thereafter, I get the 60 second delay and then a timeout. I am seeing this behavior against a variety of servers including www.apple.com.

Steps to Reproduce:

1.  Open the attached sample project.

2.  Observe that the code in the project retrieves "https://www.goldenhillsoftware.com/feed-hawk" when the app launches. That URL redirects to "https://www.goldenhillsoftware.com/feed-hawk/" (with a trailing slash). Observe that the code uses a URLSession delegate as described above. Observe that the code attempts to retrieve the same URL two seconds later. Also observe that the SessionDelegate class logs all calls.

3.  Run the code on an iPhone or in a simulator.

Expected Results:

I expect the console output to reflect that the URLSession delegate methods were called, as follows:

Starting request
got redirect
got response
got data
finished successfully
Starting request
got redirect
got response
got data
finished successfully


Actual Results:

Sometimes I see that the URLSession delegate methods are only called as expected the first time I initiate the request and that the second request times out:

Starting request
got redirect
got response
got data
finished successfully
Starting request
got redirect
(long delay)
finished with error: The request timed out.

Other times I see both requests time out:

Starting request
got redirect
Starting request
got redirect
(long delay)
finished with error: The request timed out.
finished with error: The request timed out.

Configuration:  

iPhone 5S or in the iOS Simulator. I also see this in a macOS app running on my 2012 Retina MacBook Pro.

Version & Build:  

iOS 10.3 (14E277)


Additional Notes:

Thank you for looking into this.

==

The sample project I attached to the radar is at:
https://www.virtualsanity.com/radars/31284156.zip

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!