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