NSUrlSessionTask error: The network connection was lost (TLS issues?)

We are working on an update to our app and have noticed that some of our users have issues uploading data to certain endpoints.

This is the error:

Task <FBDD7D4D-E71F-41BE-84AF-DA94F090E6F7>.<1> finished with error [-1005] Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={_kCFStreamErrorCodeKey=-4, _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <FBDD7D4D-E71F-41BE-84AF-DA94F090E6F7>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundUploadTask <FBDD7D4D-E71F-41BE-84AF-DA94F090E6F7>.<1>",
"LocalUploadTask <FBDD7D4D-E71F-41BE-84AF-DA94F090E6F7>.<1>"), NSLocalizedDescription=The network connection was lost., _kCFStreamErrorDomainKey=4, NSErrorFailingURLStringKey=<redacted>, NSErrorFailingURLKey=<redacted>}

Users who are using iOS 16.5.x do not exhibit these issues. However, uses on iOS 16.3 or 16.1 do exhibit these issues.

I was finally able to reproduce the issue by installing iOS 16.1 in the simulator and building the application and running it there.

Wireshark shows there are TCP retransmissions and then it closes the connection. It happens fairly quickly.

While using Charles proxy, it likewise fails. The "Failure" is:

Remote server closed the connection before sending response header

Building the same application on 16.5.x does not exhibit this issue.

I can confirm that Cloudflare says that the "client closed the connection" as I can see the TLS negotiation with Cloudflare. Subsequent requests don't even hit our servers. They just time out.

I'm totally lost here as while the code has changed between the 2 versions, the fact that it works on new versions but not on old versions seems to indicate a bug in the underlying library right?

I'm using AFNetworking here. This code is all Objective-C.

Replies

I'm using Charles to dig into this a bit further. Now my requests are timing out.

In iOS 16.4+ the size of the request is correct (the full fill is sent ~133KB).

In iOS 16.1, the size of the request is wrong (~3K). Charles says it is "sending headers" but then the request times out. We never get past the header.

Wireshark dumps of the pcaps of both files look very similar in that we can see the TLS exchange between client and server but then once we get to the actual transmission of the data...

In iOS 16.4, the data just starts getting sent. In iOS 16.1, full packets are never sent. Three smaller than MTU packets get sent. We get duplicate ACKs and then it all falls apart.

One of the first things I would do is test this on a real device. While the simulator is a good lint test, there is nothing more realistic than running it on an actual device. Once you are able to do that, I would first check the server logs, if you are able to get them, and make sure you are actually talking to the server you are expecting to talk to. Weird inconsistencies like this can take place when there is routing mis-matches on the server side.