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