Compare commits
No commits in common. "63723e7fa3c5ec61252e0c2530117284b44bba58" and "0363f23cefcb8e89b4d300411b8434c959aeaaea" have entirely different histories.
63723e7fa3
...
0363f23cef
|
|
@ -25,7 +25,7 @@ def _quality(accept_encoding: str, coding: str) -> float:
|
||||||
class NegotiatedGZipMiddleware:
|
class NegotiatedGZipMiddleware:
|
||||||
"""Compress substantial responses only when the client permits gzip."""
|
"""Compress substantial responses only when the client permits gzip."""
|
||||||
|
|
||||||
def __init__(self, app: ASGIApp, minimum_size: int = 500, compresslevel: int = 6):
|
def __init__(self, app: ASGIApp, minimum_size: int = 500, compresslevel: int = 9):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.minimum_size = minimum_size
|
self.minimum_size = minimum_size
|
||||||
self.compresslevel = compresslevel
|
self.compresslevel = compresslevel
|
||||||
|
|
@ -36,9 +36,7 @@ class NegotiatedGZipMiddleware:
|
||||||
return
|
return
|
||||||
|
|
||||||
accepted = Headers(scope=scope).get("Accept-Encoding", "")
|
accepted = Headers(scope=scope).get("Accept-Encoding", "")
|
||||||
path = scope.get("path", "").rsplit("/", 1)[-1]
|
if _quality(accepted, "gzip") > 0:
|
||||||
immutable_runtime = path.startswith("runtime-") and path.endswith(".js")
|
|
||||||
if _quality(accepted, "gzip") > 0 and not immutable_runtime:
|
|
||||||
responder: ASGIApp = GZipResponder(
|
responder: ASGIApp = GZipResponder(
|
||||||
self.app, self.minimum_size, compresslevel=self.compresslevel
|
self.app, self.minimum_size, compresslevel=self.compresslevel
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import gzip
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
@ -17,7 +16,6 @@ WORKER_RUNTIME_SOURCE = "static/background-issue-sync.js"
|
||||||
class FrontendBuild:
|
class FrontendBuild:
|
||||||
dashboard_html: str
|
dashboard_html: str
|
||||||
runtime_bytes: bytes
|
runtime_bytes: bytes
|
||||||
runtime_gzip_bytes: bytes
|
|
||||||
runtime_digest: str
|
runtime_digest: str
|
||||||
runtime_name: str
|
runtime_name: str
|
||||||
page_sources: tuple[str, ...]
|
page_sources: tuple[str, ...]
|
||||||
|
|
@ -40,7 +38,6 @@ def build_frontend(frontend_dir: Path) -> FrontendBuild:
|
||||||
raise ValueError("dashboard entry document has no page scripts")
|
raise ValueError("dashboard entry document has no page scripts")
|
||||||
|
|
||||||
runtime = _bundle(frontend_dir, sources)
|
runtime = _bundle(frontend_dir, sources)
|
||||||
runtime_gzip = gzip.compress(runtime, compresslevel=6, mtime=0)
|
|
||||||
digest = hashlib.sha256(runtime).hexdigest()[:16]
|
digest = hashlib.sha256(runtime).hexdigest()[:16]
|
||||||
runtime_name = f"runtime-{digest}.js"
|
runtime_name = f"runtime-{digest}.js"
|
||||||
dashboard_html = SCRIPT_TAG.sub("", source_html)
|
dashboard_html = SCRIPT_TAG.sub("", source_html)
|
||||||
|
|
@ -66,7 +63,6 @@ def build_frontend(frontend_dir: Path) -> FrontendBuild:
|
||||||
return FrontendBuild(
|
return FrontendBuild(
|
||||||
dashboard_html=dashboard_html,
|
dashboard_html=dashboard_html,
|
||||||
runtime_bytes=runtime,
|
runtime_bytes=runtime,
|
||||||
runtime_gzip_bytes=runtime_gzip,
|
|
||||||
runtime_digest=digest,
|
runtime_digest=digest,
|
||||||
runtime_name=runtime_name,
|
runtime_name=runtime_name,
|
||||||
page_sources=sources,
|
page_sources=sources,
|
||||||
|
|
|
||||||
18
src/views.py
18
src/views.py
|
|
@ -1,10 +1,9 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Request
|
from fastapi import APIRouter, HTTPException
|
||||||
from fastapi.responses import FileResponse, HTMLResponse, Response
|
from fastapi.responses import FileResponse, HTMLResponse, Response
|
||||||
|
|
||||||
from src.frontend_bundle import build_frontend
|
from src.frontend_bundle import build_frontend
|
||||||
from src.compression import _quality
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
DASHBOARD_FILE = Path(__file__).resolve().parent.parent / "frontend" / "index.html"
|
DASHBOARD_FILE = Path(__file__).resolve().parent.parent / "frontend" / "index.html"
|
||||||
|
|
@ -52,20 +51,11 @@ async def service_worker() -> Response:
|
||||||
|
|
||||||
|
|
||||||
@router.get("/runtime-{digest}.js")
|
@router.get("/runtime-{digest}.js")
|
||||||
async def runtime_bundle(digest: str, request: Request) -> Response:
|
async def runtime_bundle(digest: str) -> Response:
|
||||||
if digest != FRONTEND_BUILD.runtime_digest:
|
if digest != FRONTEND_BUILD.runtime_digest:
|
||||||
raise HTTPException(status_code=404, detail="Runtime revision not found")
|
raise HTTPException(status_code=404, detail="Runtime revision not found")
|
||||||
accepts_gzip = _quality(request.headers.get("Accept-Encoding", ""), "gzip") > 0
|
|
||||||
return Response(
|
return Response(
|
||||||
(
|
FRONTEND_BUILD.runtime_bytes,
|
||||||
FRONTEND_BUILD.runtime_gzip_bytes
|
|
||||||
if accepts_gzip
|
|
||||||
else FRONTEND_BUILD.runtime_bytes
|
|
||||||
),
|
|
||||||
media_type="application/javascript",
|
media_type="application/javascript",
|
||||||
headers={
|
headers={"Cache-Control": "public, max-age=31536000, immutable"},
|
||||||
"Cache-Control": "public, max-age=31536000, immutable",
|
|
||||||
"Vary": "Accept-Encoding",
|
|
||||||
**({"Content-Encoding": "gzip"} if accepts_gzip else {}),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from pathlib import Path
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import pytest
|
import pytest
|
||||||
from starlette.requests import Request
|
|
||||||
|
|
||||||
from src import main
|
from src import main
|
||||||
from src.frontend_bundle import build_frontend
|
from src.frontend_bundle import build_frontend
|
||||||
|
|
@ -22,8 +21,6 @@ def test_page_runtime_is_one_deterministic_content_addressed_bundle(tmp_path):
|
||||||
|
|
||||||
assert first.runtime_name == second.runtime_name
|
assert first.runtime_name == second.runtime_name
|
||||||
assert first.runtime_bytes == second.runtime_bytes
|
assert first.runtime_bytes == second.runtime_bytes
|
||||||
assert first.runtime_gzip_bytes == second.runtime_gzip_bytes
|
|
||||||
assert gzip.decompress(first.runtime_gzip_bytes) == first.runtime_bytes
|
|
||||||
assert PAGE_SCRIPT.findall(first.dashboard_html) == []
|
assert PAGE_SCRIPT.findall(first.dashboard_html) == []
|
||||||
assert first.dashboard_html.count("<script src=") == 1
|
assert first.dashboard_html.count("<script src=") == 1
|
||||||
assert f'<script src="{first.runtime_name}"></script>' in first.dashboard_html
|
assert f'<script src="{first.runtime_name}"></script>' in first.dashboard_html
|
||||||
|
|
@ -55,46 +52,11 @@ async def test_runtime_is_transferred_as_gzip_when_the_client_accepts_it():
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.headers["content-encoding"] == "gzip"
|
assert response.headers["content-encoding"] == "gzip"
|
||||||
assert response.headers["vary"] == "Accept-Encoding"
|
assert response.headers["vary"] == "Accept-Encoding"
|
||||||
assert int(response.headers["content-length"]) == len(main.FRONTEND_BUILD.runtime_gzip_bytes)
|
assert int(response.headers["content-length"]) <= 100 * 1024
|
||||||
assert response.content == main.FRONTEND_BUILD.runtime_bytes
|
assert response.content == main.FRONTEND_BUILD.runtime_bytes
|
||||||
assert response.headers["cache-control"] == "public, max-age=31536000, immutable"
|
assert response.headers["cache-control"] == "public, max-age=31536000, immutable"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
|
||||||
async def test_runtime_route_returns_the_precomputed_gzip_representation():
|
|
||||||
build = build_frontend(FRONTEND)
|
|
||||||
request = Request(
|
|
||||||
{"type": "http", "method": "GET", "path": f"/{build.runtime_name}",
|
|
||||||
"headers": [(b"accept-encoding", b"gzip")]}
|
|
||||||
)
|
|
||||||
|
|
||||||
response = await runtime_bundle(build.runtime_digest, request)
|
|
||||||
|
|
||||||
assert response.body == build.runtime_gzip_bytes
|
|
||||||
assert response.headers["content-encoding"] == "gzip"
|
|
||||||
assert response.headers["content-length"] == str(len(build.runtime_gzip_bytes))
|
|
||||||
assert response.headers["vary"] == "Accept-Encoding"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
|
||||||
async def test_runtime_requests_bypass_dynamic_gzip_compression(monkeypatch):
|
|
||||||
class UnexpectedDynamicCompression:
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
raise AssertionError("immutable runtime entered dynamic compression")
|
|
||||||
|
|
||||||
monkeypatch.setattr("src.compression.GZipResponder", UnexpectedDynamicCompression)
|
|
||||||
transport = httpx.ASGITransport(app=main.app)
|
|
||||||
|
|
||||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
|
||||||
response = await client.get(
|
|
||||||
f"/{main.FRONTEND_BUILD.runtime_name}",
|
|
||||||
headers={"Accept-Encoding": "gzip"},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response.headers["content-encoding"] == "gzip"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
@pytest.mark.parametrize("accept_encoding", ["identity", "gzip;q=0, identity;q=1"])
|
@pytest.mark.parametrize("accept_encoding", ["identity", "gzip;q=0, identity;q=1"])
|
||||||
async def test_runtime_stays_unencoded_when_the_client_declines_gzip(accept_encoding):
|
async def test_runtime_stays_unencoded_when_the_client_declines_gzip(accept_encoding):
|
||||||
|
|
@ -146,13 +108,9 @@ def test_offline_shell_precaches_exact_runtime_without_superseded_page_modules()
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_runtime_asset_is_immutable_while_html_and_worker_revalidate():
|
async def test_runtime_asset_is_immutable_while_html_and_worker_revalidate():
|
||||||
build = build_frontend(FRONTEND)
|
build = build_frontend(FRONTEND)
|
||||||
request = Request(
|
|
||||||
{"type": "http", "method": "GET", "path": f"/{build.runtime_name}",
|
|
||||||
"headers": [(b"accept-encoding", b"identity")]}
|
|
||||||
)
|
|
||||||
|
|
||||||
html = await dashboard()
|
html = await dashboard()
|
||||||
bundle = await runtime_bundle(build.runtime_digest, request)
|
bundle = await runtime_bundle(build.runtime_digest)
|
||||||
worker = await service_worker()
|
worker = await service_worker()
|
||||||
|
|
||||||
assert build.runtime_name in html
|
assert build.runtime_name in html
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user