#[cfg(feature = "p2")]
use crate::p2::bindings::http::types as p2;
#[cfg(feature = "p3")]
use crate::p3::bindings::http::types as p3;
use crate::{DEFAULT_FORBIDDEN_HEADERS, Error, RequestOptions, Result};
use bytes::Bytes;
use http::{HeaderName, uri::Scheme};
use http_body_util::combinators::UnsyncBoxBody;
use wasmtime::component::{HasData, ResourceTable};
pub struct WasiHttp;
impl HasData for WasiHttp {
type Data<'a> = WasiHttpCtxView<'a>;
}
pub trait WasiHttpView: Send {
fn http(&mut self) -> WasiHttpCtxView<'_>;
}
pub struct WasiHttpCtxView<'a> {
pub hooks: &'a mut dyn WasiHttpHooks,
pub table: &'a mut ResourceTable,
pub ctx: &'a mut WasiHttpCtx,
}
const DEFAULT_FIELD_SIZE_LIMIT: usize = 128 * 1024;
#[derive(Debug, Clone)]
pub struct WasiHttpCtx {
pub(crate) field_size_limit: usize,
}
impl WasiHttpCtx {
pub fn new() -> Self {
Self {
field_size_limit: DEFAULT_FIELD_SIZE_LIMIT,
}
}
pub fn set_field_size_limit(&mut self, limit: usize) {
self.field_size_limit = limit;
}
}
impl Default for WasiHttpCtx {
fn default() -> Self {
Self::new()
}
}
pub type WasiBody = UnsyncBoxBody<Bytes, Error>;
pub trait WasiHttpHooks: 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<WasiBody>,
options: Option<RequestOptions>,
fut: Box<dyn Future<Output = Result<(), Error>> + Send>,
) -> Box<
dyn Future<
Output = Result<(
http::Response<WasiBody>,
Box<dyn Future<Output = Result<(), Error>> + Send>,
)>,
> + Send,
> {
_ = fut;
Box::new(async move {
use http_body_util::BodyExt;
let (res, io) = crate::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<WasiBody>,
options: Option<RequestOptions>,
fut: Box<dyn Future<Output = Result<(), Error>> + Send>,
) -> Box<
dyn Future<
Output = Result<(
http::Response<WasiBody>,
Box<dyn Future<Output = Result<(), Error>> + Send>,
)>,
> + Send,
>;
#[cfg(feature = "p2")]
fn p2_outgoing_body_buffer_chunks(&mut self) -> usize {
crate::p2::DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS
}
#[cfg(feature = "p2")]
fn p2_outgoing_body_chunk_size(&mut self) -> usize {
crate::p2::DEFAULT_OUTGOING_BODY_CHUNK_SIZE
}
#[cfg(feature = "p2")]
fn p2_error_from_hyper(&mut self, err: &hyper::Error) -> p2::ErrorCode {
tracing::warn!("hyper error: {err:?}");
p2::ErrorCode::HttpProtocolError
}
#[cfg(feature = "p2")]
fn p2_error_from_connect(&mut self, err: &std::io::Error) -> p2::ErrorCode {
tracing::warn!("connect error: {err:?}");
p2::ErrorCode::ConnectionRefused
}
#[cfg(feature = "p2")]
fn p2_error_from_tls(&mut self, err: &std::io::Error) -> p2::ErrorCode {
tracing::warn!("tls error: {err:?}");
p2::ErrorCode::TlsProtocolError
}
#[cfg(all(feature = "p2", feature = "default-send-request"))]
fn p2_error_from_dns(&mut self, err: &rustls::pki_types::InvalidDnsNameError) -> p2::ErrorCode {
tracing::warn!("dns lookup error: {err:?}");
p2::ErrorCode::DnsError(p2::DnsErrorPayload {
rcode: Some("invalid dns name".to_string()),
info_code: None,
})
}
#[cfg(feature = "p3")]
fn p3_outgoing_body_chunk_size(&mut self) -> usize {
crate::p3::DEFAULT_OUTGOING_BODY_CHUNK_SIZE
}
#[cfg(feature = "p3")]
fn p3_error_from_hyper(&mut self, err: &hyper::Error) -> p3::ErrorCode {
tracing::warn!("hyper error: {err:?}");
p3::ErrorCode::HttpProtocolError
}
#[cfg(feature = "p3")]
fn p3_error_from_connect(&mut self, err: &std::io::Error) -> p3::ErrorCode {
tracing::warn!("connect error: {err:?}");
p3::ErrorCode::ConnectionRefused
}
#[cfg(feature = "p3")]
fn p3_error_from_tls(&mut self, err: &std::io::Error) -> p3::ErrorCode {
tracing::warn!("tls error: {err:?}");
p3::ErrorCode::TlsProtocolError
}
#[cfg(all(feature = "p3", feature = "default-send-request"))]
fn p3_error_from_dns(&mut self, err: &rustls::pki_types::InvalidDnsNameError) -> p3::ErrorCode {
tracing::warn!("dns lookup error: {err:?}");
p3::ErrorCode::DnsError(p3::DnsErrorPayload {
rcode: Some("invalid dns name".to_string()),
info_code: None,
})
}
}
#[cfg(feature = "default-send-request")]
pub fn default_hooks() -> &'static mut dyn WasiHttpHooks {
Default::default()
}
#[cfg(feature = "default-send-request")]
impl<'a> Default for &'a mut dyn WasiHttpHooks {
fn default() -> Self {
let x: &mut [(); 0] = &mut [];
x
}
}
#[doc(hidden)]
#[cfg(feature = "default-send-request")]
impl WasiHttpHooks for [(); 0] {}