aiohttp
aiohttp copied to clipboard
Cannot properly close ws connection in one-sided way.
Long story short
Cannot properly close ws connection from client side. Full description as code bellow
Expected behaviour
Good patter to close ws connection from the client side in one-sided way.
Actual behaviour
Need to use protected attribute to close ws connection
Steps to reproduce
server.py
import asyncio
import aiohttp.web
async def websocket_handler(request):
print('Websocket connection starting')
ws = aiohttp.web.WebSocketResponse()
await ws.prepare(request)
print('Websocket connection ready')
await ws.prepare(request)
while True:
if request.transport.is_closing():
break
await ws.send_json({'mesasge': 'test'})
await asyncio.sleep(1)
return ws
def main():
loop = asyncio.get_event_loop()
app = aiohttp.web.Application(loop=loop)
app.router.add_route('GET', '/ws', websocket_handler)
aiohttp.web.run_app(app, host='0.0.0.0', port=8080)
if __name__ == '__main__':
main()
client.py
import asyncio
import aiohttp
URL = f'http://0.0.0.0:8080/ws'
async def main():
session = aiohttp.ClientSession()
count = 0
max_query = 2
async with session.ws_connect(URL) as ws:
while True:
msg = await ws.receive_json()
print(f"get from the server {msg}")
count += 1
if count > max_query:
proto = ws._writer.protocol
proto.transport.close()
break
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
python server.py
python client.py
Your environment
macOS Mojave 10.14 python3.6.1 aiohttp==3.5.0a1
GitMate.io thinks the contributor most likely able to help you is @asvetlov.
Possibly related issues are https://github.com/aio-libs/aiohttp/issues/1814 (Close websocket connection when pong not received), https://github.com/aio-libs/aiohttp/issues/3052 (SSL with closed connections), https://github.com/aio-libs/aiohttp/issues/523 (Not all connections are closed (pending: 0)), https://github.com/aio-libs/aiohttp/issues/253 (Connection not closed when request is cancelled), and https://github.com/aio-libs/aiohttp/issues/15 (No way to close a response on a timeout).
You need to run a separate task to listen the WebSocket and handle the closing handshake.
server.py:
import asyncio
import aiohttp.web
async def websocket_handler(request):
print('Websocket connection starting')
ws = aiohttp.web.WebSocketResponse(heartbeat=1)
await ws.prepare(request)
print('Websocket connection ready')
async def _ws_listener() -> None:
async for msg in ws: # handles ping-pong and closing internally
# What to do with unexpected messages?
# It depends on you.
pass
reader = asyncio.create_task(_ws_listener())
while not ws.closed:
await ws.send_json({'mesasge': 'test'})
await asyncio.sleep(1)
await reader
return ws
def main():
app = aiohttp.web.Application()
app.router.add_route('GET', '/ws', websocket_handler)
aiohttp.web.run_app(app, host='0.0.0.0', port=8080)
if __name__ == '__main__':
main()
client.py:
import asyncio
import aiohttp
URL = f'http://0.0.0.0:8080/ws'
async def main():
session = aiohttp.ClientSession()
async with session:
count = 0
max_query = 2
async with session.ws_connect(URL) as ws:
while True:
msg = await ws.receive_json()
print(f"get from the server {msg}")
count += 1
if count > max_query:
break
if __name__ == '__main__':
asyncio.run(main())