reqwest_wasm/blocking/
request.rs

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