use super::{response::IncomingBody, Body, Error, Request, Response, Result};
use crate::io::{self, AsyncOutputStream, AsyncPollable};
use crate::time::Duration;
use wasi::http::types::{OutgoingBody, RequestOptions as WasiRequestOptions};
#[derive(Debug)]
pub struct Client {
options: Option<RequestOptions>,
}
impl Client {
pub fn new() -> Self {
Self { options: None }
}
pub async fn send<B: Body>(&self, req: Request<B>) -> Result<Response<IncomingBody>> {
let (wasi_req, body) = req.into_outgoing()?;
let wasi_body = wasi_req.body().unwrap();
let body_stream = wasi_body.write().unwrap();
let res = wasi::http::outgoing_handler::handle(wasi_req, self.wasi_options()?).unwrap();
io::copy(body, AsyncOutputStream::new(body_stream)).await?;
let trailers = None;
OutgoingBody::finish(wasi_body, trailers).unwrap();
AsyncPollable::new(res.subscribe()).wait_for().await;
let res = res.get().unwrap().unwrap()?;
Ok(Response::try_from_incoming_response(res)?)
}
pub fn set_connect_timeout(&mut self, d: impl Into<Duration>) {
self.options_mut().connect_timeout = Some(d.into());
}
pub fn set_first_byte_timeout(&mut self, d: impl Into<Duration>) {
self.options_mut().first_byte_timeout = Some(d.into());
}
pub fn set_between_bytes_timeout(&mut self, d: impl Into<Duration>) {
self.options_mut().between_bytes_timeout = Some(d.into());
}
fn options_mut(&mut self) -> &mut RequestOptions {
match &mut self.options {
Some(o) => o,
uninit => {
*uninit = Some(Default::default());
uninit.as_mut().unwrap()
}
}
}
fn wasi_options(&self) -> Result<Option<WasiRequestOptions>> {
self.options.as_ref().map(|o| o.to_wasi()).transpose()
}
}
#[derive(Default, Debug)]
struct RequestOptions {
connect_timeout: Option<Duration>,
first_byte_timeout: Option<Duration>,
between_bytes_timeout: Option<Duration>,
}
impl RequestOptions {
fn to_wasi(&self) -> Result<WasiRequestOptions> {
let wasi = WasiRequestOptions::new();
if let Some(timeout) = self.connect_timeout {
wasi.set_connect_timeout(Some(*timeout)).map_err(|()| {
Error::other("wasi-http implementation does not support connect timeout option")
})?;
}
if let Some(timeout) = self.first_byte_timeout {
wasi.set_first_byte_timeout(Some(*timeout)).map_err(|()| {
Error::other("wasi-http implementation does not support first byte timeout option")
})?;
}
if let Some(timeout) = self.between_bytes_timeout {
wasi.set_between_bytes_timeout(Some(*timeout))
.map_err(|()| {
Error::other(
"wasi-http implementation does not support between byte timeout option",
)
})?;
}
Ok(wasi)
}
}