1 """Static file handling for HTTP component."""
3 from __future__
import annotations
5 from collections.abc
import Mapping
6 from pathlib
import Path
8 from typing
import Final
10 from aiohttp.hdrs
import CACHE_CONTROL, CONTENT_TYPE
11 from aiohttp.web
import FileResponse, Request, StreamResponse
12 from aiohttp.web_fileresponse
import CONTENT_TYPES, FALLBACK_CONTENT_TYPE
13 from aiohttp.web_urldispatcher
import StaticResource
16 CACHE_TIME: Final = 31 * 86400
17 CACHE_HEADER = f
"public, max-age={CACHE_TIME}"
18 CACHE_HEADERS: Mapping[str, str] = {CACHE_CONTROL: CACHE_HEADER}
19 RESPONSE_CACHE: LRU[tuple[str, Path], tuple[Path, str]] = LRU(512)
21 if sys.version_info >= (3, 13):
26 _GUESSER = CONTENT_TYPES.guess_file_type
28 _GUESSER = CONTENT_TYPES.guess_type
32 """Static Resource handler that will add cache headers."""
34 async
def _handle(self, request: Request) -> StreamResponse:
35 """Wrap base handler to cache file path resolution and content type guess."""
36 rel_url = request.match_info[
"filename"]
37 key = (rel_url, self._directory)
38 response: StreamResponse
40 if key
in RESPONSE_CACHE:
41 file_path, content_type = RESPONSE_CACHE[key]
42 response = FileResponse(file_path, chunk_size=self._chunk_size)
43 response.headers[CONTENT_TYPE] = content_type
45 response = await super().
_handle(request)
46 if not isinstance(response, FileResponse):
49 file_path = response._path
50 response.content_type =
_GUESSER(file_path)[0]
or FALLBACK_CONTENT_TYPE
52 content_type = response.headers[CONTENT_TYPE]
53 RESPONSE_CACHE[key] = (file_path, content_type)
55 response.headers[CACHE_CONTROL] = CACHE_HEADER
StreamResponse _handle(self, Request request)