//! # Wasmtime's WASI HTTP Implementation1//!2//! This crate is Wasmtime's host implementation of the `wasi:http` package as3//! part of WASIp2. This crate's implementation is primarily built on top of4//! [`hyper`] and [`tokio`].5//!6//! # WASI HTTP Interfaces7//!8//! This crate contains implementations of the following interfaces:9//!10//! * [`wasi:http/incoming-handler`]11//! * [`wasi:http/outgoing-handler`]12//! * [`wasi:http/types`]13//!14//! The crate also contains an implementation of the [`wasi:http/proxy`] world.15//!16//! [`wasi:http/proxy`]: crate::bindings::Proxy17//! [`wasi:http/outgoing-handler`]: crate::bindings::http::outgoing_handler::Host18//! [`wasi:http/types`]: crate::bindings::http::types::Host19//! [`wasi:http/incoming-handler`]: crate::bindings::exports::wasi::http::incoming_handler::Guest20//!21//! This crate is very similar to [`wasmtime_wasi`] in the it uses the22//! `bindgen!` macro in Wasmtime to generate bindings to interfaces. Bindings23//! are located in the [`bindings`] module.24//!25//! # The `WasiHttpView` trait26//!27//! All `bindgen!`-generated `Host` traits are implemented in terms of a28//! [`WasiHttpView`] trait which provides basic access to [`WasiHttpCtx`],29//! configuration for WASI HTTP, and a [`wasmtime_wasi::ResourceTable`], the30//! state for all host-defined component model resources.31//!32//! The [`WasiHttpView`] trait additionally offers a few other configuration33//! methods such as [`WasiHttpView::send_request`] to customize how outgoing34//! HTTP requests are handled.35//!36//! # Async and Sync37//!38//! There are both asynchronous and synchronous bindings in this crate. For39//! example [`add_to_linker_async`] is for asynchronous embedders and40//! [`add_to_linker_sync`] is for synchronous embedders. Note that under the41//! hood both versions are implemented with `async` on top of [`tokio`].42//!43//! # Examples44//!45//! Usage of this crate is done through a few steps to get everything hooked up:46//!47//! 1. First implement [`WasiHttpView`] for your type which is the `T` in48//! [`wasmtime::Store<T>`].49//! 2. Add WASI HTTP interfaces to a [`wasmtime::component::Linker<T>`]. There50//! are a few options of how to do this:51//! * Use [`add_to_linker_async`] to bundle all interfaces in52//! `wasi:http/proxy` together53//! * Use [`add_only_http_to_linker_async`] to add only HTTP interfaces but54//! no others. This is useful when working with55//! [`wasmtime_wasi::p2::add_to_linker_async`] for example.56//! * Add individual interfaces such as with the57//! [`bindings::http::outgoing_handler::add_to_linker`] function.58//! 3. Use [`ProxyPre`](bindings::ProxyPre) to pre-instantiate a component59//! before serving requests.60//! 4. When serving requests use61//! [`ProxyPre::instantiate_async`](bindings::ProxyPre::instantiate_async)62//! to create instances and handle HTTP requests.63//!64//! A standalone example of doing all this looks like:65//!66//! ```no_run67//! use wasmtime::bail;68//! use hyper::server::conn::http1;69//! use std::sync::Arc;70//! use tokio::net::TcpListener;71//! use wasmtime::component::{Component, Linker, ResourceTable};72//! use wasmtime::{Engine, Result, Store};73//! use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};74//! use wasmtime_wasi_http::bindings::ProxyPre;75//! use wasmtime_wasi_http::bindings::http::types::Scheme;76//! use wasmtime_wasi_http::body::HyperOutgoingBody;77//! use wasmtime_wasi_http::io::TokioIo;78//! use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView};79//!80//! #[tokio::main]81//! async fn main() -> Result<()> {82//! let component = std::env::args().nth(1).unwrap();83//!84//! // Prepare the `Engine` for Wasmtime85//! let engine = Engine::default();86//!87//! // Compile the component on the command line to machine code88//! let component = Component::from_file(&engine, &component)?;89//!90//! // Prepare the `ProxyPre` which is a pre-instantiated version of the91//! // component that we have. This will make per-request instantiation92//! // much quicker.93//! let mut linker = Linker::new(&engine);94//! wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;95//! wasmtime_wasi_http::add_only_http_to_linker_async(&mut linker)?;96//! let pre = ProxyPre::new(linker.instantiate_pre(&component)?)?;97//!98//! // Prepare our server state and start listening for connections.99//! let server = Arc::new(MyServer { pre });100//! let listener = TcpListener::bind("127.0.0.1:8000").await?;101//! println!("Listening on {}", listener.local_addr()?);102//!103//! loop {104//! // Accept a TCP connection and serve all of its requests in a separate105//! // tokio task. Note that for now this only works with HTTP/1.1.106//! let (client, addr) = listener.accept().await?;107//! println!("serving new client from {addr}");108//!109//! let server = server.clone();110//! tokio::task::spawn(async move {111//! if let Err(e) = http1::Builder::new()112//! .keep_alive(true)113//! .serve_connection(114//! TokioIo::new(client),115//! hyper::service::service_fn(move |req| {116//! let server = server.clone();117//! async move { server.handle_request(req).await }118//! }),119//! )120//! .await121//! {122//! eprintln!("error serving client[{addr}]: {e:?}");123//! }124//! });125//! }126//! }127//!128//! struct MyServer {129//! pre: ProxyPre<MyClientState>,130//! }131//!132//! impl MyServer {133//! async fn handle_request(134//! &self,135//! req: hyper::Request<hyper::body::Incoming>,136//! ) -> Result<hyper::Response<HyperOutgoingBody>> {137//! // Create per-http-request state within a `Store` and prepare the138//! // initial resources passed to the `handle` function.139//! let mut store = Store::new(140//! self.pre.engine(),141//! MyClientState {142//! table: ResourceTable::new(),143//! wasi: WasiCtx::builder().inherit_stdio().build(),144//! http: WasiHttpCtx::new(),145//! },146//! );147//! let (sender, receiver) = tokio::sync::oneshot::channel();148//! let req = store.data_mut().new_incoming_request(Scheme::Http, req)?;149//! let out = store.data_mut().new_response_outparam(sender)?;150//! let pre = self.pre.clone();151//!152//! // Run the http request itself in a separate task so the task can153//! // optionally continue to execute beyond after the initial154//! // headers/response code are sent.155//! let task = tokio::task::spawn(async move {156//! let proxy = pre.instantiate_async(&mut store).await?;157//!158//! if let Err(e) = proxy159//! .wasi_http_incoming_handler()160//! .call_handle(store, req, out)161//! .await162//! {163//! return Err(e);164//! }165//!166//! Ok(())167//! });168//!169//! match receiver.await {170//! // If the client calls `response-outparam::set` then one of these171//! // methods will be called.172//! Ok(Ok(resp)) => Ok(resp),173//! Ok(Err(e)) => Err(e.into()),174//!175//! // Otherwise the `sender` will get dropped along with the `Store`176//! // meaning that the oneshot will get disconnected and here we can177//! // inspect the `task` result to see what happened178//! Err(_) => {179//! let e = match task.await {180//! Ok(Ok(())) => {181//! bail!("guest never invoked `response-outparam::set` method")182//! }183//! Ok(Err(e)) => e,184//! Err(e) => e.into(),185//! };186//! return Err(e.context("guest never invoked `response-outparam::set` method"));187//! }188//! }189//! }190//! }191//!192//! struct MyClientState {193//! wasi: WasiCtx,194//! http: WasiHttpCtx,195//! table: ResourceTable,196//! }197//!198//! impl WasiView for MyClientState {199//! fn ctx(&mut self) -> WasiCtxView<'_> {200//! WasiCtxView { ctx: &mut self.wasi, table: &mut self.table }201//! }202//! }203//!204//! impl WasiHttpView for MyClientState {205//! fn ctx(&mut self) -> &mut WasiHttpCtx {206//! &mut self.http207//! }208//!209//! fn table(&mut self) -> &mut ResourceTable {210//! &mut self.table211//! }212//! }213//! ```214215#![deny(missing_docs)]216#![doc(test(attr(deny(warnings))))]217#![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))]218#![cfg_attr(docsrs, feature(doc_cfg))]219220mod error;221mod http_impl;222mod types_impl;223224pub mod body;225#[cfg(feature = "component-model-async")]226pub mod handler;227pub mod io;228pub mod types;229230pub mod bindings;231232#[cfg(feature = "p3")]233pub mod p3;234235pub use crate::error::{236HttpError, HttpResult, http_request_error, hyper_request_error, hyper_response_error,237};238#[doc(inline)]239pub use crate::types::{240DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS, DEFAULT_OUTGOING_BODY_CHUNK_SIZE, WasiHttpCtx,241WasiHttpImpl, WasiHttpView,242};243use http::header::CONTENT_LENGTH;244use wasmtime::component::{HasData, Linker};245246/// Add all of the `wasi:http/proxy` world's interfaces to a [`wasmtime::component::Linker`].247///248/// This function will add the `async` variant of all interfaces into the249/// `Linker` provided. For embeddings with async support disabled see250/// [`add_to_linker_sync`] instead.251///252/// # Example253///254/// ```255/// use wasmtime::{Engine, Result};256/// use wasmtime::component::{ResourceTable, Linker};257/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};258/// use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView};259///260/// fn main() -> Result<()> {261/// let engine = Engine::default();262///263/// let mut linker = Linker::<MyState>::new(&engine);264/// wasmtime_wasi_http::add_to_linker_async(&mut linker)?;265/// // ... add any further functionality to `linker` if desired ...266///267/// Ok(())268/// }269///270/// struct MyState {271/// ctx: WasiCtx,272/// http_ctx: WasiHttpCtx,273/// table: ResourceTable,274/// }275///276/// impl WasiHttpView for MyState {277/// fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http_ctx }278/// fn table(&mut self) -> &mut ResourceTable { &mut self.table }279/// }280///281/// impl WasiView for MyState {282/// fn ctx(&mut self) -> WasiCtxView<'_> {283/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }284/// }285/// }286/// ```287pub fn add_to_linker_async<T>(l: &mut wasmtime::component::Linker<T>) -> wasmtime::Result<()>288where289T: WasiHttpView + wasmtime_wasi::WasiView + 'static,290{291wasmtime_wasi::p2::add_to_linker_proxy_interfaces_async(l)?;292add_only_http_to_linker_async(l)293}294295/// A slimmed down version of [`add_to_linker_async`] which only adds296/// `wasi:http` interfaces to the linker.297///298/// This is useful when using [`wasmtime_wasi::p2::add_to_linker_async`] for299/// example to avoid re-adding the same interfaces twice.300pub fn add_only_http_to_linker_async<T>(301l: &mut wasmtime::component::Linker<T>,302) -> wasmtime::Result<()>303where304T: WasiHttpView + 'static,305{306let options = crate::bindings::LinkOptions::default(); // FIXME: Thread through to the CLI options.307crate::bindings::http::outgoing_handler::add_to_linker::<_, WasiHttp<T>>(l, |x| {308WasiHttpImpl(x)309})?;310crate::bindings::http::types::add_to_linker::<_, WasiHttp<T>>(l, &options.into(), |x| {311WasiHttpImpl(x)312})?;313314Ok(())315}316317struct WasiHttp<T>(T);318319impl<T: 'static> HasData for WasiHttp<T> {320type Data<'a> = WasiHttpImpl<&'a mut T>;321}322323/// Add all of the `wasi:http/proxy` world's interfaces to a [`wasmtime::component::Linker`].324///325/// This function will add the `sync` variant of all interfaces into the326/// `Linker` provided. For embeddings with async support see327/// [`add_to_linker_async`] instead.328///329/// # Example330///331/// ```332/// use wasmtime::{Engine, Result, Config};333/// use wasmtime::component::{ResourceTable, Linker};334/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};335/// use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView};336///337/// fn main() -> Result<()> {338/// let config = Config::default();339/// let engine = Engine::new(&config)?;340///341/// let mut linker = Linker::<MyState>::new(&engine);342/// wasmtime_wasi_http::add_to_linker_sync(&mut linker)?;343/// // ... add any further functionality to `linker` if desired ...344///345/// Ok(())346/// }347///348/// struct MyState {349/// ctx: WasiCtx,350/// http_ctx: WasiHttpCtx,351/// table: ResourceTable,352/// }353/// impl WasiHttpView for MyState {354/// fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http_ctx }355/// fn table(&mut self) -> &mut ResourceTable { &mut self.table }356/// }357/// impl WasiView for MyState {358/// fn ctx(&mut self) -> WasiCtxView<'_> {359/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }360/// }361/// }362/// ```363pub fn add_to_linker_sync<T>(l: &mut Linker<T>) -> wasmtime::Result<()>364where365T: WasiHttpView + wasmtime_wasi::WasiView + 'static,366{367wasmtime_wasi::p2::add_to_linker_proxy_interfaces_sync(l)?;368add_only_http_to_linker_sync(l)369}370371/// A slimmed down version of [`add_to_linker_sync`] which only adds372/// `wasi:http` interfaces to the linker.373///374/// This is useful when using [`wasmtime_wasi::p2::add_to_linker_sync`] for375/// example to avoid re-adding the same interfaces twice.376pub fn add_only_http_to_linker_sync<T>(l: &mut Linker<T>) -> wasmtime::Result<()>377where378T: WasiHttpView + 'static,379{380let options = crate::bindings::LinkOptions::default(); // FIXME: Thread through to the CLI options.381crate::bindings::sync::http::outgoing_handler::add_to_linker::<_, WasiHttp<T>>(l, |x| {382WasiHttpImpl(x)383})?;384crate::bindings::sync::http::types::add_to_linker::<_, WasiHttp<T>>(l, &options.into(), |x| {385WasiHttpImpl(x)386})?;387388Ok(())389}390391/// Extract the `Content-Length` header value from a [`http::HeaderMap`], returning `None` if it's not392/// present. This function will return `Err` if it's not possible to parse the `Content-Length`393/// header.394fn get_content_length(headers: &http::HeaderMap) -> wasmtime::Result<Option<u64>> {395let Some(v) = headers.get(CONTENT_LENGTH) else {396return Ok(None);397};398let v = v.to_str()?;399let v = v.parse()?;400Ok(Some(v))401}402403404