From cf2c749b300a0a3ea21dc645bbc8fea5fefb1f01 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Fri, 11 Sep 2026 05:14:15 +0800 Subject: [PATCH] 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. --- .../moli_benchmark/wpt_cross/case_set.py | 6 +- .../moli_benchmark/wpt_cross/server.py | 62 +++++-- .../tests/test_wpt_xhr_headers_server.py | 163 ++++++++++++++++++ 3 files changed, 216 insertions(+), 15 deletions(-) create mode 100644 moli-benchmark/tests/test_wpt_xhr_headers_server.py diff --git a/moli-benchmark/moli_benchmark/wpt_cross/case_set.py b/moli-benchmark/moli_benchmark/wpt_cross/case_set.py index e2cf9e795d..e6ff3888c9 100644 --- a/moli-benchmark/moli_benchmark/wpt_cross/case_set.py +++ b/moli-benchmark/moli_benchmark/wpt_cross/case_set.py @@ -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/" ): diff --git a/moli-benchmark/moli_benchmark/wpt_cross/server.py b/moli-benchmark/moli_benchmark/wpt_cross/server.py index 33872da906..2ae801f442 100644 --- a/moli-benchmark/moli_benchmark/wpt_cross/server.py +++ b/moli-benchmark/moli_benchmark/wpt_cross/server.py @@ -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 diff --git a/moli-benchmark/tests/test_wpt_xhr_headers_server.py b/moli-benchmark/tests/test_wpt_xhr_headers_server.py new file mode 100644 index 0000000000..49d44b4988 --- /dev/null +++ b/moli-benchmark/tests/test_wpt_xhr_headers_server.py @@ -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", + ])