Skip to main content

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