zed_reqwest/async_impl/
request.rs

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