Path: blob/main/crates/wasi-http/src/p3/mod.rs
3088 views
//! Experimental, unstable and incomplete implementation of wasip3 version of `wasi:http`.1//!2//! This module is under heavy development.3//! It is not compliant with semver and is not ready4//! for production use.5//!6//! Bug and security fixes limited to wasip3 will not be given patch releases.7//!8//! Documentation of this module may be incorrect or out-of-sync with the implementation.910pub mod bindings;11mod body;12mod conv;13mod host;14mod proxy;15mod request;16mod response;1718#[cfg(feature = "default-send-request")]19pub use request::default_send_request;20pub use request::{Request, RequestOptions};21pub use response::Response;2223use crate::p3::bindings::http::types::ErrorCode;24use crate::types::DEFAULT_FORBIDDEN_HEADERS;25use bindings::http::{client, types};26use bytes::Bytes;27use core::ops::Deref;28use http::HeaderName;29use http::uri::Scheme;30use http_body_util::combinators::UnsyncBoxBody;31use std::sync::Arc;32use wasmtime::component::{HasData, Linker, ResourceTable};33use wasmtime_wasi::TrappableError;3435pub(crate) type HttpResult<T> = Result<T, HttpError>;36pub(crate) type HttpError = TrappableError<types::ErrorCode>;3738pub(crate) type HeaderResult<T> = Result<T, HeaderError>;39pub(crate) type HeaderError = TrappableError<types::HeaderError>;4041pub(crate) type RequestOptionsResult<T> = Result<T, RequestOptionsError>;42pub(crate) type RequestOptionsError = TrappableError<types::RequestOptionsError>;4344/// The type for which this crate implements the `wasi:http` interfaces.45pub struct WasiHttp;4647impl HasData for WasiHttp {48type Data<'a> = WasiHttpCtxView<'a>;49}5051/// A trait which provides internal WASI HTTP state.52pub trait WasiHttpCtx: Send {53/// Whether a given header should be considered forbidden and not allowed.54fn is_forbidden_header(&mut self, name: &HeaderName) -> bool {55DEFAULT_FORBIDDEN_HEADERS.contains(name)56}5758/// Whether a given scheme should be considered supported.59///60/// `handle` will return [ErrorCode::HttpProtocolError] for unsupported schemes.61fn is_supported_scheme(&mut self, scheme: &Scheme) -> bool {62*scheme == Scheme::HTTP || *scheme == Scheme::HTTPS63}6465/// Whether to set `host` header in the request passed to `send_request`.66fn set_host_header(&mut self) -> bool {67true68}6970/// Scheme to default to, when not set by the guest.71///72/// If [None], `handle` will return [ErrorCode::HttpProtocolError]73/// for requests missing a scheme.74fn default_scheme(&mut self) -> Option<Scheme> {75Some(Scheme::HTTPS)76}7778/// Send an outgoing request.79///80/// This function will be used by the `wasi:http/handler#handle` implementation.81///82/// The specified [Future] `fut` will be used to communicate83/// a response processing error, if any.84/// For example, if the response body is consumed via `wasi:http/types.response#consume-body`,85/// a result will be sent on `fut`.86///87/// The returned [Future] can be used to communicate88/// a request processing error, if any, to the constructor of the request.89/// For example, if the request was constructed via `wasi:http/types.request#new`,90/// a result resolved from it will be forwarded to the guest on the future handle returned.91///92/// `Content-Length` of the request passed to this function will be validated, however no93/// `Content-Length` validation will be performed for the received response.94#[cfg(feature = "default-send-request")]95fn send_request(96&mut self,97request: http::Request<UnsyncBoxBody<Bytes, ErrorCode>>,98options: Option<RequestOptions>,99fut: Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,100) -> Box<101dyn Future<102Output = HttpResult<(103http::Response<UnsyncBoxBody<Bytes, ErrorCode>>,104Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,105)>,106> + Send,107> {108_ = fut;109Box::new(async move {110use http_body_util::BodyExt;111112let (res, io) = default_send_request(request, options).await?;113Ok((114res.map(BodyExt::boxed_unsync),115Box::new(io) as Box<dyn Future<Output = _> + Send>,116))117})118}119120/// Send an outgoing request.121///122/// This function will be used by the `wasi:http/handler#handle` implementation.123///124/// The specified [Future] `fut` will be used to communicate125/// a response processing error, if any.126/// For example, if the response body is consumed via `wasi:http/types.response#consume-body`,127/// a result will be sent on `fut`.128///129/// The returned [Future] can be used to communicate130/// a request processing error, if any, to the constructor of the request.131/// For example, if the request was constructed via `wasi:http/types.request#new`,132/// a result resolved from it will be forwarded to the guest on the future handle returned.133///134/// `Content-Length` of the request passed to this function will be validated, however no135/// `Content-Length` validation will be performed for the received response.136#[cfg(not(feature = "default-send-request"))]137fn send_request(138&mut self,139request: http::Request<UnsyncBoxBody<Bytes, ErrorCode>>,140options: Option<RequestOptions>,141fut: Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,142) -> Box<143dyn Future<144Output = HttpResult<(145http::Response<UnsyncBoxBody<Bytes, ErrorCode>>,146Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,147)>,148> + Send,149>;150}151152/// Default implementation of [WasiHttpCtx].153#[cfg(feature = "default-send-request")]154#[derive(Clone, Default)]155pub struct DefaultWasiHttpCtx;156157#[cfg(feature = "default-send-request")]158impl WasiHttpCtx for DefaultWasiHttpCtx {}159160/// View into [WasiHttpCtx] implementation and [ResourceTable].161pub struct WasiHttpCtxView<'a> {162/// Mutable reference to the WASI HTTP context.163pub ctx: &'a mut dyn WasiHttpCtx,164165/// Mutable reference to table used to manage resources.166pub table: &'a mut ResourceTable,167}168169/// A trait which provides internal WASI HTTP state.170pub trait WasiHttpView: Send {171/// Return a [WasiHttpCtxView] from mutable reference to self.172fn http(&mut self) -> WasiHttpCtxView<'_>;173}174175/// Add all interfaces from this module into the `linker` provided.176///177/// This function will add all interfaces implemented by this module to the178/// [`Linker`], which corresponds to the `wasi:http/imports` world supported by179/// this module.180///181/// # Example182///183/// ```184/// use wasmtime::{Engine, Result, Store, Config};185/// use wasmtime::component::{Linker, ResourceTable};186/// use wasmtime_wasi_http::p3::{DefaultWasiHttpCtx, WasiHttpCtxView, WasiHttpView};187///188/// fn main() -> Result<()> {189/// let mut config = Config::new();190/// config.wasm_component_model_async(true);191/// let engine = Engine::new(&config)?;192///193/// let mut linker = Linker::<MyState>::new(&engine);194/// wasmtime_wasi_http::p3::add_to_linker(&mut linker)?;195/// // ... add any further functionality to `linker` if desired ...196///197/// let mut store = Store::new(198/// &engine,199/// MyState::default(),200/// );201///202/// // ... use `linker` to instantiate within `store` ...203///204/// Ok(())205/// }206///207/// #[derive(Default)]208/// struct MyState {209/// http: DefaultWasiHttpCtx,210/// table: ResourceTable,211/// }212///213/// impl WasiHttpView for MyState {214/// fn http(&mut self) -> WasiHttpCtxView<'_> {215/// WasiHttpCtxView {216/// ctx: &mut self.http,217/// table: &mut self.table,218/// }219/// }220/// }221/// ```222pub fn add_to_linker<T>(linker: &mut Linker<T>) -> wasmtime::Result<()>223where224T: WasiHttpView + 'static,225{226client::add_to_linker::<_, WasiHttp>(linker, T::http)?;227types::add_to_linker::<_, WasiHttp>(linker, T::http)?;228Ok(())229}230231/// An [Arc], which may be immutable.232///233/// In `wasi:http` resources like `fields` or `request-options` may be234/// mutable or immutable. This construct is used to model them efficiently.235pub enum MaybeMutable<T> {236/// Clone-on-write, mutable [Arc]237Mutable(Arc<T>),238/// Immutable [Arc]239Immutable(Arc<T>),240}241242impl<T> From<MaybeMutable<T>> for Arc<T> {243fn from(v: MaybeMutable<T>) -> Self {244v.into_arc()245}246}247248impl<T> Deref for MaybeMutable<T> {249type Target = Arc<T>;250251fn deref(&self) -> &Self::Target {252match self {253Self::Mutable(v) | Self::Immutable(v) => v,254}255}256}257258impl<T> MaybeMutable<T> {259/// Construct a mutable [`MaybeMutable`].260pub fn new_mutable(v: impl Into<Arc<T>>) -> Self {261Self::Mutable(v.into())262}263264/// Construct a mutable [`MaybeMutable`] filling it with default `T`.265pub fn new_mutable_default() -> Self266where267T: Default,268{269Self::new_mutable(T::default())270}271272/// Construct an immutable [`MaybeMutable`].273pub fn new_immutable(v: impl Into<Arc<T>>) -> Self {274Self::Immutable(v.into())275}276277/// Unwrap [`MaybeMutable`] into [`Arc`].278pub fn into_arc(self) -> Arc<T> {279match self {280Self::Mutable(v) | Self::Immutable(v) => v,281}282}283284/// If this [`MaybeMutable`] is [`Mutable`](MaybeMutable::Mutable),285/// return a mutable reference to it, otherwise return `None`.286///287/// Internally, this will use [`Arc::make_mut`] and will clone the underlying288/// value, if multiple strong references to the inner [`Arc`] exist.289pub fn get_mut(&mut self) -> Option<&mut T>290where291T: Clone,292{293match self {294Self::Mutable(v) => Some(Arc::make_mut(v)),295Self::Immutable(..) => None,296}297}298}299300301