From a9a3e24bde6363dec190b9e9c6907a6c8251a968 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 6 Nov 2017 03:42:31 +0100 Subject: [PATCH] Update aiohttp to 2.3.1 (#10139) * Update aiohttp to 2.3.1 * set timeout 10sec * fix freeze with new middleware handling * Convert middleware auth * Convert mittleware ipban * convert middleware static * fix lint * Update ban.py * Update auth.py * fix lint * Fix tests --- homeassistant/components/http/__init__.py | 10 ++-- homeassistant/components/http/auth.py | 63 ++++++++++------------- homeassistant/components/http/ban.py | 40 +++++++------- homeassistant/components/http/static.py | 26 ++++------ homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- setup.py | 2 +- tests/components/http/test_init.py | 14 +---- 8 files changed, 67 insertions(+), 92 deletions(-) diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 5dda8f1825d..659fd026bb8 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -182,8 +182,6 @@ class HomeAssistantWSGI(object): use_x_forwarded_for, trusted_networks, login_threshold, is_ban_enabled): """Initialize the WSGI Home Assistant server.""" - import aiohttp_cors - middlewares = [auth_middleware, staticresource_middleware] if is_ban_enabled: @@ -206,6 +204,8 @@ class HomeAssistantWSGI(object): self.server = None if cors_origins: + import aiohttp_cors + self.cors = aiohttp_cors.setup(self.app, defaults={ host: aiohttp_cors.ResourceOptions( allow_headers=ALLOWED_CORS_HEADERS, @@ -335,7 +335,9 @@ class HomeAssistantWSGI(object): _LOGGER.error("Failed to create HTTP server at port %d: %s", self.server_port, error) - self.app._frozen = False # pylint: disable=protected-access + # pylint: disable=protected-access + self.app._middlewares = tuple(self.app._prepare_middleware()) + self.app._frozen = False @asyncio.coroutine def stop(self): @@ -345,7 +347,7 @@ class HomeAssistantWSGI(object): yield from self.server.wait_closed() yield from self.app.shutdown() if self._handler: - yield from self._handler.finish_connections(60.0) + yield from self._handler.shutdown(10) yield from self.app.cleanup() diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 4b971c883d3..ce5bfca3ac1 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -5,6 +5,7 @@ import hmac import logging from aiohttp import hdrs +from aiohttp.web import middleware from homeassistant.const import HTTP_HEADER_HA_AUTH from .util import get_real_ip @@ -15,47 +16,37 @@ DATA_API_PASSWORD = 'api_password' _LOGGER = logging.getLogger(__name__) +@middleware @asyncio.coroutine -def auth_middleware(app, handler): +def auth_middleware(request, handler): """Authenticate as middleware.""" # If no password set, just always set authenticated=True - if app['hass'].http.api_password is None: - @asyncio.coroutine - def no_auth_middleware_handler(request): - """Auth middleware to approve all requests.""" - request[KEY_AUTHENTICATED] = True - return handler(request) - - return no_auth_middleware_handler - - @asyncio.coroutine - def auth_middleware_handler(request): - """Auth middleware to check authentication.""" - # Auth code verbose on purpose - authenticated = False - - if (HTTP_HEADER_HA_AUTH in request.headers and - validate_password( - request, request.headers[HTTP_HEADER_HA_AUTH])): - # A valid auth header has been set - authenticated = True - - elif (DATA_API_PASSWORD in request.query and - validate_password(request, request.query[DATA_API_PASSWORD])): - authenticated = True - - elif (hdrs.AUTHORIZATION in request.headers and - validate_authorization_header(request)): - authenticated = True - - elif is_trusted_ip(request): - authenticated = True - - request[KEY_AUTHENTICATED] = authenticated - + if request.app['hass'].http.api_password is None: + request[KEY_AUTHENTICATED] = True return handler(request) - return auth_middleware_handler + # Check authentication + authenticated = False + + if (HTTP_HEADER_HA_AUTH in request.headers and + validate_password( + request, request.headers[HTTP_HEADER_HA_AUTH])): + # A valid auth header has been set + authenticated = True + + elif (DATA_API_PASSWORD in request.query and + validate_password(request, request.query[DATA_API_PASSWORD])): + authenticated = True + + elif (hdrs.AUTHORIZATION in request.headers and + validate_authorization_header(request)): + authenticated = True + + elif is_trusted_ip(request): + authenticated = True + + request[KEY_AUTHENTICATED] = authenticated + return handler(request) def is_trusted_ip(request): diff --git a/homeassistant/components/http/ban.py b/homeassistant/components/http/ban.py index aa01ccde8d7..f636ad80c36 100644 --- a/homeassistant/components/http/ban.py +++ b/homeassistant/components/http/ban.py @@ -6,6 +6,7 @@ from ipaddress import ip_address import logging import os +from aiohttp.web import middleware from aiohttp.web_exceptions import HTTPForbidden, HTTPUnauthorized import voluptuous as vol @@ -32,35 +33,32 @@ SCHEMA_IP_BAN_ENTRY = vol.Schema({ }) +@middleware @asyncio.coroutine -def ban_middleware(app, handler): +def ban_middleware(request, handler): """IP Ban middleware.""" - if not app[KEY_BANS_ENABLED]: - return handler + if not request.app[KEY_BANS_ENABLED]: + return (yield from handler(request)) - if KEY_BANNED_IPS not in app: - hass = app['hass'] - app[KEY_BANNED_IPS] = yield from hass.async_add_job( + if KEY_BANNED_IPS not in request.app: + hass = request.app['hass'] + request.app[KEY_BANNED_IPS] = yield from hass.async_add_job( load_ip_bans_config, hass.config.path(IP_BANS_FILE)) - @asyncio.coroutine - def ban_middleware_handler(request): - """Verify if IP is not banned.""" - ip_address_ = get_real_ip(request) + # Verify if IP is not banned + ip_address_ = get_real_ip(request) - is_banned = any(ip_ban.ip_address == ip_address_ - for ip_ban in request.app[KEY_BANNED_IPS]) + is_banned = any(ip_ban.ip_address == ip_address_ + for ip_ban in request.app[KEY_BANNED_IPS]) - if is_banned: - raise HTTPForbidden() + if is_banned: + raise HTTPForbidden() - try: - return (yield from handler(request)) - except HTTPUnauthorized: - yield from process_wrong_login(request) - raise - - return ban_middleware_handler + try: + return (yield from handler(request)) + except HTTPUnauthorized: + yield from process_wrong_login(request) + raise @asyncio.coroutine diff --git a/homeassistant/components/http/static.py b/homeassistant/components/http/static.py index f991a4ee0fc..c2576358f59 100644 --- a/homeassistant/components/http/static.py +++ b/homeassistant/components/http/static.py @@ -3,7 +3,7 @@ import asyncio import re from aiohttp import hdrs -from aiohttp.web import FileResponse +from aiohttp.web import FileResponse, middleware from aiohttp.web_exceptions import HTTPNotFound from aiohttp.web_urldispatcher import StaticResource from yarl import unquote @@ -61,21 +61,17 @@ class CachingFileResponse(FileResponse): self._sendfile = sendfile +@middleware @asyncio.coroutine -def staticresource_middleware(app, handler): +def staticresource_middleware(request, handler): """Middleware to strip out fingerprint from fingerprinted assets.""" - @asyncio.coroutine - def static_middleware_handler(request): - """Strip out fingerprints from resource names.""" - if not request.path.startswith('/static/'): - return handler(request) - - fingerprinted = _FINGERPRINT.match(request.match_info['filename']) - - if fingerprinted: - request.match_info['filename'] = \ - '{}.{}'.format(*fingerprinted.groups()) - + if not request.path.startswith('/static/'): return handler(request) - return static_middleware_handler + fingerprinted = _FINGERPRINT.match(request.match_info['filename']) + + if fingerprinted: + request.match_info['filename'] = \ + '{}.{}'.format(*fingerprinted.groups()) + + return handler(request) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 7da87160684..00df81290e5 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -5,7 +5,7 @@ pip>=8.0.3 jinja2>=2.9.6 voluptuous==0.10.5 typing>=3,<4 -aiohttp==2.2.5 +aiohttp==2.3.1 async_timeout==2.0.0 chardet==3.0.4 astral==1.4 diff --git a/requirements_all.txt b/requirements_all.txt index 974191eb8ba..87025dd7e7a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -6,7 +6,7 @@ pip>=8.0.3 jinja2>=2.9.6 voluptuous==0.10.5 typing>=3,<4 -aiohttp==2.2.5 +aiohttp==2.3.1 async_timeout==2.0.0 chardet==3.0.4 astral==1.4 diff --git a/setup.py b/setup.py index 2bb78877f6d..25c38af27fb 100755 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ REQUIRES = [ 'jinja2>=2.9.6', 'voluptuous==0.10.5', 'typing>=3,<4', - 'aiohttp==2.2.5', + 'aiohttp==2.3.1', 'async_timeout==2.0.0', 'chardet==3.0.4', 'astral==1.4', diff --git a/tests/components/http/test_init.py b/tests/components/http/test_init.py index f547306ff82..4ff87efd137 100644 --- a/tests/components/http/test_init.py +++ b/tests/components/http/test_init.py @@ -143,22 +143,10 @@ def test_registering_view_while_running(hass, test_client): } ) - yield from setup.async_setup_component(hass, 'api') - yield from hass.async_start() - - yield from hass.async_block_till_done() - + # This raises a RuntimeError if app is frozen hass.http.register_view(TestView) - client = yield from test_client(hass.http.app) - - resp = yield from client.get('/hello') - assert resp.status == 200 - - text = yield from resp.text() - assert text == 'hello' - @asyncio.coroutine def test_api_base_url_with_domain(hass):