question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

HTTPStream.app_send is missing http.disconnect handling ?

See original GitHub issue

tl;dr

I don’t see the code responsible for handling http.disconnect in http stream: https://github.com/pgjones/hypercorn/blob/8cef567301a5a3f4363d1c2cad76008e58add8aa/src/hypercorn/protocol/http_stream.py#L117-L177

It looks like an omission to me, especially given the websocket stream has code for handling it corresponding websocket.close

Long versions

I’ve written a simple middleware to simulate the asgi app being not responsive:

class AsgiOfflineMiddleware:

    def __init__(self, asgi_app):
        self.asgi_app = asgi_app
        self._offline_ports = set()
        self._offline_watchdogs_parking = trio.lowlevel.ParkingLot()

    @contextmanager
    def offline(self, port: int):
        assert port not in self._offline_ports
        self._offline_ports.add(port)
        self._offline_watchdogs_parking.unpark_all()
        try:
            yield
        finally:
            self._offline_ports.remove(port)
            # No need to unpark given our port has just been re-authorized !

    async def __call__(self, scope, receive, send):
        # Special case for lifespan given it corresponds to server init and not
        # to an incoming client connection
        if scope["type"] == "lifespan":
            return await self.asgi_app(scope, receive, send)

        port = scope["server"][1]

        pretend_to_be_offline = False

        if port in self._offline_ports:
            pretend_to_be_offline = True

        else:

            async def _offline_watchdog(cancel_scope):
                nonlocal pretend_to_be_offline
                while True:
                    await self._offline_watchdogs_parking.park()
                    if port in self._offline_ports:
                        break
                pretend_to_be_offline = True
                cancel_scope.cancel()

            async with trio.open_nursery() as nursery:
                nursery.start_soon(_offline_watchdog, nursery.cancel_scope)
                await self.asgi_app(scope, receive, send)
                nursery.cancel_scope.cancel()

        if pretend_to_be_offline:
            if scope["type"] == "http":
                await send({"type": "http.disconnect"})
            elif scope["type"] == "websocket":
                await send({"type": "websocket.close"})
            else:
                assert False, scope

However it produce errors when trying to close an HTTP connection:

ERROR    hypercorn.error:logging.py:100 Error in ASGI Framework
Traceback (most recent call last):
  File "C:\Users\gbleu\source\repos\parsec-cloud\venv39\lib\site-packages\hypercorn\trio\task_group.py", line 21, in _handle
    await invoke_asgi(app, scope, receive, send)
  File "C:\Users\gbleu\source\repos\parsec-cloud\venv39\lib\site-packages\hypercorn\utils.py", line 247, in invoke_asgi
    await app(scope, receive, send)
  File "C:\Users\gbleu\source\repos\parsec-cloud\parsec-cloud\tests\common\backend.py", line 427, in __call__
    await send({"type": "http.disconnect"})
  File "C:\Users\gbleu\source\repos\parsec-cloud\venv39\lib\site-packages\hypercorn\protocol\http_stream.py", line 177, in app_send
    raise UnexpectedMessageError(self.state, message["type"])
hypercorn.utils.UnexpectedMessageError: Unexpected message type, http.disconnect given the state ASGIHTTPState.REQUEST

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
synodrivercommented, Jun 23, 2022

Hi. According to the spec, http.disconnect is an asgi receive event instead of send event, so you can not close the http connection in that way. Return 500 error would be ok.

0reactions
touilleMancommented, Jun 24, 2022

thanks a lot @synodriver 😃 🎉

so it’s websocket.close that is the event the application send, and websocket.disconnect (and http.disconnect) that are send to the application !

Read more comments on GitHub >

github_iconTop Results From Across the Web

AsyncHttpConsumer regression and disconnect handling #1249
Our library implements handle , which returns quickly and spawns a subtask to handle the ongoing stream. At the time the code was...
Read more >
ASGI http.disconnect not handled on requests with body.
http.disconnect is designed for long-polling — so we imagine a client opening a request, with a request body, and then disconnecting before the...
Read more >
C++ Tutorial: Sockets - Server & Client - 2020 - BogoToBogo
A client app send a request to a server app. The server app returns a ... The most common types are stream sockets...
Read more >
End User Authorization Flows - Zoom App Marketplace
If a user is unable to install an app from the Marketplace, the user can view which missing permissions need to be granted...
Read more >
Execute some action when client disconnects from hot stream
The exception is related to HTTP connection handling between the browser ant the controller (simply put). It can be handled in controller's ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found