rquest

🚀 Help me work seamlessly with open source sharing by sponsoring me on GitHub
An ergonomic, all-in-one JA3/JA4/HTTP2 fingerprint HTTP/WebSocket client.
- Plain, JSON, urlencoded, multipart bodies
- Header Order
- Redirect policy
- Cookie Store
- Restrict pool connections
- Proxy-level connection pool
HTTPS/WebSocket via BoringSSL
- Preconfigured
TLS/HTTP2 settings
HTTP, HTTPS, SOCKS4 and SOCKS5 proxies
- Changelog
Additional learning resources include:
⚠ This crate is under active development and the API is not yet stable.
Usage
This asynchronous example uses Tokio and enables some
optional features, so your Cargo.toml could look like this:
HTTP
[dependencies]
tokio = { version = "1", features = ["full"] }
rquest = "0.31.0"
use rquest::tls::Impersonate;
#[tokio::main]
async fn main() -> Result<(), rquest::Error> {
let client = rquest::Client::builder()
.impersonate(Impersonate::Chrome131)
.build()?;
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
println!("{}", resp.text().await?);
Ok(())
}
WebSocket
[dependencies]
tokio = { version = "1", features = ["full"] }
rquest = { version = "0.31.0", features = ["websocket"] }
use futures_util::{SinkExt, StreamExt, TryStreamExt};
use rquest::{tls::Impersonate, Client, Message};
#[tokio::main]
async fn main() -> Result<(), rquest::Error> {
let client = Client::builder()
.impersonate(Impersonate::Chrome131)
.build()?;
let websocket = client
.websocket("wss://echo.websocket.org")
.send()
.await?
.into_websocket()
.await?;
let (mut tx, mut rx) = websocket.split();
tokio::spawn(async move {
for i in 1..11 {
tx.send(Message::Text(format!("Hello, World! #{i}")))
.await
.unwrap();
}
});
while let Some(message) = rx.try_next().await? {
match message {
Message::Text(text) => println!("received: {text}"),
_ => {}
}
}
Ok(())
}
Preconfigured TLS/HTTP2
[dependencies]
tokio = { version = "1", features = ["full"] }
rquest = "0.31.0"
use http::{header, HeaderMap, HeaderName, HeaderValue};
use rquest::{
tls::{Http2Settings, ImpersonateSettings, TlsSettings, Version},
HttpVersionPref,
};
use rquest::{PseudoOrder::*, SettingsOrder::*};
use std::borrow::Cow;
static HEADER_ORDER: &[HeaderName] = &[
header::USER_AGENT,
header::ACCEPT_LANGUAGE,
header::ACCEPT_ENCODING,
header::COOKIE,
header::HOST,
];
#[tokio::main]
async fn main() -> Result<(), rquest::Error> {
let settings = ImpersonateSettings::builder()
.tls(
TlsSettings::builder()
.tls_sni(true)
.alpn_protos(HttpVersionPref::All)
.application_settings(true)
.pre_shared_key(true)
.enable_ech_grease(true)
.permute_extensions(true)
.min_tls_version(Version::TLS_1_0)
.max_tls_version(Version::TLS_1_3)
.build(),
)
.http2(
Http2Settings::builder()
.initial_stream_window_size(6291456)
.initial_connection_window_size(15728640)
.max_concurrent_streams(1000)
.max_header_list_size(262144)
.header_table_size(65536)
.enable_push(false)
.headers_priority((0, 255, true))
.headers_pseudo_order([Method, Scheme, Authority, Path])
.settings_order([
HeaderTableSize,
EnablePush,
MaxConcurrentStreams,
InitialWindowSize,
MaxFrameSize,
MaxHeaderListSize,
UnknownSetting8,
UnknownSetting9,
])
.build(),
)
.headers({
let mut headers = HeaderMap::new();
headers.insert(header::USER_AGENT, HeaderValue::from_static("rquest"));
headers.insert(
header::ACCEPT_LANGUAGE,
HeaderValue::from_static("en-US,en;q=0.9"),
);
headers.insert(
header::ACCEPT_ENCODING,
HeaderValue::from_static("gzip, deflate, br"),
);
headers.insert(header::HOST, HeaderValue::from_static("tls.peet.ws"));
headers.insert(header::COOKIE, HeaderValue::from_static("foo=bar"));
Cow::Owned(headers)
})
.headers_order(Cow::Borrowed(HEADER_ORDER))
.build();
let client = rquest::Client::builder()
.use_preconfigured_tls(settings)
.build()?;
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
println!("{}", resp.text().await?);
Ok(())
}
Modify Client settings
[dependencies]
tokio = { version = "1", features = ["full"] }
rquest = { version = "0.31.0", features = ["full"] }
use http::{header, HeaderName, HeaderValue};
use rquest::{tls::Impersonate, Client};
use std::net::Ipv4Addr;
static HEADER_ORDER: [HeaderName; 6] = [
header::ACCEPT_LANGUAGE,
header::USER_AGENT,
header::ACCEPT_ENCODING,
header::HOST,
header::COOKIE,
HeaderName::from_static("priority"),
];
#[tokio::main]
async fn main() -> Result<(), rquest::Error> {
let mut client = Client::builder()
.impersonate(Impersonate::Chrome131)
.build()?;
{
client.set_headers_order(&HEADER_ORDER);
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
println!("{}", resp.text().await?);
}
{
client.set_impersonate(Impersonate::Safari18)?;
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
println!("{}", resp.text().await?);
}
{
client.set_impersonate_without_headers(Impersonate::Edge127)?;
client
.headers_mut()
.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
client.set_cookies(
vec![HeaderValue::from_static("foo=bar; Domain=tls.peet.ws")],
"https://tls.peet.ws/api/all",
)?;
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
println!("{}", resp.text().await?);
}
{
client.set_local_address(Some(Ipv4Addr::new(172, 20, 10, 2).into()));
let resp = client.get("https://api.ip.sb/ip").send().await?;
println!("{}", resp.text().await?);
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
{
client.set_interface("eth0");
let resp = client.get("https://api.ip.sb/ip").send().await?;
println!("{}", resp.text().await?);
}
let mut client2 = client.clone();
{
client2.set_impersonate(Impersonate::Chrome131)?;
let resp = client2.get("https://api.ip.sb/ip").send().await?;
println!("{}", resp.text().await?);
}
let resp = client.get("https://api.ip.sb/ip").send().await?;
println!("{}", resp.text().await?);
Ok(())
}
Device
You can customize the TLS/HTTP2 fingerprint parameters of the device. In addition, the basic device impersonation types are provided as follows:
Chrome100,Chrome101,Chrome104,Chrome105,Chrome106,Chrome107,Chrome108,Chrome109,Chrome114,Chrome116,Chrome117,Chrome118,Chrome119,Chrome120,Chrome123,Chrome124,Chrome126,Chrome127,Chrome128,Chrome129,Chrome130,Chrome131
Edge101,Edge122,Edge127
SafariIos17_2,SafariIos17_4_1,SafariIos16_5,Safari15_3,Safari15_5,Safari15_6_1,Safari16,Safari16_5,Safari17_0,Safari17_2_1,Safari17_4_1,Safari17_5,Safari18,SafariIPad18
OkHttp3_9,OkHttp3_11,OkHttp3_13,OkHttp3_14,OkHttp4_9,OkHttp4_10,OkHttp5
Requirement
Install the environment required to build BoringSSL
Do not compile with crates that depend on OpenSSL; their prefixing symbols are the same and may cause linking failures.
If both OpenSSL and BoringSSL are used as dependencies simultaneously, even if the compilation succeeds, strange issues may still arise.
Building
sudo apt-get install build-essential cmake perl pkg-config libclang-dev musl-tools -y
cargo build --release
You can also use this GitHub Actions workflow to compile your project on Linux, Windows, and macOS.
About
The predecessor of rquest is reqwest. rquest is a specialized adaptation based on the reqwest project, supporting BoringSSL and related HTTP/2 fingerprints in requests.
It also optimizes commonly used APIs and enhances compatibility with connection pools, making it easier to switch proxies, IP addresses, and interfaces. You can directly migrate from a project using reqwest to rquest.
Due to limited time for maintaining the synchronous APIs, only asynchronous APIs are supported. I may have to give up maintenance; if possible, please consider sponsoring me.
Contributing
If you would like to submit your contribution, please open a Pull Request.
Getting help
Your question might already be answered on the issues
License
Apache-2.0 LICENSE
Accolades
The project is based on a fork of reqwest.