|
1 | 1 | // 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", |
4 | 4 | // imports: { |
5 | 5 | // default: async, |
6 | 6 | // }, |
|
9 | 9 | // } |
10 | 10 | // }); |
11 | 11 |
|
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 | +} |
171 | 143 |
|
172 | 144 | // #[tokio::test] |
173 | 145 | // async fn simple_redis() -> Result<()> { |
|
0 commit comments