Skip to content

Commit

Permalink
Implement etag support for category files
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Nov 16, 2020
1 parent 3d27b83 commit 636a64c
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ repos:
- id: mixed-line-ending
stages: [manual]
args:
- --fix=lf
- --fix=lf
21 changes: 21 additions & 0 deletions custom_components/hacs/helpers/functions/file_etag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member
import os

from custom_components.hacs.share import get_hacs
from fnvhash import fnv1a_32
from pathlib import Path


def get_etag(path) -> bool:
file_path = Path(path)
if not file_path.exists():
return None
return fnv1a_32(file_path.read_bytes())


async def async_get_etag(path) -> bool:
hass = get_hacs().hass
fnv = await hass.async_add_executor_job(get_etag, path)
if fnv is None:
return None
return str(hex(fnv))
3 changes: 2 additions & 1 deletion custom_components/hacs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
"aiofiles>=0.5.0",
"aiogithubapi>=2.0.0<3.0.0",
"backoff>=1.10.0",
"fnvhash==0.1.0",
"hacs_frontend==202011151903",
"semantic_version>=2.8.5",
"queueman==0.5"
]
}
}
90 changes: 72 additions & 18 deletions custom_components/hacs/webresponses/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,92 @@

from custom_components.hacs.helpers.functions.logger import getLogger
from custom_components.hacs.helpers.functions.path_exsist import async_path_exsist
from custom_components.hacs.helpers.functions.file_etag import async_get_etag

from custom_components.hacs.share import get_hacs

_LOGGER = getLogger()


async def async_serve_category_file(request, requested_file):
hacs = get_hacs()
response = None

try:
if requested_file.startswith("themes/"):
servefile = f"{hacs.core.config_path}/{requested_file}"
response = await async_serve_static_file(request, servefile, requested_file)
else:
servefile = f"{hacs.core.config_path}/www/community/{requested_file}"

if await async_path_exsist(servefile):
_LOGGER.debug("Serving %s from %s", requested_file, servefile)
response = web.FileResponse(servefile)
if requested_file.startswith("themes/"):
response.headers["Cache-Control"] = "public, max-age=2678400"
else:
response.headers["Cache-Control"] = "no-store, max-age=0"
response.headers["Pragma"] = "no-store"
return response
else:
_LOGGER.error(
"%s tried to request '%s' but the file does not exist",
request.remote,
servefile,
response = await async_serve_static_file_with_etag(
request, servefile, requested_file
)
except (Exception, BaseException):
_LOGGER.exception("Error trying to serve %s", requested_file)

if response is not None:
return response

return web.Response(status=404)


async def async_serve_static_file(request, servefile, requested_file):
"""Serve a static file without an etag."""
if await async_path_exsist(servefile):
_LOGGER.debug("Serving %s from %s", requested_file, servefile)
response = web.FileResponse(servefile)
response.headers["Cache-Control"] = "public, max-age=2678400"
return response

except (Exception, BaseException) as exception:
_LOGGER.error(
"%s tried to request '%s' but the file does not exist",
request.remote,
servefile,
)
return None


async def async_serve_static_file_with_etag(request, servefile, requested_file):
"""Serve a static file with an etag."""
etag = await async_get_etag(servefile)
if_none_match_header = request.headers.get("if-none-match")

if (
etag is not None
and if_none_match_header is not None
and _match_etag(etag, if_none_match_header)
):
_LOGGER.debug(
"there was an issue trying to serve %s - %s", requested_file, exception
"Serving %s from %s with etag %s (not-modified)",
requested_file,
servefile,
etag,
)
return web.Response(status=304)

return web.Response(status=404)
if etag is not None:
_LOGGER.debug(
"Serving %s from %s with etag %s (not cached)",
requested_file,
servefile,
etag,
)
response = web.FileResponse(servefile)
response.headers["Cache-Control"] = "must-revalidate, max-age=0"
response.headers["Etag"] = etag
return response

_LOGGER.error(
"%s tried to request '%s' but the file does not exist",
request.remote,
servefile,
)
return None


def _match_etag(etag, if_none_match_header):
"""Check to see if an etag matches."""
for if_none_match_ele in if_none_match_header.split(","):
if if_none_match_ele.strip() == etag:
return True
return False
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ attrs==20.3.0
backoff==1.10.0
bellybutton==0.3.0
colorlog==4.6.2
fnvhash==0.1.0
hacs_frontend==202011151903
pre-commit==2.8.2
PyGithub==1.53
Expand Down

0 comments on commit 636a64c

Please sign in to comment.