16f6a514bSRoman Volosatovs use anyhow::{Context as _, Result, anyhow};
26f6a514bSRoman Volosatovs use core::fmt;
36f6a514bSRoman Volosatovs use futures::join;
46f6a514bSRoman Volosatovs
5*1cc0bcffSBailey Hayes use crate::p3::wasi::http::{client, types};
66f6a514bSRoman Volosatovs use crate::p3::{wit_future, wit_stream};
76f6a514bSRoman Volosatovs
86f6a514bSRoman Volosatovs pub struct Response {
96f6a514bSRoman Volosatovs pub status: types::StatusCode,
106f6a514bSRoman Volosatovs pub headers: Vec<(String, Vec<u8>)>,
116f6a514bSRoman Volosatovs pub body: Vec<u8>,
126f6a514bSRoman Volosatovs pub trailers: Option<Vec<(String, Vec<u8>)>>,
136f6a514bSRoman Volosatovs }
146f6a514bSRoman Volosatovs impl fmt::Debug for Response {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result156f6a514bSRoman Volosatovs fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166f6a514bSRoman Volosatovs let mut out = f.debug_struct("Response");
176f6a514bSRoman Volosatovs out.field("status", &self.status)
186f6a514bSRoman Volosatovs .field("headers", &self.headers);
196f6a514bSRoman Volosatovs if let Ok(body) = std::str::from_utf8(&self.body) {
206f6a514bSRoman Volosatovs out.field("body", &body);
216f6a514bSRoman Volosatovs } else {
226f6a514bSRoman Volosatovs out.field("body", &self.body);
236f6a514bSRoman Volosatovs }
246f6a514bSRoman Volosatovs out.field("trailers", &self.trailers);
256f6a514bSRoman Volosatovs out.finish()
266f6a514bSRoman Volosatovs }
276f6a514bSRoman Volosatovs }
286f6a514bSRoman Volosatovs
296f6a514bSRoman Volosatovs impl Response {
header(&self, name: &str) -> Option<&Vec<u8>>306f6a514bSRoman Volosatovs pub fn header(&self, name: &str) -> Option<&Vec<u8>> {
316f6a514bSRoman Volosatovs self.headers
326f6a514bSRoman Volosatovs .iter()
336f6a514bSRoman Volosatovs .find_map(|(k, v)| if k == name { Some(v) } else { None })
346f6a514bSRoman Volosatovs }
356f6a514bSRoman Volosatovs }
366f6a514bSRoman Volosatovs
request( method: types::Method, scheme: types::Scheme, authority: &str, path_with_query: &str, body: Option<&[u8]>, additional_headers: Option<&[(String, Vec<u8>)]>, connect_timeout: Option<u64>, first_by_timeout: Option<u64>, between_bytes_timeout: Option<u64>, ) -> Result<Response>376f6a514bSRoman Volosatovs pub async fn request(
386f6a514bSRoman Volosatovs method: types::Method,
396f6a514bSRoman Volosatovs scheme: types::Scheme,
406f6a514bSRoman Volosatovs authority: &str,
416f6a514bSRoman Volosatovs path_with_query: &str,
426f6a514bSRoman Volosatovs body: Option<&[u8]>,
436f6a514bSRoman Volosatovs additional_headers: Option<&[(String, Vec<u8>)]>,
446f6a514bSRoman Volosatovs connect_timeout: Option<u64>,
456f6a514bSRoman Volosatovs first_by_timeout: Option<u64>,
466f6a514bSRoman Volosatovs between_bytes_timeout: Option<u64>,
476f6a514bSRoman Volosatovs ) -> Result<Response> {
486f6a514bSRoman Volosatovs fn header_val(v: &str) -> Vec<u8> {
496f6a514bSRoman Volosatovs v.to_string().into_bytes()
506f6a514bSRoman Volosatovs }
516f6a514bSRoman Volosatovs let headers = types::Headers::from_list(
526f6a514bSRoman Volosatovs &[
536f6a514bSRoman Volosatovs &[
546f6a514bSRoman Volosatovs ("User-agent".to_string(), header_val("WASI-HTTP/0.0.1")),
556f6a514bSRoman Volosatovs ("Content-type".to_string(), header_val("application/json")),
566f6a514bSRoman Volosatovs ],
576f6a514bSRoman Volosatovs additional_headers.unwrap_or(&[]),
586f6a514bSRoman Volosatovs ]
596f6a514bSRoman Volosatovs .concat(),
606f6a514bSRoman Volosatovs )?;
616f6a514bSRoman Volosatovs
626f6a514bSRoman Volosatovs let options = types::RequestOptions::new();
636f6a514bSRoman Volosatovs options
646f6a514bSRoman Volosatovs .set_connect_timeout(connect_timeout)
656f6a514bSRoman Volosatovs .map_err(|_err| anyhow!("failed to set connect_timeout"))?;
666f6a514bSRoman Volosatovs options
676f6a514bSRoman Volosatovs .set_first_byte_timeout(first_by_timeout)
686f6a514bSRoman Volosatovs .map_err(|_err| anyhow!("failed to set first_byte_timeout"))?;
696f6a514bSRoman Volosatovs options
706f6a514bSRoman Volosatovs .set_between_bytes_timeout(between_bytes_timeout)
716f6a514bSRoman Volosatovs .map_err(|_err| anyhow!("failed to set between_bytes_timeout"))?;
726f6a514bSRoman Volosatovs
736f6a514bSRoman Volosatovs let (mut contents_tx, contents_rx) = wit_stream::new();
746f6a514bSRoman Volosatovs let (trailers_tx, trailers_rx) = wit_future::new(|| Ok(None));
756f6a514bSRoman Volosatovs let (request, transmit) =
766f6a514bSRoman Volosatovs types::Request::new(headers, Some(contents_rx), trailers_rx, Some(options));
776f6a514bSRoman Volosatovs
786f6a514bSRoman Volosatovs request
796f6a514bSRoman Volosatovs .set_method(&method)
806f6a514bSRoman Volosatovs .map_err(|()| anyhow!("failed to set method"))?;
816f6a514bSRoman Volosatovs request
826f6a514bSRoman Volosatovs .set_scheme(Some(&scheme))
836f6a514bSRoman Volosatovs .map_err(|()| anyhow!("failed to set scheme"))?;
846f6a514bSRoman Volosatovs request
856f6a514bSRoman Volosatovs .set_authority(Some(authority))
866f6a514bSRoman Volosatovs .map_err(|()| anyhow!("failed to set authority"))?;
876f6a514bSRoman Volosatovs request
886f6a514bSRoman Volosatovs .set_path_with_query(Some(&path_with_query))
896f6a514bSRoman Volosatovs .map_err(|()| anyhow!("failed to set path_with_query"))?;
906f6a514bSRoman Volosatovs
916f6a514bSRoman Volosatovs let (transmit, handle) = join!(
926f6a514bSRoman Volosatovs async { transmit.await.context("failed to transmit request") },
936f6a514bSRoman Volosatovs async {
94*1cc0bcffSBailey Hayes let response = client::send(request).await?;
956f6a514bSRoman Volosatovs let status = response.get_status_code();
966f6a514bSRoman Volosatovs let headers = response.get_headers().copy_all();
9753059995SRoman Volosatovs let (_, result_rx) = wit_future::new(|| Ok(()));
9853059995SRoman Volosatovs let (body_rx, trailers_rx) = types::Response::consume_body(response, result_rx);
996f6a514bSRoman Volosatovs let ((), rx) = join!(
1006f6a514bSRoman Volosatovs async {
1016f6a514bSRoman Volosatovs if let Some(buf) = body {
1026f6a514bSRoman Volosatovs let remaining = contents_tx.write_all(buf.into()).await;
1036f6a514bSRoman Volosatovs assert!(remaining.is_empty());
1046f6a514bSRoman Volosatovs }
1056f6a514bSRoman Volosatovs drop(contents_tx);
1066f6a514bSRoman Volosatovs // This can fail in HTTP/1.1, since the connection might already be closed
1076f6a514bSRoman Volosatovs _ = trailers_tx.write(Ok(None)).await;
1086f6a514bSRoman Volosatovs },
1096f6a514bSRoman Volosatovs async {
1106f6a514bSRoman Volosatovs let body = body_rx.collect().await;
1116f6a514bSRoman Volosatovs let trailers = trailers_rx.await.context("failed to read body")?;
1126f6a514bSRoman Volosatovs let trailers = trailers.map(|trailers| trailers.copy_all());
1136f6a514bSRoman Volosatovs anyhow::Ok(Response {
1146f6a514bSRoman Volosatovs status,
1156f6a514bSRoman Volosatovs headers,
1166f6a514bSRoman Volosatovs body,
1176f6a514bSRoman Volosatovs trailers,
1186f6a514bSRoman Volosatovs })
1196f6a514bSRoman Volosatovs }
1206f6a514bSRoman Volosatovs );
1216f6a514bSRoman Volosatovs rx
1226f6a514bSRoman Volosatovs },
1236f6a514bSRoman Volosatovs );
1246f6a514bSRoman Volosatovs let response = handle?;
1256f6a514bSRoman Volosatovs transmit?;
1266f6a514bSRoman Volosatovs Ok(response)
1276f6a514bSRoman Volosatovs }
128