Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor http CachingStaticResource #21062

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions homeassistant/components/http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@
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']
Expand Down Expand Up @@ -272,7 +271,7 @@ def register_static_path(self, url_path, path, cache_headers=True):
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."""
Expand Down
45 changes: 18 additions & 27 deletions homeassistant/components/http/static.py
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -24,30 +35,10 @@ async def _handle(self, request):
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