reqwest/blocking/
request.rs

1use std::convert::TryFrom;
2use std::fmt;
3use std::time::Duration;
4
5use http::{request::Parts, Request as HttpRequest, Version};
6use serde::Serialize;
7#[cfg(feature = "json")]
8use serde_json;
9use serde_urlencoded;
10
11use super::body::{self, Body};
12#[cfg(feature = "multipart")]
13use super::multipart;
14use super::Client;
15use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
16use crate::{async_impl, Method, Url};
17
18/// A request which can be executed with `Client::execute()`.
19pub struct Request {
20    body: Option<Body>,
21    inner: async_impl::Request,
22}
23
24/// A builder to construct the properties of a `Request`.
25///
26/// To construct a `RequestBuilder`, refer to the `Client` documentation.
27#[derive(Debug)]
28#[must_use = "RequestBuilder does nothing until you 'send' it"]
29pub struct RequestBuilder {
30    client: Client,
31    request: crate::Result<Request>,
32}
33
34impl Request {
35    /// Constructs a new request.
36    #[inline]
37    pub fn new(method: Method, url: Url) -> Self {
38        Request {
39            body: None,
40            inner: async_impl::Request::new(method, url),
41        }
42    }
43
44    /// Get the method.
45    #[inline]
46    pub fn method(&self) -> &Method {
47        self.inner.method()
48    }
49
50    /// Get a mutable reference to the method.
51    #[inline]
52    pub fn method_mut(&mut self) -> &mut Method {
53        self.inner.method_mut()
54    }
55
56    /// Get the url.
57    #[inline]
58    pub fn url(&self) -> &Url {
59        self.inner.url()
60    }
61
62    /// Get a mutable reference to the url.
63    #[inline]
64    pub fn url_mut(&mut self) -> &mut Url {
65        self.inner.url_mut()
66    }
67
68    /// Get the headers.
69    #[inline]
70    pub fn headers(&self) -> &HeaderMap {
71        self.inner.headers()
72    }
73
74    /// Get a mutable reference to the headers.
75    #[inline]
76    pub fn headers_mut(&mut self) -> &mut HeaderMap {
77        self.inner.headers_mut()
78    }
79
80    /// Get the http version.
81    #[inline]
82    pub fn version(&self) -> Version {
83        self.inner.version()
84    }
85
86    /// Get a mutable reference to the http version.
87    #[inline]
88    pub fn version_mut(&mut self) -> &mut Version {
89        self.inner.version_mut()
90    }
91
92    /// Get the body.
93    #[inline]
94    pub fn body(&self) -> Option<&Body> {
95        self.body.as_ref()
96    }
97
98    /// Get a mutable reference to the body.
99    #[inline]
100    pub fn body_mut(&mut self) -> &mut Option<Body> {
101        &mut self.body
102    }
103
104    /// Get the timeout.
105    #[inline]
106    pub fn timeout(&self) -> Option<&Duration> {
107        self.inner.timeout()
108    }
109
110    /// Get a mutable reference to the timeout.
111    #[inline]
112    pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
113        self.inner.timeout_mut()
114    }
115
116    /// Attempts to clone the `Request`.
117    ///
118    /// None is returned if a body is which can not be cloned. This can be because the body is a
119    /// stream.
120    pub fn try_clone(&self) -> Option<Request> {
121        let body = if let Some(ref body) = self.body.as_ref() {
122            if let Some(body) = body.try_clone() {
123                Some(body)
124            } else {
125                return None;
126            }
127        } else {
128            None
129        };
130        let mut req = Request::new(self.method().clone(), self.url().clone());
131        *req.timeout_mut() = self.timeout().copied();
132        *req.headers_mut() = self.headers().clone();
133        *req.version_mut() = self.version().clone();
134        req.body = body;
135        Some(req)
136    }
137
138    pub(crate) fn into_async(self) -> (async_impl::Request, Option<body::Sender>) {
139        use crate::header::CONTENT_LENGTH;
140
141        let mut req_async = self.inner;
142        let body = self.body.and_then(|body| {
143            let (tx, body, len) = body.into_async();
144            if let Some(len) = len {
145                req_async.headers_mut().insert(CONTENT_LENGTH, len.into());
146            }
147            *req_async.body_mut() = Some(body);
148            tx
149        });
150        (req_async, body)
151    }
152}
153
154impl RequestBuilder {
155    pub(crate) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
156        let mut builder = RequestBuilder { client, request };
157
158        let auth = builder
159            .request
160            .as_mut()
161            .ok()
162            .and_then(|req| async_impl::request::extract_authority(req.url_mut()));
163
164        if let Some((username, password)) = auth {
165            builder.basic_auth(username, password)
166        } else {
167            builder
168        }
169    }
170
171    /// Assemble a builder starting from an existing `Client` and a `Request`.
172    pub fn from_parts(client: Client, request: Request) -> RequestBuilder {
173        RequestBuilder {
174            client,
175            request: crate::Result::Ok(request),
176        }
177    }
178
179    /// Add a `Header` to this Request.
180    ///
181    /// ```rust
182    /// use reqwest::header::USER_AGENT;
183    ///
184    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
185    /// let client = reqwest::blocking::Client::new();
186    /// let res = client.get("https://www.rust-lang.org")
187    ///     .header(USER_AGENT, "foo")
188    ///     .send()?;
189    /// # Ok(())
190    /// # }
191    /// ```
192    pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
193    where
194        HeaderName: TryFrom<K>,
195        HeaderValue: TryFrom<V>,
196        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
197        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
198    {
199        self.header_sensitive(key, value, false)
200    }
201
202    /// Add a `Header` to this Request with ability to define if header_value is sensitive.
203    fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
204    where
205        HeaderName: TryFrom<K>,
206        HeaderValue: TryFrom<V>,
207        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
208        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
209    {
210        let mut error = None;
211        if let Ok(ref mut req) = self.request {
212            match <HeaderName as TryFrom<K>>::try_from(key) {
213                Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
214                    Ok(mut value) => {
215                        // We want to potentially make an unsensitive header
216                        // to be sensitive, not the reverse. So, don't turn off
217                        // a previously sensitive header.
218                        if sensitive {
219                            value.set_sensitive(true);
220                        }
221                        req.headers_mut().append(key, value);
222                    }
223                    Err(e) => error = Some(crate::error::builder(e.into())),
224                },
225                Err(e) => error = Some(crate::error::builder(e.into())),
226            };
227        }
228        if let Some(err) = error {
229            self.request = Err(err);
230        }
231        self
232    }
233
234    /// Add a set of Headers to the existing ones on this Request.
235    ///
236    /// The headers will be merged in to any already set.
237    ///
238    /// ```rust
239    /// use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT, CONTENT_TYPE};
240    /// # use std::fs;
241    ///
242    /// fn construct_headers() -> HeaderMap {
243    ///     let mut headers = HeaderMap::new();
244    ///     headers.insert(USER_AGENT, HeaderValue::from_static("reqwest"));
245    ///     headers.insert(CONTENT_TYPE, HeaderValue::from_static("image/png"));
246    ///     headers
247    /// }
248    ///
249    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
250    /// let file = fs::File::open("much_beauty.png")?;
251    /// let client = reqwest::blocking::Client::new();
252    /// let res = client.post("http://httpbin.org/post")
253    ///     .headers(construct_headers())
254    ///     .body(file)
255    ///     .send()?;
256    /// # Ok(())
257    /// # }
258    /// ```
259    pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
260        if let Ok(ref mut req) = self.request {
261            crate::util::replace_headers(req.headers_mut(), headers);
262        }
263        self
264    }
265
266    /// Enable HTTP basic authentication.
267    ///
268    /// ```rust
269    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
270    /// let client = reqwest::blocking::Client::new();
271    /// let resp = client.delete("http://httpbin.org/delete")
272    ///     .basic_auth("admin", Some("good password"))
273    ///     .send()?;
274    /// # Ok(())
275    /// # }
276    /// ```
277    pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
278    where
279        U: fmt::Display,
280        P: fmt::Display,
281    {
282        let header_value = crate::util::basic_auth(username, password);
283        self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
284    }
285
286    /// Enable HTTP bearer authentication.
287    ///
288    /// ```rust
289    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
290    /// let client = reqwest::blocking::Client::new();
291    /// let resp = client.delete("http://httpbin.org/delete")
292    ///     .bearer_auth("token")
293    ///     .send()?;
294    /// # Ok(())
295    /// # }
296    /// ```
297    pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
298    where
299        T: fmt::Display,
300    {
301        let header_value = format!("Bearer {token}");
302        self.header_sensitive(crate::header::AUTHORIZATION, &*header_value, true)
303    }
304
305    /// Set the request body.
306    ///
307    /// # Examples
308    ///
309    /// Using a string:
310    ///
311    /// ```rust
312    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
313    /// let client = reqwest::blocking::Client::new();
314    /// let res = client.post("http://httpbin.org/post")
315    ///     .body("from a &str!")
316    ///     .send()?;
317    /// # Ok(())
318    /// # }
319    /// ```
320    ///
321    /// Using a `File`:
322    ///
323    /// ```rust
324    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
325    /// let file = std::fs::File::open("from_a_file.txt")?;
326    /// let client = reqwest::blocking::Client::new();
327    /// let res = client.post("http://httpbin.org/post")
328    ///     .body(file)
329    ///     .send()?;
330    /// # Ok(())
331    /// # }
332    /// ```
333    ///
334    /// Using arbitrary bytes:
335    ///
336    /// ```rust
337    /// # use std::fs;
338    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
339    /// // from bytes!
340    /// let bytes: Vec<u8> = vec![1, 10, 100];
341    /// let client = reqwest::blocking::Client::new();
342    /// let res = client.post("http://httpbin.org/post")
343    ///     .body(bytes)
344    ///     .send()?;
345    /// # Ok(())
346    /// # }
347    /// ```
348    pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
349        if let Ok(ref mut req) = self.request {
350            *req.body_mut() = Some(body.into());
351        }
352        self
353    }
354
355    /// Enables a request timeout.
356    ///
357    /// The timeout is applied from when the request starts connecting until the
358    /// response body has finished. It affects only this request and overrides
359    /// the timeout configured using `ClientBuilder::timeout()`.
360    pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
361        if let Ok(ref mut req) = self.request {
362            *req.timeout_mut() = Some(timeout);
363        }
364        self
365    }
366
367    /// Modify the query string of the URL.
368    ///
369    /// Modifies the URL of this request, adding the parameters provided.
370    /// This method appends and does not overwrite. This means that it can
371    /// be called multiple times and that existing query parameters are not
372    /// overwritten if the same key is used. The key will simply show up
373    /// twice in the query string.
374    /// Calling `.query(&[("foo", "a"), ("foo", "b")])` gives `"foo=a&foo=b"`.
375    ///
376    /// ```rust
377    /// # use reqwest::Error;
378    /// #
379    /// # fn run() -> Result<(), Error> {
380    /// let client = reqwest::blocking::Client::new();
381    /// let res = client.get("http://httpbin.org")
382    ///     .query(&[("lang", "rust")])
383    ///     .send()?;
384    /// # Ok(())
385    /// # }
386    /// ```
387    ///
388    /// # Note
389    /// This method does not support serializing a single key-value
390    /// pair. Instead of using `.query(("key", "val"))`, use a sequence, such
391    /// as `.query(&[("key", "val")])`. It's also possible to serialize structs
392    /// and maps into a key-value pair.
393    ///
394    /// # Errors
395    /// This method will fail if the object you provide cannot be serialized
396    /// into a query string.
397    pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
398        let mut error = None;
399        if let Ok(ref mut req) = self.request {
400            let url = req.url_mut();
401            let mut pairs = url.query_pairs_mut();
402            let serializer = serde_urlencoded::Serializer::new(&mut pairs);
403
404            if let Err(err) = query.serialize(serializer) {
405                error = Some(crate::error::builder(err));
406            }
407        }
408        if let Ok(ref mut req) = self.request {
409            if let Some("") = req.url().query() {
410                req.url_mut().set_query(None);
411            }
412        }
413        if let Some(err) = error {
414            self.request = Err(err);
415        }
416        self
417    }
418
419    /// Set HTTP version
420    pub fn version(mut self, version: Version) -> RequestBuilder {
421        if let Ok(ref mut req) = self.request {
422            *req.version_mut() = version;
423        }
424        self
425    }
426
427    /// Send a form body.
428    ///
429    /// Sets the body to the url encoded serialization of the passed value,
430    /// and also sets the `Content-Type: application/x-www-form-urlencoded`
431    /// header.
432    ///
433    /// ```rust
434    /// # use reqwest::Error;
435    /// # use std::collections::HashMap;
436    /// #
437    /// # fn run() -> Result<(), Error> {
438    /// let mut params = HashMap::new();
439    /// params.insert("lang", "rust");
440    ///
441    /// let client = reqwest::blocking::Client::new();
442    /// let res = client.post("http://httpbin.org")
443    ///     .form(&params)
444    ///     .send()?;
445    /// # Ok(())
446    /// # }
447    /// ```
448    ///
449    /// # Errors
450    ///
451    /// This method fails if the passed value cannot be serialized into
452    /// url encoded format
453    pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
454        let mut error = None;
455        if let Ok(ref mut req) = self.request {
456            match serde_urlencoded::to_string(form) {
457                Ok(body) => {
458                    req.headers_mut()
459                        .entry(CONTENT_TYPE)
460                        .or_insert(HeaderValue::from_static(
461                            "application/x-www-form-urlencoded",
462                        ));
463                    *req.body_mut() = Some(body.into());
464                }
465                Err(err) => error = Some(crate::error::builder(err)),
466            }
467        }
468        if let Some(err) = error {
469            self.request = Err(err);
470        }
471        self
472    }
473
474    /// Send a JSON body.
475    ///
476    /// Sets the body to the JSON serialization of the passed value, and
477    /// also sets the `Content-Type: application/json` header.
478    ///
479    /// # Optional
480    ///
481    /// This requires the optional `json` feature enabled.
482    ///
483    /// # Examples
484    ///
485    /// ```rust
486    /// # use reqwest::Error;
487    /// # use std::collections::HashMap;
488    /// #
489    /// # fn run() -> Result<(), Error> {
490    /// let mut map = HashMap::new();
491    /// map.insert("lang", "rust");
492    ///
493    /// let client = reqwest::blocking::Client::new();
494    /// let res = client.post("http://httpbin.org")
495    ///     .json(&map)
496    ///     .send()?;
497    /// # Ok(())
498    /// # }
499    /// ```
500    ///
501    /// # Errors
502    ///
503    /// Serialization can fail if `T`'s implementation of `Serialize` decides to
504    /// fail, or if `T` contains a map with non-string keys.
505    #[cfg(feature = "json")]
506    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
507    pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
508        let mut error = None;
509        if let Ok(ref mut req) = self.request {
510            match serde_json::to_vec(json) {
511                Ok(body) => {
512                    if !req.headers().contains_key(CONTENT_TYPE) {
513                        req.headers_mut()
514                            .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
515                    }
516                    *req.body_mut() = Some(body.into());
517                }
518                Err(err) => error = Some(crate::error::builder(err)),
519            }
520        }
521        if let Some(err) = error {
522            self.request = Err(err);
523        }
524        self
525    }
526
527    /// Sends a multipart/form-data body.
528    ///
529    /// ```
530    /// # use reqwest::Error;
531    ///
532    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
533    /// let client = reqwest::blocking::Client::new();
534    /// let form = reqwest::blocking::multipart::Form::new()
535    ///     .text("key3", "value3")
536    ///     .file("file", "/path/to/field")?;
537    ///
538    /// let response = client.post("your url")
539    ///     .multipart(form)
540    ///     .send()?;
541    /// # Ok(())
542    /// # }
543    /// ```
544    ///
545    /// See [`multipart`](multipart/) for more examples.
546    #[cfg(feature = "multipart")]
547    #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
548    pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
549        let mut builder = self.header(
550            CONTENT_TYPE,
551            format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
552        );
553        if let Ok(ref mut req) = builder.request {
554            *req.body_mut() = Some(match multipart.compute_length() {
555                Some(length) => Body::sized(multipart.reader(), length),
556                None => Body::new(multipart.reader()),
557            })
558        }
559        builder
560    }
561
562    /// Build a `Request`, which can be inspected, modified and executed with
563    /// `Client::execute()`.
564    pub fn build(self) -> crate::Result<Request> {
565        self.request
566    }
567
568    /// Build a `Request`, which can be inspected, modified and executed with
569    /// `Client::execute()`.
570    ///
571    /// This is similar to [`RequestBuilder::build()`], but also returns the
572    /// embedded `Client`.
573    pub fn build_split(self) -> (Client, crate::Result<Request>) {
574        (self.client, self.request)
575    }
576
577    /// Constructs the Request and sends it the target URL, returning a Response.
578    ///
579    /// # Errors
580    ///
581    /// This method fails if there was an error while sending request,
582    /// redirect loop was detected or redirect limit was exhausted.
583    pub fn send(self) -> crate::Result<super::Response> {
584        self.client.execute(self.request?)
585    }
586
587    /// Attempts to clone the `RequestBuilder`.
588    ///
589    /// None is returned if a body is which can not be cloned. This can be because the body is a
590    /// stream.
591    ///
592    /// # Examples
593    ///
594    /// With a static body
595    ///
596    /// ```rust
597    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
598    /// let client = reqwest::blocking::Client::new();
599    /// let builder = client.post("http://httpbin.org/post")
600    ///     .body("from a &str!");
601    /// let clone = builder.try_clone();
602    /// assert!(clone.is_some());
603    /// # Ok(())
604    /// # }
605    /// ```
606    ///
607    /// Without a body
608    ///
609    /// ```rust
610    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
611    /// let client = reqwest::blocking::Client::new();
612    /// let builder = client.get("http://httpbin.org/get");
613    /// let clone = builder.try_clone();
614    /// assert!(clone.is_some());
615    /// # Ok(())
616    /// # }
617    /// ```
618    ///
619    /// With a non-cloneable body
620    ///
621    /// ```rust
622    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
623    /// let client = reqwest::blocking::Client::new();
624    /// let builder = client.get("http://httpbin.org/get")
625    ///     .body(reqwest::blocking::Body::new(std::io::empty()));
626    /// let clone = builder.try_clone();
627    /// assert!(clone.is_none());
628    /// # Ok(())
629    /// # }
630    /// ```
631    pub fn try_clone(&self) -> Option<RequestBuilder> {
632        self.request
633            .as_ref()
634            .ok()
635            .and_then(|req| req.try_clone())
636            .map(|req| RequestBuilder {
637                client: self.client.clone(),
638                request: Ok(req),
639            })
640    }
641}
642
643impl<T> TryFrom<HttpRequest<T>> for Request
644where
645    T: Into<Body>,
646{
647    type Error = crate::Error;
648
649    fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
650        let (parts, body) = req.into_parts();
651        let Parts {
652            method,
653            uri,
654            headers,
655            ..
656        } = parts;
657        let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
658        let mut inner = async_impl::Request::new(method, url);
659        crate::util::replace_headers(inner.headers_mut(), headers);
660        Ok(Request {
661            body: Some(body.into()),
662            inner,
663        })
664    }
665}
666
667impl fmt::Debug for Request {
668    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
669        fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
670    }
671}
672
673fn fmt_request_fields<'a, 'b>(
674    f: &'a mut fmt::DebugStruct<'a, 'b>,
675    req: &Request,
676) -> &'a mut fmt::DebugStruct<'a, 'b> {
677    f.field("method", req.method())
678        .field("url", req.url())
679        .field("headers", req.headers())
680}
681
682#[cfg(test)]
683mod tests {
684    use super::super::{body, Client};
685    use super::{HttpRequest, Request, Version};
686    use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST};
687    use crate::Method;
688    use serde::Serialize;
689    #[cfg(feature = "json")]
690    use serde_json;
691    use serde_urlencoded;
692    use std::collections::{BTreeMap, HashMap};
693    use std::time::Duration;
694
695    #[test]
696    fn basic_get_request() {
697        let client = Client::new();
698        let some_url = "https://google.com/";
699        let r = client.get(some_url).build().unwrap();
700
701        assert_eq!(r.method(), &Method::GET);
702        assert_eq!(r.url().as_str(), some_url);
703    }
704
705    #[test]
706    fn basic_head_request() {
707        let client = Client::new();
708        let some_url = "https://google.com/";
709        let r = client.head(some_url).build().unwrap();
710
711        assert_eq!(r.method(), &Method::HEAD);
712        assert_eq!(r.url().as_str(), some_url);
713    }
714
715    #[test]
716    fn basic_post_request() {
717        let client = Client::new();
718        let some_url = "https://google.com/";
719        let r = client.post(some_url).build().unwrap();
720
721        assert_eq!(r.method(), &Method::POST);
722        assert_eq!(r.url().as_str(), some_url);
723    }
724
725    #[test]
726    fn basic_put_request() {
727        let client = Client::new();
728        let some_url = "https://google.com/";
729        let r = client.put(some_url).build().unwrap();
730
731        assert_eq!(r.method(), &Method::PUT);
732        assert_eq!(r.url().as_str(), some_url);
733    }
734
735    #[test]
736    fn basic_patch_request() {
737        let client = Client::new();
738        let some_url = "https://google.com/";
739        let r = client.patch(some_url).build().unwrap();
740
741        assert_eq!(r.method(), &Method::PATCH);
742        assert_eq!(r.url().as_str(), some_url);
743    }
744
745    #[test]
746    fn basic_delete_request() {
747        let client = Client::new();
748        let some_url = "https://google.com/";
749        let r = client.delete(some_url).build().unwrap();
750
751        assert_eq!(r.method(), &Method::DELETE);
752        assert_eq!(r.url().as_str(), some_url);
753    }
754
755    #[test]
756    fn add_header() {
757        let client = Client::new();
758        let some_url = "https://google.com/";
759        let r = client.post(some_url);
760
761        let header = HeaderValue::from_static("google.com");
762
763        // Add a copy of the header to the request builder
764        let r = r.header(HOST, header.clone()).build().unwrap();
765
766        // then check it was actually added
767        assert_eq!(r.headers().get(HOST), Some(&header));
768    }
769
770    #[test]
771    fn add_headers() {
772        let client = Client::new();
773        let some_url = "https://google.com/";
774        let r = client.post(some_url);
775
776        let header = HeaderValue::from_static("google.com");
777
778        let mut headers = HeaderMap::new();
779        headers.insert(HOST, header);
780
781        // Add a copy of the headers to the request builder
782        let r = r.headers(headers.clone()).build().unwrap();
783
784        // then make sure they were added correctly
785        assert_eq!(r.headers(), &headers);
786    }
787
788    #[test]
789    fn add_headers_multi() {
790        let client = Client::new();
791        let some_url = "https://google.com/";
792        let r = client.post(some_url);
793
794        let header_json = HeaderValue::from_static("application/json");
795        let header_xml = HeaderValue::from_static("application/xml");
796
797        let mut headers = HeaderMap::new();
798        headers.append(ACCEPT, header_json);
799        headers.append(ACCEPT, header_xml);
800
801        // Add a copy of the headers to the request builder
802        let r = r.headers(headers.clone()).build().unwrap();
803
804        // then make sure they were added correctly
805        assert_eq!(r.headers(), &headers);
806        let mut all_values = r.headers().get_all(ACCEPT).iter();
807        assert_eq!(all_values.next().unwrap(), &"application/json");
808        assert_eq!(all_values.next().unwrap(), &"application/xml");
809        assert_eq!(all_values.next(), None);
810    }
811
812    #[test]
813    fn add_body() {
814        let client = Client::new();
815        let some_url = "https://google.com/";
816        let r = client.post(some_url);
817
818        let body = "Some interesting content";
819
820        let mut r = r.body(body).build().unwrap();
821
822        let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
823
824        assert_eq!(buf, body);
825    }
826
827    #[test]
828    fn add_query_append() {
829        let client = Client::new();
830        let some_url = "https://google.com/";
831        let mut r = client.get(some_url);
832
833        r = r.query(&[("foo", "bar")]);
834        r = r.query(&[("qux", 3)]);
835
836        let req = r.build().expect("request is valid");
837        assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
838    }
839
840    #[test]
841    fn add_query_append_same() {
842        let client = Client::new();
843        let some_url = "https://google.com/";
844        let mut r = client.get(some_url);
845
846        r = r.query(&[("foo", "a"), ("foo", "b")]);
847
848        let req = r.build().expect("request is valid");
849        assert_eq!(req.url().query(), Some("foo=a&foo=b"));
850    }
851
852    #[test]
853    fn add_query_struct() {
854        #[derive(Serialize)]
855        struct Params {
856            foo: String,
857            qux: i32,
858        }
859
860        let client = Client::new();
861        let some_url = "https://google.com/";
862        let mut r = client.get(some_url);
863
864        let params = Params {
865            foo: "bar".into(),
866            qux: 3,
867        };
868
869        r = r.query(&params);
870
871        let req = r.build().expect("request is valid");
872        assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
873    }
874
875    #[test]
876    fn add_query_map() {
877        let mut params = BTreeMap::new();
878        params.insert("foo", "bar");
879        params.insert("qux", "three");
880
881        let client = Client::new();
882        let some_url = "https://google.com/";
883        let mut r = client.get(some_url);
884
885        r = r.query(&params);
886
887        let req = r.build().expect("request is valid");
888        assert_eq!(req.url().query(), Some("foo=bar&qux=three"));
889    }
890
891    #[test]
892    fn add_form() {
893        let client = Client::new();
894        let some_url = "https://google.com/";
895        let r = client.post(some_url);
896
897        let mut form_data = HashMap::new();
898        form_data.insert("foo", "bar");
899
900        let mut r = r.form(&form_data).build().unwrap();
901
902        // Make sure the content type was set
903        assert_eq!(
904            r.headers().get(CONTENT_TYPE).unwrap(),
905            &"application/x-www-form-urlencoded"
906        );
907
908        let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
909
910        let body_should_be = serde_urlencoded::to_string(&form_data).unwrap();
911        assert_eq!(buf, body_should_be);
912    }
913
914    #[test]
915    #[cfg(feature = "json")]
916    fn add_json() {
917        let client = Client::new();
918        let some_url = "https://google.com/";
919        let r = client.post(some_url);
920
921        let mut json_data = HashMap::new();
922        json_data.insert("foo", "bar");
923
924        let mut r = r.json(&json_data).build().unwrap();
925
926        // Make sure the content type was set
927        assert_eq!(r.headers().get(CONTENT_TYPE).unwrap(), &"application/json");
928
929        let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
930
931        let body_should_be = serde_json::to_string(&json_data).unwrap();
932        assert_eq!(buf, body_should_be);
933    }
934
935    #[test]
936    #[cfg(feature = "json")]
937    fn add_json_fail() {
938        use serde::ser::Error as _;
939        use serde::{Serialize, Serializer};
940        use std::error::Error as _;
941        struct MyStruct;
942        impl Serialize for MyStruct {
943            fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
944            where
945                S: Serializer,
946            {
947                Err(S::Error::custom("nope"))
948            }
949        }
950
951        let client = Client::new();
952        let some_url = "https://google.com/";
953        let r = client.post(some_url);
954        let json_data = MyStruct;
955        let err = r.json(&json_data).build().unwrap_err();
956        assert!(err.is_builder()); // well, duh ;)
957        assert!(err.source().unwrap().is::<serde_json::Error>());
958    }
959
960    #[test]
961    fn test_replace_headers() {
962        use http::HeaderMap;
963
964        let mut headers = HeaderMap::new();
965        headers.insert("foo", "bar".parse().unwrap());
966        headers.append("foo", "baz".parse().unwrap());
967
968        let client = Client::new();
969        let req = client
970            .get("https://hyper.rs")
971            .header("im-a", "keeper")
972            .header("foo", "pop me")
973            .headers(headers)
974            .build()
975            .expect("request build");
976
977        assert_eq!(req.headers()["im-a"], "keeper");
978
979        let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
980        assert_eq!(foo.len(), 2);
981        assert_eq!(foo[0], "bar");
982        assert_eq!(foo[1], "baz");
983    }
984
985    #[test]
986    fn normalize_empty_query() {
987        let client = Client::new();
988        let some_url = "https://google.com/";
989        let empty_query: &[(&str, &str)] = &[];
990
991        let req = client
992            .get(some_url)
993            .query(empty_query)
994            .build()
995            .expect("request build");
996
997        assert_eq!(req.url().query(), None);
998        assert_eq!(req.url().as_str(), "https://google.com/");
999    }
1000
1001    #[test]
1002    fn convert_url_authority_into_basic_auth() {
1003        let client = Client::new();
1004        let some_url = "https://Aladdin:open sesame@localhost/";
1005
1006        let req = client.get(some_url).build().expect("request build");
1007
1008        assert_eq!(req.url().as_str(), "https://localhost/");
1009        assert_eq!(
1010            req.headers()["authorization"],
1011            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
1012        );
1013    }
1014
1015    #[test]
1016    fn convert_from_http_request() {
1017        let http_request = HttpRequest::builder()
1018            .method("GET")
1019            .uri("http://localhost/")
1020            .header("User-Agent", "my-awesome-agent/1.0")
1021            .body("test test test")
1022            .unwrap();
1023        let req: Request = Request::try_from(http_request).unwrap();
1024        assert_eq!(req.body().is_none(), false);
1025        let test_data = b"test test test";
1026        assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
1027        let headers = req.headers();
1028        assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
1029        assert_eq!(req.method(), Method::GET);
1030        assert_eq!(req.url().as_str(), "http://localhost/");
1031    }
1032
1033    #[test]
1034    fn set_http_request_version() {
1035        let http_request = HttpRequest::builder()
1036            .method("GET")
1037            .uri("http://localhost/")
1038            .header("User-Agent", "my-awesome-agent/1.0")
1039            .version(Version::HTTP_11)
1040            .body("test test test")
1041            .unwrap();
1042        let req: Request = Request::try_from(http_request).unwrap();
1043        assert_eq!(req.body().is_none(), false);
1044        let test_data = b"test test test";
1045        assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
1046        let headers = req.headers();
1047        assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
1048        assert_eq!(req.method(), Method::GET);
1049        assert_eq!(req.url().as_str(), "http://localhost/");
1050        assert_eq!(req.version(), Version::HTTP_11);
1051    }
1052
1053    #[test]
1054    fn test_basic_auth_sensitive_header() {
1055        let client = Client::new();
1056        let some_url = "https://localhost/";
1057
1058        let req = client
1059            .get(some_url)
1060            .basic_auth("Aladdin", Some("open sesame"))
1061            .build()
1062            .expect("request build");
1063
1064        assert_eq!(req.url().as_str(), "https://localhost/");
1065        assert_eq!(
1066            req.headers()["authorization"],
1067            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
1068        );
1069        assert_eq!(req.headers()["authorization"].is_sensitive(), true);
1070    }
1071
1072    #[test]
1073    fn test_bearer_auth_sensitive_header() {
1074        let client = Client::new();
1075        let some_url = "https://localhost/";
1076
1077        let req = client
1078            .get(some_url)
1079            .bearer_auth("Hold my bear")
1080            .build()
1081            .expect("request build");
1082
1083        assert_eq!(req.url().as_str(), "https://localhost/");
1084        assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
1085        assert_eq!(req.headers()["authorization"].is_sensitive(), true);
1086    }
1087
1088    #[test]
1089    fn test_request_cloning() {
1090        let mut request = Request::new(Method::GET, "https://example.com".try_into().unwrap());
1091        *request.timeout_mut() = Some(Duration::from_secs(42));
1092        *request.version_mut() = Version::HTTP_11;
1093
1094        let clone = request.try_clone().unwrap();
1095        assert_eq!(request.version(), clone.version());
1096        assert_eq!(request.headers(), clone.headers());
1097        assert_eq!(request.timeout(), clone.timeout());
1098    }
1099}