Summary
http-service (the only wasm32-wasip3 fixture that uses world wasi:http/service) fails when the runtime-adapter prints the listener URL with a trailing slash — the URL the runner sends ends up with // and the in-fixture path-router falls through to 404. This makes the upstream tests/wasi-testsuite/adapters/wasmtime.py adapter fail the very first request of http-service.json, even though both the wasmtime side and the fixture itself are otherwise behaving correctly.
The bug is in test_suite_runner.py's URL construction, not in wasmtime or in the fixture.
Reproduction
# In a clean checkout of wasi-testsuite, on a host with `wasmtime` 44.0.1
# (the first release with `-Sp3` support) on PATH:
cd tests/rust/testsuite
make build
cd ../../..
WASMTIME=wasmtime python3 -m wasi_test_runner \
--test-suite tests/rust/testsuite/wasm32-wasip3 \
--runtime-adapter adapters/wasmtime.py
Output:
Test http-service failed
[Unexpected] Request(method='GET', path='/', response=Response(status=200, headers={'content-type': 'text/plain'}, body='hey\n')): Expected status 200, got 404
Root cause
wasi_test_runner/test_suite_runner.py:95-105 builds the server URL by reading the first stderr line and slicing from http:// to the next whitespace:
def get_http_server(self) -> str | None:
if self._http_server:
return self._http_server
line = self.get_pipe('stderr').readline().strip()
start = line.find('http://')
...
self._http_server = line[start:].split()[0]
return self._http_server
do_request then concatenates the server URL with the request path:
url = http_server + req.path
wasmtime serve prints Serving HTTP on http://127.0.0.1:<port>/ (note the trailing slash on the URL — wasmtime treats the listen URL as a base), so http_server == 'http://127.0.0.1:<port>/'. http-service.json's requests have "path": "/", so the final URL becomes http://127.0.0.1:<port>//. The fixture's path-router (src/bin/http-service.rs) matches the request path literally:
let (status, payload) = match (request.get_method(), request.get_path_with_query()) {
(Method::Get, Some(s)) if s == "/" => handle_root(&headers),
(Method::Get, _) => handle_not_found(&headers), // ← this branch wins for "//"
...
};
so the request returns 404 instead of 200.
Confirmed with the test runner's exact request via requests:
GET http://127.0.0.1:<port>/ → 200 OK body='hey\n'
GET http://127.0.0.1:<port>// → 404 Not Found body=''
The wamr runtime adapter (used by the WAMR-Zig project's parity gate) happens to print http://127.0.0.1:<port> without a trailing slash, so the concatenation produces a single / and the fixture passes. The trailing-slash form is at least as legal as the slash-less form — it's the runner's naïve concat that's wrong.
Suggested fix
Normalize the URL in get_http_server (or in do_request) so the path join can't produce //:
self._http_server = line[start:].split()[0].rstrip('/')
Alternatively, use urllib.parse.urljoin or a similar URL-aware join in do_request. Either fix gets the upstream adapters/wasmtime.py over the http-service line and lets multiple runtime adapters coexist regardless of whether they print the trailing slash.
Why this matters
This is the only wasm32-wasip3 fixture that uses world wasi:http/service, so the wasmtime adapter's http-service baseline silently fails because of a wsai-testsuite runner bug. Other runtimes (e.g. WAMR-Zig) only escape because they don't print the trailing slash — they'd hit the same bug if they ever did. Filed as part of cataggar/wamr#583 C1 — the wamr-side parity gate against wasmtime — where it surfaces as one of four documented "wasmtime side" failures.
Summary
http-service(the onlywasm32-wasip3fixture that usesworld wasi:http/service) fails when the runtime-adapter prints the listener URL with a trailing slash — the URL the runner sends ends up with//and the in-fixture path-router falls through to404. This makes the upstreamtests/wasi-testsuite/adapters/wasmtime.pyadapter fail the very first request ofhttp-service.json, even though both the wasmtime side and the fixture itself are otherwise behaving correctly.The bug is in
test_suite_runner.py's URL construction, not in wasmtime or in the fixture.Reproduction
Output:
Root cause
wasi_test_runner/test_suite_runner.py:95-105builds the server URL by reading the first stderr line and slicing fromhttp://to the next whitespace:do_requestthen concatenates the server URL with the request path:wasmtime serveprintsServing HTTP on http://127.0.0.1:<port>/(note the trailing slash on the URL — wasmtime treats the listen URL as a base), sohttp_server == 'http://127.0.0.1:<port>/'.http-service.json's requests have"path": "/", so the final URL becomeshttp://127.0.0.1:<port>//. The fixture's path-router (src/bin/http-service.rs) matches the request path literally:so the request returns
404instead of200.Confirmed with the test runner's exact request via
requests:The
wamrruntime adapter (used by the WAMR-Zig project's parity gate) happens to printhttp://127.0.0.1:<port>without a trailing slash, so the concatenation produces a single/and the fixture passes. The trailing-slash form is at least as legal as the slash-less form — it's the runner's naïve concat that's wrong.Suggested fix
Normalize the URL in
get_http_server(or indo_request) so the path join can't produce//:Alternatively, use
urllib.parse.urljoinor a similar URL-aware join indo_request. Either fix gets the upstreamadapters/wasmtime.pyover thehttp-serviceline and lets multiple runtime adapters coexist regardless of whether they print the trailing slash.Why this matters
This is the only
wasm32-wasip3fixture that usesworld wasi:http/service, so the wasmtime adapter'shttp-servicebaseline silently fails because of a wsai-testsuite runner bug. Other runtimes (e.g. WAMR-Zig) only escape because they don't print the trailing slash — they'd hit the same bug if they ever did. Filed as part of cataggar/wamr#583 C1 — the wamr-side parity gate against wasmtime — where it surfaces as one of four documented "wasmtime side" failures.