aio-libs / aio-libs/aiohttp

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

未关闭
#2,061 4 条评论 2 个 reaction 已指派 0 人 在 GitHub 查看
enhancement
主要语言
Python
星标
16.5k
派生
2.4k
平均合并
17 小时 22 分钟
30 天内合并 PR
212

描述

## 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

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。