rquest 0.28.6

An ergonomic, all-in-one JA3/JA4/HTTP2 fingerprint HTTP/WebSocket client
Documentation
rquest-0.28.6 has been yanked.

rquest

Crates.io License 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
  • 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.28"
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.28", features = ["websocket"] }
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.28"
use boring::ssl::{SslConnector, SslCurve, SslMethod, SslOptions};
use http::{header, HeaderValue};
use rquest::{
    tls::{Http2Settings, ImpersonateSettings, TlsSettings, Version},
    HttpVersionPref,
};
use rquest::{PseudoOrder::*, SettingsOrder::*};

#[tokio::main]
async fn main() -> Result<(), rquest::Error> {
    // Create a pre-configured TLS settings
    let settings = ImpersonateSettings::builder()
        .tls(
            TlsSettings::builder()
                .connector(Box::new(|| {
                    let mut builder = SslConnector::builder(SslMethod::tls_client())?;
                    builder.set_curves(&[SslCurve::SECP224R1, SslCurve::SECP521R1])?;
                    builder.set_options(SslOptions::NO_TICKET);
                    Ok(builder)
                }))
                .tls_sni(true)
                .http_version_pref(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(Box::new(|headers| {
            headers.insert(header::USER_AGENT, HeaderValue::from_static("rquest"));
        }))
        .build();

    // Build a client with pre-configured TLS settings
    let client = rquest::Client::builder()
        .use_preconfigured_tls(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(())
}

Modify Client settings

[dependencies]
tokio = { version = "1", features = ["full"] }
rquest = "0.28"
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> {
    // Build a client to mimic Chrome131
    let mut client = Client::builder()
        .impersonate(Impersonate::Chrome131)
        .build()?;

    // Set the headers order
    client.set_headers_order(&HEADER_ORDER);

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

    // Change the impersonate to Safari18
    client.set_impersonate(Impersonate::Safari18)?;

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

    // Change the impersonate to Chrome131 without setting the headers
    client.set_impersonate_without_headers(Impersonate::Edge127)?;

    // Set a header
    client
        .headers_mut()
        .insert(header::ACCEPT, "application/json".parse().unwrap());

    // Set a cookie
    client.set_cookies(
        vec![HeaderValue::from_static("foo=bar; Domain=tls.peet.ws")],
        "https://tls.peet.ws/api/all",
    )?;

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

    // Set the local address
    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?);

    // Set the interface
    #[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?);
    }

    // ⚠️ Please note that `set_impersonate`, `set_impersonate_without_headers` will reset all settings, including proxy, header information, etc.,
    // When using Client's `set_headers_order`, `headers_mut`, `set_impersonate`, `set_impersonate_without_headers`,
    // `set_interface`, `set_local_address`, `set_local_addresss`, `set_proxies` methods,
    // When the reference count of the Client is 1, the current Client's settings will be changed,
    // otherwise a new Client will be created and will not affect the previous Client, but will share the same connection pool
    let mut client2 = client.clone();

    // Set the impersonate to Chrome131
    // Expected: Chrome131
    {
        client2.set_impersonate(Impersonate::Chrome131)?;

        let resp = client2.get("https://api.ip.sb/ip").send().await?;
        println!("{}", resp.text().await?);
    }

    // But not change the original client
    // Expected: Edge127
    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:

  • Chrome

Chrome100Chrome101Chrome104Chrome105Chrome106Chrome107Chrome108Chrome109Chrome114Chrome116Chrome117Chrome118Chrome119Chrome120Chrome123Chrome124Chrome126Chrome127Chrome128Chrome129Chrome130Chrome131

  • Edge

Edge101Edge122Edge127

  • Safari

SafariIos17_2SafariIos17_4_1SafariIos16_5Safari15_3Safari15_5Safari15_6_1Safari16Safari16_5Safari17_0Safari17_2_1Safari17_4_1Safari17_5Safari18SafariIPad18

  • 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.

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.

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.