wasm-viewer tests: the cross-origin path without exposed headers
serve.py always exposed Content-Range and ETag, and Node has no CORS, so openUrl's documented cross-origin path (length from a HEAD request, answers checked by body length alone, no validator) was never run. - serve.py /noexpose/ serves ranges without Content-Range, ETag, Last-Modified or Accept-Ranges (what a page sees of a server that does not expose them); /unexposed/ sends them but exposes none, for a real browser. HEAD requests are counted (0 bytes). - test.mjs: every fixture check through /noexpose/ at 1 MiB and 512-byte blocks (one HEAD each, requests and bytes as the server counted them), concurrent reads with cacheSize 0, a short answer still caught, and a server without a HEAD length a clear error. - browser.sh: the page on 127.0.0.1 opens the file from localhost, once with Content-Range exposed and once through /unexposed/, where the server's log must show the HEAD. Checked by breaking the HEAD length in remote.js: the new checks fail. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -12,6 +12,15 @@ killed. No symlinks or copies are made: files are read where they are.
|
||||
headers exposing Content-Range, so a page on another origin can use it.
|
||||
- Under `/norange/...` the same files are served but Range is ignored
|
||||
(200 with the whole file), as by a server without range support.
|
||||
- Under `/noexpose/...` ranges are served without Content-Range, ETag,
|
||||
Last-Modified or Accept-Ranges, and CORS exposes none of them: what a
|
||||
page sees of a cross-origin server that does not list them in
|
||||
Access-Control-Expose-Headers. The length comes from Content-Length of
|
||||
a HEAD request (always readable).
|
||||
- Under `/unexposed/...` ranges are served with all those headers, but
|
||||
CORS exposes none of them: a browser page on another origin cannot read
|
||||
them (test/browser.sh loads the page from 127.0.0.1 and the file from
|
||||
localhost), so it has to take the same path.
|
||||
- `GET /__stats` returns `{"requests": n, "bytes": n, "log": [...]}` for
|
||||
file requests since the last `GET /__reset`, which zeroes them.
|
||||
"""
|
||||
@@ -80,10 +89,11 @@ def make_handler(roots):
|
||||
def log_message(self, *args):
|
||||
pass
|
||||
|
||||
def cors(self):
|
||||
def cors(self, expose=True):
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.send_header("Access-Control-Expose-Headers",
|
||||
"Content-Range, Content-Length, ETag, Accept-Ranges")
|
||||
if expose:
|
||||
self.send_header("Access-Control-Expose-Headers",
|
||||
"Content-Range, Content-Length, ETag, Accept-Ranges")
|
||||
|
||||
def do_OPTIONS(self):
|
||||
self.send_response(204)
|
||||
@@ -117,10 +127,18 @@ def make_handler(roots):
|
||||
with lock:
|
||||
stats.update(requests=0, bytes=0, log=[])
|
||||
return self.json({})
|
||||
ranges = True
|
||||
# ranges: honour Range; send_all: send Content-Range, ETag and
|
||||
# Accept-Ranges; expose: list them for CORS.
|
||||
ranges = send_all = expose = True
|
||||
if path.startswith("/norange/"):
|
||||
ranges = False
|
||||
path = path[len("/norange"):]
|
||||
elif path.startswith("/noexpose/"):
|
||||
expose = send_all = False
|
||||
path = path[len("/noexpose"):]
|
||||
elif path.startswith("/unexposed/"):
|
||||
expose = False
|
||||
path = path[len("/unexposed"):]
|
||||
f = resolve(roots, path)
|
||||
if f is None:
|
||||
self.send_response(404)
|
||||
@@ -142,22 +160,26 @@ def make_handler(roots):
|
||||
return
|
||||
start, end = r if r else (0, size)
|
||||
self.send_response(206 if r else 200)
|
||||
self.cors()
|
||||
self.cors(expose)
|
||||
ext = os.path.splitext(f)[1]
|
||||
self.send_header("Content-Type", TYPES.get(ext, "application/octet-stream"))
|
||||
self.send_header("Content-Length", str(end - start))
|
||||
self.send_header("ETag", etag)
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
if ranges:
|
||||
self.send_header("Accept-Ranges", "bytes")
|
||||
if r:
|
||||
self.send_header("Content-Range", f"bytes {start}-{end - 1}/{size}")
|
||||
if send_all:
|
||||
self.send_header("ETag", etag)
|
||||
if ranges:
|
||||
self.send_header("Accept-Ranges", "bytes")
|
||||
if r:
|
||||
self.send_header("Content-Range", f"bytes {start}-{end - 1}/{size}")
|
||||
self.end_headers()
|
||||
with lock:
|
||||
# A HEAD is a request too (openUrl makes one when it cannot
|
||||
# see Content-Range); it sends no bytes.
|
||||
stats["requests"] += 1
|
||||
sent = 0 if head else end - start
|
||||
stats["bytes"] += sent
|
||||
stats["log"].append([path, start, end, 206 if r else 200, "HEAD" if head else "GET"])
|
||||
if not head:
|
||||
with lock:
|
||||
stats["requests"] += 1
|
||||
stats["bytes"] += end - start
|
||||
stats["log"].append([path, start, end, 206 if r else 200])
|
||||
with open(f, "rb") as fh:
|
||||
fh.seek(start)
|
||||
left = end - start
|
||||
|
||||
Reference in New Issue
Block a user