pub mod bindings;
mod body;
mod conv;
mod host;
mod proxy;
mod request;
mod response;
#[cfg(feature = "default-send-request")]
pub use request::default_send_request;
pub use request::{Request, RequestOptions};
pub use response::Response;
use crate::p3::bindings::http::types::ErrorCode;
use crate::types::DEFAULT_FORBIDDEN_HEADERS;
use bindings::http::{client, types};
use bytes::Bytes;
use core::ops::Deref;
use http::HeaderName;
use http::uri::Scheme;
use http_body_util::combinators::UnsyncBoxBody;
use std::sync::Arc;
use wasmtime::component::{HasData, Linker, ResourceTable};
use wasmtime_wasi::TrappableError;
pub(crate) type HttpResult<T> = Result<T, HttpError>;
pub(crate) type HttpError = TrappableError<types::ErrorCode>;
pub(crate) type HeaderResult<T> = Result<T, HeaderError>;
pub(crate) type HeaderError = TrappableError<types::HeaderError>;
pub(crate) type RequestOptionsResult<T> = Result<T, RequestOptionsError>;
pub(crate) type RequestOptionsError = TrappableError<types::RequestOptionsError>;
pub struct WasiHttp;
impl HasData for WasiHttp {
type Data<'a> = WasiHttpCtxView<'a>;
}
pub trait WasiHttpCtx: Send {
fn is_forbidden_header(&mut self, name: &HeaderName) -> bool {
DEFAULT_FORBIDDEN_HEADERS.contains(name)
}
fn is_supported_scheme(&mut self, scheme: &Scheme) -> bool {
*scheme == Scheme::HTTP || *scheme == Scheme::HTTPS
}
fn set_host_header(&mut self) -> bool {
true
}
fn default_scheme(&mut self) -> Option<Scheme> {
Some(Scheme::HTTPS)
}
#[cfg(feature = "default-send-request")]
fn send_request(
&mut self,
request: http::Request<UnsyncBoxBody<Bytes, ErrorCode>>,
options: Option<RequestOptions>,
fut: Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,
) -> Box<
dyn Future<
Output = HttpResult<(
http::Response<UnsyncBoxBody<Bytes, ErrorCode>>,
Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,
)>,
> + Send,
> {
_ = fut;
Box::new(async move {
use http_body_util::BodyExt;
let (res, io) = default_send_request(request, options).await?;
Ok((
res.map(BodyExt::boxed_unsync),
Box::new(io) as Box<dyn Future<Output = _> + Send>,
))
})
}
#[cfg(not(feature = "default-send-request"))]
fn send_request(
&mut self,
request: http::Request<UnsyncBoxBody<Bytes, ErrorCode>>,
options: Option<RequestOptions>,
fut: Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,
) -> Box<
dyn Future<
Output = HttpResult<(
http::Response<UnsyncBoxBody<Bytes, ErrorCode>>,
Box<dyn Future<Output = Result<(), ErrorCode>> + Send>,
)>,
> + Send,
>;
}
#[cfg(feature = "default-send-request")]
#[derive(Clone, Default)]
pub struct DefaultWasiHttpCtx;
#[cfg(feature = "default-send-request")]
impl WasiHttpCtx for DefaultWasiHttpCtx {}
pub struct WasiHttpCtxView<'a> {
pub ctx: &'a mut dyn WasiHttpCtx,
pub table: &'a mut ResourceTable,
}
pub trait WasiHttpView: Send {
fn http(&mut self) -> WasiHttpCtxView<'_>;
}
pub fn add_to_linker<T>(linker: &mut Linker<T>) -> wasmtime::Result<()>
where
T: WasiHttpView + 'static,
{
client::add_to_linker::<_, WasiHttp>(linker, T::http)?;
types::add_to_linker::<_, WasiHttp>(linker, T::http)?;
Ok(())
}
pub enum MaybeMutable<T> {
Mutable(Arc<T>),
Immutable(Arc<T>),
}
impl<T> From<MaybeMutable<T>> for Arc<T> {
fn from(v: MaybeMutable<T>) -> Self {
v.into_arc()
}
}
impl<T> Deref for MaybeMutable<T> {
type Target = Arc<T>;
fn deref(&self) -> &Self::Target {
match self {
Self::Mutable(v) | Self::Immutable(v) => v,
}
}
}
impl<T> MaybeMutable<T> {
pub fn new_mutable(v: impl Into<Arc<T>>) -> Self {
Self::Mutable(v.into())
}
pub fn new_mutable_default() -> Self
where
T: Default,
{
Self::new_mutable(T::default())
}
pub fn new_immutable(v: impl Into<Arc<T>>) -> Self {
Self::Immutable(v.into())
}
pub fn into_arc(self) -> Arc<T> {
match self {
Self::Mutable(v) | Self::Immutable(v) => v,
}
}
pub fn get_mut(&mut self) -> Option<&mut T>
where
T: Clone,
{
match self {
Self::Mutable(v) => Some(Arc::make_mut(v)),
Self::Immutable(..) => None,
}
}
}