Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Websocket does not register disconnected Safari browser #2025

Closed
jhrmnn opened this issue Jun 27, 2017 · 11 comments
Closed

Websocket does not register disconnected Safari browser #2025

jhrmnn opened this issue Jun 27, 2017 · 11 comments
Assignees
Labels
Milestone

Comments

@jhrmnn
Copy link

jhrmnn commented Jun 27, 2017

With Websockets, when Safari is reloaded or tab closed, the websocket server registers a closed connection. This is not the case with Aiohttp. But Aiohttp correctly registers the disconnection with Chrome. I don't know anything about the websocket protocol internals, but I guess there is a difference in what both browsers send on tab close/reload. In any case, Websockets somehow registers that event in both browsers, but Aiohttp doesn't.

Take the following app. When going to http://localhost:8080/websockets and reloading the site several times, the app emits alternating "Got client", "Lost client" with both Safari and Chrome. Now go to http://localhost:8080/aiohttp: only Chrome triggers lost connections.

import asyncio
from aiohttp import web
import websockets

asyncio.get_event_loop().set_debug(True)

site = """\
<!doctype html>
<script>
const ws = new WebSocket(`ws://{href}`);
</script>
<body></body>"""


async def websockets_handler(ws, path):
    print('Got client')
    while True:
        try:
            print(await ws.recv())
        except websockets.ConnectionClosed as e:
            break
    print('Lost client')


async def aiohttp_ws_handler(request):
    ws = web.WebSocketResponse(autoclose=False)
    await ws.prepare(request)
    print('Got client')
    async for msg in ws:
        print(msg)
    print('Lost client')
    return ws


async def site_handler(request):
    if request.path == '/aiohttp':
        href = 'localhost:8080/ws'
    elif request.path == '/websockets':
        href = 'localhost:6060'
    return web.Response(text=site.format(href=href), content_type='text/html')

asyncio.ensure_future(websockets.serve(websockets_handler, 'localhost', 6060))

app = web.Application()
app.router.add_get('/aiohttp', site_handler)
app.router.add_get('/websockets', site_handler)
app.router.add_get('/ws', aiohttp_ws_handler)
web.run_app(app, host='localhost', port=8080)
@asvetlov
Copy link
Member

Sorry, I have no MacOSX box -- Linux only.
Could somebody take a look on the issue?

@jhrmnn
Copy link
Author

jhrmnn commented Jun 27, 2017

Btw, Firefox behaves the same way as Chrome: Both aiohttp and websockets register the disconnect. So yes, this will need someone with macOS.

@fafhrd91
Copy link
Member

I'll check this

@fafhrd91 fafhrd91 self-assigned this Jun 27, 2017
@fafhrd91 fafhrd91 added this to the 2.3.0 milestone Jun 27, 2017
@asvetlov asvetlov modified the milestones: 2.3, 3.0 Oct 17, 2017
@asvetlov asvetlov modified the milestones: 3.0, 3.1 Feb 12, 2018
@asvetlov asvetlov modified the milestones: 3.1, 3.2 Mar 22, 2018
@asvetlov asvetlov modified the milestones: 3.2, 3.3 May 7, 2018
@s-kostyuk
Copy link

s-kostyuk commented May 7, 2018

@Azag0 I think it's related with #2368 and #2309.

Looks like Firefox and Chrome send a special CLOSE frame just before the tab will be reloaded and the connection will be closed. So, in a case of those two browsers, connection is closed gracefully, by a server, in response to the CLOSE frame.

Safari, on the other side, just closes connection abruptly, without sending a CLOSE frame. And thus for some reason aiohttp keeps listening for new packets, even if the client is gone and underlying connection was closed.

Again, it's all just assumptions. I'm a Linux user too and can't test behavior of Safari. But you can test it yourself - just enable asyncio debugging mode and watch for the following message: Ignored premature client disconnection (#2309 (comment)) in logs. If you found that message in logs - congratulations! aiohttp still can't handle such disconnections yet

UPD: But there is a workaround: #2283 (comment)

UPD2: Also attempts to receive the next frame on a closed connection raise CancelledErrors: #2061. So we can just catch CancelledError exceptions to detect disconnections.

UPD3: Possibly, heartbeats (periodic pings) are also mandatory for disconnection detection to work

@asvetlov
Copy link
Member

asvetlov commented May 8, 2018

See #2675 for cancellation logic changing.

I'm closing the issue because of long inactivity period, I even not sure that the problem exists.
Please feel free to open a new one with detailed info if needed.

@asvetlov asvetlov closed this as completed May 8, 2018
@jhrmnn
Copy link
Author

jhrmnn commented May 22, 2018

I have slightly updated the code snippet to work with the current websockets version.

@s-kostyuk I get no messages about client disconnection when the asyncio debug mode is enabled.

This is really a non-issue, I guess. If the aiohttp websocket server just responds to requests from the client, it will never know that the client had died, but that doesn't really matter. If it sends messages to the client without any prompt, it will find out from the thrown exception.

@s-kostyuk
Copy link

@Azag0, so, the only thing I can recommend is to enable heatbeat, handle exceptions and implement some acknowledge mechanisms if it's important to ensure that a message was delivered to client. Otherwise, if client lost connection to the server (WiFi gone down, for example), the server will not notice half-closed connection and will send messages as usual... to nowhere

@james4388
Copy link
Contributor

Problem still exist with 3.3.2 :(

@james4388
Copy link
Contributor

james4388 commented Jul 14, 2018

@s-kostyuk Also try heartbeat and timeout and auto close but it's not working
Exception handler is not working
only see WARNING:asyncio:socket.send() raised exception.
Also check for socket.closed it say still open

@s-kostyuk
Copy link

s-kostyuk commented Aug 25, 2018

@james4388 I'm starting to thing that the whole situation with disconnection detection will stay mysterious until someone will get a Wireshark, a descent debugger and will finally debug/fix WebSocket implementation. And considering to try Sanic for future projects, because Sanic uses aaugustin/websockets as WS implementation

@lock
Copy link

lock bot commented Oct 28, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a [new issue] for related bugs.
If you feel like there's important points made in this discussion, please include those exceprts into that [new issue].
[new issue]: https://github.com/aio-libs/aiohttp/issues/new

@lock lock bot added the outdated label Oct 28, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants