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