mirror of
https://github.com/lexmount/moli.git
synced 2026-10-05 16:00:54 +00:00
fix(wpt): serve XHR request header inspection fixture
Implement xhr/resources/inspect-headers.py with raw header names, duplicate values, byte-preserving filters, and its upstream CORS response headers. Support HEAD, uploads, and custom methods through the shared XHR dispatch, and recognize the supported fixture references during case selection. Validation: all 516 benchmark unit tests pass. 84 HTTP comparisons match the unmodified upstream handler. With the same Moli binary, 31 WPT cases improve from 9 to 18 passes and from 80 to 114 passing subtests, without regressions.
This commit is contained in:
@@ -715,9 +715,9 @@ def _html_path_is_supported(
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def _xhr_url_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
|
||||
def _xhr_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
|
||||
references = []
|
||||
for name in ("requri.py", "redirect.py"):
|
||||
for name in ("requri.py", "redirect.py", "inspect-headers.py"):
|
||||
resource = f"xhr/resources/{name}"
|
||||
relative = posixpath.relpath(resource, directory)
|
||||
references.extend(("/" + resource, relative, "./" + relative))
|
||||
@@ -740,7 +740,7 @@ def _supported_wptserve_handler_references(
|
||||
supported += SUPPORTED_WASM_WEBAPI_WPTSERVE_HANDLER_PATTERNS
|
||||
if rel is not None and rel.startswith("xhr/"):
|
||||
supported += SUPPORTED_XHR_DELAY_WPTSERVE_HANDLER_PATTERNS
|
||||
supported += _xhr_url_handler_reference_patterns(rel.rsplit("/", 1)[0])
|
||||
supported += _xhr_handler_reference_patterns(rel.rsplit("/", 1)[0])
|
||||
if rel is not None and rel.startswith(
|
||||
"html/semantics/scripting-1/the-script-element/module/"
|
||||
):
|
||||
|
||||
@@ -69,9 +69,10 @@ from .case_set import (
|
||||
DEFAULT_TESTHARNESS_TIMEOUT_SECONDS = 10.0
|
||||
MAX_REQUEST_BODY_BYTES = 16 * 1024 * 1024
|
||||
MAX_REQUEST_BODY_LINE_BYTES = 64 * 1024
|
||||
XHR_URL_RESOURCE_PATHS = {
|
||||
XHR_RESOURCE_PATHS = {
|
||||
"/xhr/resources/requri.py",
|
||||
"/xhr/resources/redirect.py",
|
||||
"/xhr/resources/inspect-headers.py",
|
||||
}
|
||||
FETCH_ABORT_RESOURCE_PATHS = {
|
||||
"/fetch/api/resources/stash-put.py",
|
||||
@@ -1203,6 +1204,38 @@ def _xhr_redirect_fixture_response(
|
||||
return code, "WEBSRT MARKETING", [("Location", location)], b"TEST", delay
|
||||
|
||||
|
||||
def _xhr_inspect_headers_fixture_response(
|
||||
query: str, raw_headers: list[tuple[str, str]]
|
||||
) -> tuple[list[tuple[str, str]], bytes]:
|
||||
"""Model xhr/resources/inspect-headers.py's raw header filtering."""
|
||||
params = parse_qs(query, keep_blank_values=True, encoding="latin-1")
|
||||
filter_value = params.get("filter_value", [""])[0].encode("latin-1")
|
||||
filter_name = params.get("filter_name", [""])[0].encode("latin-1").lower()
|
||||
parts = []
|
||||
for raw_name, raw_value in raw_headers:
|
||||
name, value = raw_name.encode("latin-1"), raw_value.encode("latin-1")
|
||||
if filter_value:
|
||||
if value == filter_value:
|
||||
parts.append(name + b",")
|
||||
elif name.lower() == filter_name:
|
||||
parts.append(name + b": " + value + b"\n")
|
||||
headers = []
|
||||
if "cors" in params:
|
||||
headers.extend([
|
||||
("Access-Control-Allow-Origin", "*"),
|
||||
("Access-Control-Allow-Credentials", "true"),
|
||||
("Access-Control-Allow-Methods", "GET, POST, PUT, FOO"),
|
||||
("Access-Control-Allow-Headers", "x-test, x-foo"),
|
||||
(
|
||||
"Access-Control-Expose-Headers",
|
||||
"x-request-method, x-request-content-type, x-request-query, "
|
||||
"x-request-content-length",
|
||||
),
|
||||
])
|
||||
headers.append(("content-type", "text/plain"))
|
||||
return headers, b"".join(parts)
|
||||
|
||||
|
||||
def _redirect_fixture_response(query: str) -> tuple[int, str] | None:
|
||||
"""Return the shared redirect response used by static WPT fixture handlers."""
|
||||
|
||||
@@ -1845,13 +1878,13 @@ def _make_handler(
|
||||
if (
|
||||
name.startswith("do_")
|
||||
and unquote(urlsplit(getattr(self, "path", "")).path)
|
||||
in XHR_URL_RESOURCE_PATHS
|
||||
in XHR_RESOURCE_PATHS
|
||||
):
|
||||
return self._serve_xhr_url_method
|
||||
return self._serve_xhr_method
|
||||
raise AttributeError(name)
|
||||
|
||||
def _serve_xhr_url_method(self) -> None:
|
||||
self._serve_xhr_url_resource(emit_body=self.command != "HEAD")
|
||||
def _serve_xhr_method(self) -> None:
|
||||
self._serve_xhr_resource(emit_body=self.command != "HEAD")
|
||||
|
||||
def do_GET(self) -> None: # noqa: N802 (BaseHTTPRequestHandler API)
|
||||
if self.headers.get("Upgrade", "").lower() == "websocket":
|
||||
@@ -1863,7 +1896,7 @@ def _make_handler(
|
||||
self._serve(emit_body=False)
|
||||
|
||||
def do_OPTIONS(self) -> None: # noqa: N802
|
||||
if self._serve_xhr_url_resource(emit_body=True):
|
||||
if self._serve_xhr_resource(emit_body=True):
|
||||
return
|
||||
parsed = urlparse(self.path)
|
||||
path = unquote(parsed.path)
|
||||
@@ -1890,7 +1923,7 @@ def _make_handler(
|
||||
self.send_error(404)
|
||||
|
||||
def do_POST(self) -> None: # noqa: N802
|
||||
if self._serve_xhr_url_resource(emit_body=True):
|
||||
if self._serve_xhr_resource(emit_body=True):
|
||||
return
|
||||
parsed = urlparse(self.path)
|
||||
path = unquote(parsed.path)
|
||||
@@ -1958,7 +1991,7 @@ def _make_handler(
|
||||
self.end_headers()
|
||||
|
||||
def _serve_fetch_resource_method(self) -> None:
|
||||
if self._serve_xhr_url_resource(emit_body=True):
|
||||
if self._serve_xhr_resource(emit_body=True):
|
||||
return
|
||||
parsed = urlparse(self.path)
|
||||
if unquote(parsed.path) in FETCH_ABORT_RESOURCE_PATHS:
|
||||
@@ -1980,7 +2013,7 @@ def _make_handler(
|
||||
do_DELETE = _serve_fetch_resource_method
|
||||
|
||||
def do_YO(self) -> None: # noqa: N802 (WPT custom method)
|
||||
if self._serve_xhr_url_resource(emit_body=True):
|
||||
if self._serve_xhr_resource(emit_body=True):
|
||||
return
|
||||
parsed = urlparse(self.path)
|
||||
if unquote(parsed.path) in FETCH_ABORT_RESOURCE_PATHS | {
|
||||
@@ -2108,7 +2141,7 @@ def _make_handler(
|
||||
return
|
||||
|
||||
def _serve(self, *, emit_body: bool) -> None:
|
||||
if self._serve_xhr_url_resource(emit_body=emit_body):
|
||||
if self._serve_xhr_resource(emit_body=emit_body):
|
||||
return
|
||||
parsed = urlparse(self.path)
|
||||
path = unquote(parsed.path)
|
||||
@@ -2650,10 +2683,10 @@ def _make_handler(
|
||||
self.send_error(status_code)
|
||||
return False
|
||||
|
||||
def _serve_xhr_url_resource(self, *, emit_body: bool) -> bool:
|
||||
def _serve_xhr_resource(self, *, emit_body: bool) -> bool:
|
||||
parsed = urlsplit(self.path)
|
||||
path = unquote(parsed.path)
|
||||
if path not in XHR_URL_RESOURCE_PATHS:
|
||||
if path not in XHR_RESOURCE_PATHS:
|
||||
return False
|
||||
try:
|
||||
if path == "/xhr/resources/requri.py":
|
||||
@@ -2669,6 +2702,11 @@ def _make_handler(
|
||||
authority += ":" + str(self.server.server_address[1])
|
||||
uri = f"http://{authority}{uri}"
|
||||
status, reason, headers, body = 200, None, [], uri.encode("utf-8")
|
||||
elif path == "/xhr/resources/inspect-headers.py":
|
||||
status, reason = 200, None
|
||||
headers, body = _xhr_inspect_headers_fixture_response(
|
||||
parsed.query, list(self.headers.raw_items())
|
||||
)
|
||||
else:
|
||||
status, reason, headers, body, delay = _xhr_redirect_fixture_response(
|
||||
parsed.path, parsed.query
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
from contextlib import ExitStack
|
||||
from http.client import HTTPConnection
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from moli_benchmark.wpt_cross.case_set import enumerate_cases
|
||||
from moli_benchmark.wpt_cross.server import WptFixtureServer
|
||||
|
||||
|
||||
RESOURCE = "/xhr/resources/inspect-headers.py"
|
||||
|
||||
|
||||
class XhrHeaderFixtureTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.stack = ExitStack()
|
||||
self.addCleanup(self.stack.close)
|
||||
self.root = Path(self.stack.enter_context(tempfile.TemporaryDirectory()))
|
||||
(self.root / "resources").mkdir()
|
||||
(self.root / "resources/testharness.js").write_text("// testharness")
|
||||
self.stack.enter_context(patch(
|
||||
"moli_benchmark.wpt_cross.server._global_ipv6_address", return_value=None
|
||||
))
|
||||
|
||||
def server(self) -> WptFixtureServer:
|
||||
return self.stack.enter_context(WptFixtureServer(self.root))
|
||||
|
||||
def request(
|
||||
self, port: int, query: str, headers: list[tuple[str, str]],
|
||||
*, method: str = "GET",
|
||||
) -> tuple[int, dict[str, str], bytes]:
|
||||
connection = HTTPConnection("127.0.0.1", port, timeout=2)
|
||||
try:
|
||||
connection.putrequest(method, RESOURCE + "?" + query)
|
||||
for name, value in headers:
|
||||
connection.putheader(name, value)
|
||||
connection.endheaders()
|
||||
response = connection.getresponse()
|
||||
return (
|
||||
response.status,
|
||||
{name.lower(): value for name, value in response.getheaders()},
|
||||
response.read(),
|
||||
)
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
def test_name_filter_preserves_case_duplicates_whitespace_and_bytes(self) -> None:
|
||||
server = self.server()
|
||||
status, headers, body = self.request(server.port, "filter_name=X-CaSe", [
|
||||
("x-Case", "first"),
|
||||
("X-Unrelated", "ignored"),
|
||||
("X-CASE", ""),
|
||||
("x-cASE", "caf\xe9\xff \t"),
|
||||
("X-case", "folded\r\n\tvalue"),
|
||||
])
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(body, b"x-Case: first\nX-CASE: \nx-cASE: caf\xe9\xff \t\n"
|
||||
b"X-case: folded\r\n\tvalue\n")
|
||||
self.assertEqual(headers["content-type"], "text/plain")
|
||||
self.assertEqual(headers["content-length"], str(len(body)))
|
||||
self.assertFalse(any(name.startswith("access-control-") for name in headers))
|
||||
|
||||
def test_value_filter_is_exact_and_takes_priority_over_name(self) -> None:
|
||||
server = self.server()
|
||||
status, _, body = self.request(
|
||||
server.port, "filter_value=%E9%FF&filter_name=x-other", [
|
||||
("X-First", "\xe9\xff"),
|
||||
("x-second", "\xe9\xff"),
|
||||
("X-First", "\xe9\xff"),
|
||||
("X-Different-Case", "\xc9\xff"),
|
||||
("X-Longer", "\xe9\xff "),
|
||||
("X-Other", "not-matched"),
|
||||
]
|
||||
)
|
||||
self.assertEqual((status, body), (200, b"X-First,x-second,X-First,"))
|
||||
|
||||
def test_query_uses_first_values_and_empty_value_falls_back_to_name(self) -> None:
|
||||
server = self.server()
|
||||
request_headers = [("X-First", "match"), ("X-Second", "other")]
|
||||
for query, expected in (
|
||||
("", b""),
|
||||
("filter_name=missing", b""),
|
||||
("filter_name=x-first&filter_name=x-second", b"X-First: match\n"),
|
||||
("filter_value=match&filter_value=other", b"X-First,"),
|
||||
("filter_value=&filter_value=match&filter_name=x-second", b"X-Second: other\n"),
|
||||
("filter_name=&filter_name=x-first", b""),
|
||||
("filter_value=missing&filter_name=x-first", b""),
|
||||
):
|
||||
with self.subTest(query=query):
|
||||
status, _, body = self.request(server.port, query, request_headers)
|
||||
self.assertEqual((status, body), (200, expected))
|
||||
|
||||
def test_cors_flag_and_methods_match_upstream(self) -> None:
|
||||
server = self.server()
|
||||
cors_headers = {
|
||||
"access-control-allow-origin": "*",
|
||||
"access-control-allow-credentials": "true",
|
||||
"access-control-allow-methods": "GET, POST, PUT, FOO",
|
||||
"access-control-allow-headers": "x-test, x-foo",
|
||||
"access-control-expose-headers": (
|
||||
"x-request-method, x-request-content-type, x-request-query, "
|
||||
"x-request-content-length"
|
||||
),
|
||||
}
|
||||
for method in ("GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "YO", "FOO"):
|
||||
for flag in ("", "&cors", "&cors=", "&cors=false"):
|
||||
with self.subTest(method=method, flag=flag):
|
||||
status, headers, body = self.request(
|
||||
server.port, "filter_name=x-test" + flag, [
|
||||
("X-Test", "value"),
|
||||
("Origin", "http://example.test"),
|
||||
("Access-Control-Request-Method", "DELETE"),
|
||||
("Access-Control-Request-Headers", "x-unexpected"),
|
||||
], method=method,
|
||||
)
|
||||
expected = b"X-Test: value\n"
|
||||
self.assertEqual((status, body), (200, b"" if method == "HEAD" else expected))
|
||||
self.assertEqual(headers["content-type"], "text/plain")
|
||||
self.assertEqual(headers["content-length"], str(len(expected)))
|
||||
self.assertEqual(
|
||||
{name: value for name, value in headers.items()
|
||||
if name.startswith("access-control-")},
|
||||
cors_headers if flag else {},
|
||||
)
|
||||
self.assertFalse(any(name.startswith("x-request-") for name in headers))
|
||||
|
||||
def test_header_response_does_not_wait_for_upload(self) -> None:
|
||||
server = self.server()
|
||||
for name, value in (("Content-Length", "1000000"), ("Transfer-Encoding", "chunked")):
|
||||
with self.subTest(framing=name):
|
||||
# request() sends only headers, leaving the promised body unread.
|
||||
status, headers, body = self.request(
|
||||
server.port, "filter_name=" + name, [(name, value)], method="POST"
|
||||
)
|
||||
self.assertEqual((status, body), (200, f"{name}: {value}\n".encode()))
|
||||
self.assertEqual(headers["connection"], "close")
|
||||
|
||||
def test_case_selection_allows_only_supported_header_fixture_references(self) -> None:
|
||||
sources = {
|
||||
"xhr/absolute.window.js": "fetch('/xhr/resources/inspect-headers.py');",
|
||||
"xhr/relative.window.js": "fetch('./resources/inspect-headers.py?filter_name=test');",
|
||||
"xhr/nested/parent.window.js": "fetch('../resources/inspect-headers.py');",
|
||||
"xhr/unknown.window.js": "fetch('resources/inspect-headers.py'); fetch('resources/unknown.py');",
|
||||
"xhr/suffix.window.js": "fetch('resources/inspect-headers.py2');",
|
||||
"xhr/script-suffix.window.js": "fetch('resources/inspect-headers.py.js');",
|
||||
"xhr/prefix.window.js": "fetch('/wrong/xhr/resources/inspect-headers.py');",
|
||||
"xhr/wrong-parent.window.js": "fetch('../resources/inspect-headers.py');",
|
||||
"xhr/wrong-root.window.js": "fetch('inspect-headers.py');",
|
||||
"xhr/nested/wrong-relative.window.js": "fetch('resources/inspect-headers.py');",
|
||||
}
|
||||
for path, source in sources.items():
|
||||
target = self.root / path
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_text(source)
|
||||
selected = enumerate_cases(self.root, dir_prefixes=("xhr",))
|
||||
self.assertEqual([case.case_path for case in selected], [
|
||||
"xhr/absolute.window.js?moli-wpt-script=window",
|
||||
"xhr/nested/parent.window.js?moli-wpt-script=window",
|
||||
"xhr/relative.window.js?moli-wpt-script=window",
|
||||
])
|
||||
Reference in New Issue
Block a user