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