p2 only.Expand description
§Wasmtime’s WASI HTTPp2 Implementation
This module is Wasmtime’s host implementation of the wasi:http package as
part of WASIp2. This crate’s implementation is primarily built on top of
[hyper] and [tokio].
§WASI HTTP Interfaces
This crate contains implementations of the following interfaces:
The crate also contains an implementation of the wasi:http/proxy world.
This crate is very similar to wasmtime_wasi in the it uses the
bindgen! macro in Wasmtime to generate bindings to interfaces. Bindings
are located in the bindings module.
§The WasiHttp{View,Hooks} traits
All bindgen!-generated Host traits are implemented for the
WasiHttpCtxView type. This type is created from a store’s data T
through the WasiHttpView trait. The add_to_linker_async function,
for example, uses WasiHttpView to acquire the context view.
The WasiHttpCtxView structure requires that a ResourceTable and
WasiHttpCtx live within the store. This is store-specific state that is
used to implement various APIs and store host state.
The final hooks field within WasiHttpCtxView is a trait object of
WasiHttpHooks. This provides a few more hooks, dynamically, to configure
how wasi:http behaves. For example WasiHttpHooks::send_request can
customize how outgoing HTTP requests are handled. The hooks field can be
initialized with the default_hooks function for the default behavior.
§Async and Sync
There are both asynchronous and synchronous bindings in this crate. For
example add_to_linker_async is for asynchronous embedders and
add_to_linker_sync is for synchronous embedders. Note that under the
hood both versions are implemented with async on top of [tokio].
§Examples
Usage of this crate is done through a few steps to get everything hooked up:
- First implement
WasiHttpViewfor your type which is theTinwasmtime::Store<T>. - Add WASI HTTP interfaces to a
wasmtime::component::Linker<T>. There are a few options of how to do this:- Use
add_to_linker_asyncto bundle all interfaces inwasi:http/proxytogether - Use
add_only_http_to_linker_asyncto add only HTTP interfaces but no others. This is useful when working withwasmtime_wasi::p2::add_to_linker_asyncfor example. - Add individual interfaces such as with the
bindings::http::outgoing_handler::add_to_linkerfunction.
- Use
- Use
ProxyPreto pre-instantiate a component before serving requests. - When serving requests use
ProxyPre::instantiate_asyncto create instances and handle HTTP requests.
A standalone example of doing all this looks like:
use wasmtime::bail;
use hyper::server::conn::http1;
use std::sync::Arc;
use tokio::net::TcpListener;
use wasmtime::component::{Component, Linker, ResourceTable};
use wasmtime::{Engine, Result, Store};
use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
use wasmtime_wasi_http::p2::bindings::ProxyPre;
use wasmtime_wasi_http::p2::bindings::http::types::Scheme;
use wasmtime_wasi_http::p2::body::HyperOutgoingBody;
use wasmtime_wasi_http::io::TokioIo;
use wasmtime_wasi_http::{WasiHttpCtx, p2::{WasiHttpView, WasiHttpCtxView}};
#[tokio::main]
async fn main() -> Result<()> {
let component = std::env::args().nth(1).unwrap();
// Prepare the `Engine` for Wasmtime
let engine = Engine::default();
// Compile the component on the command line to machine code
let component = Component::from_file(&engine, &component)?;
// Prepare the `ProxyPre` which is a pre-instantiated version of the
// component that we have. This will make per-request instantiation
// much quicker.
let mut linker = Linker::new(&engine);
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
wasmtime_wasi_http::p2::add_only_http_to_linker_async(&mut linker)?;
let pre = ProxyPre::new(linker.instantiate_pre(&component)?)?;
// Prepare our server state and start listening for connections.
let server = Arc::new(MyServer { pre });
let listener = TcpListener::bind("127.0.0.1:8000").await?;
println!("Listening on {}", listener.local_addr()?);
loop {
// Accept a TCP connection and serve all of its requests in a separate
// tokio task. Note that for now this only works with HTTP/1.1.
let (client, addr) = listener.accept().await?;
println!("serving new client from {addr}");
let server = server.clone();
tokio::task::spawn(async move {
if let Err(e) = http1::Builder::new()
.keep_alive(true)
.serve_connection(
TokioIo::new(client),
hyper::service::service_fn(move |req| {
let server = server.clone();
async move { server.handle_request(req).await }
}),
)
.await
{
eprintln!("error serving client[{addr}]: {e:?}");
}
});
}
}
struct MyServer {
pre: ProxyPre<MyClientState>,
}
impl MyServer {
async fn handle_request(
&self,
req: hyper::Request<hyper::body::Incoming>,
) -> Result<hyper::Response<HyperOutgoingBody>> {
// Create per-http-request state within a `Store` and prepare the
// initial resources passed to the `handle` function.
let mut store = Store::new(
self.pre.engine(),
MyClientState {
table: ResourceTable::new(),
wasi: WasiCtx::builder().inherit_stdio().build(),
http: WasiHttpCtx::new(),
},
);
let (sender, receiver) = tokio::sync::oneshot::channel();
let req = store.data_mut().http().new_incoming_request(Scheme::Http, req)?;
let out = store.data_mut().http().new_response_outparam(sender)?;
let pre = self.pre.clone();
// Run the http request itself in a separate task so the task can
// optionally continue to execute beyond after the initial
// headers/response code are sent.
let task = tokio::task::spawn(async move {
let proxy = pre.instantiate_async(&mut store).await?;
if let Err(e) = proxy
.wasi_http_incoming_handler()
.call_handle(store, req, out)
.await
{
return Err(e);
}
Ok(())
});
match receiver.await {
// If the client calls `response-outparam::set` then one of these
// methods will be called.
Ok(Ok(resp)) => Ok(resp),
Ok(Err(e)) => Err(e.into()),
// Otherwise the `sender` will get dropped along with the `Store`
// meaning that the oneshot will get disconnected and here we can
// inspect the `task` result to see what happened
Err(_) => {
let e = match task.await {
Ok(Ok(())) => {
bail!("guest never invoked `response-outparam::set` method")
}
Ok(Err(e)) => e,
Err(e) => e.into(),
};
return Err(e.context("guest never invoked `response-outparam::set` method"));
}
}
}
}
struct MyClientState {
wasi: WasiCtx,
http: WasiHttpCtx,
table: ResourceTable,
}
impl WasiView for MyClientState {
fn ctx(&mut self) -> WasiCtxView<'_> {
WasiCtxView { ctx: &mut self.wasi, table: &mut self.table }
}
}
impl WasiHttpView for MyClientState {
fn http(&mut self) -> WasiHttpCtxView<'_> {
WasiHttpCtxView {
ctx: &mut self.http,
table: &mut self.table,
hooks: Default::default(),
}
}
}Modules§
- bindings
- Raw bindings to the
wasi:httppackage. - body
- Implementation of the
wasi:http/typesinterface’s various body types. - types
- Implements the base structure that will provide the implementation of the wasi-http API.
Structs§
- Header
Error - A
wasi:http-specific error type used to represent either a trap or antypes::HeaderError. - Http
Error - A
wasi:http-specific error type used to represent either a trap or anErrorCode. - Wasi
Http - The type for which this crate implements the
wasi:httpinterfaces. - Wasi
Http CtxView - Structure which
wasi:httpHost-style traits are implemented for.
Constants§
- DEFAULT_
OUTGOING_ BODY_ BUFFER_ CHUNKS - The default value configured for
WasiHttpHooks::outgoing_body_buffer_chunksinWasiHttpView. - DEFAULT_
OUTGOING_ BODY_ CHUNK_ SIZE - The default value configured for
WasiHttpHooks::outgoing_body_chunk_sizeinWasiHttpView.
Traits§
- Wasi
Http Hooks - A trait which provides hooks into internal WASI HTTP operations.
- Wasi
Http View - A trait used to project state that this crate needs to implement
wasi:httpfrom theselftype.
Functions§
- add_
only_ http_ to_ linker_ async - A slimmed down version of
add_to_linker_asyncwhich only addswasi:httpinterfaces to the linker. - add_
only_ http_ to_ linker_ sync - A slimmed down version of
add_to_linker_syncwhich only addswasi:httpinterfaces to the linker. - add_
to_ linker_ async - Add all of the
wasi:http/proxyworld’s interfaces to awasmtime::component::Linker. - add_
to_ linker_ sync - Add all of the
wasi:http/proxyworld’s interfaces to awasmtime::component::Linker. - default_
hooks default-send-request - Returns a value suitable for the
WasiHttpCtxView::hooksfield which has the default behavior forwasi:http. - default_
send_ request default-send-request - The default implementation of how an outgoing request is sent.
- default_
send_ request_ handler default-send-request - The underlying implementation of how an outgoing request is sent. This should likely be spawned in a task.
- http_
request_ error - Translate a [
http::Error] to a wasi-httpErrorCodein the context of a request. - hyper_
request_ error - Translate a [
hyper::Error] to a wasi-httpErrorCodein the context of a request. - hyper_
response_ error - Translate a [
hyper::Error] to a wasi-httpErrorCodein the context of a response.
Type Aliases§
- Header
Result - A
Resulttype where the error type defaults toHeaderError. - Http
Result - A
Resulttype where the error type defaults toHttpError.