In-flight requests on browser close
In-flight requests on localhost
Let’s suppose you have a web application built with a typical framework like Laravel (PHP), Flask (Python), or Spring Boot (Java). If you start the application on your local machine and hit http://localhost:8080 in the browser a TCP connection will be created, an HTTP request will be sent to the local server, and your application code will start processing the request. This is what the state will be like:
Now one of two scenarios can happen:
If the browser stays open long enough your application will finish processing the request, and it will send an HTTP response using the same TCP connection. The browser will parse&display it, and it will eventually close the TCP connection.
But if the browser gets closed during the request is in-flight the TCP connection will be closed
(a FIN packet will be sent to the server), but your application code will still process the request to the end. As long as your code
does nothing with the response output stream (e.g. it saves/reads from a database and returns some HTML or JSON),
no exception will occur in your code. In this case, it will be the application server (Tomcat, Jetty, Nginx, or Apache)
who will notice that the TCP connection is closed when trying to write/flush the output stream.
But most servers behave as nothing special happened: in access.log you will find the usual response HTTP status code
(including Jetty, Tomcat, or Werkzeug’s server).
Only Nginx will log an unofficial HTTP status 499. With other application servers depending on the configuration you could
also see an exception in the logs (org.eclipse.jetty.io.EofException
in Jetty), but most of the time you get no hints from
the application server. And this is generally a problem because it could hide a latent problem (unacceptable response times, impatient customers, or configuration in the server)
or a DOS attack.
In-flight requests with reverse proxies
So far we analyzed the situation when the browser reaches directly the server, but production infrastructure is usually more complex. There will usually be one or more reverse proxies in between the browser and your application. A reverse proxy can have different roles, like load balancing, SSL termination, compressing. To achieve this browser will connect to the reverse proxy using one TCP connection and then the reverse proxy will create a second TCP connection to the next hop (instead of a single TCP connection between browser and application server). The next hop could be your application server if there is a single reverse proxy on the path, but in big projects/companies, there could be multiple reverse proxies.
This is how it looks like with a single reverse proxy, right after the application starts processing the HTTP request.
And now if the browser gets closed, the first TCP connection will also be closed. What happens with the second TCP connection depends on reverse proxy and its configuration. If it closes also the second connection (e.g. Nginx does this always) then your application will behave like explained above (with no reverse proxy involved). But if reverse proxy leaves the connection open (e.g. Classic Load Balancer of AWS) neither your application nor your application server will notice anything.
Conclusion
We have seen what happens under the hood when you close the browser during an in-flight request. The same happens when you close a tab in modern browsers or when you hit the stop button during page load. In such a case the browser will close its connection, but whether the last TCP connection to the application also gets closed depends on the existing reverse proxy and its configuration. In any case, if the application started processing the request, it will not be interrupted and, as long as it does not mess with the output stream, it will continue without any exceptions. Besides, it is a good idea to see it in the logs when a TCP connection was closed before the response was sent, but you don’t get much help from most application servers (Nginx is an exception).