From 46efc0eafbef3dde7e945c933c429dfbd19d1f81 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 15 Feb 2019 09:31:54 -0800 Subject: [PATCH] Refactor http CachingStaticResource (#21062) * Simplify http.CachingStaticResource implementation * Sync up CachingStaticResource._handle() implementation from aiohttp * Ignore pylint duplicate-base warning * Try to disable pylint for http/static.py Caused by https://github.com/PyCQA/astroid/issues/633#issuecomment-463879288 * Remove pylint ignore * Ignore pylint duplicate-base warning --- homeassistant/components/http/__init__.py | 5 +-- homeassistant/components/http/static.py | 45 +++++++++-------------- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 10d6ae5148b..cdbbbe551ab 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -16,13 +16,12 @@ import homeassistant.util as hass_util from homeassistant.util import ssl as ssl_util from homeassistant.util.logging import HideSensitiveDataFilter -# Import as alias from .auth import setup_auth from .ban import setup_bans from .const import KEY_AUTHENTICATED, KEY_REAL_IP # noqa from .cors import setup_cors from .real_ip import setup_real_ip -from .static import CachingFileResponse, CachingStaticResource +from .static import CACHE_HEADERS, CachingStaticResource from .view import HomeAssistantView # noqa REQUIREMENTS = ['aiohttp_cors==0.7.0'] @@ -272,7 +271,7 @@ class HomeAssistantHTTP: if cache_headers: async def serve_file(request): """Serve file from disk.""" - return CachingFileResponse(path) + return web.FileResponse(path, headers=CACHE_HEADERS) else: async def serve_file(request): """Serve file from disk.""" diff --git a/homeassistant/components/http/static.py b/homeassistant/components/http/static.py index 54e72c88ff3..4fac9bf1ae9 100644 --- a/homeassistant/components/http/static.py +++ b/homeassistant/components/http/static.py @@ -1,18 +1,29 @@ """Static file handling for HTTP component.""" +from pathlib import Path + from aiohttp import hdrs from aiohttp.web import FileResponse -from aiohttp.web_exceptions import HTTPNotFound +from aiohttp.web_exceptions import HTTPNotFound, HTTPForbidden from aiohttp.web_urldispatcher import StaticResource -from yarl import URL + +CACHE_TIME = 31 * 86400 # = 1 month +CACHE_HEADERS = {hdrs.CACHE_CONTROL: "public, max-age={}".format(CACHE_TIME)} +# https://github.com/PyCQA/astroid/issues/633 +# pylint: disable=duplicate-bases class CachingStaticResource(StaticResource): """Static Resource handler that will add cache headers.""" async def _handle(self, request): - filename = URL(request.match_info['filename']).path + rel_url = request.match_info['filename'] try: - # PyLint is wrong about resolve not being a member. + filename = Path(rel_url) + if filename.anchor: + # rel_url is an absolute name like + # /static/\\machine_name\c$ or /static/D:\path + # where the static dir is totally different + raise HTTPForbidden() filepath = self._directory.joinpath(filename).resolve() if not self._follow_symlinks: filepath.relative_to(self._directory) @@ -24,30 +35,10 @@ class CachingStaticResource(StaticResource): request.app.logger.exception(error) raise HTTPNotFound() from error + # on opening a dir, load its contents if allowed if filepath.is_dir(): return await super()._handle(request) if filepath.is_file(): - return CachingFileResponse(filepath, chunk_size=self._chunk_size) + return FileResponse( + filepath, chunk_size=self._chunk_size, headers=CACHE_HEADERS) raise HTTPNotFound - - -# pylint: disable=too-many-ancestors -class CachingFileResponse(FileResponse): - """FileSender class that caches output if not in dev mode.""" - - def __init__(self, *args, **kwargs): - """Initialize the hass file sender.""" - super().__init__(*args, **kwargs) - - orig_sendfile = self._sendfile - - async def sendfile(request, fobj, count): - """Sendfile that includes a cache header.""" - cache_time = 31 * 86400 # = 1 month - self.headers[hdrs.CACHE_CONTROL] = "public, max-age={}".format( - cache_time) - - await orig_sendfile(request, fobj, count) - - # Overwriting like this because __init__ can change implementation. - self._sendfile = sendfile