I would disagree with this statement, but only narrowly, once you start talking about proxies and reverse proxies and things of that nature, it becomes much harder because the client isn’t directly connected anymore. In most of todays environments you are right that it is really hard to know if a remote client went away or if a request was actually successfully returned to the remote client (and not just any intermediary proxies/servers that may be buffering the request).
It is hard to do in Python based threaded servers because even though the code is running in a thread, and the main thread knows about the connection being dropped, there’s no way for the main thread to cancel the running of the worker thread and notify it. pthread_cancel does not work, and there is no good way to signal to the thread to stop running code or to interrupt it, especially during heavy computation. HTTP/1.1 also makes this somewhat more difficult, in that the only way for the main thread to know is to continue telling the kernel it wants to read from the socket the client is connected on, with HTTP pipelining the client can send multiple requests at once, and we’d have to buffer those requests, otherwise the call to select()/poll() becomes a busy loop because each time we call select()/poll() the OS would tell us the socket is ready for reading. Thankfully HTTP pipelining these days is very rare because of incredibly poor support for having multiple requests in flight, while the response being returned would close the connection due to an error (the client would have to retry any in-flight requests that were pipelined but not replied to).
You can emulate it somewhat by checking to see during various points of computation whether the client has gone away, and then manually acting upon it, and waitress has support for that. It is not enabled by default because of the HTTP pipelining issue, and the issue of spinning on select(), but it is configurable to attempt to buffer up to X requests by setting the flag `channel_request_lookahead` to something that is non-zero.
There’s just no predefined way to do it across WSGI servers, nor does pyramid_tm provide any helpers for it since it can’t add those checks for you as you are generating your response. This is a waitress extension.
The feature was introduced in this PR:
I don’t think there’s good example of how to use it in the documentation, but here’s a quick and dirty example:
log = logging.getLogger(__name__)
def application(environ, start_response):
check = environ["waitress.client_disconnected"]
for i in range(10):
# do some computation
log.debug("Starting computation for %d", i)
log.debug("Completed computation for %d", i)
log.debug("Remote client went away, processed %d items", i + 1)
return [b"work completed"]
if __name__ == "__main__":
format="%(asctime)-15s %(levelname)-8s %(name)s %(message)s",
Now start this process, and then run curl but hit Ctrl + C on curl a second or two after you start curl:
You should see something like the following:
2022-03-08 20:12:01,081 INFO waitress Serving on http://0.0.0.0:8080
2022-03-08 20:12:04,206 DEBUG __main__ Starting computation for 0
2022-03-08 20:12:06,211 DEBUG __main__ Completed computation for 0
2022-03-08 20:12:06,211 DEBUG __main__ Starting computation for 1
2022-03-08 20:12:08,215 DEBUG __main__ Completed computation for 1
2022-03-08 20:12:08,216 DEBUG __main__ Remote client went away, processed 2 items
2022-03-08 20:12:08,217 INFO waitress Client disconnected while serving /
An app developer who knows that the clients are always going to be directly connected, can add code similar to the above in their response code and do these checks manually during their computation, and if they raise an error, pyramid_tm will appropriate abort the transaction, and pyramid should run the exception view machinery (although that response will never make it back to the client, it should be possible to use it at that point to do any extra cleanup or whatnot though)
Hopefully Andrew Free this helps somewhat, in that it is possible, it’s just extra code you have to write and be aware of, it is not something that comes for free, and requires that you use waitress, and it requires that you set the `channel_request_lookhead` flag, and it requires that you know your clients are directly connected.
Bert JW Regeer
> To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-discuss/113341b5-529b-4bb8-b1e8-5a3d28ce028dn%40googlegroups.com