[go: up one dir, main page]

chttp/
lib.rs

1//! The practical HTTP client that is fun to use.
2//!
3//! Here are some of cHTTP's key features:
4//!
5//! - Full support for HTTP/1.1 and HTTP/2.
6//! - Configurable request timeouts.
7//! - Fully asynchronous core, with asynchronous and incremental reading and
8//!   writing of request and response bodies.
9//! - Offers an ergonomic synchronous API as well as an asynchronous API with
10//!   support for async/await.
11//! - Optional automatic redirect following.
12//! - Sessions and cookie persistence.
13//!
14//! # Getting started
15//!
16//! Sending requests is as easy as calling a single function. Let's make a
17//! simple GET request to an example website:
18//!
19//! ```no_run
20//! use chttp::prelude::*;
21//!
22//! let mut response = chttp::get("https://example.org")?;
23//! println!("{}", response.text()?);
24//! # Ok::<(), chttp::Error>(())
25//! ```
26//!
27//! By default, sending a request will wait for the response, up until the
28//! response headers are received. The returned response struct includes the
29//! response body as an open stream implementing [`Read`](std::io::Read).
30//!
31//! Sending a POST request is also easy, and takes an additional argument for
32//! the request body:
33//!
34//! ```no_run
35//! let response = chttp::post("https://httpbin.org/post", "make me a salad")?;
36//! # Ok::<(), chttp::Error>(())
37//! ```
38//!
39//! cHTTP provides several other simple functions for common HTTP request types:
40//!
41//! ```no_run
42//! chttp::put("https://httpbin.org/put", "have a salad")?;
43//! chttp::head("https://httpbin.org/get")?;
44//! chttp::delete("https://httpbin.org/delete")?;
45//! # Ok::<(), chttp::Error>(())
46//! ```
47//!
48//! If you want to customize the request by adding headers, setting timeouts,
49//! etc, then you can create a [`Request`][prelude::Request] using a
50//! builder-style fluent interface, then finishing it off with a
51//! [`send`][RequestExt::send]:
52//!
53//! ```no_run
54//! use chttp::prelude::*;
55//! use std::time::Duration;
56//!
57//! let response = Request::post("https://httpbin.org/post")
58//!     .header("Content-Type", "application/json")
59//!     .timeout(Duration::from_secs(5))
60//!     .body(r#"{
61//!         "speed": "fast",
62//!         "cool_name": true
63//!     }"#)?
64//!     .send()?;
65//! # Ok::<(), chttp::Error>(())
66//! ```
67//!
68//! Check out the [examples] directory in the project sources for even more
69//! examples.
70//!
71//! # Feature tour
72//!
73//! Below is a brief overview of some notable features of cHTTP. Check out the
74//! rest of the documentation for even more guides and examples.
75//!
76//! ## Easy request functions
77//!
78//! You can start sending requests without any configuration by using the global
79//! functions in this module, including [`get`], [`post`], and [`send`]. These
80//! use a shared HTTP client instance with sane defaults, so it is easy to get
81//! up and running. They should work perfectly fine for many use-cases, so don't
82//! about graduating to more complex APIs if you don't need them.
83//!
84//! ## Request and response traits
85//!
86//! cHTTP includes a number of traits in the [`prelude`] module that extend the
87//! [`Request`] and [`Response`] types with a plethora of extra methods that
88//! make common tasks convenient and allow you to make more advanced
89//! configuration.
90//!
91//! Some key traits to read about include [`RequestExt`], [`RequestBuilderExt`],
92//! and [`ResponseExt`].
93//!
94//! ## Custom clients
95//!
96//! The free-standing functions for sending requests use a shared [`HttpClient`]
97//! instance, but you can also create your own client instances, which allows
98//! you to customize the default behavior for requests that use it.
99//!
100//! See the documentation for [`HttpClient`] and [`HttpClientBuilder`] for more
101//! information on creating custom clients.
102//!
103//! ## Asynchronous requests
104//!
105//! Requests are always executed asynchronously under the hood. This allows a
106//! single client to execute a large number of requests concurrently with
107//! minimal overhead.
108//!
109//! If you are writing an asynchronous application, you can additionally benefit
110//! from the async nature of the client by using the asynchronous methods
111//! available to prevent blocking threads in your code. All request methods have
112//! an asynchronous variant that ends with `_async` in the name. Here is our
113//! first example rewritten to use async/await syntax (nightly Rust only):
114//!
115//! ```ignore
116//! use chttp::prelude::*;
117//!
118//! let mut response = chttp::get_async("https://httpbin.org/get").await?;
119//! println!("{}", response.text_async().await?);
120//! ```
121//!
122//! # Logging
123//!
124//! cHTTP logs quite a bit of useful information at various levels using the
125//! [log] crate.
126//!
127//! If you set the log level to `Trace` for the `chttp::wire` target, cHTTP will
128//! also log all incoming and outgoing data while in flight. This may come in
129//! handy if you are debugging code and need to see the exact data being sent to
130//! the server and being received.
131//!
132//! [examples]: https://github.com/sagebind/chttp/tree/master/examples
133//! [log]: https://docs.rs/log
134
135#![deny(unsafe_code)]
136#![warn(
137    future_incompatible,
138    missing_debug_implementations,
139    missing_docs,
140    rust_2018_idioms,
141    unreachable_pub,
142    unused,
143    clippy::all,
144)]
145
146use http::{Request, Response};
147use lazy_static::lazy_static;
148
149#[cfg(feature = "cookies")]
150pub mod cookies;
151
152#[cfg(feature = "middleware-api")]
153pub mod middleware;
154#[cfg(not(feature = "middleware-api"))]
155#[allow(unreachable_pub, unused)]
156mod middleware;
157
158mod agent;
159mod body;
160mod client;
161pub mod config;
162mod error;
163mod handler;
164mod io;
165mod parse;
166mod request;
167mod response;
168mod task;
169
170pub use crate::{
171    body::Body,
172    client::{HttpClient, HttpClientBuilder, ResponseFuture},
173    error::Error,
174    request::{RequestBuilderExt, RequestExt},
175    response::ResponseExt,
176};
177
178/// Re-export of the standard HTTP types.
179pub use http;
180
181/// A "prelude" for importing common cHTTP types.
182pub mod prelude {
183    pub use crate::{
184        Body,
185        HttpClient,
186        RequestExt,
187        RequestBuilderExt,
188        ResponseExt,
189    };
190
191    pub use http::{Request, Response};
192}
193
194/// Send a GET request to the given URI.
195///
196/// The request is executed using a shared [`HttpClient`] instance. See
197/// [`HttpClient::get`] for details.
198pub fn get<U>(uri: U) -> Result<Response<Body>, Error>
199where
200    http::Uri: http::HttpTryFrom<U>,
201{
202    HttpClient::shared().get(uri)
203}
204
205/// Send a GET request to the given URI asynchronously.
206///
207/// The request is executed using a shared [`HttpClient`] instance. See
208/// [`HttpClient::get_async`] for details.
209pub fn get_async<U>(uri: U) -> ResponseFuture<'static>
210where
211    http::Uri: http::HttpTryFrom<U>,
212{
213    HttpClient::shared().get_async(uri)
214}
215
216/// Send a HEAD request to the given URI.
217///
218/// The request is executed using a shared [`HttpClient`] instance. See
219/// [`HttpClient::head`] for details.
220pub fn head<U>(uri: U) -> Result<Response<Body>, Error>
221where
222    http::Uri: http::HttpTryFrom<U>,
223{
224    HttpClient::shared().head(uri)
225}
226
227/// Send a HEAD request to the given URI asynchronously.
228///
229/// The request is executed using a shared [`HttpClient`] instance. See
230/// [`HttpClient::head_async`] for details.
231pub fn head_async<U>(uri: U) -> ResponseFuture<'static>
232where
233    http::Uri: http::HttpTryFrom<U>,
234{
235    HttpClient::shared().head_async(uri)
236}
237
238/// Send a POST request to the given URI with a given request body.
239///
240/// The request is executed using a shared [`HttpClient`] instance. See
241/// [`HttpClient::post`] for details.
242pub fn post<U>(uri: U, body: impl Into<Body>) -> Result<Response<Body>, Error>
243where
244    http::Uri: http::HttpTryFrom<U>,
245{
246    HttpClient::shared().post(uri, body)
247}
248
249/// Send a POST request to the given URI asynchronously with a given request
250/// body.
251///
252/// The request is executed using a shared [`HttpClient`] instance. See
253/// [`HttpClient::post_async`] for details.
254pub fn post_async<U>(uri: U, body: impl Into<Body>) -> ResponseFuture<'static>
255where
256    http::Uri: http::HttpTryFrom<U>,
257{
258    HttpClient::shared().post_async(uri, body)
259}
260
261/// Send a PUT request to the given URI with a given request body.
262///
263/// The request is executed using a shared [`HttpClient`] instance. See
264/// [`HttpClient::put`] for details.
265pub fn put<U>(uri: U, body: impl Into<Body>) -> Result<Response<Body>, Error>
266where
267    http::Uri: http::HttpTryFrom<U>,
268{
269    HttpClient::shared().put(uri, body)
270}
271
272/// Send a PUT request to the given URI asynchronously with a given request
273/// body.
274///
275/// The request is executed using a shared [`HttpClient`] instance. See
276/// [`HttpClient::put_async`] for details.
277pub fn put_async<U>(uri: U, body: impl Into<Body>) -> ResponseFuture<'static>
278where
279    http::Uri: http::HttpTryFrom<U>,
280{
281    HttpClient::shared().put_async(uri, body)
282}
283
284/// Send a DELETE request to the given URI.
285///
286/// The request is executed using a shared [`HttpClient`] instance. See
287/// [`HttpClient::delete`] for details.
288pub fn delete<U>(uri: U) -> Result<Response<Body>, Error>
289where
290    http::Uri: http::HttpTryFrom<U>,
291{
292    HttpClient::shared().delete(uri)
293}
294
295/// Send a DELETE request to the given URI asynchronously.
296///
297/// The request is executed using a shared [`HttpClient`] instance. See
298/// [`HttpClient::delete_async`] for details.
299pub fn delete_async<U>(uri: U) -> ResponseFuture<'static>
300where
301    http::Uri: http::HttpTryFrom<U>,
302{
303    HttpClient::shared().delete_async(uri)
304}
305
306/// Send an HTTP request and return the HTTP response.
307///
308/// The request is executed using a shared [`HttpClient`] instance. See
309/// [`HttpClient::send`] for details.
310pub fn send<B: Into<Body>>(request: Request<B>) -> Result<Response<Body>, Error> {
311    HttpClient::shared().send(request)
312}
313
314/// Send an HTTP request and return the HTTP response asynchronously.
315///
316/// The request is executed using a shared [`HttpClient`] instance. See
317/// [`HttpClient::send_async`] for details.
318pub fn send_async<B: Into<Body>>(request: Request<B>) -> ResponseFuture<'static> {
319    HttpClient::shared().send_async(request)
320}
321
322/// Gets a human-readable string with the version number of cHTTP and its
323/// dependencies.
324///
325/// This function can be helpful when troubleshooting issues in cHTTP or one of
326/// its dependencies.
327pub fn version() -> &'static str {
328    static FEATURES_STRING: &str = include_str!(concat!(env!("OUT_DIR"), "/features.txt"));
329
330    lazy_static! {
331        static ref VERSION_STRING: String = format!(
332            "chttp/{} (features:{}) {}",
333            env!("CARGO_PKG_VERSION"),
334            FEATURES_STRING,
335            curl::Version::num(),
336        );
337    }
338
339    &VERSION_STRING
340}