Connection: Keep-Alive not properly terminating connections.
Description
When requests are sent with Connection: Keep-Alive, they can be kept indefinitely if the client exhibits unusual circumstances.
To recreate:
Server
Simple server running on linux server with arbitrary endpoint (/ hello world)
Client
iOS Client with button(s) to hit the endpoint with URLSession (Note: URLSession has known issues with keep-alive as well).
var request = URLRequest(url: URL(string: "http://testserver.local:3000/")!)
let task = URLSession.shared.dataTask(with: request)
task.resume()
Press button multiple times, close app, reopen app, repeat multiple times.
Server
Observe output of [ps|top|htop], over time the number of threads listed grows (presumably) due to threads waiting on dead connections.
Alternatives tested
Switching to an HTTP library that's not backed on NSURLSession (thus doesn't automatically overwrite intentional Connection: close headers), like Just, and repeating the same behavior with Connection: close, the number of threads reported by [ps|top|htop] only grows as requests comes in and appropriately levels back off (to 2 threads on my specific linux device).
Proposed Solutions
- Adding this known caveat to documentation
- Adding a server option to ignore keep-alive headers and always close sockets
- Adding timeouts to the socket class as well as configurable timeouts on the server.
The last and obviously best option is however the most complicated as it requires adding a lot to the socket class itself. I'd propose simply adding a separate socket class, i.e. IBM's BlueSocket, which is very lightweight and also feature complete by itself (and can support SSL easily), however that adds a dependency which I'm not a fan of. A compromise is redesigning the existing socket class basing it off of BlueSocket, there's room to take only whats needed from there for proper timeouts without adding extra stuff for unix sockets, etc.