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