From e5504b39ecbe61378732cd0150cac9e0d88e6da1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 30 Nov 2016 13:05:58 -0800 Subject: [PATCH] Close aiohttp responses (#4624) * Close aiohttp responses * Update generic.py --- homeassistant/components/camera/generic.py | 10 ++-- homeassistant/components/camera/mjpeg.py | 27 +++++----- homeassistant/components/camera/synology.py | 54 +++++++++++-------- .../components/media_player/__init__.py | 35 +++++++----- homeassistant/components/sensor/yr.py | 7 ++- homeassistant/components/switch/hook.py | 21 ++++++-- 6 files changed, 98 insertions(+), 56 deletions(-) diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index a73132282bf..74b4161f438 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -97,8 +97,7 @@ class GenericCamera(Camera): def fetch(): """Read image from a URL.""" try: - kwargs = {'timeout': 10, 'auth': self._auth} - response = requests.get(url, **kwargs) + response = requests.get(url, timeout=10, auth=self._auth) return response.content except requests.exceptions.RequestException as error: _LOGGER.error('Error getting camera image: %s', error) @@ -108,13 +107,13 @@ class GenericCamera(Camera): None, fetch) # async else: + response = None try: websession = async_get_clientsession(self.hass) with async_timeout.timeout(10, loop=self.hass.loop): response = yield from websession.get( url, auth=self._auth) - self._last_image = yield from response.read() - yield from response.release() + self._last_image = yield from response.read() except asyncio.TimeoutError: _LOGGER.error('Timeout getting camera image') return self._last_image @@ -122,6 +121,9 @@ class GenericCamera(Camera): aiohttp.errors.ClientDisconnectedError) as err: _LOGGER.error('Error getting new camera image: %s', err) return self._last_image + finally: + if response is not None: + self.hass.async_add_job(response.release()) self._last_url = url return self._last_image diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index d96ea4ab0a3..981ed9dbf49 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -103,29 +103,32 @@ class MjpegCamera(Camera): # connect to stream websession = async_get_clientsession(self.hass) + stream = None + response = None try: with async_timeout.timeout(10, loop=self.hass.loop): - stream = yield from websession.get( - self._mjpeg_url, - auth=self._auth - ) - except asyncio.TimeoutError: - raise HTTPGatewayTimeout() + stream = yield from websession.get(self._mjpeg_url, + auth=self._auth) - response = web.StreamResponse() - response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) + response = web.StreamResponse() + response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) - yield from response.prepare(request) + yield from response.prepare(request) - try: while True: data = yield from stream.content.read(102400) if not data: break response.write(data) + + except asyncio.TimeoutError: + raise HTTPGatewayTimeout() + finally: - self.hass.async_add_job(stream.release()) - yield from response.write_eof() + if stream is not None: + self.hass.async_add_job(stream.release()) + if response is not None: + yield from response.write_eof() @property def name(self): diff --git a/homeassistant/components/camera/synology.py b/homeassistant/components/camera/synology.py index 1db83ddf762..6d5b4546933 100644 --- a/homeassistant/components/camera/synology.py +++ b/homeassistant/components/camera/synology.py @@ -73,24 +73,27 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): 'version': '1', 'query': 'SYNO.' } + query_req = None try: with async_timeout.timeout(TIMEOUT, loop=hass.loop): query_req = yield from websession_init.get( syno_api_url, params=query_payload ) + + query_resp = yield from query_req.json() + auth_path = query_resp['data'][AUTH_API]['path'] + camera_api = query_resp['data'][CAMERA_API]['path'] + camera_path = query_resp['data'][CAMERA_API]['path'] + streaming_path = query_resp['data'][STREAMING_API]['path'] + except (asyncio.TimeoutError, aiohttp.errors.ClientError): _LOGGER.exception("Error on %s", syno_api_url) return False - query_resp = yield from query_req.json() - auth_path = query_resp['data'][AUTH_API]['path'] - camera_api = query_resp['data'][CAMERA_API]['path'] - camera_path = query_resp['data'][CAMERA_API]['path'] - streaming_path = query_resp['data'][STREAMING_API]['path'] - - # cleanup - yield from query_req.release() + finally: + if query_req is not None: + yield from query_req.release() # Authticate to NAS to get a session id syno_auth_url = SYNO_API_URL.format( @@ -166,20 +169,23 @@ def get_session_id(hass, websession, username, password, login_url): 'session': 'SurveillanceStation', 'format': 'sid' } + auth_req = None try: with async_timeout.timeout(TIMEOUT, loop=hass.loop): auth_req = yield from websession.get( login_url, params=auth_payload ) + auth_resp = yield from auth_req.json() + return auth_resp['data']['sid'] + except (asyncio.TimeoutError, aiohttp.errors.ClientError): _LOGGER.exception("Error on %s", login_url) return False - auth_resp = yield from auth_req.json() - yield from auth_req.release() - - return auth_resp['data']['sid'] + finally: + if auth_req is not None: + yield from auth_req.release() class SynologyCamera(Camera): @@ -247,30 +253,34 @@ class SynologyCamera(Camera): 'cameraId': self._camera_id, 'format': 'mjpeg' } + stream = None + response = None try: with async_timeout.timeout(TIMEOUT, loop=self.hass.loop): stream = yield from self._websession.get( streaming_url, params=streaming_payload ) - except (asyncio.TimeoutError, aiohttp.errors.ClientError): - _LOGGER.exception("Error on %s", streaming_url) - raise HTTPGatewayTimeout() + response = web.StreamResponse() + response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) - response = web.StreamResponse() - response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) + yield from response.prepare(request) - yield from response.prepare(request) - - try: while True: data = yield from stream.content.read(102400) if not data: break response.write(data) + + except (asyncio.TimeoutError, aiohttp.errors.ClientError): + _LOGGER.exception("Error on %s", streaming_url) + raise HTTPGatewayTimeout() + finally: - self.hass.async_add_job(stream.release()) - yield from response.write_eof() + if stream is not None: + self.hass.async_add_job(stream.release()) + if response is not None: + yield from response.write_eof() @property def name(self): diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index d0cacd47b75..fa2ecee4337 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -722,28 +722,35 @@ def _async_fetch_image(hass, url): return cache_images[url] content, content_type = (None, None) + websession = async_get_clientsession(hass) + response = None try: - websession = async_get_clientsession(hass) with async_timeout.timeout(10, loop=hass.loop): response = yield from websession.get(url) - if response.status == 200: - content = yield from response.read() - content_type = response.headers.get(CONTENT_TYPE_HEADER) - yield from response.release() + if response.status == 200: + content = yield from response.read() + content_type = response.headers.get(CONTENT_TYPE_HEADER) + except asyncio.TimeoutError: pass - if content: - cache_images[url] = (content, content_type) - cache_urls.append(url) + finally: + if response is not None: + yield from response.release() - while len(cache_urls) > cache_maxsize: - # remove oldest item from cache - oldest_url = cache_urls[0] - if oldest_url in cache_images: - del cache_images[oldest_url] + if not content: + return (None, None) - cache_urls = cache_urls[1:] + cache_images[url] = (content, content_type) + cache_urls.append(url) + + while len(cache_urls) > cache_maxsize: + # remove oldest item from cache + oldest_url = cache_urls[0] + if oldest_url in cache_images: + del cache_images[oldest_url] + + cache_urls = cache_urls[1:] return content, content_type diff --git a/homeassistant/components/sensor/yr.py b/homeassistant/components/sensor/yr.py index e3cc5186230..4eeb809b7cf 100644 --- a/homeassistant/components/sensor/yr.py +++ b/homeassistant/components/sensor/yr.py @@ -155,6 +155,7 @@ class YrData(object): nxt) if self._nextrun is None or dt_util.utcnow() >= self._nextrun: + resp = None try: websession = async_get_clientsession(self.hass) with async_timeout.timeout(10, loop=self.hass.loop): @@ -163,12 +164,16 @@ class YrData(object): try_again('{} returned {}'.format(self._url, resp.status)) return text = yield from resp.text() - self.hass.async_add_job(resp.release()) + except (asyncio.TimeoutError, aiohttp.errors.ClientError, aiohttp.errors.ClientDisconnectedError) as err: try_again(err) return + finally: + if resp is not None: + self.hass.async_add_job(resp.release()) + try: import xmltodict self.data = xmltodict.parse(text)['weatherdata'] diff --git a/homeassistant/components/switch/hook.py b/homeassistant/components/switch/hook.py index 29fe8372fab..eba64c6aeb1 100644 --- a/homeassistant/components/switch/hook.py +++ b/homeassistant/components/switch/hook.py @@ -34,6 +34,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): password = config.get(CONF_PASSWORD) websession = async_get_clientsession(hass) + response = None try: with async_timeout.timeout(TIMEOUT, loop=hass.loop): response = yield from websession.post( @@ -41,12 +42,15 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): data={ 'username': username, 'password': password}) - data = yield from response.json() + data = yield from response.json() except (asyncio.TimeoutError, aiohttp.errors.ClientError, aiohttp.errors.ClientDisconnectedError) as error: _LOGGER.error("Failed authentication API call: %s", error) return False + finally: + if response is not None: + yield from response.close() try: token = data['data']['token'] @@ -54,17 +58,21 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.error("No token. Check username and password") return False + response = None try: with async_timeout.timeout(TIMEOUT, loop=hass.loop): response = yield from websession.get( '{}{}'.format(HOOK_ENDPOINT, 'device'), params={"token": data['data']['token']}) - data = yield from response.json() + data = yield from response.json() except (asyncio.TimeoutError, aiohttp.errors.ClientError, aiohttp.errors.ClientDisconnectedError) as error: _LOGGER.error("Failed getting devices: %s", error) return False + finally: + if response is not None: + yield from response.close() yield from async_add_devices( HookSmartHome( @@ -102,18 +110,25 @@ class HookSmartHome(SwitchDevice): @asyncio.coroutine def _send(self, url): """Send the url to the Hook API.""" + response = None try: _LOGGER.debug("Sending: %s", url) websession = async_get_clientsession(self.hass) with async_timeout.timeout(TIMEOUT, loop=self.hass.loop): response = yield from websession.get( url, params={"token": self._token}) - data = yield from response.json() + data = yield from response.json() + except (asyncio.TimeoutError, aiohttp.errors.ClientError, aiohttp.errors.ClientDisconnectedError) as error: _LOGGER.error("Failed setting state: %s", error) return False + + finally: + if response is not None: + yield from response.close() + _LOGGER.debug("Got: %s", data) return data['return_value'] == '1'