reqwest_h3/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()
458                        .entry(CONTENT_TYPE)
459                        .or_insert(HeaderValue::from_static(
460                            "application/x-www-form-urlencoded",
461                        ));
462                    *req.body_mut() = Some(body.into());
463                }
464                Err(err) => error = Some(crate::error::builder(err)),
465            }
466        }
467        if let Some(err) = error {
468            self.request = Err(err);
469        }
470        self
471    }
472
473    /// Send a JSON body.
474    ///
475    /// Sets the body to the JSON serialization of the passed value, and
476    /// also sets the `Content-Type: application/json` header.
477    ///
478    /// # Optional
479    ///
480    /// This requires the optional `json` feature enabled.
481    ///
482    /// # Examples
483    ///
484    /// ```rust
485    /// # use reqwest::Error;
486    /// # use std::collections::HashMap;
487    /// #
488    /// # fn run() -> Result<(), Error> {
489    /// let mut map = HashMap::new();
490    /// map.insert("lang", "rust");
491    ///
492    /// let client = reqwest::blocking::Client::new();
493    /// let res = client.post("http://httpbin.org")
494    ///     .json(&map)
495    ///     .send()?;
496    /// # Ok(())
497    /// # }
498    /// ```
499    ///
500    /// # Errors
501    ///
502    /// Serialization can fail if `T`'s implementation of `Serialize` decides to
503    /// fail, or if `T` contains a map with non-string keys.
504    #[cfg(feature = "json")]
505    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
506    pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
507        let mut error = None;
508        if let Ok(ref mut req) = self.request {
509            match serde_json::to_vec(json) {
510                Ok(body) => {
511                    if !req.headers().contains_key(CONTENT_TYPE) {
512                        req.headers_mut()
513                            .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
514                    }
515                    *req.body_mut() = Some(body.into());
516                }
517                Err(err) => error = Some(crate::error::builder(err)),
518            }
519        }
520        if let Some(err) = error {
521            self.request = Err(err);
522        }
523        self
524    }
525
526    /// Sends a multipart/form-data body.
527    ///
528    /// ```
529    /// # use reqwest::Error;
530    ///
531    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
532    /// let client = reqwest::blocking::Client::new();
533    /// let form = reqwest::blocking::multipart::Form::new()
534    ///     .text("key3", "value3")
535    ///     .file("file", "/path/to/field")?;
536    ///
537    /// let response = client.post("your url")
538    ///     .multipart(form)
539    ///     .send()?;
540    /// # Ok(())
541    /// # }
542    /// ```
543    ///
544    /// See [`multipart`](multipart/) for more examples.
545    #[cfg(feature = "multipart")]
546    #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
547    pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
548        let mut builder = self.header(
549            CONTENT_TYPE,
550            format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
551        );
552        if let Ok(ref mut req) = builder.request {
553            *req.body_mut() = Some(match multipart.compute_length() {
554                Some(length) => Body::sized(multipart.reader(), length),
555                None => Body::new(multipart.reader()),
556            })
557        }
558        builder
559    }
560
561    /// Build a `Request`, which can be inspected, modified and executed with
562    /// `Client::execute()`.
563    pub fn build(self) -> crate::Result<Request> {
564        self.request
565    }
566
567    /// Build a `Request`, which can be inspected, modified and executed with
568    /// `Client::execute()`.
569    ///
570    /// This is similar to [`RequestBuilder::build()`], but also returns the
571    /// embedded `Client`.
572    pub fn build_split(self) -> (Client, crate::Result<Request>) {
573        (self.client, self.request)
574    }
575
576    /// Constructs the Request and sends it the target URL, returning a Response.
577    ///
578    /// # Errors
579    ///
580    /// This method fails if there was an error while sending request,
581    /// redirect loop was detected or redirect limit was exhausted.
582    pub fn send(self) -> crate::Result<super::Response> {
583        self.client.execute(self.request?)
584    }
585
586    /// Attempts to clone the `RequestBuilder`.
587    ///
588    /// None is returned if a body is which can not be cloned. This can be because the body is a
589    /// stream.
590    ///
591    /// # Examples
592    ///
593    /// With a static body
594    ///
595    /// ```rust
596    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
597    /// let client = reqwest::blocking::Client::new();
598    /// let builder = client.post("http://httpbin.org/post")
599    ///     .body("from a &str!");
600    /// let clone = builder.try_clone();
601    /// assert!(clone.is_some());
602    /// # Ok(())
603    /// # }
604    /// ```
605    ///
606    /// Without a body
607    ///
608    /// ```rust
609    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
610    /// let client = reqwest::blocking::Client::new();
611    /// let builder = client.get("http://httpbin.org/get");
612    /// let clone = builder.try_clone();
613    /// assert!(clone.is_some());
614    /// # Ok(())
615    /// # }
616    /// ```
617    ///
618    /// With a non-cloneable body
619    ///
620    /// ```rust
621    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
622    /// let client = reqwest::blocking::Client::new();
623    /// let builder = client.get("http://httpbin.org/get")
624    ///     .body(reqwest::blocking::Body::new(std::io::empty()));
625    /// let clone = builder.try_clone();
626    /// assert!(clone.is_none());
627    /// # Ok(())
628    /// # }
629    /// ```
630    pub fn try_clone(&self) -> Option<RequestBuilder> {
631        self.request
632            .as_ref()
633            .ok()
634            .and_then(|req| req.try_clone())
635            .map(|req| RequestBuilder {
636                client: self.client.clone(),
637                request: Ok(req),
638            })
639    }
640}
641
642impl<T> TryFrom<HttpRequest<T>> for Request
643where
644    T: Into<Body>,
645{
646    type Error = crate::Error;
647
648    fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
649        let (parts, body) = req.into_parts();
650        let Parts {
651            method,
652            uri,
653            headers,
654            ..
655        } = parts;
656        let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
657        let mut inner = async_impl::Request::new(method, url);
658        crate::util::replace_headers(inner.headers_mut(), headers);
659        Ok(Request {
660            body: Some(body.into()),
661            inner,
662        })
663    }
664}
665
666impl fmt::Debug for Request {
667    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
668        fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
669    }
670}
671
672fn fmt_request_fields<'a, 'b>(
673    f: &'a mut fmt::DebugStruct<'a, 'b>,
674    req: &Request,
675) -> &'a mut fmt::DebugStruct<'a, 'b> {
676    f.field("method", req.method())
677        .field("url", req.url())
678        .field("headers", req.headers())
679}
680
681#[cfg(test)]
682mod tests {
683    use super::super::{body, Client};
684    use super::{HttpRequest, Request, Version};
685    use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST};
686    use crate::Method;
687    use serde::Serialize;
688    #[cfg(feature = "json")]
689    use serde_json;
690    use serde_urlencoded;
691    use std::collections::{BTreeMap, HashMap};
692    use std::convert::TryFrom;
693
694    #[test]
695    fn basic_get_request() {
696        let client = Client::new();
697        let some_url = "https://google.com/";
698        let r = client.get(some_url).build().unwrap();
699
700        assert_eq!(r.method(), &Method::GET);
701        assert_eq!(r.url().as_str(), some_url);
702    }
703
704    #[test]
705    fn basic_head_request() {
706        let client = Client::new();
707        let some_url = "https://google.com/";
708        let r = client.head(some_url).build().unwrap();
709
710        assert_eq!(r.method(), &Method::HEAD);
711        assert_eq!(r.url().as_str(), some_url);
712    }
713
714    #[test]
715    fn basic_post_request() {
716        let client = Client::new();
717        let some_url = "https://google.com/";
718        let r = client.post(some_url).build().unwrap();
719
720        assert_eq!(r.method(), &Method::POST);
721        assert_eq!(r.url().as_str(), some_url);
722    }
723
724    #[test]
725    fn basic_put_request() {
726        let client = Client::new();
727        let some_url = "https://google.com/";
728        let r = client.put(some_url).build().unwrap();
729
730        assert_eq!(r.method(), &Method::PUT);
731        assert_eq!(r.url().as_str(), some_url);
732    }
733
734    #[test]
735    fn basic_patch_request() {
736        let client = Client::new();
737        let some_url = "https://google.com/";
738        let r = client.patch(some_url).build().unwrap();
739
740        assert_eq!(r.method(), &Method::PATCH);
741        assert_eq!(r.url().as_str(), some_url);
742    }
743
744    #[test]
745    fn basic_delete_request() {
746        let client = Client::new();
747        let some_url = "https://google.com/";
748        let r = client.delete(some_url).build().unwrap();
749
750        assert_eq!(r.method(), &Method::DELETE);
751        assert_eq!(r.url().as_str(), some_url);
752    }
753
754    #[test]
755    fn add_header() {
756        let client = Client::new();
757        let some_url = "https://google.com/";
758        let r = client.post(some_url);
759
760        let header = HeaderValue::from_static("google.com");
761
762        // Add a copy of the header to the request builder
763        let r = r.header(HOST, header.clone()).build().unwrap();
764
765        // then check it was actually added
766        assert_eq!(r.headers().get(HOST), Some(&header));
767    }
768
769    #[test]
770    fn add_headers() {
771        let client = Client::new();
772        let some_url = "https://google.com/";
773        let r = client.post(some_url);
774
775        let header = HeaderValue::from_static("google.com");
776
777        let mut headers = HeaderMap::new();
778        headers.insert(HOST, header);
779
780        // Add a copy of the headers to the request builder
781        let r = r.headers(headers.clone()).build().unwrap();
782
783        // then make sure they were added correctly
784        assert_eq!(r.headers(), &headers);
785    }
786
787    #[test]
788    fn add_headers_multi() {
789        let client = Client::new();
790        let some_url = "https://google.com/";
791        let r = client.post(some_url);
792
793        let header_json = HeaderValue::from_static("application/json");
794        let header_xml = HeaderValue::from_static("application/xml");
795
796        let mut headers = HeaderMap::new();
797        headers.append(ACCEPT, header_json);
798        headers.append(ACCEPT, header_xml);
799
800        // Add a copy of the headers to the request builder
801        let r = r.headers(headers.clone()).build().unwrap();
802
803        // then make sure they were added correctly
804        assert_eq!(r.headers(), &headers);
805        let mut all_values = r.headers().get_all(ACCEPT).iter();
806        assert_eq!(all_values.next().unwrap(), &"application/json");
807        assert_eq!(all_values.next().unwrap(), &"application/xml");
808        assert_eq!(all_values.next(), None);
809    }
810
811    #[test]
812    fn add_body() {
813        let client = Client::new();
814        let some_url = "https://google.com/";
815        let r = client.post(some_url);
816
817        let body = "Some interesting content";
818
819        let mut r = r.body(body).build().unwrap();
820
821        let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
822
823        assert_eq!(buf, body);
824    }
825
826    #[test]
827    fn add_query_append() {
828        let client = Client::new();
829        let some_url = "https://google.com/";
830        let mut r = client.get(some_url);
831
832        r = r.query(&[("foo", "bar")]);
833        r = r.query(&[("qux", 3)]);
834
835        let req = r.build().expect("request is valid");
836        assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
837    }
838
839    #[test]
840    fn add_query_append_same() {
841        let client = Client::new();
842        let some_url = "https://google.com/";
843        let mut r = client.get(some_url);
844
845        r = r.query(&[("foo", "a"), ("foo", "b")]);
846
847        let req = r.build().expect("request is valid");
848        assert_eq!(req.url().query(), Some("foo=a&foo=b"));
849    }
850
851    #[test]
852    fn add_query_struct() {
853        #[derive(Serialize)]
854        struct Params {
855            foo: String,
856            qux: i32,
857        }
858
859        let client = Client::new();
860        let some_url = "https://google.com/";
861        let mut r = client.get(some_url);
862
863        let params = Params {
864            foo: "bar".into(),
865            qux: 3,
866        };
867
868        r = r.query(&params);
869
870        let req = r.build().expect("request is valid");
871        assert_eq!(req.url().query(), Some("foo=bar&qux=3"));
872    }
873
874    #[test]
875    fn add_query_map() {
876        let mut params = BTreeMap::new();
877        params.insert("foo", "bar");
878        params.insert("qux", "three");
879
880        let client = Client::new();
881        let some_url = "https://google.com/";
882        let mut r = client.get(some_url);
883
884        r = r.query(&params);
885
886        let req = r.build().expect("request is valid");
887        assert_eq!(req.url().query(), Some("foo=bar&qux=three"));
888    }
889
890    #[test]
891    fn add_form() {
892        let client = Client::new();
893        let some_url = "https://google.com/";
894        let r = client.post(some_url);
895
896        let mut form_data = HashMap::new();
897        form_data.insert("foo", "bar");
898
899        let mut r = r.form(&form_data).build().unwrap();
900
901        // Make sure the content type was set
902        assert_eq!(
903            r.headers().get(CONTENT_TYPE).unwrap(),
904            &"application/x-www-form-urlencoded"
905        );
906
907        let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
908
909        let body_should_be = serde_urlencoded::to_string(&form_data).unwrap();
910        assert_eq!(buf, body_should_be);
911    }
912
913    #[test]
914    #[cfg(feature = "json")]
915    fn add_json() {
916        let client = Client::new();
917        let some_url = "https://google.com/";
918        let r = client.post(some_url);
919
920        let mut json_data = HashMap::new();
921        json_data.insert("foo", "bar");
922
923        let mut r = r.json(&json_data).build().unwrap();
924
925        // Make sure the content type was set
926        assert_eq!(r.headers().get(CONTENT_TYPE).unwrap(), &"application/json");
927
928        let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
929
930        let body_should_be = serde_json::to_string(&json_data).unwrap();
931        assert_eq!(buf, body_should_be);
932    }
933
934    #[test]
935    #[cfg(feature = "json")]
936    fn add_json_fail() {
937        use serde::ser::Error as _;
938        use serde::{Serialize, Serializer};
939        use std::error::Error as _;
940        struct MyStruct;
941        impl Serialize for MyStruct {
942            fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
943            where
944                S: Serializer,
945            {
946                Err(S::Error::custom("nope"))
947            }
948        }
949
950        let client = Client::new();
951        let some_url = "https://google.com/";
952        let r = client.post(some_url);
953        let json_data = MyStruct;
954        let err = r.json(&json_data).build().unwrap_err();
955        assert!(err.is_builder()); // well, duh ;)
956        assert!(err.source().unwrap().is::<serde_json::Error>());
957    }
958
959    #[test]
960    fn test_replace_headers() {
961        use http::HeaderMap;
962
963        let mut headers = HeaderMap::new();
964        headers.insert("foo", "bar".parse().unwrap());
965        headers.append("foo", "baz".parse().unwrap());
966
967        let client = Client::new();
968        let req = client
969            .get("https://hyper.rs")
970            .header("im-a", "keeper")
971            .header("foo", "pop me")
972            .headers(headers)
973            .build()
974            .expect("request build");
975
976        assert_eq!(req.headers()["im-a"], "keeper");
977
978        let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
979        assert_eq!(foo.len(), 2);
980        assert_eq!(foo[0], "bar");
981        assert_eq!(foo[1], "baz");
982    }
983
984    #[test]
985    fn normalize_empty_query() {
986        let client = Client::new();
987        let some_url = "https://google.com/";
988        let empty_query: &[(&str, &str)] = &[];
989
990        let req = client
991            .get(some_url)
992            .query(empty_query)
993            .build()
994            .expect("request build");
995
996        assert_eq!(req.url().query(), None);
997        assert_eq!(req.url().as_str(), "https://google.com/");
998    }
999
1000    #[test]
1001    fn convert_url_authority_into_basic_auth() {
1002        let client = Client::new();
1003        let some_url = "https://Aladdin:open sesame@localhost/";
1004
1005        let req = client.get(some_url).build().expect("request build");
1006
1007        assert_eq!(req.url().as_str(), "https://localhost/");
1008        assert_eq!(
1009            req.headers()["authorization"],
1010            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
1011        );
1012    }
1013
1014    #[test]
1015    fn convert_from_http_request() {
1016        let http_request = HttpRequest::builder()
1017            .method("GET")
1018            .uri("http://localhost/")
1019            .header("User-Agent", "my-awesome-agent/1.0")
1020            .body("test test test")
1021            .unwrap();
1022        let req: Request = Request::try_from(http_request).unwrap();
1023        assert_eq!(req.body().is_none(), false);
1024        let test_data = b"test test test";
1025        assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
1026        let headers = req.headers();
1027        assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
1028        assert_eq!(req.method(), Method::GET);
1029        assert_eq!(req.url().as_str(), "http://localhost/");
1030    }
1031
1032    #[test]
1033    fn set_http_request_version() {
1034        let http_request = HttpRequest::builder()
1035            .method("GET")
1036            .uri("http://localhost/")
1037            .header("User-Agent", "my-awesome-agent/1.0")
1038            .version(Version::HTTP_11)
1039            .body("test test test")
1040            .unwrap();
1041        let req: Request = Request::try_from(http_request).unwrap();
1042        assert_eq!(req.body().is_none(), false);
1043        let test_data = b"test test test";
1044        assert_eq!(req.body().unwrap().as_bytes(), Some(&test_data[..]));
1045        let headers = req.headers();
1046        assert_eq!(headers.get("User-Agent").unwrap(), "my-awesome-agent/1.0");
1047        assert_eq!(req.method(), Method::GET);
1048        assert_eq!(req.url().as_str(), "http://localhost/");
1049        assert_eq!(req.version(), Version::HTTP_11);
1050    }
1051
1052    #[test]
1053    fn test_basic_auth_sensitive_header() {
1054        let client = Client::new();
1055        let some_url = "https://localhost/";
1056
1057        let req = client
1058            .get(some_url)
1059            .basic_auth("Aladdin", Some("open sesame"))
1060            .build()
1061            .expect("request build");
1062
1063        assert_eq!(req.url().as_str(), "https://localhost/");
1064        assert_eq!(
1065            req.headers()["authorization"],
1066            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
1067        );
1068        assert_eq!(req.headers()["authorization"].is_sensitive(), true);
1069    }
1070
1071    #[test]
1072    fn test_bearer_auth_sensitive_header() {
1073        let client = Client::new();
1074        let some_url = "https://localhost/";
1075
1076        let req = client
1077            .get(some_url)
1078            .bearer_auth("Hold my bear")
1079            .build()
1080            .expect("request build");
1081
1082        assert_eq!(req.url().as_str(), "https://localhost/");
1083        assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
1084        assert_eq!(req.headers()["authorization"].is_sensitive(), true);
1085    }
1086}