Skip to main content

wasmtime_wasi_http/p3/
mod.rs

1//! Experimental, unstable and incomplete implementation of wasip3 version of `wasi:http`.
2//!
3//! This module is under heavy development.
4//! It is not compliant with semver and is not ready
5//! for production use.
6//!
7//! Bug and security fixes limited to wasip3 will not be given patch releases.
8//!
9//! Documentation of this module may be incorrect or out-of-sync with the implementation.
10
11pub mod bindings;
12mod body;
13mod conv;
14mod helpers;
15mod host;
16mod proxy;
17mod request;
18mod response;
19
20pub use request::Request;
21pub use response::Response;
22
23/// The default value configured for [`WasiHttpHooks::p3_outgoing_body_chunk_size`].
24///
25/// [`WasiHttpHooks::p3_outgoing_body_chunk_size`]: crate::WasiHttpHooks::p3_outgoing_body_chunk_size
26pub const DEFAULT_OUTGOING_BODY_CHUNK_SIZE: usize = 1024 * 1024;
27
28use crate::{FieldMapError, WasiHttp, WasiHttpView};
29use bindings::http::{client, types};
30use core::ops::Deref;
31use std::sync::Arc;
32use wasmtime::component::Linker;
33use wasmtime_wasi::TrappableError;
34
35pub(crate) type HttpResult<T> = Result<T, HttpError>;
36pub(crate) type HttpError = TrappableError<types::ErrorCode>;
37
38pub(crate) type HeaderResult<T> = Result<T, HeaderError>;
39pub(crate) type HeaderError = TrappableError<types::HeaderError>;
40
41impl From<FieldMapError> for HeaderError {
42    fn from(e: FieldMapError) -> Self {
43        match e {
44            FieldMapError::Immutable => types::HeaderError::Immutable.into(),
45            FieldMapError::InvalidHeaderName | FieldMapError::InvalidHeaderValue => {
46                types::HeaderError::InvalidSyntax.into()
47            }
48            FieldMapError::TooManyFields | FieldMapError::TotalSizeTooBig => {
49                types::HeaderError::SizeExceeded.into()
50            }
51            FieldMapError::Forbidden => types::HeaderError::Forbidden.into(),
52        }
53    }
54}
55
56pub(crate) type RequestOptionsResult<T> = Result<T, RequestOptionsError>;
57pub(crate) type RequestOptionsError = TrappableError<types::RequestOptionsError>;
58
59/// Add all interfaces from this module into the `linker` provided.
60///
61/// This function will add all interfaces implemented by this module to the
62/// [`Linker`], which corresponds to the `wasi:http/imports` world supported by
63/// this module.
64///
65/// # Example
66///
67/// ```
68/// use wasmtime::{Engine, Result, Store, Config};
69/// use wasmtime::component::{Linker, ResourceTable};
70/// use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpCtxView, WasiHttpView};
71///
72/// fn main() -> Result<()> {
73///     let mut config = Config::new();
74///     config.wasm_component_model_async(true);
75///     let engine = Engine::new(&config)?;
76///
77///     let mut linker = Linker::<MyState>::new(&engine);
78///     wasmtime_wasi_http::p3::add_to_linker(&mut linker)?;
79///     // ... add any further functionality to `linker` if desired ...
80///
81///     let mut store = Store::new(
82///         &engine,
83///         MyState::default(),
84///     );
85///
86///     // ... use `linker` to instantiate within `store` ...
87///
88///     Ok(())
89/// }
90///
91/// #[derive(Default)]
92/// struct MyState {
93///     http: WasiHttpCtx,
94///     table: ResourceTable,
95/// }
96///
97/// impl WasiHttpView for MyState {
98///     fn http(&mut self) -> WasiHttpCtxView<'_> {
99///         WasiHttpCtxView {
100///             ctx: &mut self.http,
101///             table: &mut self.table,
102///             hooks: Default::default(),
103///         }
104///     }
105/// }
106/// ```
107pub fn add_to_linker<T>(linker: &mut Linker<T>) -> wasmtime::Result<()>
108where
109    T: WasiHttpView + 'static,
110{
111    client::add_to_linker::<_, WasiHttp>(linker, T::http)?;
112    types::add_to_linker::<_, WasiHttp>(linker, T::http)?;
113    Ok(())
114}
115
116/// An [Arc], which may be immutable.
117///
118/// In `wasi:http` resources like `fields` or `request-options` may be
119/// mutable or immutable. This construct is used to model them efficiently.
120pub enum MaybeMutable<T> {
121    /// Clone-on-write, mutable [Arc]
122    Mutable(Arc<T>),
123    /// Immutable [Arc]
124    Immutable(Arc<T>),
125}
126
127impl<T> From<MaybeMutable<T>> for Arc<T> {
128    fn from(v: MaybeMutable<T>) -> Self {
129        v.into_arc()
130    }
131}
132
133impl<T> Deref for MaybeMutable<T> {
134    type Target = Arc<T>;
135
136    fn deref(&self) -> &Self::Target {
137        match self {
138            Self::Mutable(v) | Self::Immutable(v) => v,
139        }
140    }
141}
142
143impl<T> MaybeMutable<T> {
144    /// Construct a mutable [`MaybeMutable`].
145    pub fn new_mutable(v: impl Into<Arc<T>>) -> Self {
146        Self::Mutable(v.into())
147    }
148
149    /// Construct a mutable [`MaybeMutable`] filling it with default `T`.
150    pub fn new_mutable_default() -> Self
151    where
152        T: Default,
153    {
154        Self::new_mutable(T::default())
155    }
156
157    /// Construct an immutable [`MaybeMutable`].
158    pub fn new_immutable(v: impl Into<Arc<T>>) -> Self {
159        Self::Immutable(v.into())
160    }
161
162    /// Unwrap [`MaybeMutable`] into [`Arc`].
163    pub fn into_arc(self) -> Arc<T> {
164        match self {
165            Self::Mutable(v) | Self::Immutable(v) => v,
166        }
167    }
168
169    /// If this [`MaybeMutable`] is [`Mutable`](MaybeMutable::Mutable),
170    /// return a mutable reference to it, otherwise return `None`.
171    ///
172    /// Internally, this will use [`Arc::make_mut`] and will clone the underlying
173    /// value, if multiple strong references to the inner [`Arc`] exist.
174    pub fn get_mut(&mut self) -> Option<&mut T>
175    where
176        T: Clone,
177    {
178        match self {
179            Self::Mutable(v) => Some(Arc::make_mut(v)),
180            Self::Immutable(..) => None,
181        }
182    }
183}