1*6f6a514bSRoman Volosatovs use anyhow::{Context as _, Result, anyhow};
2*6f6a514bSRoman Volosatovs use core::fmt;
3*6f6a514bSRoman Volosatovs use futures::join;
4*6f6a514bSRoman Volosatovs 
5*6f6a514bSRoman Volosatovs use crate::p3::wasi::http::{handler, types};
6*6f6a514bSRoman Volosatovs use crate::p3::{wit_future, wit_stream};
7*6f6a514bSRoman Volosatovs 
8*6f6a514bSRoman Volosatovs pub struct Response {
9*6f6a514bSRoman Volosatovs     pub status: types::StatusCode,
10*6f6a514bSRoman Volosatovs     pub headers: Vec<(String, Vec<u8>)>,
11*6f6a514bSRoman Volosatovs     pub body: Vec<u8>,
12*6f6a514bSRoman Volosatovs     pub trailers: Option<Vec<(String, Vec<u8>)>>,
13*6f6a514bSRoman Volosatovs }
14*6f6a514bSRoman Volosatovs impl fmt::Debug for Response {
15*6f6a514bSRoman Volosatovs     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16*6f6a514bSRoman Volosatovs         let mut out = f.debug_struct("Response");
17*6f6a514bSRoman Volosatovs         out.field("status", &self.status)
18*6f6a514bSRoman Volosatovs             .field("headers", &self.headers);
19*6f6a514bSRoman Volosatovs         if let Ok(body) = std::str::from_utf8(&self.body) {
20*6f6a514bSRoman Volosatovs             out.field("body", &body);
21*6f6a514bSRoman Volosatovs         } else {
22*6f6a514bSRoman Volosatovs             out.field("body", &self.body);
23*6f6a514bSRoman Volosatovs         }
24*6f6a514bSRoman Volosatovs         out.field("trailers", &self.trailers);
25*6f6a514bSRoman Volosatovs         out.finish()
26*6f6a514bSRoman Volosatovs     }
27*6f6a514bSRoman Volosatovs }
28*6f6a514bSRoman Volosatovs 
29*6f6a514bSRoman Volosatovs impl Response {
30*6f6a514bSRoman Volosatovs     pub fn header(&self, name: &str) -> Option<&Vec<u8>> {
31*6f6a514bSRoman Volosatovs         self.headers
32*6f6a514bSRoman Volosatovs             .iter()
33*6f6a514bSRoman Volosatovs             .find_map(|(k, v)| if k == name { Some(v) } else { None })
34*6f6a514bSRoman Volosatovs     }
35*6f6a514bSRoman Volosatovs }
36*6f6a514bSRoman Volosatovs 
37*6f6a514bSRoman Volosatovs pub async fn request(
38*6f6a514bSRoman Volosatovs     method: types::Method,
39*6f6a514bSRoman Volosatovs     scheme: types::Scheme,
40*6f6a514bSRoman Volosatovs     authority: &str,
41*6f6a514bSRoman Volosatovs     path_with_query: &str,
42*6f6a514bSRoman Volosatovs     body: Option<&[u8]>,
43*6f6a514bSRoman Volosatovs     additional_headers: Option<&[(String, Vec<u8>)]>,
44*6f6a514bSRoman Volosatovs     connect_timeout: Option<u64>,
45*6f6a514bSRoman Volosatovs     first_by_timeout: Option<u64>,
46*6f6a514bSRoman Volosatovs     between_bytes_timeout: Option<u64>,
47*6f6a514bSRoman Volosatovs ) -> Result<Response> {
48*6f6a514bSRoman Volosatovs     fn header_val(v: &str) -> Vec<u8> {
49*6f6a514bSRoman Volosatovs         v.to_string().into_bytes()
50*6f6a514bSRoman Volosatovs     }
51*6f6a514bSRoman Volosatovs     let headers = types::Headers::from_list(
52*6f6a514bSRoman Volosatovs         &[
53*6f6a514bSRoman Volosatovs             &[
54*6f6a514bSRoman Volosatovs                 ("User-agent".to_string(), header_val("WASI-HTTP/0.0.1")),
55*6f6a514bSRoman Volosatovs                 ("Content-type".to_string(), header_val("application/json")),
56*6f6a514bSRoman Volosatovs             ],
57*6f6a514bSRoman Volosatovs             additional_headers.unwrap_or(&[]),
58*6f6a514bSRoman Volosatovs         ]
59*6f6a514bSRoman Volosatovs         .concat(),
60*6f6a514bSRoman Volosatovs     )?;
61*6f6a514bSRoman Volosatovs 
62*6f6a514bSRoman Volosatovs     let options = types::RequestOptions::new();
63*6f6a514bSRoman Volosatovs     options
64*6f6a514bSRoman Volosatovs         .set_connect_timeout(connect_timeout)
65*6f6a514bSRoman Volosatovs         .map_err(|_err| anyhow!("failed to set connect_timeout"))?;
66*6f6a514bSRoman Volosatovs     options
67*6f6a514bSRoman Volosatovs         .set_first_byte_timeout(first_by_timeout)
68*6f6a514bSRoman Volosatovs         .map_err(|_err| anyhow!("failed to set first_byte_timeout"))?;
69*6f6a514bSRoman Volosatovs     options
70*6f6a514bSRoman Volosatovs         .set_between_bytes_timeout(between_bytes_timeout)
71*6f6a514bSRoman Volosatovs         .map_err(|_err| anyhow!("failed to set between_bytes_timeout"))?;
72*6f6a514bSRoman Volosatovs 
73*6f6a514bSRoman Volosatovs     let (mut contents_tx, contents_rx) = wit_stream::new();
74*6f6a514bSRoman Volosatovs     let (trailers_tx, trailers_rx) = wit_future::new(|| Ok(None));
75*6f6a514bSRoman Volosatovs     let (request, transmit) =
76*6f6a514bSRoman Volosatovs         types::Request::new(headers, Some(contents_rx), trailers_rx, Some(options));
77*6f6a514bSRoman Volosatovs 
78*6f6a514bSRoman Volosatovs     request
79*6f6a514bSRoman Volosatovs         .set_method(&method)
80*6f6a514bSRoman Volosatovs         .map_err(|()| anyhow!("failed to set method"))?;
81*6f6a514bSRoman Volosatovs     request
82*6f6a514bSRoman Volosatovs         .set_scheme(Some(&scheme))
83*6f6a514bSRoman Volosatovs         .map_err(|()| anyhow!("failed to set scheme"))?;
84*6f6a514bSRoman Volosatovs     request
85*6f6a514bSRoman Volosatovs         .set_authority(Some(authority))
86*6f6a514bSRoman Volosatovs         .map_err(|()| anyhow!("failed to set authority"))?;
87*6f6a514bSRoman Volosatovs     request
88*6f6a514bSRoman Volosatovs         .set_path_with_query(Some(&path_with_query))
89*6f6a514bSRoman Volosatovs         .map_err(|()| anyhow!("failed to set path_with_query"))?;
90*6f6a514bSRoman Volosatovs 
91*6f6a514bSRoman Volosatovs     let (transmit, handle) = join!(
92*6f6a514bSRoman Volosatovs         async { transmit.await.context("failed to transmit request") },
93*6f6a514bSRoman Volosatovs         async {
94*6f6a514bSRoman Volosatovs             let response = handler::handle(request).await?;
95*6f6a514bSRoman Volosatovs             let status = response.get_status_code();
96*6f6a514bSRoman Volosatovs             let headers = response.get_headers().copy_all();
97*6f6a514bSRoman Volosatovs             let (body_rx, trailers_rx) = response
98*6f6a514bSRoman Volosatovs                 .consume_body()
99*6f6a514bSRoman Volosatovs                 .expect("failed to get response body");
100*6f6a514bSRoman Volosatovs             let ((), rx) = join!(
101*6f6a514bSRoman Volosatovs                 async {
102*6f6a514bSRoman Volosatovs                     if let Some(buf) = body {
103*6f6a514bSRoman Volosatovs                         let remaining = contents_tx.write_all(buf.into()).await;
104*6f6a514bSRoman Volosatovs                         assert!(remaining.is_empty());
105*6f6a514bSRoman Volosatovs                     }
106*6f6a514bSRoman Volosatovs                     drop(contents_tx);
107*6f6a514bSRoman Volosatovs                     // This can fail in HTTP/1.1, since the connection might already be closed
108*6f6a514bSRoman Volosatovs                     _ = trailers_tx.write(Ok(None)).await;
109*6f6a514bSRoman Volosatovs                 },
110*6f6a514bSRoman Volosatovs                 async {
111*6f6a514bSRoman Volosatovs                     let body = body_rx.collect().await;
112*6f6a514bSRoman Volosatovs                     let trailers = trailers_rx.await.context("failed to read body")?;
113*6f6a514bSRoman Volosatovs                     let trailers = trailers.map(|trailers| trailers.copy_all());
114*6f6a514bSRoman Volosatovs                     anyhow::Ok(Response {
115*6f6a514bSRoman Volosatovs                         status,
116*6f6a514bSRoman Volosatovs                         headers,
117*6f6a514bSRoman Volosatovs                         body,
118*6f6a514bSRoman Volosatovs                         trailers,
119*6f6a514bSRoman Volosatovs                     })
120*6f6a514bSRoman Volosatovs                 }
121*6f6a514bSRoman Volosatovs             );
122*6f6a514bSRoman Volosatovs             rx
123*6f6a514bSRoman Volosatovs         },
124*6f6a514bSRoman Volosatovs     );
125*6f6a514bSRoman Volosatovs     let response = handle?;
126*6f6a514bSRoman Volosatovs     transmit?;
127*6f6a514bSRoman Volosatovs     Ok(response)
128*6f6a514bSRoman Volosatovs }
129