Re: NSURLSession does not call delegate method for redirections from „http“ to „https"

I have encountered an issue with NSURLSession when a request to "http" results in a redirection to "https" with the same host and path (HTTP status code 302). In this case the delegate method "URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:" is not(!) called. For other redirections the delegate methods is called as expected.

In my App I'm using UIWebView to access web sites and NSURLProtocol to do filtering and other stuff, so I can see the details of the network traffic.

This iOS bug introduces some issues because the components which have initiated the request (like an instance of UIWebView) does not get notified about the redirection and assumes that the URL scheme is still "http". So even if the real URL of the site is a secure one, the web page still assumes an unsecure one. The Javascript property "location.href" contains the unsecure URL (with the scheme "http"). All relative URLs will be resolved with the wrong base URL etc.

Is there a worksround available for this? Am I doing something wrong? Why is NSURLSession forgetting to call the delegate method in this case?

Right now I'm checking in the delegate method "URLSession:dataTask:didReceiveResponse:completionHandler:" if the URL of the response has a different URL scheme ("https") than the initial request - if yes I call "[URLProtocolClient URLProtocol:wasRedirectedToRequest:request redirectResponse:]" in my NSURLProtocol class to "simulate" the missing redirection notification, so the caller (UIWebView) gets notified. But I'm not sure if this has some negative side effects, especially because I'm not yet sure if I've found all circumstances under which this bug within NSURLSession occurs and what exactly UIWebView is doing when it receives a redirection notification.

I suspect that the problem here is your NSURLProtocol subclass. There’s some serious pitfalls getting redirects to work in that context. If you run the same test with the CustomHTTPProtocol sample code, does it exhibit the same problem.

I can definitely say that NSURLProtocol is not responsible for the issue. I've disabled my NSURLProtocol class and just called the NSURLSession API directly (just using the default configuration of NSURLSession). But the problem remains.

In this case the delegate method for the redirection is also called correctly.

So only in those cases where the redirection changes the scheme, but not the domain part (another example where this happens would be "http://github.com/"), the delegate method for the redirection is NOT called. This all happens under iOS 10.2 (the latest public iOS release).

BTW: under iOS 9.3.x the delegate method for the redirection is always sent correctly. So this seems to be a bug of iOS 10.2 only (maybe other iOS 10 releases as well).

I haven't checked in 10.3 Beta yet, because this new beta and its Beta of XCode requires "macOS Sierra", which I do not have installed on my Mac for various reasons.

Curiously, in that case I can then remove NSAllowsArbitraryLoads and it also works.

A quick check of the CFNetwork diagnostics log shows that the HTTP request never makes it to the ‘wire’. Somehow it’s getting rewritten to HTTPS before it’s seen by the core HTTP engine.

These two factoids (site-specific behaviour, never hitting the wire) got me thinking about HSTS. And some quick spelunking in the debugger reveals that this is, indeed, the culprit. GitHub has a built-in HSTS entry, so the HTTP request always gets rewritten as HTTPS. DuckDuckGo, OTOH, does not have a built-in HSTS entry, so the HTTP request hits the ‘wire’, triggers an HTTP redirect, which results in a willPerformHTTPRedirection delegate callback.

Finally, I suspect that you are seeing the problem with DuckDuckGo because your app has previously cached an HSTS requirement from that site. My app, OTOH, is brand new, so it’s relying on the built in HSTS requirements.

Interesting. It’s not obvious whether the HSTS rewrite should result in an willPerformHTTPRedirection delegate callback. I can see why you’d want it to, but it’d be a bit weird, architecturally, to synthesise an HTTP response in this case.

If you were the only client of these NSURLSession tasks then it would be easy to get around this problem by noticing the HTTPS URL in the response passed to the didReceiveResponse delegate callback. However, in your case UIWebView is the main client, and that complicates things.

I suspect the only way around this would be to have your NSURLProtocol subclass detect the HSTS rewrite and synthesise a redirect response to pass up to the web view.

Regardless of what else you do, you should file a bug about this oddity. In fact, it may make sense to file a suite of bugs, including:

Thank you very much for your response. Everthing makes perfect sense now.

For now, my workaround/solution looks like this:

In the "didReceiveResponse" method of my "NSURLProtocol" class, I check if the URL of the original request and the final destination are the same, besides the URL scheme, which has changed from "http" to "https". If this is the case and the "willPerformHTTPRedirection" method was not called for this request, I 'm calling [NSURLProtocolClient URLProtocol:wasRedirectedToRequest:redirectResponse:] to notify the component which made the request about the redirection. Of course I can not pass the "original" redirection response and requests here, because I do not have them, but I can create a "fake" one which includes at least all the values that are needed, borrow them from the response and request which I do have. And it seems to work fine so far. UIWebView seems to accept this and is working with the correct (secure) URLs now.

I faced the kind of same issue. I'm also able to resolve it by using the "didReceiveResponse" method of my "NSURLProtocol" class.

When I received the call of the following method:

NSURLConnectionDataDelegate's -> (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse in my NSURLProtocol, I keep the request and response for the redirect request in memory and pass that to [NSURLProtocolClient URLProtocol:wasRedirectedToRequest:redirectResponse:] to notify the component which made the request about the redirection. And it works fine.

More Like This

Retrieving data ...

This site contains user submitted content, comments and opinions and is for informational purposes only. Apple disclaims any and all liability for the acts, omissions and conduct of any third parties in connection with or related to your use of the site. All postings and use of the content on this site are subject to the Apple Developer Forums Participation Agreement.