reqwest_wasm/async_impl/
request.rs

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