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