Skip to content

Commit

Permalink
feat(wasi-http): expose sync api for components
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardomourar committed Aug 26, 2023
1 parent 61dec00 commit d9e3a44
Show file tree
Hide file tree
Showing 6 changed files with 430 additions and 9 deletions.
11 changes: 11 additions & 0 deletions crates/wasi-http/src/component_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,3 +618,14 @@ pub fn add_component_to_linker<T: WasiHttpView>(
)?;
Ok(())
}

pub mod sync {
use crate::WasiHttpView;

pub fn add_component_to_linker<T: WasiHttpView>(
linker: &mut wasmtime::Linker<T>,
get_cx: impl Fn(&mut T) -> &mut T + Send + Sync + Copy + 'static,
) -> anyhow::Result<()> {
unimplemented!("linker synchronous version")
}
}
52 changes: 49 additions & 3 deletions crates/wasi-http/src/http_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,40 @@ impl<T: WasiHttpView> crate::bindings::http::outgoing_handler::Host for T {
}
}

#[cfg(feature = "sync")]
pub mod sync {
use crate::bindings::http::outgoing_handler::{
Host as AsyncHost, RequestOptions as AsyncRequestOptions,
};
use crate::bindings::sync::http::types::{
FutureIncomingResponse, OutgoingRequest, RequestOptions,
};
use crate::WasiHttpView;
use wasmtime_wasi::preview2::in_tokio;

// same boilerplate everywhere, converting between two identical types with different
// definition sites. one day wasmtime-wit-bindgen will make all this unnecessary
impl From<RequestOptions> for AsyncRequestOptions {
fn from(other: RequestOptions) -> Self {
Self {
connect_timeout_ms: other.connect_timeout_ms,
first_byte_timeout_ms: other.first_byte_timeout_ms,
between_bytes_timeout_ms: other.between_bytes_timeout_ms,
}
}
}

impl<T: WasiHttpView> crate::bindings::sync::http::outgoing_handler::Host for T {
fn handle(
&mut self,
request_id: OutgoingRequest,
options: Option<RequestOptions>,
) -> wasmtime::Result<FutureIncomingResponse> {
in_tokio(async { AsyncHost::handle(self, request_id, options.map(|v| v.into())).await })
}
}
}

fn port_for_scheme(scheme: &Option<Scheme>) -> &str {
match scheme {
Some(s) => match s {
Expand Down Expand Up @@ -60,6 +94,7 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
request_id: OutgoingRequest,
options: Option<RequestOptions>,
) -> wasmtime::Result<FutureIncomingResponse, crate::bindings::http::types::Error> {
tracing::debug!("preparing outgoing request");
let opts = options.unwrap_or(
// TODO: Configurable defaults here?
RequestOptions {
Expand All @@ -80,6 +115,7 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
.get_request(request_id)
.context("[handle_async] getting request")?
.clone();
tracing::debug!("http request retrieved from table");

let method = match request.method() {
crate::bindings::http::types::Method::Get => Method::GET,
Expand Down Expand Up @@ -119,6 +155,7 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
};
let tcp_stream = TcpStream::connect(authority.clone()).await?;
let mut sender = if scheme == "https://" {
tracing::debug!("initiating client connection client with TLS");
#[cfg(not(any(target_arch = "riscv64", target_arch = "s390x")))]
{
//TODO: uncomment this code and make the tls implementation a feature decision.
Expand Down Expand Up @@ -158,7 +195,7 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
let (s, conn) = t?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
println!("[host/client] Connection failed: {:?}", err);
}
});
s
Expand All @@ -168,6 +205,7 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
"unsupported architecture for SSL".to_string(),
));
} else {
tracing::debug!("initiating client connection without TLS");
let t = timeout(
connect_timeout,
hyper::client::conn::http1::handshake(tcp_stream),
Expand All @@ -176,14 +214,15 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
let (s, conn) = t?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
println!("[host/client] Connection failed: {:?}", err);
}
});
s
};

let url = scheme.to_owned() + &request.authority() + &request.path_with_query();

tracing::debug!("request to url {:?}", &url);
let mut call = Request::builder()
.method(method)
.uri(url)
Expand Down Expand Up @@ -227,8 +266,11 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
}
None => Empty::<Bytes>::new().boxed(),
};
let t = timeout(first_bytes_timeout, sender.send_request(call.body(body)?)).await?;
let request = call.body(body)?;
tracing::trace!("hyper request {:?}", request);
let t = timeout(first_bytes_timeout, sender.send_request(request)).await?;
let mut res = t?;
tracing::trace!("hyper response {:?}", res);
response.status = res.status().as_u16();

let mut map = ActiveFields::new();
Expand All @@ -246,10 +288,12 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
let mut buf: Vec<u8> = Vec::new();
while let Some(next) = timeout(between_bytes_timeout, res.frame()).await? {
let frame = next?;
tracing::debug!("response body next frame");
if let Some(chunk) = frame.data_ref() {
buf.extend_from_slice(chunk);
}
if let Some(trailers) = frame.trailers_ref() {
tracing::debug!("response trailers present");
let mut map = ActiveFields::new();
for (name, value) in trailers.iter() {
let key = name.to_string();
Expand All @@ -267,6 +311,7 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
.push_fields(Box::new(map))
.context("[handle_async] pushing response trailers")?;
response.set_trailers(trailers);
tracing::debug!("http trailers saved to table");
}
}

Expand All @@ -283,6 +328,7 @@ impl<T: WasiHttpView> WasiHttpViewExt for T {
.get_response_mut(response_id)
.context("[handle_async] getting mutable response")?;
response.set_body(stream_id);
tracing::debug!("http response saved to table with id {:?}", response_id);

self.http_ctx_mut().streams.insert(stream_id, stream);

Expand Down
18 changes: 18 additions & 0 deletions crates/wasi-http/src/incoming_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,21 @@ impl<T: WasiHttpView> crate::bindings::http::incoming_handler::Host for T {
anyhow::bail!("unimplemented: [incoming_handler] handle")
}
}

#[cfg(feature = "sync")]
pub mod sync {
use crate::bindings::http::incoming_handler::Host as AsyncHost;
use crate::bindings::sync::http::types::{IncomingRequest, ResponseOutparam};
use crate::WasiHttpView;
use wasmtime_wasi::preview2::in_tokio;

impl<T: WasiHttpView> crate::bindings::sync::http::incoming_handler::Host for T {
fn handle(
&mut self,
request: IncomingRequest,
response_out: ResponseOutparam,
) -> wasmtime::Result<()> {
in_tokio(async { AsyncHost::handle(self, request, response_out).await })
}
}
}
31 changes: 29 additions & 2 deletions crates/wasi-http/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::component_impl::add_component_to_linker;
pub use crate::http_impl::WasiHttpViewExt;
pub use crate::types::{WasiHttpCtx, WasiHttpView};
use core::fmt::Formatter;
Expand All @@ -12,6 +11,26 @@ pub mod types;
pub mod types_impl;

pub mod bindings {
#[cfg(feature = "sync")]
pub mod sync {
pub(crate) mod _internal {
wasmtime::component::bindgen!({
path: "wit",
interfaces: "
import wasi:http/incoming-handler
import wasi:http/outgoing-handler
import wasi:http/types
",
tracing: true,
with: {
"wasi:io/streams": wasmtime_wasi::preview2::bindings::sync_io::io::streams,
"wasi:poll/poll": wasmtime_wasi::preview2::bindings::sync_io::poll::poll,
}
});
}
pub use self::_internal::wasi::http;
}

pub(crate) mod _internal_rest {
wasmtime::component::bindgen!({
path: "wit",
Expand All @@ -33,7 +52,15 @@ pub mod bindings {
}

pub fn add_to_linker<T: WasiHttpView>(linker: &mut wasmtime::Linker<T>) -> anyhow::Result<()> {
add_component_to_linker::<T>(linker, |t| t)
crate::component_impl::add_component_to_linker::<T>(linker, |t| t)
}

pub mod sync {
use crate::r#struct::WasiHttpView;

pub fn add_to_linker<T: WasiHttpView>(linker: &mut wasmtime::Linker<T>) -> anyhow::Result<()> {
crate::component_impl::sync::add_component_to_linker::<T>(linker, |t| t)
}
}

impl std::error::Error for crate::bindings::http::types::Error {}
Expand Down
41 changes: 37 additions & 4 deletions crates/wasi-http/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,46 @@ wasmtime::component::bindgen!({

pub fn add_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
where
T: WasiHttpView
+ bindings::http::incoming_handler::Host
+ bindings::http::outgoing_handler::Host
+ bindings::http::types::Host,
T: WasiHttpView + bindings::http::types::Host,
{
bindings::http::incoming_handler::add_to_linker(l, |t| t)?;
bindings::http::outgoing_handler::add_to_linker(l, |t| t)?;
bindings::http::types::add_to_linker(l, |t| t)?;
Ok(())
}

#[cfg(feature = "sync")]
pub mod sync {
use crate::{bindings, WasiHttpView};
use wasmtime_wasi::preview2;

wasmtime::component::bindgen!({
world: "wasi:http/proxy",
tracing: true,
async: false,
with: {
"wasi:cli/stderr": preview2::bindings::cli::stderr,
"wasi:cli/stdin": preview2::bindings::cli::stdin,
"wasi:cli/stdout": preview2::bindings::cli::stdout,
"wasi:clocks/monotonic-clock": preview2::bindings::clocks::monotonic_clock,
"wasi:clocks/timezone": preview2::bindings::clocks::timezone,
"wasi:clocks/wall-clock": preview2::bindings::clocks::wall_clock,
"wasi:http/incoming-handler": bindings::sync::http::incoming_handler,
"wasi:http/outgoing-handler": bindings::sync::http::outgoing_handler,
"wasi:http/types": bindings::sync::http::types,
"wasi:io/streams": preview2::bindings::sync_io::io::streams,
"wasi:poll/poll": preview2::bindings::sync_io::poll::poll,
"wasi:random/random": preview2::bindings::random::random,
},
});

pub fn add_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
where
T: WasiHttpView + bindings::sync::http::types::Host,
{
bindings::sync::http::incoming_handler::add_to_linker(l, |t| t)?;
bindings::sync::http::outgoing_handler::add_to_linker(l, |t| t)?;
bindings::sync::http::types::add_to_linker(l, |t| t)?;
Ok(())
}
}
Loading

0 comments on commit d9e3a44

Please sign in to comment.