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