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
This commit is contained in:
Jason Hu 2019-02-15 09:31:54 -08:00 committed by Paulus Schoutsen
parent 383813bfe6
commit 46efc0eafb
2 changed files with 20 additions and 30 deletions

View File

@ -16,13 +16,12 @@ import homeassistant.util as hass_util
from homeassistant.util import ssl as ssl_util from homeassistant.util import ssl as ssl_util
from homeassistant.util.logging import HideSensitiveDataFilter from homeassistant.util.logging import HideSensitiveDataFilter
# Import as alias
from .auth import setup_auth from .auth import setup_auth
from .ban import setup_bans from .ban import setup_bans
from .const import KEY_AUTHENTICATED, KEY_REAL_IP # noqa from .const import KEY_AUTHENTICATED, KEY_REAL_IP # noqa
from .cors import setup_cors from .cors import setup_cors
from .real_ip import setup_real_ip from .real_ip import setup_real_ip
from .static import CachingFileResponse, CachingStaticResource from .static import CACHE_HEADERS, CachingStaticResource
from .view import HomeAssistantView # noqa from .view import HomeAssistantView # noqa
REQUIREMENTS = ['aiohttp_cors==0.7.0'] REQUIREMENTS = ['aiohttp_cors==0.7.0']
@ -272,7 +271,7 @@ class HomeAssistantHTTP:
if cache_headers: if cache_headers:
async def serve_file(request): async def serve_file(request):
"""Serve file from disk.""" """Serve file from disk."""
return CachingFileResponse(path) return web.FileResponse(path, headers=CACHE_HEADERS)
else: else:
async def serve_file(request): async def serve_file(request):
"""Serve file from disk.""" """Serve file from disk."""

View File

@ -1,18 +1,29 @@
"""Static file handling for HTTP component.""" """Static file handling for HTTP component."""
from pathlib import Path
from aiohttp import hdrs from aiohttp import hdrs
from aiohttp.web import FileResponse 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 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): class CachingStaticResource(StaticResource):
"""Static Resource handler that will add cache headers.""" """Static Resource handler that will add cache headers."""
async def _handle(self, request): async def _handle(self, request):
filename = URL(request.match_info['filename']).path rel_url = request.match_info['filename']
try: 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() filepath = self._directory.joinpath(filename).resolve()
if not self._follow_symlinks: if not self._follow_symlinks:
filepath.relative_to(self._directory) filepath.relative_to(self._directory)
@ -24,30 +35,10 @@ class CachingStaticResource(StaticResource):
request.app.logger.exception(error) request.app.logger.exception(error)
raise HTTPNotFound() from error raise HTTPNotFound() from error
# on opening a dir, load its contents if allowed
if filepath.is_dir(): if filepath.is_dir():
return await super()._handle(request) return await super()._handle(request)
if filepath.is_file(): 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 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