isahc/lib.rs
1//! The practical HTTP client that is fun to use.
2//!
3//! Here are some of Isahc's key features:
4//!
5//! - Full support for HTTP/1.1 and HTTP/2.
6//! - Configurable request timeouts, redirect policies, Unix sockets, and many
7//! more settings.
8//! - Offers an ergonomic synchronous API as well as a runtime-agnostic
9//! asynchronous API with support for async/await.
10//! - Fully asynchronous core, with incremental reading and writing of request
11//! and response bodies and connection multiplexing.
12//! - Sessions and cookie persistence.
13//! - Automatic request cancellation on drop.
14//!
15//! # Getting started
16//!
17//! Sending requests is as easy as calling a single function. Let's make a
18//! simple GET request to an example website:
19//!
20//! ```no_run
21//! use isahc::prelude::*;
22//!
23//! let mut response = isahc::get("https://example.org")?;
24//! println!("{}", response.text()?);
25//! # Ok::<(), isahc::Error>(())
26//! ```
27//!
28//! By default, sending a request will wait for the response, up until the
29//! response headers are received. The returned response struct includes the
30//! response body as an open stream implementing [`Read`](std::io::Read).
31//!
32//! Sending a POST request is also easy, and takes an additional argument for
33//! the request body:
34//!
35//! ```no_run
36//! let response = isahc::post("https://httpbin.org/post", "make me a salad")?;
37//! # Ok::<(), isahc::Error>(())
38//! ```
39//!
40//! Isahc provides several other simple functions for common HTTP request types:
41//!
42//! ```no_run
43//! isahc::put("https://httpbin.org/put", "have a salad")?;
44//! isahc::head("https://httpbin.org/get")?;
45//! isahc::delete("https://httpbin.org/delete")?;
46//! # Ok::<(), isahc::Error>(())
47//! ```
48//!
49//! If you want to customize the request by adding headers, setting timeouts,
50//! etc, then you can create a [`Request`][Request] using a
51//! builder-style fluent interface, then finishing it off with a
52//! [`send`][RequestExt::send]:
53//!
54//! ```no_run
55//! use isahc::{prelude::*, Request};
56//! use std::time::Duration;
57//!
58//! let response = Request::post("https://httpbin.org/post")
59//! .header("Content-Type", "application/json")
60//! .timeout(Duration::from_secs(5))
61//! .body(r#"{
62//! "speed": "fast",
63//! "cool_name": true
64//! }"#)?
65//! .send()?;
66//! # Ok::<(), isahc::Error>(())
67//! ```
68//!
69//! For even more examples used in complete programs, please check out the
70//! [examples](https://github.com/sagebind/isahc/tree/master/examples) directory
71//! in the project repo.
72//!
73//! # Feature tour
74//!
75//! Below is a brief overview of some notable features of Isahc. Check out the
76//! rest of the documentation for even more guides and examples.
77//!
78//! ## Easy request functions
79//!
80//! You can start sending requests without any configuration by using the global
81//! functions in this module, including [`get`], [`post`], and [`send`]. These
82//! use a shared HTTP client instance with sane defaults, so it is easy to get
83//! up and running. They should work perfectly fine for many use-cases, so don't
84//! worry about graduating to more complex APIs if you don't need them.
85//!
86//! ## Request and response traits
87//!
88//! Isahc includes a number of traits in the [`prelude`] module that extend the
89//! [`Request`] and [`Response`] types with a plethora of extra methods that
90//! make common tasks convenient and allow you to configure more advanced
91//! connection and protocol details.
92//!
93//! Here are some of the key traits to read about:
94//!
95//! - [`Configurable`](config::Configurable): Configure request parameters.
96//! - [`RequestExt`]: Manipulate and send requests.
97//! - [`ResponseExt`]: Get information about the corresponding request or
98//! response statistics.
99//! - [`ReadResponseExt`]: Consume a response body in a variety of ways.
100//! - [`AsyncReadResponseExt`]: Consume an asynchronous response body in a
101//! variety of ways.
102//!
103//! ## Custom clients
104//!
105//! The free-standing functions for sending requests use a shared [`HttpClient`]
106//! instance, but you can also create your own client instances, which allows
107//! you to customize the default behavior for requests that use it.
108//!
109//! See the documentation for [`HttpClient`] and [`HttpClientBuilder`] for more
110//! information on creating custom clients.
111//!
112//! ## Asynchronous requests
113//!
114//! Requests are always executed asynchronously under the hood. This allows a
115//! single client to execute a large number of requests concurrently with
116//! minimal overhead. Even synchronous applications can benefit!
117//!
118//! If you are writing an asynchronous application, you can reap additional
119//! benefits from the async nature of the client by using the asynchronous
120//! methods available to prevent blocking threads in your code. All request
121//! methods have an asynchronous variant that ends with `_async` in the name.
122//! Here is our first example rewritten to use async/await syntax:
123//!
124//! ```no_run
125//! # async fn run() -> Result<(), isahc::Error> {
126//! use isahc::prelude::*;
127//!
128//! let mut response = isahc::get_async("https://httpbin.org/get").await?;
129//! println!("{}", response.text().await?);
130//! # Ok(()) }
131//! ```
132//!
133//! Since we sent our request using [`get_async`], no blocking will occur, and
134//! the asynchronous versions of all response methods (such as
135//! [`text`](AsyncReadResponseExt::text)) will also automatically be selected by
136//! the compiler.
137//!
138//! # Feature flags
139//!
140//! Isahc is designed to be as "pay-as-you-need" as possible using Cargo feature
141//! flags and optional dependencies. Unstable features are also initially
142//! released behind feature flags until they are stabilized. You can add the
143//! feature names below to your `Cargo.toml` file to enable them:
144//!
145//! ```toml
146//! [dependencies.isahc]
147//! version = "1.7"
148//! features = ["psl"]
149//! ```
150//!
151//! Below is a list of all available feature flags and their meanings.
152//!
153//! ## `cookies`
154//!
155//! Enable persistent HTTP cookie support. Disabled by default.
156//!
157//! ## `http2`
158//!
159//! Enable compile-time support for HTTP/2 in libcurl via libnghttp2. This does
160//! not actually affect whether HTTP/2 is used for a given request, but simply
161//! makes it available. To configure which HTTP versions to use in a request,
162//! see [`VersionNegotiation`](config::VersionNegotiation).
163//!
164//! To check which HTTP versions are supported at runtime, you can use
165//! [`is_http_version_supported`].
166//!
167//! Enabled by default.
168//!
169//! ## `json`
170//!
171//! Additional serialization and deserialization of JSON bodies via
172//! [serde](https://serde.rs). Disabled by default.
173//!
174//! ## `psl`
175//!
176//! Enable use of the Public Suffix List to filter out potentially malicious
177//! cross-domain cookies. Implies `cookies`, disabled by default.
178//!
179//! ## `spnego`
180//!
181//! Enable support for [SPNEGO-based HTTP
182//! authentication](https://tools.ietf.org/html/rfc4559) (`negotiate` auth
183//! scheme). This makes the `negotiate` scheme available in the API and, if
184//! `static-curl` is enabled, compiles libcurl with GSS-API APIs. The [MIT
185//! Kerberos](https://web.mit.edu/kerberos/) headers must be pre-installed at
186//! compile time.
187//!
188//! ## `static-curl`
189//!
190//! Use a bundled libcurl version and statically link to it. Enabled by default.
191//!
192//! ## `text-decoding`
193//!
194//! Enable support for decoding text-based responses in various charsets into
195//! strings. Enabled by default.
196//!
197//! ## Unstable APIs
198//!
199//! There are also some features that enable new incubating APIs that do not
200//! have stability guarantees:
201//!
202//! ### `unstable-interceptors`
203//!
204//! Enable the new interceptors API (replaces the old unstable middleware API).
205//! Unstable until the API is finalized. This an unstable feature whose
206//! interface may change between patch releases.
207//!
208//! # Logging and tracing
209//!
210//! Isahc logs quite a bit of useful information at various levels compatible
211//! with the [log](https://docs.rs/log) crate. For even more in-depth
212//! diagnostics, you can use a [tracing](https://docs.rs/tracing) subscriber to
213//! track log events grouped by individual requests. This can be especially
214//! useful if you are sending multiple requests concurrently.
215//!
216//! If you set the log level to `Trace` for the `isahc::wire` target, Isahc will
217//! also log all incoming and outgoing data while in flight. This may come in
218//! handy if you are debugging code and need to see the exact data being sent to
219//! the server and being received.
220
221#![doc(
222 html_logo_url = "https://raw.githubusercontent.com/sagebind/isahc/master/media/isahc.svg.png",
223 html_favicon_url = "https://raw.githubusercontent.com/sagebind/isahc/master/media/icon.png"
224)]
225#![deny(unsafe_code)]
226#![cfg_attr(feature = "nightly", feature(doc_cfg))]
227#![cfg_attr(feature = "nightly", feature(doc_auto_cfg))]
228#![warn(
229 future_incompatible,
230 missing_debug_implementations,
231 missing_docs,
232 rust_2018_idioms,
233 unreachable_pub,
234 unused,
235 clippy::all
236)]
237// These lints suggest to use features not available in our MSRV.
238#![allow(clippy::manual_strip, clippy::match_like_matches_macro)]
239
240use std::convert::TryFrom;
241
242#[macro_use]
243mod macros;
244
245#[cfg(feature = "cookies")]
246pub mod cookies;
247
248mod agent;
249mod body;
250mod client;
251mod default_headers;
252mod handler;
253mod headers;
254mod info;
255mod metrics;
256mod parsing;
257mod redirect;
258mod request;
259mod response;
260mod task;
261mod text;
262mod trailer;
263
264pub mod auth;
265pub mod config;
266pub mod error;
267
268#[cfg(feature = "unstable-interceptors")]
269pub mod interceptor;
270#[cfg(not(feature = "unstable-interceptors"))]
271#[allow(unreachable_pub, unused)]
272pub(crate) mod interceptor;
273
274pub use crate::{
275 body::{AsyncBody, Body},
276 client::{HttpClient, HttpClientBuilder, ResponseFuture},
277 error::Error,
278 http::{request::Request, response::Response},
279 info::*,
280 metrics::Metrics,
281 request::RequestExt,
282 response::{AsyncReadResponseExt, ReadResponseExt, ResponseExt},
283 trailer::Trailer,
284};
285
286/// Re-export of HTTP types.
287pub use http;
288
289/// A "prelude" for importing commonly used Isahc types and traits.
290///
291/// The prelude re-exports most commonly used traits and macros from this crate.
292///
293/// # Example
294///
295/// Import the prelude with:
296///
297/// ```
298/// use isahc::prelude::*;
299/// ```
300pub mod prelude {
301 #[doc(no_inline)]
302 pub use crate::{
303 config::Configurable,
304 AsyncReadResponseExt,
305 ReadResponseExt,
306 RequestExt,
307 ResponseExt,
308 };
309}
310
311/// Send a GET request to the given URI.
312///
313/// The request is executed using a shared [`HttpClient`] instance. See
314/// [`HttpClient::get`] for details.
315pub fn get<U>(uri: U) -> Result<Response<Body>, Error>
316where
317 http::Uri: TryFrom<U>,
318 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
319{
320 HttpClient::shared().get(uri)
321}
322
323/// Send a GET request to the given URI asynchronously.
324///
325/// The request is executed using a shared [`HttpClient`] instance. See
326/// [`HttpClient::get_async`] for details.
327pub fn get_async<U>(uri: U) -> ResponseFuture<'static>
328where
329 http::Uri: TryFrom<U>,
330 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
331{
332 HttpClient::shared().get_async(uri)
333}
334
335/// Send a HEAD request to the given URI.
336///
337/// The request is executed using a shared [`HttpClient`] instance. See
338/// [`HttpClient::head`] for details.
339pub fn head<U>(uri: U) -> Result<Response<Body>, Error>
340where
341 http::Uri: TryFrom<U>,
342 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
343{
344 HttpClient::shared().head(uri)
345}
346
347/// Send a HEAD request to the given URI asynchronously.
348///
349/// The request is executed using a shared [`HttpClient`] instance. See
350/// [`HttpClient::head_async`] for details.
351pub fn head_async<U>(uri: U) -> ResponseFuture<'static>
352where
353 http::Uri: TryFrom<U>,
354 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
355{
356 HttpClient::shared().head_async(uri)
357}
358
359/// Send a POST request to the given URI with a given request body.
360///
361/// The request is executed using a shared [`HttpClient`] instance. See
362/// [`HttpClient::post`] for details.
363pub fn post<U, B>(uri: U, body: B) -> Result<Response<Body>, Error>
364where
365 http::Uri: TryFrom<U>,
366 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
367 B: Into<Body>,
368{
369 HttpClient::shared().post(uri, body)
370}
371
372/// Send a POST request to the given URI asynchronously with a given request
373/// body.
374///
375/// The request is executed using a shared [`HttpClient`] instance. See
376/// [`HttpClient::post_async`] for details.
377///
378/// # Examples
379///
380/// ```no_run
381/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
382/// use isahc::prelude::*;
383///
384/// let mut response = isahc::post_async("https://httpbin.org/post", r#"{
385/// "speed": "fast",
386/// "cool_name": true
387/// }"#).await?;
388///
389/// let mut body: Vec<u8> = vec![];
390/// response.copy_to(&mut body).await?;
391///
392/// let msg: serde_json::Value = serde_json::from_slice(&body)?;
393/// println!("{}", msg);
394/// # Ok(()) }
395/// ```
396pub fn post_async<U, B>(uri: U, body: B) -> ResponseFuture<'static>
397where
398 http::Uri: TryFrom<U>,
399 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
400 B: Into<AsyncBody>,
401{
402 HttpClient::shared().post_async(uri, body)
403}
404
405/// Send a PUT request to the given URI with a given request body.
406///
407/// The request is executed using a shared [`HttpClient`] instance. See
408/// [`HttpClient::put`] for details.
409pub fn put<U, B>(uri: U, body: B) -> Result<Response<Body>, Error>
410where
411 http::Uri: TryFrom<U>,
412 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
413 B: Into<Body>,
414{
415 HttpClient::shared().put(uri, body)
416}
417
418/// Send a PUT request to the given URI asynchronously with a given request
419/// body.
420///
421/// The request is executed using a shared [`HttpClient`] instance. See
422/// [`HttpClient::put_async`] for details.
423pub fn put_async<U, B>(uri: U, body: B) -> ResponseFuture<'static>
424where
425 http::Uri: TryFrom<U>,
426 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
427 B: Into<AsyncBody>,
428{
429 HttpClient::shared().put_async(uri, body)
430}
431
432/// Send a DELETE request to the given URI.
433///
434/// The request is executed using a shared [`HttpClient`] instance. See
435/// [`HttpClient::delete`] for details.
436pub fn delete<U>(uri: U) -> Result<Response<Body>, Error>
437where
438 http::Uri: TryFrom<U>,
439 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
440{
441 HttpClient::shared().delete(uri)
442}
443
444/// Send a DELETE request to the given URI asynchronously.
445///
446/// The request is executed using a shared [`HttpClient`] instance. See
447/// [`HttpClient::delete_async`] for details.
448pub fn delete_async<U>(uri: U) -> ResponseFuture<'static>
449where
450 http::Uri: TryFrom<U>,
451 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
452{
453 HttpClient::shared().delete_async(uri)
454}
455
456/// Send an HTTP request and return the HTTP response.
457///
458/// The request is executed using a shared [`HttpClient`] instance. See
459/// [`HttpClient::send`] for details.
460pub fn send<B: Into<Body>>(request: Request<B>) -> Result<Response<Body>, Error> {
461 HttpClient::shared().send(request)
462}
463
464/// Send an HTTP request and return the HTTP response asynchronously.
465///
466/// The request is executed using a shared [`HttpClient`] instance. See
467/// [`HttpClient::send_async`] for details.
468pub fn send_async<B: Into<AsyncBody>>(request: Request<B>) -> ResponseFuture<'static> {
469 HttpClient::shared().send_async(request)
470}