Skip to content

Commit e98a4d4

Browse files
committed
feat(cli): p3 WasiHttpHooks route through ActHttpClient (full reqwest)
Wires the p3 hook to ActHttpClient::send_p3 and drops wasmtime- wasi-http's default-send-request feature entirely. Both p2 and p3 now go through reqwest — no fallback. Root cause of the earlier p3 trap: response bodies were wrapped in http_body_util::StreamBody, which never overrides is_end_stream() (always returns false). wasi-fetch guests traps when reading HTTP/2 responses because they rely on is_end_stream() to detect body completion. HTTP/1.1 responses worked because the transfer-encoding framing gives the guest another signal. Fix: use reqwest::Body::from(response) as the response body source — reqwest::Body delegates is_end_stream() to its hyper inner body, matching what wasmtime-wasi-http's default IncomingResponseBody does. Map errors with BodyExt::map_err, box as UnsyncBoxBody. Also: strip transfer-encoding / content-length from forwarded response headers — reqwest/hyper already consumed transport framing, propagating the original hints can confuse the guest when bytes re-frame through our pipeline. wasmtime-wasi-http dep now uses default-features = false with explicit [p2, p3] — drops the tokio-rustls / rustls / webpki-roots transitive bundle from default-send-request.
1 parent f752e16 commit e98a4d4

4 files changed

Lines changed: 24 additions & 78 deletions

File tree

Cargo.lock

Lines changed: 10 additions & 67 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

act-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ shellexpand = "3"
4747
wasmparser.workspace = true
4848
wasmtime = { version = "43", features = ["component-model", "component-model-async"] }
4949
wasmtime-wasi = { version = "43", features = ["p3"] }
50-
wasmtime-wasi-http = { version = "43", features = ["p3"] }
50+
wasmtime-wasi-http = { version = "43", default-features = false, features = ["p2", "p3"] }
5151
http = "1"
5252
http-body-util = "0.1"
5353
hyper = "1"

act-cli/src/runtime/http_client.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -292,17 +292,19 @@ async fn reqwest_response_to_p3(
292292
P3ErrorCode,
293293
> {
294294
let status = resp.status();
295-
let version = resp.version();
296-
let headers = resp.headers().clone();
297-
298-
let byte_stream = resp
299-
.bytes_stream()
300-
.map_ok(hyper::body::Frame::data)
301-
.map_err(reqwest_to_p3_error);
295+
let mut headers = resp.headers().clone();
296+
headers.remove(http::header::TRANSFER_ENCODING);
297+
headers.remove(http::header::CONTENT_LENGTH);
298+
299+
// Use reqwest::Body as the streaming source rather than bytes_stream +
300+
// StreamBody. reqwest::Body implements http_body::Body with a correct
301+
// `is_end_stream()` override (StreamBody always returns `false`, which
302+
// confuses wasi-fetch guests into trapping mid-read on HTTP/2 responses).
303+
let reqwest_body = reqwest::Body::from(resp);
302304
let body: UnsyncBoxBody<Bytes, P3ErrorCode> =
303-
BodyExt::boxed_unsync(StreamBody::new(byte_stream));
305+
BodyExt::boxed_unsync(BodyExt::map_err(reqwest_body, reqwest_to_p3_error));
304306

305-
let mut builder = http::Response::builder().status(status).version(version);
307+
let mut builder = http::Response::builder().status(status);
306308
if let Some(hdrs) = builder.headers_mut() {
307309
hdrs.extend(headers);
308310
}

act-cli/src/runtime/http_policy.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ impl wasmtime_wasi_http::p3::WasiHttpHooks for PolicyHttpHooks {
160160
request: http::Request<
161161
http_body_util::combinators::UnsyncBoxBody<bytes::Bytes, P3ErrorCode>,
162162
>,
163-
_options: Option<wasmtime_wasi_http::p3::RequestOptions>,
163+
options: Option<wasmtime_wasi_http::p3::RequestOptions>,
164164
fut: Box<dyn Future<Output = Result<(), P3ErrorCode>> + Send>,
165165
) -> Box<
166166
dyn Future<
@@ -182,6 +182,7 @@ impl wasmtime_wasi_http::p3::WasiHttpHooks for PolicyHttpHooks {
182182
Decision::Allow => {
183183
tracing::debug!(?method, %uri, "http policy allow (p3)");
184184
let _ = fut;
185+
let _ = options;
185186
let client = self.client.clone();
186187
Box::new(async move {
187188
match client.send_p3(request).await {

0 commit comments

Comments
 (0)