1#![forbid(unsafe_code)]
2#![warn(clippy::all)]
3// new is just more readable than ..Default::default().
4#![allow(clippy::new_without_default)]
5// the matches! macro is obscure and not widely known.
6#![allow(clippy::match_like_matches_macro)]
7// we're not changing public api due to a lint.
8#![allow(clippy::upper_case_acronyms)]
9#![allow(clippy::result_large_err)]
10#![allow(clippy::only_used_in_recursion)]
11// println!("{var}") doesn't allow even the simplest expressions for var,
12// such as "{foo.var}" – hence this lint forces us to have inconsistent
13// formatting args. I prefer a lint that forbid "{var}".
14#![allow(clippy::uninlined_format_args)]
15// if we want a range, we will make a range.
16#![allow(clippy::manual_range_patterns)]
17#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
18
19//!<div align="center">
20//! <!-- Version -->
21//! <a href="https://crates.io/crates/ureq">
22//! <img src="https://img.shields.io/crates/v/ureq.svg?style=flat-square"
23//! alt="Crates.io version" />
24//! </a>
25//! <!-- Docs -->
26//! <a href="https://docs.rs/ureq">
27//! <img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
28//! alt="docs.rs docs" />
29//! </a>
30//! <!-- Downloads -->
31//! <a href="https://crates.io/crates/ureq">
32//! <img src="https://img.shields.io/crates/d/ureq.svg?style=flat-square"
33//! alt="Crates.io downloads" />
34//! </a>
35//!</div>
36//!
37//! A simple, safe HTTP client.
38//!
39//!
40//! Ureq's first priority is being easy for you to use. It's great for
41//! anyone who wants a low-overhead HTTP client that just gets the job done. Works
42//! very well with HTTP APIs. Its features include cookies, JSON, HTTP proxies,
43//! HTTPS, interoperability with the `http` crate, and charset decoding.
44//!
45//! Ureq is in pure Rust for safety and ease of understanding. It avoids using
46//! `unsafe` directly. It [uses blocking I/O][blocking] instead of async I/O, because that keeps
47//! the API simple and keeps dependencies to a minimum. For TLS, ureq uses
48//! [rustls or native-tls](#https--tls--ssl).
49//!
50//! See the [changelog] for details of recent releases.
51//!
52//! [blocking]: #blocking-io-for-simplicity
53//! [changelog]: https://github.com/algesten/ureq/blob/main/CHANGELOG.md
54//!
55//!
56//! ## Usage
57//!
58//! In its simplest form, ureq looks like this:
59//!
60//! ```rust
61//! fn main() -> Result<(), ureq::Error> {
62//! # ureq::is_test(true);
63//! let body: String = ureq::get("http://example.com")
64//! .set("Example-Header", "header value")
65//! .call()?
66//! .into_string()?;
67//! Ok(())
68//! }
69//! ```
70//!
71//! For more involved tasks, you'll want to create an [Agent]. An Agent
72//! holds a connection pool for reuse, and a cookie store if you use the
73//! "cookies" feature. An Agent can be cheaply cloned due to an internal
74//! [Arc](std::sync::Arc) and all clones of an Agent share state among each other. Creating
75//! an Agent also allows setting options like the TLS configuration.
76//!
77//! ```no_run
78//! # fn main() -> std::result::Result<(), ureq::Error> {
79//! # ureq::is_test(true);
80//! use ureq::{Agent, AgentBuilder};
81//! use std::time::Duration;
82//!
83//! let agent: Agent = ureq::AgentBuilder::new()
84//! .timeout_read(Duration::from_secs(5))
85//! .timeout_write(Duration::from_secs(5))
86//! .build();
87//! let body: String = agent.get("http://example.com/page")
88//! .call()?
89//! .into_string()?;
90//!
91//! // Reuses the connection from previous request.
92//! let response: String = agent.put("http://example.com/upload")
93//! .set("Authorization", "example-token")
94//! .call()?
95//! .into_string()?;
96//! # Ok(())
97//! # }
98//! ```
99//!
100//! Ureq supports sending and receiving json, if you enable the "json" feature:
101//!
102//! ```rust
103//! # #[cfg(feature = "json")]
104//! # fn main() -> std::result::Result<(), ureq::Error> {
105//! # ureq::is_test(true);
106//! // Requires the `json` feature enabled.
107//! let resp: String = ureq::post("http://myapi.example.com/post/ingest")
108//! .set("X-My-Header", "Secret")
109//! .send_json(ureq::json!({
110//! "name": "martin",
111//! "rust": true
112//! }))?
113//! .into_string()?;
114//! # Ok(())
115//! # }
116//! # #[cfg(not(feature = "json"))]
117//! # fn main() {}
118//! ```
119//!
120//! ## Error handling
121//!
122//! ureq returns errors via `Result<T, ureq::Error>`. That includes I/O errors,
123//! protocol errors, and status code errors (when the server responded 4xx or
124//! 5xx)
125//!
126//! ```rust
127//! use ureq::Error;
128//!
129//! # fn req() {
130//! match ureq::get("http://mypage.example.com/").call() {
131//! Ok(response) => { /* it worked */},
132//! Err(Error::Status(code, response)) => {
133//! /* the server returned an unexpected status
134//! code (such as 400, 500 etc) */
135//! }
136//! Err(_) => { /* some kind of io/transport error */ }
137//! }
138//! # }
139//! # fn main() {}
140//! ```
141//!
142//! More details on the [Error] type.
143//!
144//! ## Features
145//!
146//! To enable a minimal dependency tree, some features are off by default.
147//! You can control them when including ureq as a dependency.
148//!
149//! `ureq = { version = "*", features = ["json", "charset"] }`
150//!
151//! * `tls` enables https. This is enabled by default.
152//! * `native-certs` makes the default TLS implementation use the OS' trust store (see TLS doc below).
153//! * `cookies` enables cookies.
154//! * `json` enables [Response::into_json()] and [Request::send_json()] via serde_json.
155//! * `charset` enables interpreting the charset part of the Content-Type header
156//! (e.g. `Content-Type: text/plain; charset=iso-8859-1`). Without this, the
157//! library defaults to Rust's built in `utf-8`.
158//! * `socks-proxy` enables proxy config using the `socks4://`, `socks4a://`, `socks5://` and `socks://` (equal to `socks5://`) prefix.
159//! * `native-tls` enables an adapter so you can pass a `native_tls::TlsConnector` instance
160//! to `AgentBuilder::tls_connector`. Due to the risk of diamond dependencies accidentally switching on an unwanted
161//! TLS implementation, `native-tls` is never picked up as a default or used by the crate level
162//! convenience calls (`ureq::get` etc) – it must be configured on the agent. The `native-certs` feature
163//! does nothing for `native-tls`.
164//! * `gzip` enables requests of gzip-compressed responses and decompresses them. This is enabled by default.
165//! * `brotli` enables requests brotli-compressed responses and decompresses them.
166//! * `http-interop` enables conversion methods to and from `http::Response` and `http::request::Builder` (v0.2).
167//! * `http` enables conversion methods to and from `http::Response` and `http::request::Builder` (v1.0).
168//!
169//! # Plain requests
170//!
171//! Most standard methods (GET, POST, PUT etc), are supported as functions from the
172//! top of the library ([get()], [post()], [put()], etc).
173//!
174//! These top level http method functions create a [Request] instance
175//! which follows a build pattern. The builders are finished using:
176//!
177//! * [`.call()`][Request::call()] without a request body.
178//! * [`.send()`][Request::send()] with a request body as [Read][std::io::Read] (chunked encoding support for non-known sized readers).
179//! * [`.send_string()`][Request::send_string()] body as string.
180//! * [`.send_bytes()`][Request::send_bytes()] body as bytes.
181//! * [`.send_form()`][Request::send_form()] key-value pairs as application/x-www-form-urlencoded.
182//!
183//! # JSON
184//!
185//! By enabling the `ureq = { version = "*", features = ["json"] }` feature,
186//! the library supports serde json.
187//!
188//! * [`request.send_json()`][Request::send_json()] send body as serde json.
189//! * [`response.into_json()`][Response::into_json()] transform response to json.
190//!
191//! # Content-Length and Transfer-Encoding
192//!
193//! The library will send a Content-Length header on requests with bodies of
194//! known size, in other words, those sent with
195//! [`.send_string()`][Request::send_string()],
196//! [`.send_bytes()`][Request::send_bytes()],
197//! [`.send_form()`][Request::send_form()], or
198//! [`.send_json()`][Request::send_json()]. If you send a
199//! request body with [`.send()`][Request::send()],
200//! which takes a [Read][std::io::Read] of unknown size, ureq will send Transfer-Encoding:
201//! chunked, and encode the body accordingly. Bodyless requests
202//! (GETs and HEADs) are sent with [`.call()`][Request::call()]
203//! and ureq adds neither a Content-Length nor a Transfer-Encoding header.
204//!
205//! If you set your own Content-Length or Transfer-Encoding header before
206//! sending the body, ureq will respect that header by not overriding it,
207//! and by encoding the body or not, as indicated by the headers you set.
208//!
209//! ```
210//! let resp = ureq::post("http://my-server.com/ingest")
211//! .set("Transfer-Encoding", "chunked")
212//! .send_string("Hello world");
213//! ```
214//!
215//! # Character encoding
216//!
217//! By enabling the `ureq = { version = "*", features = ["charset"] }` feature,
218//! the library supports sending/receiving other character sets than `utf-8`.
219//!
220//! For [`response.into_string()`][Response::into_string()] we read the
221//! header `Content-Type: text/plain; charset=iso-8859-1` and if it contains a charset
222//! specification, we try to decode the body using that encoding. In the absence of, or failing
223//! to interpret the charset, we fall back on `utf-8`.
224//!
225//! Similarly when using [`request.send_string()`][Request::send_string()],
226//! we first check if the user has set a `; charset=<whatwg charset>` and attempt
227//! to encode the request body using that.
228//!
229//!
230//! # Proxying
231//!
232//! ureq supports two kinds of proxies, [`HTTP`] ([`CONNECT`]), [`SOCKS4`] and [`SOCKS5`],
233//! the former is always available while the latter must be enabled using the feature
234//! `ureq = { version = "*", features = ["socks-proxy"] }`.
235//!
236//! Proxies settings are configured on an [Agent] (using [AgentBuilder]). All request sent
237//! through the agent will be proxied.
238//!
239//! ## Example using HTTP
240//!
241//! ```rust
242//! fn proxy_example_1() -> std::result::Result<(), ureq::Error> {
243//! // Configure an http connect proxy. Notice we could have used
244//! // the http:// prefix here (it's optional).
245//! let proxy = ureq::Proxy::new("user:password@cool.proxy:9090")?;
246//! let agent = ureq::AgentBuilder::new()
247//! .proxy(proxy)
248//! .build();
249//!
250//! // This is proxied.
251//! let resp = agent.get("http://cool.server").call()?;
252//! Ok(())
253//! }
254//! # fn main() {}
255//! ```
256//!
257//! ## Example using SOCKS5
258//!
259//! ```rust
260//! # #[cfg(feature = "socks-proxy")]
261//! fn proxy_example_2() -> std::result::Result<(), ureq::Error> {
262//! // Configure a SOCKS proxy.
263//! let proxy = ureq::Proxy::new("socks5://user:password@cool.proxy:9090")?;
264//! let agent = ureq::AgentBuilder::new()
265//! .proxy(proxy)
266//! .build();
267//!
268//! // This is proxied.
269//! let resp = agent.get("http://cool.server").call()?;
270//! Ok(())
271//! }
272//! # fn main() {}
273//! ```
274//!
275//! # HTTPS / TLS / SSL
276//!
277//! On platforms that support rustls, ureq uses rustls. On other platforms, native-tls can
278//! be manually configured using [`AgentBuilder::tls_connector`].
279//!
280//! You might want to use native-tls if you need to interoperate with servers that
281//! only support less-secure TLS configurations (rustls doesn't support TLS 1.0 and 1.1, for
282//! instance). You might also want to use it if you need to validate certificates for IP addresses,
283//! which are not currently supported in rustls.
284//!
285//! Here's an example of constructing an Agent that uses native-tls. It requires the
286//! "native-tls" feature to be enabled.
287//!
288//! ```no_run
289//! # #[cfg(feature = "native-tls")]
290//! # fn build() -> std::result::Result<(), Box<dyn std::error::Error>> {
291//! # ureq::is_test(true);
292//! use std::sync::Arc;
293//! use ureq::Agent;
294//!
295//! let agent = ureq::AgentBuilder::new()
296//! .tls_connector(Arc::new(native_tls::TlsConnector::new()?))
297//! .build();
298//! # Ok(())
299//! # }
300//! # fn main() {}
301//! ```
302//!
303//! ## Trusted Roots
304//!
305//! When you use rustls (`tls` feature), ureq defaults to trusting
306//! [webpki-roots](https://docs.rs/webpki-roots/), a
307//! copy of the Mozilla Root program that is bundled into your program (and so won't update if your
308//! program isn't updated). You can alternately configure
309//! [rustls-native-certs](https://docs.rs/rustls-native-certs/) which extracts the roots from your
310//! OS' trust store. That means it will update when your OS is updated, and also that it will
311//! include locally installed roots.
312//!
313//! When you use `native-tls`, ureq will use your OS' certificate verifier and root store.
314//!
315//! # Blocking I/O for simplicity
316//!
317//! Ureq uses blocking I/O rather than Rust's newer [asynchronous (async) I/O][async]. Async I/O
318//! allows serving many concurrent requests without high costs in memory and OS threads. But
319//! it comes at a cost in complexity. Async programs need to pull in a runtime (usually
320//! [async-std] or [tokio]). They also need async variants of any method that might block, and of
321//! [any method that might call another method that might block][what-color]. That means async
322//! programs usually have a lot of dependencies - which adds to compile times, and increases
323//! risk.
324//!
325//! The costs of async are worth paying, if you're writing an HTTP server that must serve
326//! many many clients with minimal overhead. However, for HTTP _clients_, we believe that the
327//! cost is usually not worth paying. The low-cost alternative to async I/O is blocking I/O,
328//! which has a different price: it requires an OS thread per concurrent request. However,
329//! that price is usually not high: most HTTP clients make requests sequentially, or with
330//! low concurrency.
331//!
332//! That's why ureq uses blocking I/O and plans to stay that way. Other HTTP clients offer both
333//! an async API and a blocking API, but we want to offer a blocking API without pulling in all
334//! the dependencies required by an async API.
335//!
336//! [async]: https://rust-lang.github.io/async-book/01_getting_started/02_why_async.html
337//! [async-std]: https://github.com/async-rs/async-std#async-std
338//! [tokio]: https://github.com/tokio-rs/tokio#tokio
339//! [what-color]: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
340//! [`HTTP`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling#http_tunneling
341//! [`CONNECT`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT
342//! [`SOCKS4`]: https://en.wikipedia.org/wiki/SOCKS#SOCKS4
343//! [`SOCKS5`]: https://en.wikipedia.org/wiki/SOCKS#SOCKS5
344//!
345//! ------------------------------------------------------------------------------
346//!
347//! Ureq is inspired by other great HTTP clients like
348//! [superagent](http://visionmedia.github.io/superagent/) and
349//! [the fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
350//!
351//! If ureq is not what you're looking for, check out these other Rust HTTP clients:
352//! [surf](https://crates.io/crates/surf), [reqwest](https://crates.io/crates/reqwest),
353//! [isahc](https://crates.io/crates/isahc), [attohttpc](https://crates.io/crates/attohttpc),
354//! [actix-web](https://crates.io/crates/actix-web), and [hyper](https://crates.io/crates/hyper).
355//!
356
357mod agent;
358mod body;
359mod chunked;
360mod error;
361mod header;
362mod middleware;
363mod pool;
364mod proxy;
365mod request;
366mod resolve;
367mod response;
368mod stream;
369mod unit;
370
371// rustls is our default tls engine. If the feature is on, it will be
372// used for the shortcut calls the top of the crate (`ureq::get` etc).
373#[cfg(feature = "tls")]
374mod rtls;
375
376// native-tls is a feature that must be configured via the AgentBuilder.
377// it is never picked up as a default (and never used by `ureq::get` etc).
378#[cfg(feature = "native-tls")]
379mod ntls;
380
381// If we have rustls compiled, that is the default.
382#[cfg(feature = "tls")]
383pub(crate) fn default_tls_config() -> std::sync::Arc<dyn TlsConnector> {
384 rtls::default_tls_config()
385}
386
387// Without rustls compiled, we just fail on https when using the shortcut
388// calls at the top of the crate (`ureq::get` etc).
389#[cfg(not(feature = "tls"))]
390pub(crate) fn default_tls_config() -> std::sync::Arc<dyn TlsConnector> {
391 use std::sync::Arc;
392
393 struct NoTlsConfig;
394
395 impl TlsConnector for NoTlsConfig {
396 fn connect(
397 &self,
398 _dns_name: &str,
399 _io: Box<dyn ReadWrite>,
400 ) -> Result<Box<dyn ReadWrite>, crate::error::Error> {
401 Err(ErrorKind::UnknownScheme
402 .msg("cannot make HTTPS request because no TLS backend is configured"))
403 }
404 }
405
406 Arc::new(NoTlsConfig)
407}
408
409#[cfg(feature = "cookies")]
410mod cookies;
411
412#[cfg(feature = "json")]
413pub use serde_json::json;
414use url::Url;
415
416#[cfg(test)]
417mod test;
418#[doc(hidden)]
419mod testserver;
420
421#[cfg(feature = "http-interop")]
422// 0.2 version dependency (deprecated)
423mod http_interop;
424
425#[cfg(feature = "http-crate")]
426// 1.0 version dependency.
427mod http_crate;
428
429pub use crate::agent::Agent;
430pub use crate::agent::AgentBuilder;
431pub use crate::agent::RedirectAuthHeaders;
432pub use crate::error::{Error, ErrorKind, OrAnyStatus, Transport};
433pub use crate::middleware::{Middleware, MiddlewareNext};
434pub use crate::proxy::Proxy;
435pub use crate::request::{Request, RequestUrl};
436pub use crate::resolve::Resolver;
437pub use crate::response::Response;
438pub use crate::stream::{ReadWrite, TlsConnector};
439
440// re-export
441#[cfg(feature = "cookies")]
442pub use cookie::Cookie;
443
444#[cfg(feature = "json")]
445pub use {serde, serde_json};
446
447#[cfg(feature = "json")]
448#[deprecated(note = "use ureq::serde_json::Map instead")]
449pub type SerdeMap<K, V> = serde_json::Map<K, V>;
450
451#[cfg(feature = "json")]
452#[deprecated(note = "use ureq::serde_json::Value instead")]
453pub type SerdeValue = serde_json::Value;
454
455#[cfg(feature = "json")]
456#[deprecated(note = "use ureq::serde_json::to_value instead")]
457pub fn serde_to_value<T: serde::Serialize>(
458 value: T,
459) -> std::result::Result<serde_json::Value, serde_json::Error> {
460 serde_json::to_value(value)
461}
462
463use once_cell::sync::Lazy;
464use std::sync::atomic::{AtomicBool, Ordering};
465
466/// Creates an [AgentBuilder].
467pub fn builder() -> AgentBuilder {
468 AgentBuilder::new()
469}
470
471// is_test returns false so long as it has only ever been called with false.
472// If it has ever been called with true, it will always return true after that.
473// This is a public but hidden function used to allow doctests to use the test_agent.
474// Note that we use this approach for doctests rather the #[cfg(test)], because
475// doctests are run against a copy of the crate build without cfg(test) set.
476// We also can't use #[cfg(doctest)] to do this, because cfg(doctest) is only set
477// when collecting doctests, not when building the crate.
478#[doc(hidden)]
479pub fn is_test(is: bool) -> bool {
480 static IS_TEST: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
481 if is {
482 IS_TEST.store(val:true, order:Ordering::SeqCst);
483 }
484 IS_TEST.load(order:Ordering::SeqCst)
485}
486
487/// Agents are used to hold configuration and keep state between requests.
488pub fn agent() -> Agent {
489 #[cfg(not(test))]
490 if is_test(is:false) {
491 testserver::test_agent()
492 } else {
493 AgentBuilder::new().build()
494 }
495 #[cfg(test)]
496 testserver::test_agent()
497}
498
499/// Make a request with the HTTP verb as a parameter.
500///
501/// This allows making requests with verbs that don't have a dedicated
502/// method.
503///
504/// If you've got an already-parsed [`Url`], try [`request_url()`].
505///
506/// ```
507/// # fn main() -> Result<(), ureq::Error> {
508/// # ureq::is_test(true);
509/// let resp: ureq::Response = ureq::request("OPTIONS", "http://example.com/")
510/// .call()?;
511/// # Ok(())
512/// # }
513/// ```
514pub fn request(method: &str, path: &str) -> Request {
515 agent().request(method, path)
516}
517/// Make a request using an already-parsed [Url].
518///
519/// This is useful if you've got a parsed [`Url`] from some other source, or if
520/// you want to parse the URL and then modify it before making the request.
521/// If you'd just like to pass a [`String`] or a [`&str`], try [`request()`].
522///
523/// ```
524/// # fn main() -> Result<(), ureq::Error> {
525/// # ureq::is_test(true);
526/// use url::Url;
527/// let agent = ureq::agent();
528///
529/// let mut url: Url = "http://example.com/some-page".parse()?;
530/// url.set_path("/get/robots.txt");
531/// let resp: ureq::Response = ureq::request_url("GET", &url)
532/// .call()?;
533/// # Ok(())
534/// # }
535/// ```
536pub fn request_url(method: &str, url: &Url) -> Request {
537 agent().request_url(method, url)
538}
539
540/// Make a GET request.
541pub fn get(path: &str) -> Request {
542 request(method:"GET", path)
543}
544
545/// Make a HEAD request.
546pub fn head(path: &str) -> Request {
547 request(method:"HEAD", path)
548}
549
550/// Make a PATCH request.
551pub fn patch(path: &str) -> Request {
552 request(method:"PATCH", path)
553}
554
555/// Make a POST request.
556pub fn post(path: &str) -> Request {
557 request(method:"POST", path)
558}
559
560/// Make a PUT request.
561pub fn put(path: &str) -> Request {
562 request(method:"PUT", path)
563}
564
565/// Make a DELETE request.
566pub fn delete(path: &str) -> Request {
567 request(method:"DELETE", path)
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573
574 #[test]
575 fn connect_http_google() {
576 let agent = Agent::new();
577
578 let resp = agent.get("http://www.google.com/").call().unwrap();
579 assert_eq!(
580 "text/html;charset=ISO-8859-1",
581 resp.header("content-type").unwrap().replace("; ", ";")
582 );
583 assert_eq!("text/html", resp.content_type());
584 }
585
586 #[test]
587 #[cfg(feature = "tls")]
588 fn connect_https_google_rustls() {
589 let agent = Agent::new();
590
591 let resp = agent.get("https://www.google.com/").call().unwrap();
592 assert_eq!(
593 "text/html;charset=ISO-8859-1",
594 resp.header("content-type").unwrap().replace("; ", ";")
595 );
596 assert_eq!("text/html", resp.content_type());
597 }
598
599 #[test]
600 #[cfg(feature = "native-tls")]
601 fn connect_https_google_native_tls() {
602 use std::sync::Arc;
603
604 let tls_config = native_tls::TlsConnector::new().unwrap();
605 let agent = builder().tls_connector(Arc::new(tls_config)).build();
606
607 let resp = agent.get("https://www.google.com/").call().unwrap();
608 assert_eq!(
609 "text/html;charset=ISO-8859-1",
610 resp.header("content-type").unwrap().replace("; ", ";")
611 );
612 assert_eq!("text/html", resp.content_type());
613 }
614
615 #[test]
616 fn connect_https_invalid_name() {
617 let result = get("https://example.com{REQUEST_URI}/").call();
618 let e = ErrorKind::Dns;
619 assert_eq!(result.unwrap_err().kind(), e);
620 }
621}
622