Skip to content

Asynchronous Rust HTTP client with TLS, JA3/JA4, and HTTP2 fingerprint spoof

License

Notifications You must be signed in to change notification settings

penumbra-x/rquest

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rquest

Crates.io License Crates.io MSRV crates.io Crates.io Total Downloads

🚀 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.33.3"
use rquest::tls::Impersonate;

#[tokio::main]
async fn main() -> Result<(), rquest::Error> {
    // Build a client to mimic Chrome131
    let client = rquest::Client::builder()
        .impersonate(Impersonate::Chrome131)
        .build()?;

    // Use the API you're already familiar with
    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.33.3", features = ["websocket"] }
futures-util = { version = "0.3.0", default-features = false, features = ["std"] }
use futures_util::{SinkExt, StreamExt, TryStreamExt};
use rquest::{tls::Impersonate, Client, Message};

#[tokio::main]
async fn main() -> Result<(), rquest::Error> {
    // Build a client to mimic Chrome131
    let client = Client::builder()
        .impersonate(Impersonate::Chrome131)
        .build()?;

    // Use the API you're already familiar with
    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.33.3"
use http::{header, HeaderMap, HeaderName, HeaderValue};
use rquest::{
    tls::{Http2Settings, ImpersonateSettings, TlsSettings, TlsVersion},
    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> {
    // TLS settings
    let tls_settings = 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(TlsVersion::TLS_1_0)
        .max_tls_version(TlsVersion::TLS_1_3)
        .build();

    // HTTP/2 settings
    let http2_settings = 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 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)
    };

    // Create impersonate settings
    let settings = ImpersonateSettings::builder()
        .tls(tls_settings)
        .http2(http2_settings)
        .headers(headers)
        .headers_order(Cow::Borrowed(HEADER_ORDER))
        .build();

    // Build a client with impersonate settings
    let client = rquest::Client::builder()
        .impersonate_settings(settings)
        .build()?;

    // Use the API you're already familiar with
    let resp = client.get("https://tls.peet.ws/api/all").send().await?;
    println!("{}", resp.text().await?);

    Ok(())
}

More examples can be found in the examples directory.

Root Certificate

By default, rquest uses Mozilla's root certificates through the webpki-roots crate. This is a static root certificate bundle that is not automatically updated. It also ignores any root certificates installed on the host running rquest, which may be a good thing or a bad thing, depending on your point of view. But you can turn off default-features to cancel the default certificate bundle, and the system default certificate path will be used to load the certificate. In addition, rquest also provides a certificate store for users to customize the update certificate.

Device

You can customize the TLS/HTTP2 fingerprint parameters of the device. In addition, the basic device impersonation types are provided as follows:

  • Chrome

Chrome100Chrome101Chrome104Chrome105Chrome106Chrome107Chrome108Chrome109Chrome114Chrome116Chrome117Chrome118Chrome119Chrome120Chrome123Chrome124Chrome126Chrome127Chrome128Chrome129Chrome130Chrome131

  • Edge

Edge101Edge122Edge127Edge131

  • Safari

SafariIos17_2SafariIos17_4_1SafariIos16_5Safari15_3Safari15_5Safari15_6_1Safari16Safari16_5Safari17_0Safari17_2_1Safari17_4_1Safari17_5Safari18SafariIPad18, Safari18_2, Safari18_1_1

  • OkHttp

OkHttp3_9OkHttp3_11OkHttp3_13OkHttp3_14OkHttp4_9OkHttp4_10OkHttp5

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.

If you prefer compiling for the musl target, it is recommended to use the tikv-jemallocator memory allocator; otherwise, multithreaded performance may be suboptimal. Only available in version 0.6.0, details: tikv/jemallocator#70

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.