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