From 8c97bccaaa3950fbe2701d213ab51782f62daec5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 31 Mar 2017 02:55:22 -0700 Subject: [PATCH] Handle aiohttp task cancellation better (#6862) --- homeassistant/components/camera/__init__.py | 21 ++++++++++----------- homeassistant/helpers/aiohttp_client.py | 13 +++++++++++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index d3c6699f77c8e..388a9fce39bbb 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -7,6 +7,7 @@ """ import asyncio import collections +from contextlib import suppress from datetime import timedelta import logging import hashlib @@ -167,7 +168,7 @@ def write(img_bytes): if not img_bytes: break - if img_bytes is not None and img_bytes != last_image: + if img_bytes and img_bytes != last_image: write(img_bytes) # Chrome seems to always ignore first picture, @@ -180,8 +181,8 @@ def write(img_bytes): yield from asyncio.sleep(.5) - except (asyncio.CancelledError, ConnectionResetError): - _LOGGER.debug("Close stream by frontend.") + except asyncio.CancelledError: + _LOGGER.debug("Stream closed by frontend.") response = None finally: @@ -263,16 +264,14 @@ class CameraImageView(CameraView): @asyncio.coroutine def handle(self, request, camera): """Serve camera image.""" - try: - image = yield from camera.async_camera_image() + with suppress(asyncio.CancelledError, asyncio.TimeoutError): + with async_timeout.timeout(10, loop=request.app['hass'].loop): + image = yield from camera.async_camera_image() - if image is None: - return web.Response(status=500) + if image: + return web.Response(body=image) - return web.Response(body=image) - - except asyncio.CancelledError: - _LOGGER.debug("Close stream by frontend.") + return web.Response(status=500) class CameraMjpegStream(CameraView): diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 8dc64105e8b52..9aac258684f85 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -78,10 +78,16 @@ def async_aiohttp_proxy_web(hass, request, web_coro, buffer_size=102400, with async_timeout.timeout(timeout, loop=hass.loop): req = yield from web_coro + except asyncio.CancelledError: + # The user cancelled the request + return + except asyncio.TimeoutError as err: + # Timeout trying to start the web request raise HTTPGatewayTimeout() from err except aiohttp.ClientError as err: + # Something went wrong with the connection raise HTTPBadGateway() from err yield from async_aiohttp_proxy_stream(hass, request, req.content, @@ -108,9 +114,12 @@ def async_aiohttp_proxy_stream(hass, request, stream, content_type, response.write(data) except (asyncio.TimeoutError, aiohttp.ClientError): - pass + # Something went wrong fetching data, close connection gracefully + yield from response.write_eof() - yield from response.write_eof() + except asyncio.CancelledError: + # The user closed the connection + pass @callback