reqwest/wasm/
request.rs

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