Frame curl requests and scope the test fixture lifecycle

This commit is contained in:
2026-11-06 22:41:47 +00:00
parent 6c9eccaa21
commit 47948d7047
3 changed files with 39 additions and 16 deletions
+8 -5
View File
@@ -4,6 +4,10 @@ This CLI sends HTTP and HTTPS requests through standard WASI HTTP.
The host supplies TLS and transparent credential injection.
The CLI does not request or read host credentials.
Each redirect creates a separate request, so the host checks every hop.
Every request has Content-Length, including empty requests.
The client finishes the body before it waits for a response.
It does not flush or poll the outgoing stream after its final byte.
A fast peer can close that stream after it consumes the framed body.
Verbose request output contains only headers that the CLI supplies.
## Supported options
@@ -66,18 +70,17 @@ The shared `cwd-init/init.o` must exist first. Generate it from the repository r
```sh
nix develop -c nu userland/cwd-init/build.nu
CARGO_BUILD_JOBS=2 CARGO_TARGET_DIR=/tmp/p1-curl/server-target nix develop -c cargo build --manifest-path userland/curl/tests/server/Cargo.toml --locked
mkdir -p /tmp/p1-curl
/tmp/p1-curl/server-target/debug/curl-test-server /tmp/p1-curl/port
```
Run the server as a managed job or in another terminal.
Then run the comparisons from the repository root:
Run the comparisons from the repository root:
```sh
nix develop -c nu userland/curl/test.nu
```
The script builds the native fixture with a persistent cache and starts it on a free port.
Each run uses a fresh readiness file.
The script stops the fixture after success or failure.
The script compares deterministic local responses with native curl.
It also checks a public HTTPS GET and verifies WASI-only component imports.
The script retains its temporary files for inspection.
+9 -9
View File
@@ -132,11 +132,13 @@ fn send(
.append(name, value)
.map_err(|_| error(2, "Invalid HTTP header"))?;
}
if let Some(data) = data {
fields
.set("content-length", &[data.len().to_string().into_bytes()])
.map_err(|_| error(2, "Invalid content length"))?;
}
// Frame even empty requests so Hyper does not discard an unknown-length body.
fields
.set(
"content-length",
&[data.map_or(0, <[u8]>::len).to_string().into_bytes()],
)
.map_err(|_| error(2, "Invalid content length"))?;
let request = OutgoingRequest::new(fields);
let method = match method {
"GET" => Method::Get,
@@ -204,10 +206,8 @@ fn send(
.map_err(|_| error(55, "Request write failed"))?;
remaining = &remaining[count..];
}
stream
.flush()
.map_err(|_| error(55, "Request flush failed"))?;
wait(stream.subscribe(), deadline)?;
// After the last byte, the peer can already answer and close its reader.
// Do not flush or poll that reader after the framed body is complete.
drop(stream);
}
OutgoingBody::finish(outgoing, None).map_err(transport_error)?;
+22 -2
View File
@@ -12,10 +12,30 @@ def compare [name: string, component: path, root: path, url: string, args: list,
if $stderr { assert equal $wasi.stderr $native.stderr $name }
print $'($name): PASS'
}
def main [component: path = '/tmp/p1-curl/target/wasm32-wasip2/release/curl.wasm', readiness: path = '/tmp/p1-curl/port'] {
def main [component: path = '/tmp/p1-curl/target/wasm32-wasip2/release/curl.wasm'] {
let component = ($component | path expand)
mkdir /tmp/p1-curl
let root = (^mktemp -d -p /tmp/p1-curl comparison.XXXXXX | str trim)
let port = (open --raw $readiness | str trim)
let manifest = ($env.FILE_PWD | path join tests/server/Cargo.toml)
let build = (with-env {CARGO_BUILD_JOBS: '2', CARGO_TARGET_DIR: '/tmp/p1-curl/server-target'} { ^cargo build --manifest-path $manifest --locked } | complete)
if $build.exit_code != 0 { error make {msg: ($build | to json)} }
let readiness = ($root | path join port)
let server = job spawn { ^/tmp/p1-curl/server-target/debug/curl-test-server $readiness out> ($root | path join server.out) err> ($root | path join server.err) }
try {
let deadline = (date now) + 5sec
while not ($readiness | path exists) {
if (date now) >= $deadline { error make {msg: $'Fixture did not start. Inspect ($root)/server.err'} }
}
let port = (open --raw $readiness | str trim)
run-tests $component $root $port
} catch {|failure|
job kill $server
error make {msg: $failure.msg}
}
job kill $server
print 'Fixture stopped: PASS'
}
def run-tests [component: path, root: path, port: string] {
let base = $'http://127.0.0.1:($port)'
let wit = (^wasm-tools component wit $component | complete)
assert equal $wit.exit_code 0