Skip to content

Commit 3bdeedb

Browse files
committed
Re-enable HTTP test
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent 98a91e5 commit 3bdeedb

10 files changed

Lines changed: 1363 additions & 168 deletions

File tree

.github/workflows/build-examples.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ jobs:
2424
uses: dtolnay/rust-toolchain@stable
2525
with:
2626
toolchain: "${{ env.RUST_VERSION }}"
27-
targets: wasm32-wasip1
27+
targets:
28+
- wasm32-wasip1
29+
- wasm32-wasip2
2830
- name: Install Spin
2931
uses: fermyon/actions/spin/setup@v1
3032
- name: Run build_examples.sh

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
rustup toolchain install ${{ env.RUST_VERSION }} --component clippy --component rustfmt
3232
rustup default ${{ env.RUST_VERSION }}
3333
rustup target add wasm32-wasip1
34+
rustup target add wasm32-wasip2
3435
3536
- name: Lint
3637
shell: bash

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/spin-sdk/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,5 @@ tokio = { version = "1.48.0", features = [
7474
] }
7575
wasmtime = { version = "43.0.1" }
7676
wasmtime-wasi = "43.0.1"
77-
wasmtime-wasi-http = "43.0.1"
77+
wasmtime-wasi-http = { version = "43.0.1", features = ["p3"] }
7878
wit-component = "0.245"

crates/spin-sdk/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,6 @@ pub mod experimental {
8484
generate_all,
8585
});
8686
}
87+
88+
#[cfg(test)]
89+
mod test;

crates/spin-sdk/src/test.rs

Lines changed: 133 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// wasmtime::component::bindgen!({
2-
// path: "wit",
3-
// world: "fermyon:spin/redis-trigger",
2+
// path: "../../../spin/wit",
3+
// world: "spin:up/redis-trigger@4.0.0",
44
// imports: {
55
// default: async,
66
// },
@@ -9,165 +9,137 @@
99
// }
1010
// });
1111

12-
// use {
13-
// anyhow::{anyhow, bail, Context, Result},
14-
// http_body_util::{combinators::BoxBody, BodyExt, Empty},
15-
// hyper::Request,
16-
// std::{ops::Deref, sync::OnceLock},
17-
// tokio::{
18-
// fs,
19-
// process::Command,
20-
// sync::{oneshot, OnceCell},
21-
// task,
22-
// },
23-
// wasmtime::{
24-
// component::{Component, Linker, ResourceTable},
25-
// Config, Engine, Store,
26-
// },
27-
// wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView},
28-
// wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView},
29-
// wit_component::ComponentEncoder,
30-
// };
31-
32-
// struct Ctx {
33-
// table: ResourceTable,
34-
// wasi: WasiCtx,
35-
// wasi_http: WasiHttpCtx,
36-
// }
37-
38-
// impl WasiHttpView for Ctx {
39-
// fn ctx(&mut self) -> &mut WasiHttpCtx {
40-
// &mut self.wasi_http
41-
// }
42-
43-
// fn table(&mut self) -> &mut ResourceTable {
44-
// &mut self.table
45-
// }
46-
// }
47-
48-
// impl WasiView for Ctx {
49-
// fn ctx(&mut self) -> WasiCtxView<'_> {
50-
// WasiCtxView {
51-
// ctx: &mut self.wasi,
52-
// table: &mut self.table,
53-
// }
54-
// }
55-
// }
56-
57-
// async fn build_component(name: &str) -> Result<Vec<u8>> {
58-
// static BUILD: OnceCell<()> = OnceCell::const_new();
59-
60-
// BUILD
61-
// .get_or_init(|| async {
62-
// assert!(
63-
// Command::new("cargo")
64-
// .current_dir("test-cases")
65-
// .args(["build", "--workspace", "--target", "wasm32-wasip1"])
66-
// .status()
67-
// .await
68-
// .unwrap()
69-
// .success(),
70-
// "cargo build failed"
71-
// );
72-
// })
73-
// .await;
74-
75-
// const ADAPTER_PATH: &str = "adapters/ab5a4484/wasi_snapshot_preview1.reactor.wasm";
76-
77-
// ComponentEncoder::default()
78-
// .validate(true)
79-
// .module(&fs::read(format!("target/wasm32-wasip1/debug/{name}.wasm")).await?)?
80-
// .adapter("wasi_snapshot_preview1", &fs::read(ADAPTER_PATH).await?)?
81-
// .encode()
82-
// }
83-
84-
// fn engine() -> &'static Engine {
85-
// static ENGINE: OnceLock<Engine> = OnceLock::new();
86-
87-
// ENGINE.get_or_init(|| {
88-
// let mut config = Config::new();
89-
// config.async_support(true);
90-
91-
// Engine::new(&config).unwrap()
92-
// })
93-
// }
94-
95-
// fn store_and_linker() -> Result<(Store<Ctx>, Linker<Ctx>)> {
96-
// let mut linker = Linker::new(engine());
97-
98-
// wasmtime_wasi_http::add_only_http_to_linker_async(&mut linker)?;
99-
// wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
100-
101-
// Ok((
102-
// Store::new(
103-
// engine(),
104-
// Ctx {
105-
// table: ResourceTable::new(),
106-
// wasi: WasiCtxBuilder::new().inherit_stdio().build(),
107-
// wasi_http: WasiHttpCtx::new(),
108-
// },
109-
// ),
110-
// linker,
111-
// ))
112-
// }
113-
114-
// #[tokio::test]
115-
// async fn simple_http() -> Result<()> {
116-
// use wasmtime_wasi_http::bindings::http::types::Scheme;
117-
118-
// let component = Component::new(engine(), build_component("simple_http").await?)?;
119-
120-
// let (mut store, linker) = store_and_linker()?;
121-
122-
// let request = Request::builder()
123-
// .method(hyper::Method::GET)
124-
// .uri("http://test.com:8080/")
125-
// .body(BoxBody::new(Empty::new().map_err(|_| unreachable!())))?;
126-
127-
// let request = store
128-
// .data_mut()
129-
// .new_incoming_request(Scheme::Http, request)?;
130-
131-
// let (response_tx, response_rx) = oneshot::channel();
132-
// let response = store.data_mut().new_response_outparam(response_tx)?;
133-
134-
// let proxy =
135-
// wasmtime_wasi_http::bindings::Proxy::instantiate_async(&mut store, &component, &linker)
136-
// .await?;
137-
138-
// let handle = task::spawn(async move {
139-
// proxy
140-
// .wasi_http_incoming_handler()
141-
// .call_handle(&mut store, request, response)
142-
// .await
143-
// });
144-
145-
// let response = match response_rx.await {
146-
// Ok(response) => response.context("guest failed to produce a response")?,
147-
148-
// Err(_) => {
149-
// handle
150-
// .await
151-
// .context("guest invocation panicked")?
152-
// .context("guest invocation failed")?;
153-
154-
// bail!("guest failed to produce a response prior to returning")
155-
// }
156-
// };
157-
158-
// assert!(response.status().is_success());
159-
// assert_eq!(
160-
// response.into_body().collect().await?.to_bytes().deref(),
161-
// b"Hello, world!"
162-
// );
163-
164-
// handle
165-
// .await
166-
// .context("guest invocation panicked")?
167-
// .context("guest invocation failed")?;
168-
169-
// Ok(())
170-
// }
12+
use {
13+
anyhow::Result,
14+
http_body_util::BodyExt,
15+
std::sync::OnceLock,
16+
tokio::{fs, process::Command, sync::OnceCell},
17+
wasmtime::{
18+
component::{Component, Linker, ResourceTable},
19+
Config, Engine, Store,
20+
},
21+
wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView},
22+
wasmtime_wasi_http::{p3::WasiHttpView, WasiHttpCtx},
23+
};
24+
25+
struct Ctx {
26+
table: ResourceTable,
27+
wasi: WasiCtx,
28+
wasi_http: WasiHttpCtx,
29+
}
30+
31+
impl WasiHttpView for Ctx {
32+
fn http(&mut self) -> wasmtime_wasi_http::p3::WasiHttpCtxView<'_> {
33+
wasmtime_wasi_http::p3::WasiHttpCtxView {
34+
hooks: wasmtime_wasi_http::p3::default_hooks(),
35+
table: &mut self.table,
36+
ctx: &mut self.wasi_http,
37+
}
38+
}
39+
}
40+
41+
impl WasiView for Ctx {
42+
fn ctx(&mut self) -> WasiCtxView<'_> {
43+
WasiCtxView {
44+
ctx: &mut self.wasi,
45+
table: &mut self.table,
46+
}
47+
}
48+
}
49+
50+
async fn build_component(name: &str) -> Result<Vec<u8>> {
51+
static BUILD: OnceCell<()> = OnceCell::const_new();
52+
53+
BUILD
54+
.get_or_init(|| async {
55+
assert!(
56+
Command::new("cargo")
57+
.current_dir(format!("test-cases/{name}"))
58+
.args(["build", "--workspace", "--target", "wasm32-wasip2"])
59+
.status()
60+
.await
61+
.unwrap()
62+
.success(),
63+
"cargo build failed"
64+
);
65+
})
66+
.await;
67+
68+
let out_file = format!(
69+
"test-cases/{name}/target/wasm32-wasip2/debug/{}.wasm",
70+
name.replace('-', "_")
71+
);
72+
Ok(fs::read(&out_file).await?)
73+
}
74+
75+
fn engine() -> &'static Engine {
76+
static ENGINE: OnceLock<Engine> = OnceLock::new();
77+
78+
ENGINE.get_or_init(|| {
79+
let mut config = Config::new();
80+
config.wasm_component_model_async(true);
81+
82+
Engine::new(&config).unwrap()
83+
})
84+
}
85+
86+
fn store_and_linker() -> Result<(Store<Ctx>, Linker<Ctx>)> {
87+
let mut linker = Linker::new(engine());
88+
89+
wasmtime_wasi_http::p3::add_to_linker(&mut linker)?;
90+
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
91+
92+
Ok((
93+
Store::new(
94+
engine(),
95+
Ctx {
96+
table: ResourceTable::new(),
97+
wasi: WasiCtxBuilder::new().inherit_stdio().build(),
98+
wasi_http: WasiHttpCtx::new(),
99+
},
100+
),
101+
linker,
102+
))
103+
}
104+
105+
#[tokio::test]
106+
async fn simple_http() -> Result<()> {
107+
let component = Component::new(engine(), build_component("simple-http").await?)?;
108+
109+
let (mut store, linker) = store_and_linker()?;
110+
111+
let (request, fut) = wasmtime_wasi_http::p3::Request::from_http(
112+
crate::http::Request::post("http://localhost:3000").body(crate::http::FullBody::new(
113+
bytes::Bytes::copy_from_slice(b"test"),
114+
))?,
115+
);
116+
117+
let http_service = wasmtime_wasi_http::p3::bindings::Service::instantiate_async(
118+
&mut store, &component, &linker,
119+
)
120+
.await?;
121+
122+
let (status, resp_bytes) = store
123+
.run_concurrent(async |accessor| {
124+
let resp_fut = async {
125+
let resp = http_service.handle(accessor, request).await??;
126+
let resp = accessor.with(|access| resp.into_http(access, async { Ok(()) }))?;
127+
let status = resp.status();
128+
let body = resp.into_body().collect().await?.to_bytes();
129+
anyhow::Ok((status, body))
130+
};
131+
let (status_body, ()) = tokio::try_join!(resp_fut, async {
132+
fut.await.map_err(|e| anyhow::anyhow!("{e:?}"))
133+
})?;
134+
anyhow::Ok(status_body)
135+
})
136+
.await??;
137+
138+
assert!(status.is_success());
139+
assert_eq!(resp_bytes.as_ref(), b"Hello, world!");
140+
141+
Ok(())
142+
}
171143

172144
// #[tokio::test]
173145
// async fn simple_redis() -> Result<()> {

0 commit comments

Comments
 (0)