reqwest_wasm/wasm/
request.rs

1use std::convert::TryFrom;
2use std::fmt;
3use std::io::Write;
4
5use base64::write::EncoderWriter as Base64Encoder;
6use bytes::Bytes;
7use http::{request::Parts, Method, Request as HttpRequest};
8use serde::Serialize;
9#[cfg(feature = "json")]
10use serde_json;
11use url::Url;
12use web_sys::RequestCredentials;
13
14use super::{Body, Client, Response};
15use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
16
17/// A request which can be executed with `Client::execute()`.
18pub struct Request {
19    method: Method,
20    url: Url,
21    headers: HeaderMap,
22    body: Option<Body>,
23    pub(super) cors: bool,
24    pub(super) credentials: Option<RequestCredentials>,
25}
26
27/// A builder to construct the properties of a `Request`.
28pub struct RequestBuilder {
29    client: Client,
30    request: crate::Result<Request>,
31}
32
33impl Request {
34    /// Constructs a new request.
35    #[inline]
36    pub fn new(method: Method, url: Url) -> Self {
37        Request {
38            method,
39            url,
40            headers: HeaderMap::new(),
41            body: None,
42            cors: true,
43            credentials: None,
44        }
45    }
46
47    /// Get the method.
48    #[inline]
49    pub fn method(&self) -> &Method {
50        &self.method
51    }
52
53    /// Get a mutable reference to the method.
54    #[inline]
55    pub fn method_mut(&mut self) -> &mut Method {
56        &mut self.method
57    }
58
59    /// Get the url.
60    #[inline]
61    pub fn url(&self) -> &Url {
62        &self.url
63    }
64
65    /// Get a mutable reference to the url.
66    #[inline]
67    pub fn url_mut(&mut self) -> &mut Url {
68        &mut self.url
69    }
70
71    /// Get the headers.
72    #[inline]
73    pub fn headers(&self) -> &HeaderMap {
74        &self.headers
75    }
76
77    /// Get a mutable reference to the headers.
78    #[inline]
79    pub fn headers_mut(&mut self) -> &mut HeaderMap {
80        &mut self.headers
81    }
82
83    /// Get the body.
84    #[inline]
85    pub fn body(&self) -> Option<&Body> {
86        self.body.as_ref()
87    }
88
89    /// Get a mutable reference to the body.
90    #[inline]
91    pub fn body_mut(&mut self) -> &mut Option<Body> {
92        &mut self.body
93    }
94
95    /// Attempts to clone the `Request`.
96    ///
97    /// None is returned if a body is which can not be cloned.
98    pub fn try_clone(&self) -> Option<Request> {
99        let body = match self.body.as_ref() {
100            Some(body) => Some(body.try_clone()?),
101            None => None,
102        };
103
104        Some(Self {
105            method: self.method.clone(),
106            url: self.url.clone(),
107            headers: self.headers.clone(),
108            body,
109            cors: self.cors,
110            credentials: self.credentials,
111        })
112    }
113}
114
115impl RequestBuilder {
116    pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
117        RequestBuilder { client, request }
118    }
119
120    /// Modify the query string of the URL.
121    ///
122    /// Modifies the URL of this request, adding the parameters provided.
123    /// This method appends and does not overwrite. This means that it can
124    /// be called multiple times and that existing query parameters are not
125    /// overwritten if the same key is used. The key will simply show up
126    /// twice in the query string.
127    /// Calling `.query([("foo", "a"), ("foo", "b")])` gives `"foo=a&foo=b"`.
128    ///
129    /// # Note
130    /// This method does not support serializing a single key-value
131    /// pair. Instead of using `.query(("key", "val"))`, use a sequence, such
132    /// as `.query(&[("key", "val")])`. It's also possible to serialize structs
133    /// and maps into a key-value pair.
134    ///
135    /// # Errors
136    /// This method will fail if the object you provide cannot be serialized
137    /// into a query string.
138    pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
139        let mut error = None;
140        if let Ok(ref mut req) = self.request {
141            let url = req.url_mut();
142            let mut pairs = url.query_pairs_mut();
143            let serializer = serde_urlencoded::Serializer::new(&mut pairs);
144
145            if let Err(err) = query.serialize(serializer) {
146                error = Some(crate::error::builder(err));
147            }
148        }
149        if let Ok(ref mut req) = self.request {
150            if let Some("") = req.url().query() {
151                req.url_mut().set_query(None);
152            }
153        }
154        if let Some(err) = error {
155            self.request = Err(err);
156        }
157        self
158    }
159
160    /// Send a form body.
161    ///
162    /// Sets the body to the url encoded serialization of the passed value,
163    /// and also sets the `Content-Type: application/x-www-form-urlencoded`
164    /// header.
165    ///
166    /// # Errors
167    ///
168    /// This method fails if the passed value cannot be serialized into
169    /// url encoded format
170    pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
171        let mut error = None;
172        if let Ok(ref mut req) = self.request {
173            match serde_urlencoded::to_string(form) {
174                Ok(body) => {
175                    req.headers_mut().insert(
176                        CONTENT_TYPE,
177                        HeaderValue::from_static("application/x-www-form-urlencoded"),
178                    );
179                    *req.body_mut() = Some(body.into());
180                }
181                Err(err) => error = Some(crate::error::builder(err)),
182            }
183        }
184        if let Some(err) = error {
185            self.request = Err(err);
186        }
187        self
188    }
189
190    #[cfg(feature = "json")]
191    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
192    /// Set the request json
193    pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
194        let mut error = None;
195        if let Ok(ref mut req) = self.request {
196            match serde_json::to_vec(json) {
197                Ok(body) => {
198                    req.headers_mut()
199                        .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
200                    *req.body_mut() = Some(body.into());
201                }
202                Err(err) => error = Some(crate::error::builder(err)),
203            }
204        }
205        if let Some(err) = error {
206            self.request = Err(err);
207        }
208        self
209    }
210
211    /// Enable HTTP basic authentication.
212    pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
213    where
214        U: fmt::Display,
215        P: fmt::Display,
216    {
217        let mut header_value = b"Basic ".to_vec();
218        {
219            let mut encoder = Base64Encoder::new(&mut header_value, base64::STANDARD);
220            // The unwraps here are fine because Vec::write* is infallible.
221            write!(encoder, "{}:", username).unwrap();
222            if let Some(password) = password {
223                write!(encoder, "{}", password).unwrap();
224            }
225        }
226
227        self.header(crate::header::AUTHORIZATION, header_value)
228    }
229
230    /// Enable HTTP bearer authentication.
231    pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
232    where
233        T: fmt::Display,
234    {
235        let header_value = format!("Bearer {}", token);
236        self.header(crate::header::AUTHORIZATION, header_value)
237    }
238
239    /// Set the request body.
240    pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
241        if let Ok(ref mut req) = self.request {
242            req.body = Some(body.into());
243        }
244        self
245    }
246
247    /// TODO
248    #[cfg(feature = "multipart")]
249    #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
250    pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder {
251        if let Ok(ref mut req) = self.request {
252            *req.body_mut() = Some(Body::from_form(multipart))
253        }
254        self
255    }
256
257    /// Add a `Header` to this Request.
258    pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
259    where
260        HeaderName: TryFrom<K>,
261        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
262        HeaderValue: TryFrom<V>,
263        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
264    {
265        let mut error = None;
266        if let Ok(ref mut req) = self.request {
267            match <HeaderName as TryFrom<K>>::try_from(key) {
268                Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
269                    Ok(value) => {
270                        req.headers_mut().append(key, value);
271                    }
272                    Err(e) => error = Some(crate::error::builder(e.into())),
273                },
274                Err(e) => error = Some(crate::error::builder(e.into())),
275            };
276        }
277        if let Some(err) = error {
278            self.request = Err(err);
279        }
280        self
281    }
282
283    /// Add a set of Headers to the existing ones on this Request.
284    ///
285    /// The headers will be merged in to any already set.
286    pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
287        if let Ok(ref mut req) = self.request {
288            crate::util::replace_headers(req.headers_mut(), headers);
289        }
290        self
291    }
292
293    /// Disable CORS on fetching the request.
294    ///
295    /// # WASM
296    ///
297    /// This option is only effective with WebAssembly target.
298    ///
299    /// The [request mode][mdn] will be set to 'no-cors'.
300    ///
301    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/mode
302    pub fn fetch_mode_no_cors(mut self) -> RequestBuilder {
303        if let Ok(ref mut req) = self.request {
304            req.cors = false;
305        }
306        self
307    }
308
309    /// Set fetch credentials to 'same-origin'
310    ///
311    /// # WASM
312    ///
313    /// This option is only effective with WebAssembly target.
314    ///
315    /// The [request credentials][mdn] will be set to 'same-origin'.
316    ///
317    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials
318    pub fn fetch_credentials_same_origin(mut self) -> RequestBuilder {
319        if let Ok(ref mut req) = self.request {
320            req.credentials = Some(RequestCredentials::SameOrigin);
321        }
322        self
323    }
324
325    /// Set fetch credentials to 'include'
326    ///
327    /// # WASM
328    ///
329    /// This option is only effective with WebAssembly target.
330    ///
331    /// The [request credentials][mdn] will be set to 'include'.
332    ///
333    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials
334    pub fn fetch_credentials_include(mut self) -> RequestBuilder {
335        if let Ok(ref mut req) = self.request {
336            req.credentials = Some(RequestCredentials::Include);
337        }
338        self
339    }
340
341    /// Set fetch credentials to 'omit'
342    ///
343    /// # WASM
344    ///
345    /// This option is only effective with WebAssembly target.
346    ///
347    /// The [request credentials][mdn] will be set to 'omit'.
348    ///
349    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials
350    pub fn fetch_credentials_omit(mut self) -> RequestBuilder {
351        if let Ok(ref mut req) = self.request {
352            req.credentials = Some(RequestCredentials::Omit);
353        }
354        self
355    }
356
357    /// Build a `Request`, which can be inspected, modified and executed with
358    /// `Client::execute()`.
359    pub fn build(self) -> crate::Result<Request> {
360        self.request
361    }
362
363    /// Constructs the Request and sends it to the target URL, returning a
364    /// future Response.
365    ///
366    /// # Errors
367    ///
368    /// This method fails if there was an error while sending request.
369    ///
370    /// # Example
371    ///
372    /// ```no_run
373    /// # use reqwest::Error;
374    /// #
375    /// # async fn run() -> Result<(), Error> {
376    /// let response = reqwest::Client::new()
377    ///     .get("https://hyper.rs")
378    ///     .send()
379    ///     .await?;
380    /// # Ok(())
381    /// # }
382    /// ```
383    pub async fn send(self) -> crate::Result<Response> {
384        let req = self.request?;
385        self.client.execute_request(req).await
386    }
387
388    /// Attempt to clone the RequestBuilder.
389    ///
390    /// `None` is returned if the RequestBuilder can not be cloned.
391    ///
392    /// # Examples
393    ///
394    /// ```no_run
395    /// # use reqwest::Error;
396    /// #
397    /// # fn run() -> Result<(), Error> {
398    /// let client = reqwest::Client::new();
399    /// let builder = client.post("http://httpbin.org/post")
400    ///     .body("from a &str!");
401    /// let clone = builder.try_clone();
402    /// assert!(clone.is_some());
403    /// # Ok(())
404    /// # }
405    /// ```
406    pub fn try_clone(&self) -> Option<RequestBuilder> {
407        self.request
408            .as_ref()
409            .ok()
410            .and_then(|req| req.try_clone())
411            .map(|req| RequestBuilder {
412                client: self.client.clone(),
413                request: Ok(req),
414            })
415    }
416}
417
418impl fmt::Debug for Request {
419    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
420        fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
421    }
422}
423
424impl fmt::Debug for RequestBuilder {
425    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
426        let mut builder = f.debug_struct("RequestBuilder");
427        match self.request {
428            Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
429            Err(ref err) => builder.field("error", err).finish(),
430        }
431    }
432}
433
434fn fmt_request_fields<'a, 'b>(
435    f: &'a mut fmt::DebugStruct<'a, 'b>,
436    req: &Request,
437) -> &'a mut fmt::DebugStruct<'a, 'b> {
438    f.field("method", &req.method)
439        .field("url", &req.url)
440        .field("headers", &req.headers)
441}
442
443impl<T> TryFrom<HttpRequest<T>> for Request
444where
445    T: Into<Body>,
446{
447    type Error = crate::Error;
448
449    fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
450        let (parts, body) = req.into_parts();
451        let Parts {
452            method,
453            uri,
454            headers,
455            ..
456        } = parts;
457        let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
458        Ok(Request {
459            method,
460            url,
461            headers,
462            body: Some(body.into()),
463            cors: true,
464            credentials: None,
465        })
466    }
467}
468
469impl TryFrom<Request> for HttpRequest<Body> {
470    type Error = crate::Error;
471
472    fn try_from(req: Request) -> crate::Result<Self> {
473        let Request {
474            method,
475            url,
476            headers,
477            body,
478            ..
479        } = req;
480
481        let mut req = HttpRequest::builder()
482            .method(method)
483            .uri(url.as_str())
484            .body(body.unwrap_or_else(|| Body::from(Bytes::default())))
485            .map_err(crate::error::builder)?;
486
487        *req.headers_mut() = headers;
488        Ok(req)
489    }
490}