wasi_http_client/
request.rs1use crate::Response;
2use anyhow::{anyhow, Error, Result};
3use serde::Serialize;
4use std::time::Duration;
5use url::Url;
6use wasi::http::{
7 outgoing_handler,
8 types::{
9 FieldKey, FieldValue, Headers, Method, OutgoingBody, OutgoingRequest, RequestOptions,
10 Scheme,
11 },
12};
13
14pub struct RequestBuilder {
15 request: Result<Request>,
17}
18
19impl RequestBuilder {
20 pub(crate) fn new(method: Method, url: &str) -> Self {
21 Self {
22 request: Url::parse(url)
23 .map_or_else(|e| Err(Error::new(e)), |url| Ok(Request::new(method, url))),
24 }
25 }
26
27 pub fn header<K, V>(mut self, key: K, value: V) -> Self
40 where
41 K: Into<FieldKey>,
42 V: Into<FieldValue>,
43 {
44 let mut err = None;
45 if let Ok(ref mut req) = self.request {
46 if let Err(e) = req.headers.set(&key.into(), &[value.into()]) {
47 err = Some(e);
48 }
49 }
50 if let Some(e) = err {
51 self.request = Err(e.into());
52 }
53 self
54 }
55
56 pub fn headers<K, V, I>(mut self, headers: I) -> Self
71 where
72 K: Into<FieldKey>,
73 V: Into<FieldValue>,
74 I: IntoIterator<Item = (K, V)>,
75 {
76 let mut err = None;
77 if let Ok(ref mut req) = self.request {
78 let entries: Vec<(FieldKey, FieldValue)> = headers
79 .into_iter()
80 .map(|(k, v)| (k.into(), v.into()))
81 .collect();
82 match Headers::from_list(&entries) {
83 Ok(fields) => req.headers = fields,
84 Err(e) => err = Some(e),
85 }
86 }
87 if let Some(e) = err {
88 self.request = Err(e.into());
89 }
90 self
91 }
92
93 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> Self {
106 let mut err = None;
107 if let Ok(ref mut req) = self.request {
108 let mut pairs = req.url.query_pairs_mut();
109 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
110 if let Err(e) = query.serialize(serializer) {
111 err = Some(e);
112 }
113 }
114 if let Some(e) = err {
115 self.request = Err(e.into());
116 }
117 self
118 }
119
120 pub fn body(mut self, body: &[u8]) -> Self {
133 if let Ok(ref mut req) = self.request {
134 req.body = Some(body.into());
135 }
136 self
137 }
138
139 #[cfg(feature = "json")]
157 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
158 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> Self {
159 let mut err = None;
160 if let Ok(ref mut req) = self.request {
161 if let Err(e) = req
162 .headers
163 .set(&"Content-Type".to_string(), &["application/json".into()])
164 {
165 err = Some(e.into());
166 }
167 match serde_json::to_vec(json) {
168 Ok(data) => req.body = Some(data),
169 Err(e) => err = Some(e.into()),
170 }
171 }
172 if let Some(e) = err {
173 self.request = Err(e);
174 }
175 self
176 }
177
178 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> Self {
191 let mut err = None;
192 if let Ok(ref mut req) = self.request {
193 if let Err(e) = req.headers.set(
194 &"Content-Type".to_string(),
195 &["application/x-www-form-urlencoded".into()],
196 ) {
197 err = Some(e.into());
198 }
199 match serde_urlencoded::to_string(form) {
200 Ok(data) => req.body = Some(data.into()),
201 Err(e) => err = Some(e.into()),
202 }
203 }
204 if let Some(e) = err {
205 self.request = Err(e);
206 }
207 self
208 }
209
210 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
224 if let Ok(ref mut req) = self.request {
225 req.connect_timeout = Some(timeout.as_nanos() as u64);
226 }
227 self
228 }
229
230 pub fn send(self) -> Result<Response> {
232 match self.request {
233 Ok(req) => req.send(),
234 Err(e) => Err(e),
235 }
236 }
237}
238
239struct Request {
240 method: Method,
241 url: Url,
242 headers: Headers,
243 body: Option<Vec<u8>>,
244 connect_timeout: Option<u64>,
245}
246
247impl Request {
248 fn new(method: Method, url: Url) -> Self {
249 Self {
250 method,
251 url,
252 headers: Headers::new(),
253 body: None,
254 connect_timeout: None,
255 }
256 }
257
258 fn send(self) -> Result<Response> {
259 let req = OutgoingRequest::new(self.headers);
260 req.set_method(&self.method)
261 .map_err(|()| anyhow!("failed to set method"))?;
262
263 let scheme = match self.url.scheme() {
264 "http" => Scheme::Http,
265 "https" => Scheme::Https,
266 other => Scheme::Other(other.to_string()),
267 };
268 req.set_scheme(Some(&scheme))
269 .map_err(|()| anyhow!("failed to set scheme"))?;
270
271 req.set_authority(Some(self.url.authority()))
272 .map_err(|()| anyhow!("failed to set authority"))?;
273
274 let path = match self.url.query() {
275 Some(query) => format!("{}?{query}", self.url.path()),
276 None => self.url.path().to_string(),
277 };
278 req.set_path_with_query(Some(&path))
279 .map_err(|()| anyhow!("failed to set path_with_query"))?;
280
281 let outgoing_body = req
282 .body()
283 .map_err(|_| anyhow!("outgoing request write failed"))?;
284 if let Some(body) = self.body {
285 let request_body = outgoing_body
286 .write()
287 .map_err(|_| anyhow!("outgoing request write failed"))?;
288 request_body.blocking_write_and_flush(&body)?;
289 }
290 OutgoingBody::finish(outgoing_body, None)?;
291
292 let options = RequestOptions::new();
293 options
294 .set_connect_timeout(self.connect_timeout)
295 .map_err(|()| anyhow!("failed to set connect_timeout"))?;
296
297 let future_response = outgoing_handler::handle(req, Some(options))?;
298 let incoming_response = match future_response.get() {
299 Some(result) => result.map_err(|()| anyhow!("response already taken"))?,
300 None => {
301 let pollable = future_response.subscribe();
302 pollable.block();
303
304 future_response
305 .get()
306 .expect("incoming response available")
307 .map_err(|()| anyhow!("response already taken"))?
308 }
309 }?;
310 drop(future_response);
311
312 Response::new(incoming_response)
313 }
314}