slinger/lib.rs
1#![deny(missing_docs)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc(
4 html_favicon_url = "https://raw.githubusercontent.com/emo-crab/slinger/main/images/logo.svg",
5 html_logo_url = "https://raw.githubusercontent.com/emo-crab/slinger/main/images/screenshot.png"
6)]
7//! [![GitHub]](https://github.com/emo-crab/slinger) [![crates-io]](https://crates.io/crates/slinger) [![docs-rs]](crate)
8//!
9//! [GitHub]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
10//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
11//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
12//!
13//! # slinger (投石器)
14//!
15//! The `slinger` crate provides a convenient, low-level HTTP
16//! [`Client`].
17//!
18//! It handles many of the things that most people just expect an HTTP client
19//! to do for them.
20//!
21//! - Customizable [redirect policy](#redirect-policies)
22//! - HTTP [Proxies](#proxies)
23//! - Uses [TLS](#tls) by default
24//! - Cookies
25//!
26//!
27//! Additional learning resources include:
28//!
29//! - [Slinger Repository Examples](https://github.com/emo-crab/slinger/tree/master/examples)
30//!
31//! ## Making a GET request
32//!
33//! For a single request, you can use the [`get`] shortcut method.
34//!
35//!```rust
36//! async fn run() -> Result<(), Box<dyn std::error::Error>> {
37//! let body = slinger::get("https://httpbin.org/get").await?
38//! .text()?;
39//! println!("body = {body:?}");
40//! Ok(())
41//! }
42//! ```
43//!
44//! **NOTE**: If you plan to perform multiple requests, it is best to create a
45//! [`Client`] and reuse it, taking advantage of keep-alive connection
46//! pooling.
47//!
48//! ## Making POST requests (or setting request bodies)
49//!
50//! There are several ways you can set the body of a request. The basic one is
51//! by using the `body()` method of a [`RequestBuilder`]. This lets you set the
52//! exact raw bytes of what the body should be. It accepts various types,
53//! including `String` and `Vec<u8>`. If you wish to pass a custom
54//! type, you can use the `slinger::Body` constructors.
55//!
56//!```rust
57//! # use slinger::Error;
58//! #
59//! # async fn run() -> Result<(), Error> {
60//! let client = slinger::Client::new();
61//! let res = client.post("http://httpbin.org/post")
62//! .body("the exact body that is sent")
63//! .send().await?;
64//! # Ok(())
65//! # }
66//! ```
67//!
68//! ## Redirect Policies
69//!
70//! By default, a `Client` will automatically handle HTTP redirects, having a
71//! maximum redirect chain of 10 hops. To customize this behavior, a
72//! [`redirect::Policy`][redirect] can be used with a `ClientBuilder`.
73//!
74//! ## Cookies
75//!
76//! The automatic storing and sending of session cookies can be enabled with
77//! the [`cookie_store`][client::ClientBuilder::cookie_store] method on `ClientBuilder`.
78//!
79//! ## Proxies
80//!```rust
81//! async fn run() -> std::result::Result<(), Box<dyn std::error::Error>> {
82//! let proxy = slinger::Proxy::parse("http://user:pass@127.0.0.1:1080").unwrap();
83//! // let proxy = slinger::Proxy::parse("socks5://user:pass@127.0.0.1:1080").unwrap();
84//! let client = slinger::ClientBuilder::default().proxy(proxy).build().unwrap();
85//! let resp = client.get("https://httpbin.org/get").send().await?;
86//! println!("{:?}", resp);
87//! Ok(())
88//! }
89//!```
90//! ## TLS
91//!
92//! A `Client` will use transport layer security (TLS) by default to connect to
93//! HTTPS destinations.
94//!
95//! - Additional server certificates can be configured on a `ClientBuilder`
96//! with the [`tls::Certificate`][tls::Certificate] type.
97//! - Client certificates can be added to a `ClientBuilder` with the
98//! [`tls::Identity`][tls::Identity] type.
99//! - Various parts of TLS can also be configured or even disabled on the
100//! `ClientBuilder`.
101//!
102//! ### TLS Backend
103//!
104//! When the `rustls` feature is enabled (recommended), slinger uses rustls as the default
105//! TLS backend through the unified `CustomTlsConnector` interface. This provides a consistent
106//! API regardless of which TLS implementation you use.
107//!
108//! You can also provide your own TLS implementation by implementing the `CustomTlsConnector`
109//! trait and setting it with `ConnectorBuilder::custom_tls_connector()`.
110//!
111//! ## Optional Features
112//!
113//! The following are a list of [Cargo features]\[cargo-features\] that can be
114//! enabled or disabled:
115//!
116//! - **charset**: Improved support for decoding text.
117//! - **cookie**: Provides cookie session support.
118//! - **tls**: Base TLS feature flag. Enables TLS types and interfaces.
119//! - **rustls**: Provides HTTPS support via rustls (requires `tls` feature). This is the recommended TLS backend.
120//! - **serde**: Provides serialization and deserialization support.
121//! - **gzip**: Provides response body gzip decompression.
122//! - **http2**: Provides HTTP/2 support (requires a TLS backend).
123//!
124//! ### TLS Backend Selection
125//!
126//! When the `tls` feature is enabled, you have several options:
127//! - Enable `rustls` for the default pure Rust TLS implementation (recommended)
128//! - Or provide a custom TLS connector by implementing the `CustomTlsConnector` trait
129//!
130//! ### Custom TLS Backend
131//!
132//! If you enable the `tls` feature, you can implement your own TLS handshake logic
133//! using any TLS library (OpenSSL, BoringSSL, native-tls, etc.):
134//!
135//! ```ignore
136//! use slinger::{ConnectorBuilder, CustomTlsConnector};
137//!
138//! struct MyTlsConnector;
139//!
140//! impl CustomTlsConnector for MyTlsConnector {
141//! fn connect<'a>(&'a self, domain: &'a str, stream: Socket)
142//! -> Pin<Box<dyn Future<Output = Result<Socket>> + Send + 'a>>
143//! {
144//! // Your TLS implementation here
145//! todo!()
146//! }
147//! }
148//!
149//! let connector = ConnectorBuilder::default()
150//! .custom_tls_connector(Arc::new(MyTlsConnector))
151//! .build()?;
152//! ```
153//!
154//! When the `rustls` feature is enabled, rustls is provided as the default implementation
155//! but you can still override it with your own custom connector if needed.
156//!
157mod body;
158mod client;
159mod connector;
160#[cfg(feature = "cookie")]
161mod cookies;
162mod errors;
163#[cfg(feature = "http2")]
164mod h2_client;
165mod proxy;
166/// record info
167pub mod record;
168/// Redirect Handling
169pub mod redirect;
170mod request;
171mod response;
172#[cfg(feature = "serde")]
173mod serde_schema;
174mod socket;
175#[cfg(feature = "tls")]
176pub mod tls;
177pub use body::Body;
178use bytes::Bytes;
179pub use client::{Client, ClientBuilder};
180pub use connector::{Connector, ConnectorBuilder};
181pub use errors::{Error, Result};
182pub use http;
183#[cfg(feature = "serde")]
184pub use http_serde;
185pub use proxy::Proxy;
186pub use request::{RawRequest, Request, RequestBuilder};
187pub use response::{Response, ResponseBuilder, ResponseConfig, StreamingResponse};
188pub use socket::Socket;
189#[cfg(feature = "tls")]
190pub use socket::StreamWrapper;
191
192/// Shortcut method to quickly make a `GET` request.
193///
194/// See also the methods on the [`slinger::Response`](./struct.Response.html)
195/// type.
196///
197/// **NOTE**: This function creates a new internal `Client` on each call,
198/// and so should not be used if making many requests. Create a
199/// [`Client`](./struct.Client.html) instead.
200///
201/// # Examples
202///
203/// ```rust
204/// # async fn run() -> Result<(), slinger::Error> {
205/// let body = slinger::get("https://www.rust-lang.org").await?
206/// .text()?;
207/// # Ok(())
208/// # }
209/// ```
210///
211pub async fn get<U>(url: U) -> errors::Result<Response>
212where
213 http::Uri: TryFrom<U>,
214 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
215{
216 Client::builder().build()?.get(url).send().await
217}
218
219/// Shortcut method to quickly make a `RAW` request.
220///
221/// See also the methods on the [`slinger::Response`](./struct.Response.html)
222/// type.
223///
224/// **NOTE**: This function creates a new internal `Client` on each call,
225/// and so should not be used if making many requests. Create a
226/// [`Client`](./struct.Client.html) instead.
227///
228/// # Examples
229///
230/// ```rust
231/// # async fn run() -> Result<(), slinger::Error> {
232/// let body = slinger::raw("http://httpbin.org","GET /robots HTTP/1.1\r\n\r\n",true).await?
233/// .text()?;
234/// # Ok(())
235/// # }
236/// ```
237///
238pub async fn raw<U, R>(uri: U, raw: R, unsafe_raw: bool) -> errors::Result<Response>
239where
240 Bytes: From<R>,
241 http::Uri: TryFrom<U>,
242 <http::Uri as TryFrom<U>>::Error: Into<http::Error>,
243{
244 Client::builder()
245 .build()?
246 .raw(uri, raw, unsafe_raw)
247 .send()
248 .await
249}
250
251pub(crate) const CR_LF: &[u8] = &[13, 10];
252pub(crate) const SPACE: &[u8] = &[32];
253pub(crate) const COLON_SPACE: &[u8] = &[58, 32];
254
255#[cfg(test)]
256mod tests {
257 #[test]
258 fn it_works() {
259 let result = 2 + 2;
260 assert_eq!(result, 4);
261 }
262}