Skip to content

Commit 8025952

Browse files
committed
add WASIp3 port of wasi-http-streaming test
This required adding the 0.3.0-rc-2025-08-15 WIT files to the tree so `wit-bindgen` could use them. I've also addressed Till's feedback to match only the exact 0.3.0 RC we support when looking for the `wasi:http/handler` export. Finally, this includes a temporary workaround for bytecodealliance/wasmtime#11703, which was uncovered by the streaming test added in this commit. Signed-off-by: Joel Dice <[email protected]>
1 parent 48a457e commit 8025952

File tree

38 files changed

+3145
-87
lines changed

38 files changed

+3145
-87
lines changed

Cargo.lock

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

crates/factor-outbound-http/src/wasi.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{
1111

1212
use bytes::Bytes;
1313
use http::{header::HOST, uri::Scheme, Uri};
14-
use http_body::{Body, Frame};
14+
use http_body::{Body, Frame, SizeHint};
1515
use http_body_util::{combinators::BoxBody, BodyExt};
1616
use hyper_util::{
1717
client::legacy::{
@@ -166,6 +166,14 @@ impl<B: Body<Error = p2_types::ErrorCode>> Body for BetweenBytesTimeoutBody<B> {
166166
}
167167
}
168168
}
169+
170+
fn is_end_stream(&self) -> bool {
171+
self.body.is_end_stream()
172+
}
173+
174+
fn size_hint(&self) -> SizeHint {
175+
self.body.size_hint()
176+
}
169177
}
170178

171179
pub(crate) fn add_to_linker<C>(ctx: &mut C) -> anyhow::Result<()>

crates/http/src/trigger.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ const WASI_HTTP_EXPORT_2023_10_18: &str = "wasi:http/[email protected]
3434
const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/[email protected]";
3535
/// The `incoming-handler` export prefix for all `wasi:http` 0.2 versions
3636
const WASI_HTTP_EXPORT_0_2_PREFIX: &str = "wasi:http/[email protected]";
37-
/// The `handler` export prefix for all `wasi:http` 0.3 versions
38-
const WASI_HTTP_EXPORT_0_3_PREFIX: &str = "wasi:http/[email protected]";
37+
/// The `handler` export `wasi:http` version 0.3.0-rc-2025-08-15
38+
const WASI_HTTP_EXPORT_0_3_0_RC_2025_08_15: &str = "wasi:http/[email protected].0-rc-2025-08-15";
3939
/// The `inbound-http` export for `fermyon:spin`
4040
const SPIN_HTTP_EXPORT: &str = "fermyon:spin/inbound-http";
4141

@@ -70,7 +70,7 @@ impl HandlerType {
7070
`{WASI_HTTP_EXPORT_2023_10_18}`, \
7171
`{WASI_HTTP_EXPORT_2023_11_10}`, \
7272
`{WASI_HTTP_EXPORT_0_2_PREFIX}.*`, \
73-
`{WASI_HTTP_EXPORT_0_3_PREFIX}.*`, \
73+
`{WASI_HTTP_EXPORT_0_3_0_RC_2025_08_15}`, \
7474
or `{SPIN_HTTP_EXPORT}` but it exported none of those. \
7575
This may mean the component handles a different trigger, or that its `wasi:http` export is newer then those supported by Spin. \
7676
If you're sure this is an HTTP module, check if a Spin upgrade is available: this may handle the newer version."

crates/trigger-http/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ anyhow = { workspace = true }
1212
clap = { workspace = true }
1313
futures = { workspace = true }
1414
http = { workspace = true }
15+
http-body = { workspace = true }
1516
http-body-util = { workspace = true }
1617
hyper = { workspace = true }
1718
hyper-util = { workspace = true }
19+
pin-project-lite = { workspace = true }
1820
rustls = { workspace = true }
1921
rustls-pki-types = { workspace = true }
2022
serde = { workspace = true }

crates/trigger-http/src/wasip3.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use crate::{server::HttpExecutor, TriggerInstanceBuilder};
2-
use anyhow::{Context, Result};
2+
use anyhow::{Context as _, Result};
33
use futures::{channel::oneshot, FutureExt};
44
use http_body_util::BodyExt;
55
use spin_factors::RuntimeFactors;
66
use spin_factors_executor::InstanceState;
77
use spin_http::routes::RouteMatch;
8-
use std::net::SocketAddr;
8+
use std::{
9+
net::SocketAddr,
10+
pin::Pin,
11+
task::{Context, Poll},
12+
};
913
use tokio::task;
1014
use tracing::{instrument, Level};
1115
use wasmtime_wasi_http::{
@@ -59,9 +63,20 @@ impl HttpExecutor for Wasip3HttpExecutor<'_> {
5963
response.into_http_with_getter(&mut store, request_io_result, getter)
6064
})?;
6165

62-
_ = tx.send(response);
66+
let (response_tx, response_rx) = oneshot::channel::<()>();
67+
_ = tx.send(response.map(|body| BodyWithAttachment {
68+
body,
69+
_attachment: response_tx,
70+
}));
6371

6472
task.block(store).await;
73+
74+
// TODO: This is a temporary workaround for
75+
// https://github.com/bytecodealliance/wasmtime/issues/11703.
76+
// Remove this (and `BodyWithAttachment`) once that
77+
// issue is addressed:
78+
_ = response_rx.await;
79+
6580
anyhow::Ok(())
6681
})
6782
.await?
@@ -80,6 +95,34 @@ impl HttpExecutor for Wasip3HttpExecutor<'_> {
8095
}
8196
}
8297

98+
pin_project_lite::pin_project! {
99+
struct BodyWithAttachment<B, A> {
100+
#[pin]
101+
body: B,
102+
_attachment: A,
103+
}
104+
}
105+
106+
impl<B: http_body::Body, A> http_body::Body for BodyWithAttachment<B, A> {
107+
type Data = B::Data;
108+
type Error = B::Error;
109+
110+
fn poll_frame(
111+
self: Pin<&mut Self>,
112+
cx: &mut Context<'_>,
113+
) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
114+
self.project().body.poll_frame(cx)
115+
}
116+
117+
fn is_end_stream(&self) -> bool {
118+
self.body.is_end_stream()
119+
}
120+
121+
fn size_hint(&self) -> http_body::SizeHint {
122+
self.body.size_hint()
123+
}
124+
}
125+
83126
fn wasi_http<F: RuntimeFactors>(
84127
data: &mut InstanceState<F::InstanceState, ()>,
85128
) -> Result<WasiHttpCtxView<'_>> {

examples/spin-timer/Cargo.lock

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

tests/integration.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ mod integration_tests {
143143
Err(e) => {
144144
return Err(
145145
anyhow::anyhow!("could not read stdout file: {e}").into()
146-
)
146+
);
147147
}
148148
}
149149
},
@@ -201,7 +201,7 @@ mod integration_tests {
201201
Err(e) => {
202202
return Err(
203203
anyhow::anyhow!("could not read stdout file: {e}").into()
204-
)
204+
);
205205
}
206206
}
207207
},
@@ -1195,16 +1195,38 @@ route = "/..."
11951195
}
11961196

11971197
#[test]
1198-
fn test_wasi_http_echo() -> anyhow::Result<()> {
1199-
wasi_http_echo("echo".into(), None)
1198+
fn test_wasi_http_p2_echo() -> anyhow::Result<()> {
1199+
wasi_http_echo("wasi-http-p2-streaming", "echo".into(), None)
12001200
}
12011201

12021202
#[test]
1203-
fn test_wasi_http_double_echo() -> anyhow::Result<()> {
1204-
wasi_http_echo("double-echo".into(), Some("echo".into()))
1203+
fn test_wasi_http_p3_echo() -> anyhow::Result<()> {
1204+
wasi_http_echo("wasi-http-p3-streaming", "echo".into(), None)
12051205
}
12061206

1207-
fn wasi_http_echo(uri: String, url_header_uri: Option<String>) -> anyhow::Result<()> {
1207+
#[test]
1208+
fn test_wasi_http_p2_double_echo() -> anyhow::Result<()> {
1209+
wasi_http_echo(
1210+
"wasi-http-p2-streaming",
1211+
"double-echo".into(),
1212+
Some("echo".into()),
1213+
)
1214+
}
1215+
1216+
#[test]
1217+
fn test_wasi_http_p3_double_echo() -> anyhow::Result<()> {
1218+
wasi_http_echo(
1219+
"wasi-http-p2-streaming",
1220+
"double-echo".into(),
1221+
Some("echo".into()),
1222+
)
1223+
}
1224+
1225+
fn wasi_http_echo(
1226+
test: &str,
1227+
uri: String,
1228+
url_header_uri: Option<String>,
1229+
) -> anyhow::Result<()> {
12081230
let body_bytes = {
12091231
// A sorta-random-ish megabyte
12101232
let mut n = 0_u8;
@@ -1227,7 +1249,7 @@ route = "/..."
12271249
));
12281250

12291251
run_test(
1230-
"wasi-http-streaming",
1252+
test,
12311253
SpinConfig {
12321254
binary_path: spin_binary(),
12331255
spin_up_args: Vec::new(),
@@ -1267,7 +1289,16 @@ route = "/..."
12671289
}
12681290

12691291
#[test]
1270-
fn test_wasi_http_hash_all() -> anyhow::Result<()> {
1292+
fn test_wasi_http_p2_hash_all() -> anyhow::Result<()> {
1293+
wasi_http_hash_all("wasi-http-p2-streaming")
1294+
}
1295+
1296+
#[test]
1297+
fn test_wasi_http_p3_hash_all() -> anyhow::Result<()> {
1298+
wasi_http_hash_all("wasi-http-p3-streaming")
1299+
}
1300+
1301+
fn wasi_http_hash_all(test: &str) -> anyhow::Result<()> {
12711302
let requests = [
12721303
("/a", "’Twas brillig, and the slithy toves"),
12731304
("/b", "Did gyre and gimble in the wabe:"),
@@ -1277,7 +1308,7 @@ route = "/..."
12771308
.into_iter()
12781309
.collect::<HashMap<_, _>>();
12791310
run_test(
1280-
"wasi-http-streaming",
1311+
test,
12811312
SpinConfig {
12821313
binary_path: spin_binary(),
12831314
spin_up_args: Vec::new(),

0 commit comments

Comments
 (0)