-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow ETag based caching of static web pages
Send the md5 hash of static files as the file etag, for all static files. Send a 304 Not Modified response for requests containing a "If-None-Match" header with the correct etag. The server now sends a Cache-Control header with the value "public, no-cache" for static files and "no-store" for dynamic pages. Other changes: * Fix embedded files always needing to be rebuilt * Merge Accept openmetrics and AcceptEncoding gzip check into csvHeaderContains * Make uzlib_gzip_wrapper a bit more resilient against missing input
- Loading branch information
Showing
15 changed files
with
587 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,9 @@ | |
# Python | ||
__pycache__ | ||
|
||
# MyPy | ||
.mypy_cache | ||
|
||
# Credentials | ||
wifissid.txt | ||
wifipass.txt | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import hashlib | ||
import os | ||
from os import path, PathLike | ||
import sys | ||
from typing import Dict, Final, List | ||
|
||
try: | ||
Import ("env") # type: ignore[name-defined] | ||
except: | ||
print("Failed to load platformio environment!", file=sys.stderr) | ||
sys.exit(1) | ||
|
||
# The file endings of files to generate hashes for. | ||
# Files also have to be in the directory specified in "static_dir" for a hash to be calculated. | ||
static_types: Final[List[str]] = ['.gz', '.png', '.ico', '.html', '.css', '.js'] | ||
|
||
# The directory containing the files to calculate a hash for. | ||
static_dir: Final[str] = 'data' | ||
|
||
# A list of files that would be considered static, but should not have their hashes calculated. | ||
static_file_blacklist: Final[List[str]] = [path.join('data', 'index.html'), path.join('data', 'error.html')] | ||
|
||
# The path of the header file to generated. | ||
hash_header_path: Final[str] = path.join(env.subst('$PROJECT_SRC_DIR'), 'generated', 'web_file_hashes.h') # type: ignore[name-defined] | ||
|
||
|
||
def hash_file(path: PathLike | str) -> str: | ||
"""Calculates the md5 hash of a file. | ||
This function reads the content of a file and calculates its md5 hash. | ||
Note that this function reads the entire file into memory at once, so it should not be used for large files. | ||
@param path: The path of the file to hash. | ||
@return The hex representation of the resulting hash. | ||
@raise IOError: If opening or reading the file fails. | ||
""" | ||
with open(path, 'rb') as file: | ||
md5 = hashlib.md5(file.read()) | ||
return md5.hexdigest() | ||
|
||
|
||
def hash_files(paths: List[PathLike] | List[str]) -> Dict[str, str]: | ||
"""Calculates the hashes for a list of files. | ||
Creates a dictionary mapping from the filename to its md5 hash. | ||
@param paths: The paths of the files to hash. | ||
@return A dictionary containing the file hashes. | ||
@raise IOError: If reading one of the files fails. | ||
""" | ||
hashes: Dict[str, str] = {} | ||
for p in paths: | ||
id: str = path.basename(p) | ||
hashes[id] = hash_file(p) | ||
|
||
return hashes | ||
|
||
|
||
def generate_hash_header(hashes: Dict[str, str]) -> None: | ||
"""Generates the header file containing the hashes of the static files. | ||
Generates a header file, in src/generated, containing constant definitions with the hashes of the static files. | ||
@param hashes: The filenames and hashes to generate the header for. | ||
""" | ||
|
||
print("Generating " + path.relpath(hash_header_path, env.subst("$PROJECT_ROOT"))) # type: ignore[name-defined] | ||
|
||
header = open(hash_header_path, 'w') | ||
header.write( | ||
"""/* | ||
* web_file_hashes.h | ||
* | ||
* **Warning:** This file is automatically generated, and should not be edited manually. | ||
* | ||
* This file contains constant definitions for the hashes of the static files sent by the web server. | ||
* | ||
* This project is licensed under the MIT License. | ||
* The MIT license can be found in the project root and at https://opensource.org/licenses/MIT. | ||
*/ | ||
#ifndef SRC_GENERATED_WEB_FILE_HASHES_H_ | ||
#define SRC_GENERATED_WEB_FILE_HASHES_H_ | ||
""") | ||
|
||
for file, hash in hashes.items(): | ||
header.write( | ||
f"""/** | ||
* The md5 hash of the file "{file}". | ||
*/ | ||
""") | ||
|
||
id: str = file.upper() | ||
for c in ['.', '-', '/', ' ']: | ||
id = id.replace(c, '_') | ||
|
||
header.write(f"static constexpr const char {id}_HASH[] = \"{hash}\";") | ||
header.write(os.linesep + os.linesep) | ||
|
||
header.write("#endif /* SRC_GENERATED_WEB_FILE_HASHES_H_ */" + os.linesep) | ||
|
||
|
||
def main() -> int: | ||
"""The main entrypoint of this script. | ||
The main function executing all the functionality of this script. | ||
Hashes the static web files, and generates a header file containing those hashes. | ||
@return Zero if nothing goes wrong. | ||
@raise IOError: If opening or reading the file fails. | ||
""" | ||
|
||
static_path_abs: Final[str] = path.abspath(static_dir) | ||
static_path_blacklist_abs: Final[List[str]] = [path.abspath(path.join(env.subst('$PROJECT_DIR'), file.strip())) for file in static_file_blacklist] # type: ignore[name-defined] | ||
|
||
files: List[str] = env.GetProjectOption('board_build.embed_files', '').splitlines() # type: ignore[name-defined] | ||
files.extend(env.GetProjectOption('board_build.embed_txtfiles', '').splitlines()) # type: ignore[name-defined] | ||
files = [path.abspath(path.join(env.subst('$PROJECT_DIR'), file.strip())) for file in files if file.strip()] # type: ignore[name-defined] | ||
files = [file for file in files if file.startswith(path.abspath(static_dir)) and path.splitext(file)[1] in static_types and file not in static_path_blacklist_abs] | ||
|
||
hashes = hash_files(files) | ||
generate_hash_header(hashes) | ||
|
||
return 0 | ||
|
||
|
||
if __name__ == '__main__' or __name__ == 'SCons.Script': | ||
error: int = main() | ||
if error != 0: | ||
sys.exit(error) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
#!/usr/bin/python | ||
#!/usr/bin/env python3 | ||
|
||
Import ("env") | ||
|
||
import os | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Generated Sources | ||
This directory contains automatically generated source files and **IS NOT INTENDED TO BE MANUALLY EDITED**. | ||
Any changes to the files in this directory are likely to be overridden during the build process. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* web_file_hashes.h | ||
* | ||
* **Warning:** This file is automatically generated, and should not be edited manually. | ||
* | ||
* This file contains constant definitions for the hashes of the static files sent by the web server. | ||
* | ||
* This project is licensed under the MIT License. | ||
* The MIT license can be found in the project root and at https://opensource.org/licenses/MIT. | ||
*/ | ||
|
||
#ifndef SRC_GENERATED_WEB_FILE_HASHES_H_ | ||
#define SRC_GENERATED_WEB_FILE_HASHES_H_ | ||
|
||
/** | ||
* The md5 hash of the file "main.css.gz". | ||
*/ | ||
static constexpr const char MAIN_CSS_GZ_HASH[] = "f144c285f1ef9fd820edeac7e3d79263"; | ||
|
||
/** | ||
* The md5 hash of the file "index.js.gz". | ||
*/ | ||
static constexpr const char INDEX_JS_GZ_HASH[] = "b7d1cc7b510da0eb5044b08fd49f6d7a"; | ||
|
||
/** | ||
* The md5 hash of the file "manifest.json.gz". | ||
*/ | ||
static constexpr const char MANIFEST_JSON_GZ_HASH[] = "0f2bc88509c2e5fa90582f041843c42f"; | ||
|
||
/** | ||
* The md5 hash of the file "favicon.ico.gz". | ||
*/ | ||
static constexpr const char FAVICON_ICO_GZ_HASH[] = "31fc8a37562605f1d0b3c919dffa4e96"; | ||
|
||
/** | ||
* The md5 hash of the file "favicon.png.gz". | ||
*/ | ||
static constexpr const char FAVICON_PNG_GZ_HASH[] = "f18756d0d8dbb95f5493d805443fa023"; | ||
|
||
/** | ||
* The md5 hash of the file "favicon.svg.gz". | ||
*/ | ||
static constexpr const char FAVICON_SVG_GZ_HASH[] = "de3746021c5d6d082e271941e00f7dbc"; | ||
|
||
#endif /* SRC_GENERATED_WEB_FILE_HASHES_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.