falconry / falcon

The no-magic web data plane API and microservices framework for Python developers, with a focus on reliability, correctness, and performance at scale.
https://falcon.readthedocs.io/en/stable/
Apache License 2.0
9.49k stars 933 forks source link

How to handle client disconnection? #1483

Open vikramarsid opened 5 years ago

vikramarsid commented 5 years ago

Hello,

Using Nginx + Gunicorn(gthreads) + Falcon. How should I detect if a client has disconnected from the API server? Is there a way to handle client-side disconnection to cleanup upstream database jobs?

Thanks for the help.

Cheers.

CC: @vytas7

@vytas7 says: adding a checklist.

kgriffs commented 5 years ago

Hi @vikramarsid, you could see if gunicorn triggers any of its server hooks in that case, although that would depend on NGINX prematurely disconnecting from gunicorn rather than eating the response, which AFAICT is what happens by default (see also: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_client_abort).

@jmvrbanac has an example of using hooks here: https://github.com/jmvrbanac/falcon-example/blob/master/example/app.py

vytas7 commented 3 years ago

See also #1800

vytas7 commented 3 years ago

I believe we should add some documentation to our FAQ (or elsewhere) how to handle this in popular WSGI servers (at least Gunicorn and uWSGI).

As per the discussion above, it's also worth adding a note that this only applies if Nginx is configured to stream requests to the application server. While this is not a common setting for JSON APIs (typically set to buffer + small max body size), it is essential for file uploads, proxying large requests etc.

Uvicorn (and ASGI in general) handles this more explicitly out of the box, but we could check if it deserves a mention too.

vytas7 commented 3 years ago

FWIW, this is what uWSGI seems to be doing:

2020-12-14 18:28:00 [FALCON] [ERROR] POST / => Traceback (most recent call last):
  File "falcon/app.py", line 331, in falcon.app.App.__call__
  File "test.py", line 10, in on_post
    chunk = req.bounded_stream.read(4096)
  File "falcon/request_helpers.py", line 264, in falcon.request_helpers.BoundedStream.read
  File "falcon/request_helpers.py", line 238, in falcon.request_helpers.BoundedStream._read
OSError: error during read(4096) on wsgi.input

And Gunicorn simply returns less data when expected, when doing req.stream.read() (or bounded_stream.read()). Which is ugly IMHO!

I tried skimming once again through PEP 3333, and the spec is woefully silent on these issues... So I guess there's no right and wrong.

metallerok commented 2 years ago

Hi @vikramarsid, you could see if gunicorn triggers any of its server hooks in that case, although that would depend on NGINX prematurely disconnecting from gunicorn rather than eating the response, which AFAICT is what happens by default (see also: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_client_abort).

@jmvrbanac has an example of using hooks here: https://github.com/jmvrbanac/falcon-example/blob/master/example/app.py

This example gunicorn hooks doesn't work