xkit/lib.rs
1#![deny(missing_docs)]
2#![deny(missing_debug_implementations)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![cfg_attr(test, deny(warnings))]
5
6//! # rquest
7//!
8//! An ergonomic, all-in-one `JA3`/`JA4`/`HTTP2` fingerprint `HTTP`/`WebSocket` client.
9//!
10//! - Plain bodies, [JSON](#json), [urlencoded](#forms), [multipart], [websocket](#websocket)
11//! - Header Order
12//! - Cookies Store
13//! - [Redirect policy](#redirect-policies)
14//! - Uses [BoringSSL](#tls)
15//! - HTTP [Proxies](#proxies)
16//! - [Preconfigured](#preconfigured-tls) `TLS`/`HTTP2`/`Headers` settings
17//! - Chrome / Safari / Edge / OkHttp [Fingerprint](#impersonate)
18//! - [Changelog](https://github.com/0x676e67/rquest/blob/main/CHANGELOG.md)
19//!
20//! Additional learning resources include:
21//!
22//! - [The Rust Cookbook](https://rust-lang-nursery.github.io/rust-cookbook/web/clients.html)
23//! - [Repository Examples](https://github.com/0x676e67/rquest/tree/master/examples)
24//!
25//! ## Impersonate
26//!
27//! The `impersonate` module provides a way to simulate various browser fingerprints.
28//!
29//! ```rust,no_run
30//! use rquest::tls::Impersonate;
31//!
32//! #[tokio::main]
33//! async fn main() -> Result<(), rquest::Error> {
34//! // Build a client to mimic Chrome131
35//! let client = rquest::Client::builder()
36//! .impersonate(Impersonate::Chrome131)
37//! .build()?;
38//!
39//! // Use the API you're already familiar with
40//! let resp = client.get("https://tls.peet.ws/api/all").send().await?;
41//! println!("{}", resp.text().await?);
42//!
43//! Ok(())
44//! }
45//! ```
46//!
47//! ## Websocket
48//!
49//! The `websocket` module provides a way to upgrade a connection to a websocket.
50//!
51//! ```rust,no_run
52//! use futures_util::{SinkExt, StreamExt, TryStreamExt};
53//! use rquest::{tls::Impersonate, Client, Message};
54//!
55//! #[tokio::main]
56//! async fn main() -> Result<(), rquest::Error> {
57//! // Build a client to mimic Chrome131
58//! let websocket = Client::builder()
59//! .impersonate(Impersonate::Chrome131)
60//! .build()?
61//! .websocket("wss://echo.websocket.org")
62//! .send()
63//! .await?
64//! .into_websocket()
65//! .await?;
66//!
67//! let (mut tx, mut rx) = websocket.split();
68//!
69//! tokio::spawn(async move {
70//! for i in 1..11 {
71//! tx.send(Message::Text(format!("Hello, World! #{i}")))
72//! .await
73//! .unwrap();
74//! }
75//! });
76//!
77//! while let Some(message) = rx.try_next().await? {
78//! match message {
79//! Message::Text(text) => println!("received: {text}"),
80//! _ => {}
81//! }
82//! }
83//!
84//! Ok(())
85//! }
86//! ```
87//!
88//! ## Preconfigured-TLS
89//! If you need to use a pre-configured TLS settings, you can use the [use_preconfigured_tls][preconfigured] method on the `ClientBuilder`.
90//!
91//! ```rust
92//!use boring::ssl::{SslConnector, SslCurve, SslMethod, SslOptions};
93//!use http::{header, HeaderValue};
94//!use rquest::{
95//! tls::{Http2Settings, ImpersonateSettings, TlsSettings, Version},
96//! HttpVersionPref,
97//!};
98//!use rquest::{PseudoOrder::*, SettingsOrder::*};
99//!
100//!#[tokio::main]
101//!async fn main() -> Result<(), rquest::Error> {
102//! // Create a pre-configured TLS settings
103//! let settings = ImpersonateSettings::builder()
104//! .tls(
105//! TlsSettings::builder()
106//! .connector(Box::new(|| {
107//! let mut builder = SslConnector::builder(SslMethod::tls_client())?;
108//! builder.set_curves(&[SslCurve::SECP224R1, SslCurve::SECP521R1])?;
109//! builder.set_options(SslOptions::NO_TICKET);
110//! Ok(builder)
111//! }))
112//! .tls_sni(true)
113//! .http_version_pref(HttpVersionPref::All)
114//! .application_settings(true)
115//! .pre_shared_key(true)
116//! .enable_ech_grease(true)
117//! .permute_extensions(true)
118//! .min_tls_version(Version::TLS_1_0)
119//! .max_tls_version(Version::TLS_1_3)
120//! .build(),
121//! )
122//! .http2(
123//! Http2Settings::builder()
124//! .initial_stream_window_size(6291456)
125//! .initial_connection_window_size(15728640)
126//! .max_concurrent_streams(1000)
127//! .max_header_list_size(262144)
128//! .header_table_size(65536)
129//! .enable_push(false)
130//! .headers_priority((0, 255, true))
131//! .headers_pseudo_order([Method, Scheme, Authority, Path])
132//! .settings_order([
133//! HeaderTableSize,
134//! EnablePush,
135//! MaxConcurrentStreams,
136//! InitialWindowSize,
137//! MaxFrameSize,
138//! MaxHeaderListSize,
139//! UnknownSetting8,
140//! UnknownSetting9,
141//! ])
142//! .build(),
143//! )
144//! .headers(Box::new(|headers| {
145//! headers.insert(header::USER_AGENT, HeaderValue::from_static("rquest"));
146//! }))
147//! .build();
148//!
149//! // Build a client with pre-configured TLS settings
150//! let client = rquest::Client::builder()
151//! .use_preconfigured_tls(settings)
152//! .build()?;
153//!
154//! // Use the API you're already familiar with
155//! let resp = client.get("https://tls.peet.ws/api/all").send().await?;
156//! println!("{}", resp.text().await?);
157//!
158//! Ok(())
159//!}
160//!
161//! ```
162//!
163//! ## Making a GET request
164//!
165//! For a single request, you can use the [`get`][get] shortcut method.
166//!
167//! ```rust
168//! # async fn run() -> Result<(), rquest::Error> {
169//! let body = rquest::get("https://www.rust-lang.org")
170//! .await?
171//! .text()
172//! .await?;
173//!
174//! println!("body = {:?}", body);
175//! # Ok(())
176//! # }
177//! ```
178//!
179//! **NOTE**: If you plan to perform multiple requests, it is best to create a
180//! [`Client`][client] and reuse it, taking advantage of keep-alive connection
181//! pooling.
182//!
183//! ## Making POST requests (or setting request bodies)
184//!
185//! There are several ways you can set the body of a request. The basic one is
186//! by using the `body()` method of a [`RequestBuilder`][builder]. This lets you set the
187//! exact raw bytes of what the body should be. It accepts various types,
188//! including `String` and `Vec<u8>`. If you wish to pass a custom
189//! type, you can use the `rquest::Body` constructors.
190//!
191//! ```rust
192//! # use rquest::Error;
193//! #
194//! # async fn run() -> Result<(), Error> {
195//! let client = rquest::Client::new();
196//! let res = client.post("http://httpbin.org/post")
197//! .body("the exact body that is sent")
198//! .send()
199//! .await?;
200//! # Ok(())
201//! # }
202//! ```
203//!
204//! ### Forms
205//!
206//! It's very common to want to send form data in a request body. This can be
207//! done with any type that can be serialized into form data.
208//!
209//! This can be an array of tuples, or a `HashMap`, or a custom type that
210//! implements [`Serialize`][serde].
211//!
212//! ```rust
213//! # use rquest::Error;
214//! #
215//! # async fn run() -> Result<(), Error> {
216//! // This will POST a body of `foo=bar&baz=quux`
217//! let params = [("foo", "bar"), ("baz", "quux")];
218//! let client = rquest::Client::new();
219//! let res = client.post("http://httpbin.org/post")
220//! .form(¶ms)
221//! .send()
222//! .await?;
223//! # Ok(())
224//! # }
225//! ```
226//!
227//! ### JSON
228//!
229//! There is also a `json` method helper on the [`RequestBuilder`][builder] that works in
230//! a similar fashion the `form` method. It can take any value that can be
231//! serialized into JSON. The feature `json` is required.
232//!
233//! ```rust
234//! # use rquest::Error;
235//! # use std::collections::HashMap;
236//! #
237//! # #[cfg(feature = "json")]
238//! # async fn run() -> Result<(), Error> {
239//! // This will POST a body of `{"lang":"rust","body":"json"}`
240//! let mut map = HashMap::new();
241//! map.insert("lang", "rust");
242//! map.insert("body", "json");
243//!
244//! let client = rquest::Client::new();
245//! let res = client.post("http://httpbin.org/post")
246//! .json(&map)
247//! .send()
248//! .await?;
249//! # Ok(())
250//! # }
251//! ```
252//!
253//! ## Redirect Policies
254//!
255//! By default, the client does not handle HTTP redirects.
256//! To customize this behavior, you can use [`redirect::Policy`][redirect] with ClientBuilder.
257//!
258//! ## Cookies
259//!
260//! The automatic storing and sending of session cookies can be enabled with
261//! the [`cookie_store`][ClientBuilder::cookie_store] method on `ClientBuilder`.
262//!
263//! ## Proxies
264//!
265//! **NOTE**: System proxies are enabled by default.
266//!
267//! System proxies look in environment variables to set HTTP or HTTPS proxies.
268//!
269//! `HTTP_PROXY` or `http_proxy` provide HTTP proxies for HTTP connections while
270//! `HTTPS_PROXY` or `https_proxy` provide HTTPS proxies for HTTPS connections.
271//! `ALL_PROXY` or `all_proxy` provide proxies for both HTTP and HTTPS connections.
272//! If both the all proxy and HTTP or HTTPS proxy variables are set the more specific
273//! HTTP or HTTPS proxies take precedence.
274//!
275//! These can be overwritten by adding a [`Proxy`] to `ClientBuilder`
276//! i.e. `let proxy = rquest::Proxy::http("https://secure.example")?;`
277//! or disabled by calling `ClientBuilder::no_proxy()`.
278//!
279//! `socks` feature is required if you have configured socks proxy like this:
280//!
281//! ```bash
282//! export https_proxy=socks5://127.0.0.1:1086
283//! ```
284//! ## TLS
285//!
286//! By default, clients will utilize BoringSSL transport layer security to connect to HTTPS targets.
287//!
288//! - Various parts of TLS can also be configured or even disabled on the
289//! `ClientBuilder`.
290//!
291//! ## Optional Features
292//!
293//! The following are a list of [Cargo features][cargo-features] that can be
294//! enabled or disabled:
295//!
296//! - **boring-tls** *(enabled by default)*: Provides TLS support to connect
297//! over HTTPS.
298//! - **websocket**: Provides websocket support.
299//! - **cookies**: Provides cookie session support.
300//! - **gzip**: Provides response body gzip decompression.
301//! - **brotli**: Provides response body brotli decompression.
302//! - **zstd**: Provides response body zstd decompression.
303//! - **deflate**: Provides response body deflate decompression.
304//! - **json**: Provides serialization and deserialization for JSON bodies.
305//! - **multipart**: Provides functionality for multipart forms.
306//! - **stream**: Adds support for `futures::Stream`.
307//! - **socks**: Provides SOCKS5 proxy support.
308//! - **hickory-dns**: Enables a hickory-dns async resolver instead of default
309//! threadpool using `getaddrinfo`.
310//!
311//! [hyper]: http://hyper.rs
312//! [client]: ./struct.Client.html
313//! [response]: ./struct.Response.html
314//! [get]: ./fn.get.html
315//! [builder]: ./struct.RequestBuilder.html
316//! [serde]: http://serde.rs
317//! [redirect]: crate::redirect
318//! [Proxy]: ./struct.Proxy.html
319//! [preconfigured]: ./struct.ClientBuilder.html#method.use_preconfigured_tls
320//! [cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section
321
322/// Re-export of boring to keep versions in check
323#[cfg(feature = "boring-tls")]
324pub use boring;
325#[cfg(feature = "boring-tls")]
326pub use boring_sys;
327#[cfg(feature = "hickory-dns")]
328pub use hickory_resolver;
329pub use http::header;
330pub use http::Method;
331pub use http::{StatusCode, Version};
332#[cfg(feature = "boring-tls")]
333pub use tokio_boring;
334pub use url::Url;
335
336// universal mods
337#[macro_use]
338mod error;
339mod into_url;
340mod response;
341
342pub use self::error::{Error, Result};
343pub use self::into_url::IntoUrl;
344pub use self::response::ResponseBuilderExt;
345
346/// Shortcut method to quickly make a `GET` request.
347///
348/// See also the methods on the [`rquest::Response`](./struct.Response.html)
349/// type.
350///
351/// **NOTE**: This function creates a new internal `Client` on each call,
352/// and so should not be used if making many requests. Create a
353/// [`Client`](./struct.Client.html) instead.
354///
355/// # Examples
356///
357/// ```rust
358/// # async fn run() -> Result<(), rquest::Error> {
359/// let body = rquest::get("https://www.rust-lang.org").await?
360/// .text().await?;
361/// # Ok(())
362/// # }
363/// ```
364///
365/// # Errors
366///
367/// This function fails if:
368///
369/// - native TLS backend cannot be initialized
370/// - supplied `Url` cannot be parsed
371/// - there was an error while sending request
372/// - redirect limit was exhausted
373pub async fn get<T: IntoUrl>(url: T) -> crate::Result<Response> {
374 Client::builder().build()?.get(url).send().await
375}
376
377/// Opens a websocket at the specified URL.
378///
379/// This is a shorthand for creating a request, sending it, and turning the
380/// response into a websocket.
381#[cfg(feature = "websocket")]
382pub async fn websocket<T: IntoUrl>(url: T) -> crate::Result<WebSocket> {
383 Client::builder()
384 .build()?
385 .websocket(url)
386 .send()
387 .await?
388 .into_websocket()
389 .await
390}
391
392fn _assert_impls() {
393 fn assert_send<T: Send>() {}
394 fn assert_sync<T: Sync>() {}
395 fn assert_clone<T: Clone>() {}
396
397 assert_send::<Client>();
398 assert_sync::<Client>();
399 assert_clone::<Client>();
400
401 assert_send::<Request>();
402 assert_send::<RequestBuilder>();
403
404 #[cfg(not(target_arch = "wasm32"))]
405 {
406 assert_send::<Response>();
407 }
408
409 assert_send::<Error>();
410 assert_sync::<Error>();
411}
412
413#[cfg(test)]
414doc_comment::doctest!("../README.md");
415
416#[cfg(feature = "multipart")]
417pub use self::client::multipart;
418#[cfg(feature = "websocket")]
419pub use self::client::websocket::{
420 CloseCode, Message, WebSocket, WebSocketRequestBuilder, WebSocketResponse,
421};
422pub use self::client::{
423 Body, Client, ClientBuilder, HttpVersionPref, Request, RequestBuilder, Response, Upgraded,
424};
425pub use self::proxy::{NoProxy, Proxy};
426
427#[cfg(feature = "boring-tls")]
428pub use hyper::{PseudoOrder, SettingsOrder};
429
430mod client;
431mod connect;
432#[cfg(feature = "cookies")]
433pub mod cookie;
434pub mod dns;
435mod proxy;
436pub mod redirect;
437#[cfg(feature = "boring-tls")]
438pub mod tls;
439mod util;