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:

https://github.com/Pylons/waitress/pull/310

I don’t think there’s good example of how to use it in the documentation, but 
here’s a quick and dirty example:

import logging
import time

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)
        time.sleep(2)
        log.debug("Completed computation for %d", i)

        if check():
            log.debug("Remote client went away, processed %d items", i + 1)
            break

    start_response(
        "200 OK",
        [
            ("Content-Type", "application/octet-stream"),
        ],
    )

    return [b"work completed"]


if __name__ == "__main__":
    import waitress

    logging.basicConfig(
        format="%(asctime)-15s %(levelname)-8s %(name)s %(message)s",
        level=logging.DEBUG,
    )

    waitress.serve(application, channel_request_lookahead=5)

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:

python client_disconnected.py
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.

Caveat emptor.

Thanks,
Bert JW Regeer

> On Mar 7, 2022, at 13:05, Jonathan Vanasco <[email protected]> wrote:
> 
> Just to clarify the above comment, this concept isn't really possible with 
> any internet technology in general, not just Pyramid.
> On Thursday, February 17, 2022 at 8:35:25 PM UTC-5 Bert JW Regeer wrote:
> No, this is not possible *.
> 
> * Except under some very narrow circumstances, but none that are easy to use 
> or directly supported in Pyramid
> 
>> On Feb 17, 2022, at 13:12, Andrew Free <[email protected]> wrote:
>> 
>> Is there a way to subscribe to any events of a dropped/lost connection?
>> 
>> For example, if the user closed the browser window in the middle of a 
>> request.  I am using pyramid_tm and having a hard time finding a method for 
>> this. I just want to run some code based on the request object state in the 
>> event that the response doesn't make it back to the client and the 
>> transaction does not complete. I've looked into the exception_view_config 
>> and this doesn't appear to help. Would a tween be the best way to handle 
>> this?
>> Thanks.
>> 
>> 
>> --
>> You received this message because you are subscribed to the Google Groups 
>> "pylons-discuss" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to [email protected].
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/pylons-discuss/cab03e28-c370-4dcb-917a-7b5d36e7a86fn%40googlegroups.com.
> 
> 
> --
> You received this message because you are subscribed to the Google Groups 
> "pylons-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/pylons-discuss/113341b5-529b-4bb8-b1e8-5a3d28ce028dn%40googlegroups.com.

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pylons-discuss/3B2C7EFF-A9A1-40CE-9B0A-2CB01B6EC5A0%400x58.com.

Attachment: signature.asc
Description: Message signed with OpenPGP

Reply via email to