aio-libs / aio-libs/aiohttp

Websocket reading message loop raises low-level CanceledError when connection is closed unexpectedly

Abierto
#2,061 4 comentarios 2 reacciones 0 asignados Ver en GitHub
enhancement
Lenguaje dominante
Python
Estrellas
16.5k
Forks
2.4k
Merge medio
17 h 22 min
PR fusionados (30 d)
212

Descripción

## Actual behaviour

Reading message loop `async for msg in ws:` raises low-level `concurrent.futures._base.CancelledError` when connection is closed unexpectedly.

## Expected behaviour

Expected to get message with type `aiohtto.http_websocket.WSMsgType.ERROR`, or silently stop the loop, or at least `aiohtto.http_websocket.WebSocketError`.

## Steps to reproduce

Run the following two scripts `server.py` and `client.py`, then stop `client.py` by `Ctrl+C`.

### server.py
```python
import logging

from aiohttp import web

logger = logging.getLogger(__name__)

async def index(request):
ws = web.WebSocketResponse()
request.app['websockets'].add(ws)

try:
await ws.prepare(request)
logger.debug('Connected')
async for msg in ws:
logger.info('Received: %r', msg.data)
except Exception:
logger.exception('Error')
logger.debug('Disconnected')

request.app['websockets'].discard(ws)
return ws

async def on_shutdown(app):
for ws in app['websockets']:
await ws.close()
app['websockets'].clear()

def main():
logging.basicConfig(level=logging.DEBUG)

app = web.Application()
app['websockets'] = set()
app.router.add_get('/', index)
app.on_shutdown.append(on_shutdown)

web.run_app(app, host='127.0.0.1', port=9000)

if __name__ == '__main__':
main()
```
### client.py
```python
import asyncio

import aiohttp

async def communicate(loop):
async with aiohttp.ClientSession(loop=loop) as session:
async with session.ws_connect('http://127.0.0.1:9000') as ws:
while True:
await ws.send_str('Hello')
await asyncio.sleep(1, loop=loop)

def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(communicate(loop))

if __name__ == '__main__':
main()
```
### Log output of `server.py`
```
$ python server.py
DEBUG:asyncio:Using selector: EpollSelector
======== Running on http://127.0.0.1:9000 ========
(Press CTRL+C to quit)
DEBUG:__main__:Connected
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
ERROR:__main__:Error
Traceback (most recent call last):
File "server.py", line 16, in index
async for msg in ws:
File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/web_ws.py", line 343, in __anext__
msg = yield from self.receive()
File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/web_ws.py", line 273, in receive
msg = yield from self._reader.read()
File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/streams.py", line 627, in read
return (yield from super().read())
File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/streams.py", line 509, in read
yield from self._waiter
File "/usr/lib64/python3.5/asyncio/futures.py", line 380, in __iter__
yield self # This tells Task to wait for completion.
File "/usr/lib64/python3.5/asyncio/tasks.py", line 304, in _wakeup
future.result()
File "/usr/lib64/python3.5/asyncio/futures.py", line 285, in result
raise CancelledError
concurrent.futures._base.CancelledError
DEBUG:__main__:Disconnected
INFO:aiohttp.access:- - - [06/Jul/2017:11:41:25 +0000] "GET / HTTP/1.1" 101 0 "-" "Python/3.5 aiohttp/2.2.3"
```

## Your environment

OS: CentOS Linux 7
Linux kernel: 3.10.0-514.16.1.el7.x86_64
Python: 3.5.3
aiohttp: 2.2.3

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.