1 use std::io::Read;
2 use std::{fmt, time};
3 
4 use url::{form_urlencoded, ParseError, Url};
5 
6 use crate::body::Payload;
7 use crate::header::{self, Header};
8 use crate::unit::{self, Unit};
9 use crate::Response;
10 use crate::{agent::Agent, error::Error};
11 
12 #[cfg(feature = "json")]
13 use super::SerdeValue;
14 
15 pub type Result<T> = std::result::Result<T, Error>;
16 
17 /// Request instances are builders that creates a request.
18 ///
19 /// ```
20 /// # fn main() -> Result<(), ureq::Error> {
21 /// # ureq::is_test(true);
22 /// let response = ureq::get("http://example.com/form")
23 ///     .query("foo", "bar baz")  // add ?foo=bar+baz
24 ///     .call()?;                 // run the request
25 /// # Ok(())
26 /// # }
27 /// ```
28 #[derive(Clone)]
29 pub struct Request {
30     agent: Agent,
31     method: String,
32     url: String,
33     headers: Vec<Header>,
34     timeout: Option<time::Duration>,
35 }
36 
37 impl fmt::Debug for Request {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result38     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39         write!(
40             f,
41             "Request({} {}, {:?})",
42             self.method, self.url, self.headers
43         )
44     }
45 }
46 
47 impl Request {
new(agent: Agent, method: String, url: String) -> Request48     pub(crate) fn new(agent: Agent, method: String, url: String) -> Request {
49         Request {
50             agent,
51             method,
52             url,
53             headers: vec![],
54             timeout: None,
55         }
56     }
57 
58     #[inline(always)]
59     /// Sets overall timeout for the request, overriding agent's configuration if any.
timeout(mut self, timeout: time::Duration) -> Self60     pub fn timeout(mut self, timeout: time::Duration) -> Self {
61         self.timeout = Some(timeout);
62         self
63     }
64 
65     /// Sends the request with no body and blocks the caller until done.
66     ///
67     /// Use this with GET, HEAD, OPTIONS or TRACE. It sends neither
68     /// Content-Length nor Transfer-Encoding.
69     ///
70     /// ```
71     /// # fn main() -> Result<(), ureq::Error> {
72     /// # ureq::is_test(true);
73     /// let resp = ureq::get("http://example.com/")
74     ///     .call()?;
75     /// # Ok(())
76     /// # }
77     /// ```
call(self) -> Result<Response>78     pub fn call(self) -> Result<Response> {
79         self.do_call(Payload::Empty)
80     }
81 
parse_url(&self) -> Result<Url>82     fn parse_url(&self) -> Result<Url> {
83         Ok(self.url.parse().and_then(|url: Url|
84             // No hostname is fine for urls in general, but not for website urls.
85             if url.host_str().is_none() {
86                 Err(ParseError::EmptyHost)
87             } else {
88                 Ok(url)
89             }
90         )?)
91     }
92 
do_call(self, payload: Payload) -> Result<Response>93     fn do_call(self, payload: Payload) -> Result<Response> {
94         for h in &self.headers {
95             h.validate()?;
96         }
97         let url = self.parse_url()?;
98 
99         let deadline = match self.timeout.or(self.agent.config.timeout) {
100             None => None,
101             Some(timeout) => {
102                 let now = time::Instant::now();
103                 Some(now.checked_add(timeout).unwrap())
104             }
105         };
106 
107         let reader = payload.into_read();
108         let unit = Unit::new(
109             &self.agent,
110             &self.method,
111             &url,
112             &self.headers,
113             &reader,
114             deadline,
115         );
116         let response = unit::connect(unit, true, reader).map_err(|e| e.url(url.clone()))?;
117 
118         if response.status() >= 400 {
119             Err(Error::Status(response.status(), response))
120         } else {
121             Ok(response)
122         }
123     }
124 
125     /// Send data a json value.
126     ///
127     /// Requires feature `ureq = { version = "*", features = ["json"] }`
128     ///
129     /// The `Content-Length` header is implicitly set to the length of the serialized value.
130     ///
131     /// ```
132     /// # fn main() -> Result<(), ureq::Error> {
133     /// # ureq::is_test(true);
134     /// let resp = ureq::post("http://httpbin.org/post")
135     ///     .send_json(ureq::json!({
136     ///       "name": "martin",
137     ///       "rust": true,
138     ///     }))?;
139     /// # Ok(())
140     /// # }
141     /// ```
142     #[cfg(feature = "json")]
send_json(mut self, data: SerdeValue) -> Result<Response>143     pub fn send_json(mut self, data: SerdeValue) -> Result<Response> {
144         if self.header("Content-Type").is_none() {
145             self = self.set("Content-Type", "application/json");
146         }
147         self.do_call(Payload::JSON(data))
148     }
149 
150     /// Send data as bytes.
151     ///
152     /// The `Content-Length` header is implicitly set to the length of the serialized value.
153     ///
154     /// ```
155     /// # fn main() -> Result<(), ureq::Error> {
156     /// # ureq::is_test(true);
157     /// let resp = ureq::put("http://httpbin.org/put")
158     ///     .send_bytes(&[0; 1000])?;
159     /// # Ok(())
160     /// # }
161     /// ```
send_bytes(self, data: &[u8]) -> Result<Response>162     pub fn send_bytes(self, data: &[u8]) -> Result<Response> {
163         self.do_call(Payload::Bytes(data))
164     }
165 
166     /// Send data as a string.
167     ///
168     /// The `Content-Length` header is implicitly set to the length of the serialized value.
169     /// Defaults to `utf-8`
170     ///
171     /// ## Charset support
172     ///
173     /// Requires feature `ureq = { version = "*", features = ["charset"] }`
174     ///
175     /// If a `Content-Type` header is present and it contains a charset specification, we
176     /// attempt to encode the string using that character set. If it fails, we fall back
177     /// on utf-8.
178     ///
179     /// ```
180     /// // this example requires features = ["charset"]
181     ///
182     /// # fn main() -> Result<(), ureq::Error> {
183     /// # ureq::is_test(true);
184     /// let resp = ureq::post("http://httpbin.org/post")
185     ///     .set("Content-Type", "text/plain; charset=iso-8859-1")
186     ///     .send_string("Hällo Wörld!")?;
187     /// # Ok(())
188     /// # }
189     /// ```
send_string(self, data: &str) -> Result<Response>190     pub fn send_string(self, data: &str) -> Result<Response> {
191         let charset =
192             crate::response::charset_from_content_type(self.header("content-type")).to_string();
193         self.do_call(Payload::Text(data, charset))
194     }
195 
196     /// Send a sequence of (key, value) pairs as form-urlencoded data.
197     ///
198     /// The `Content-Type` header is implicitly set to application/x-www-form-urlencoded.
199     /// The `Content-Length` header is implicitly set to the length of the serialized value.
200     ///
201     /// ```
202     /// # fn main() -> Result<(), ureq::Error> {
203     /// # ureq::is_test(true);
204     /// let resp = ureq::post("http://httpbin.org/post")
205     ///     .send_form(&[
206     ///       ("foo", "bar"),
207     ///       ("foo2", "bar2"),
208     ///     ])?;
209     /// # Ok(())
210     /// # }
211     /// ```
send_form(mut self, data: &[(&str, &str)]) -> Result<Response>212     pub fn send_form(mut self, data: &[(&str, &str)]) -> Result<Response> {
213         if self.header("Content-Type").is_none() {
214             self = self.set("Content-Type", "application/x-www-form-urlencoded");
215         }
216         let encoded = form_urlencoded::Serializer::new(String::new())
217             .extend_pairs(data)
218             .finish();
219         self.do_call(Payload::Bytes(&encoded.into_bytes()))
220     }
221 
222     /// Send data from a reader.
223     ///
224     /// If no Content-Length and Transfer-Encoding header has been set, it uses the [chunked transfer encoding](https://tools.ietf.org/html/rfc7230#section-4.1).
225     ///
226     /// The caller may set the Content-Length header to the expected byte size of the reader if is
227     /// known.
228     ///
229     /// The input from the reader is buffered into chunks of size 16,384, the max size of a TLS fragment.
230     ///
231     /// ```
232     /// use std::io::Cursor;
233     /// # fn main() -> Result<(), ureq::Error> {
234     /// # ureq::is_test(true);
235     /// let read = Cursor::new(vec![0x20; 100]);
236     /// let resp = ureq::post("http://httpbin.org/post")
237     ///     .send(read)?;
238     /// # Ok(())
239     /// # }
240     /// ```
send(self, reader: impl Read) -> Result<Response>241     pub fn send(self, reader: impl Read) -> Result<Response> {
242         self.do_call(Payload::Reader(Box::new(reader)))
243     }
244 
245     /// Set a header field.
246     ///
247     /// ```
248     /// # fn main() -> Result<(), ureq::Error> {
249     /// # ureq::is_test(true);
250     /// let resp = ureq::get("http://httpbin.org/bytes/1000")
251     ///     .set("Accept", "text/plain")
252     ///     .set("Range", "bytes=500-999")
253     ///     .call()?;
254     /// # Ok(())
255     /// # }
256     /// ```
set(mut self, header: &str, value: &str) -> Self257     pub fn set(mut self, header: &str, value: &str) -> Self {
258         header::add_header(&mut self.headers, Header::new(header, value));
259         self
260     }
261 
262     /// Returns the value for a set header.
263     ///
264     /// ```
265     /// let req = ureq::get("/my_page")
266     ///     .set("X-API-Key", "foobar");
267     /// assert_eq!("foobar", req.header("x-api-Key").unwrap());
268     /// ```
header(&self, name: &str) -> Option<&str>269     pub fn header(&self, name: &str) -> Option<&str> {
270         header::get_header(&self.headers, name)
271     }
272 
273     /// A list of the set header names in this request. Lowercased to be uniform.
274     ///
275     /// ```
276     /// let req = ureq::get("/my_page")
277     ///     .set("X-API-Key", "foobar")
278     ///     .set("Content-Type", "application/json");
279     /// assert_eq!(req.header_names(), vec!["x-api-key", "content-type"]);
280     /// ```
header_names(&self) -> Vec<String>281     pub fn header_names(&self) -> Vec<String> {
282         self.headers
283             .iter()
284             .map(|h| h.name().to_ascii_lowercase())
285             .collect()
286     }
287 
288     /// Tells if the header has been set.
289     ///
290     /// ```
291     /// let req = ureq::get("/my_page")
292     ///     .set("X-API-Key", "foobar");
293     /// assert_eq!(true, req.has("x-api-Key"));
294     /// ```
has(&self, name: &str) -> bool295     pub fn has(&self, name: &str) -> bool {
296         header::has_header(&self.headers, name)
297     }
298 
299     /// All headers corresponding values for the give name, or empty vector.
300     ///
301     /// ```
302     /// let req = ureq::get("/my_page")
303     ///     .set("X-Forwarded-For", "1.2.3.4")
304     ///     .set("X-Forwarded-For", "2.3.4.5");
305     ///
306     /// assert_eq!(req.all("x-forwarded-for"), vec![
307     ///     "1.2.3.4",
308     ///     "2.3.4.5",
309     /// ]);
310     /// ```
all(&self, name: &str) -> Vec<&str>311     pub fn all(&self, name: &str) -> Vec<&str> {
312         header::get_all_headers(&self.headers, name)
313     }
314 
315     /// Set a query parameter.
316     ///
317     /// For example, to set `?format=json&dest=/login`
318     ///
319     /// ```
320     /// # fn main() -> Result<(), ureq::Error> {
321     /// # ureq::is_test(true);
322     /// let resp = ureq::get("http://httpbin.org/get")
323     ///     .query("format", "json")
324     ///     .query("dest", "/login")
325     ///     .call()?;
326     /// # Ok(())
327     /// # }
328     /// ```
query(mut self, param: &str, value: &str) -> Self329     pub fn query(mut self, param: &str, value: &str) -> Self {
330         if let Ok(mut url) = self.parse_url() {
331             url.query_pairs_mut().append_pair(param, value);
332 
333             // replace url
334             self.url = url.to_string();
335         }
336         self
337     }
338 
339     /// Returns the value of the request method. Something like `GET`, `POST`, `PUT` etc.
340     ///
341     /// ```
342     /// let req = ureq::put("http://httpbin.org/put");
343     ///
344     /// assert_eq!(req.method(), "PUT");
345     /// ```
method(&self) -> &str346     pub fn method(&self) -> &str {
347         &self.method
348     }
349 
350     /// Get the url str that will be used for this request.
351     ///
352     /// The url might differ from that originally provided when constructing the
353     /// request if additional query parameters have been added using [`Request::query()`].
354     ///
355     /// In case the original url provided to build the request is not possible to
356     /// parse to a Url, this function returns the original, and it will error once the
357     /// Request object is used.
358     ///
359     /// ```
360     /// # fn main() -> Result<(), ureq::Error> {
361     /// # ureq::is_test(true);
362     /// let req = ureq::get("http://httpbin.org/get")
363     ///     .query("foo", "bar");
364     ///
365     /// assert_eq!(req.url(), "http://httpbin.org/get?foo=bar");
366     /// # Ok(())
367     /// # }
368     /// ```
369     ///
370     /// ```
371     /// # fn main() -> Result<(), ureq::Error> {
372     /// # ureq::is_test(true);
373     /// let req = ureq::get("SO WRONG")
374     ///     .query("foo", "bar"); // does nothing
375     ///
376     /// assert_eq!(req.url(), "SO WRONG");
377     /// # Ok(())
378     /// # }
379     /// ```
url(&self) -> &str380     pub fn url(&self) -> &str {
381         &self.url
382     }
383 
384     /// Get the parsed url that will be used for this request. The parsed url
385     /// has functions to inspect the parts of the url further.
386     ///
387     /// The url might differ from that originally provided when constructing the
388     /// request if additional query parameters have been added using [`Request::query()`].
389     ///
390     /// Returns a `Result` since a common use case is to construct
391     /// the [`Request`] using a `&str` in which case the url needs to be parsed
392     /// to inspect the parts. If the Request url is not possible to parse, this
393     /// function produces the same error that would otherwise happen when
394     /// `call` or `send_*` is called.
395     ///
396     /// ```
397     /// # fn main() -> Result<(), ureq::Error> {
398     /// # ureq::is_test(true);
399     /// let req = ureq::get("http://httpbin.org/get")
400     ///     .query("foo", "bar");
401     ///
402     /// assert_eq!(req.request_url().unwrap().host(), "httpbin.org");
403     /// # Ok(())
404     /// # }
405     /// ```
request_url(&self) -> Result<RequestUrl>406     pub fn request_url(&self) -> Result<RequestUrl> {
407         Ok(RequestUrl::new(self.parse_url()?))
408     }
409 }
410 
411 /// Parsed result of a request url with handy inspection methods.
412 #[derive(Debug, Clone)]
413 pub struct RequestUrl {
414     url: Url,
415     query_pairs: Vec<(String, String)>,
416 }
417 
418 impl RequestUrl {
new(url: Url) -> Self419     fn new(url: Url) -> Self {
420         // This is needed to avoid url::Url Cow<str>. We want ureq API to work with &str.
421         let query_pairs = url
422             .query_pairs()
423             .map(|(k, v)| (k.to_string(), v.to_string()))
424             .collect();
425 
426         RequestUrl { url, query_pairs }
427     }
428 
429     /// Handle the request url as a standard [`url::Url`].
as_url(&self) -> &Url430     pub fn as_url(&self) -> &Url {
431         &self.url
432     }
433 
434     /// Get the scheme of the request url, i.e. "https" or "http".
scheme(&self) -> &str435     pub fn scheme(&self) -> &str {
436         self.url.scheme()
437     }
438 
439     /// Host of the request url.
host(&self) -> &str440     pub fn host(&self) -> &str {
441         // this unwrap() is ok, because RequestUrl is tested for empty host
442         // urls in Request::parse_url().
443         self.url.host_str().unwrap()
444     }
445 
446     /// Port of the request url, if available. Ports are only available if they
447     /// are present in the original url. Specifically the scheme default ports,
448     /// 443 for `https` and and 80 for `http` are `None` unless explicitly
449     /// set in the url, i.e. `https://my-host.com:443/some/path`.
port(&self) -> Option<u16>450     pub fn port(&self) -> Option<u16> {
451         self.url.port()
452     }
453 
454     /// Path of the request url.
path(&self) -> &str455     pub fn path(&self) -> &str {
456         self.url.path()
457     }
458 
459     /// Returns all query parameters as a vector of key-value pairs.
460     ///
461     /// ```
462     /// # fn main() -> Result<(), ureq::Error> {
463     /// # ureq::is_test(true);
464     /// let req = ureq::get("http://httpbin.org/get")
465     ///     .query("foo", "42")
466     ///     .query("foo", "43");
467     ///
468     /// assert_eq!(req.request_url().unwrap().query_pairs(), vec![
469     ///     ("foo", "42"),
470     ///     ("foo", "43")
471     /// ]);
472     /// # Ok(())
473     /// # }
474     /// ```
query_pairs(&self) -> Vec<(&str, &str)>475     pub fn query_pairs(&self) -> Vec<(&str, &str)> {
476         self.query_pairs
477             .iter()
478             .map(|(k, v)| (k.as_str(), v.as_str()))
479             .collect()
480     }
481 }
482 
483 #[cfg(test)]
484 mod tests {
485     use super::*;
486 
487     #[test]
request_implements_send_and_sync()488     fn request_implements_send_and_sync() {
489         let _request: Box<dyn Send> = Box::new(Request::new(
490             Agent::new(),
491             "GET".to_string(),
492             "https://example.com/".to_string(),
493         ));
494         let _request: Box<dyn Sync> = Box::new(Request::new(
495             Agent::new(),
496             "GET".to_string(),
497             "https://example.com/".to_string(),
498         ));
499     }
500 
501     #[test]
send_byte_slice()502     fn send_byte_slice() {
503         let bytes = vec![1, 2, 3];
504         crate::agent()
505             .post("http://example.com")
506             .send(&bytes[1..2])
507             .ok();
508     }
509 
510     #[test]
disallow_empty_host()511     fn disallow_empty_host() {
512         let req = crate::agent().get("file:///some/path");
513 
514         // Both request_url and call() must surface the same error.
515         assert_eq!(
516             req.request_url().unwrap_err().kind(),
517             crate::ErrorKind::InvalidUrl
518         );
519 
520         assert_eq!(req.call().unwrap_err().kind(), crate::ErrorKind::InvalidUrl);
521     }
522 }
523