Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Clean docs folder
run: cargo clean --doc
- name: Build docs
run: RUSTFLAGS='--cfg reqwest_unstable' cargo doc --no-deps --manifest-path impit/Cargo.toml
run: RUSTFLAGS='--cfg reqwest_unstable' cargo doc --no-deps --features http3 --manifest-path impit/Cargo.toml
- name: Remove lock file
run: rm target/doc/.lock
- name: Copy redirect
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/format.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,7 @@ jobs:
- uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2

- name: Run tests
run: cargo test -p impit
run: cargo test -p impit

- name: Run tests with HTTP/3
run: cargo test -p impit --features http3
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use impit::{impit::Impit, fingerprint::database as fingerprints};
async fn main() {
let impit = Impit::<Jar>::builder()
.with_fingerprint(fingerprints::firefox_144::fingerprint())
.with_http3()
.build()
.unwrap();

Expand Down Expand Up @@ -106,5 +105,5 @@ h2 = { git="https://github.com/apify/h2.git" }

Without the patched dependencies, the project won't build.

Note that you also have to build your project with `rustflags = "--cfg reqwest_unstable"`, otherwise, the build will also fail.
This is because `impit` uses unstable features of `reqwest` (namely `http3` support), which are not available in the stable version of the library.
HTTP/3 support is behind the `http3` feature (`impit = { ..., features = ["http3"] }`). With it, you also have to build your project with `rustflags = "--cfg reqwest_unstable"`, otherwise, the build will fail.
This is because HTTP/3 support in `reqwest` is unstable, so it's not available in the stable version of the library.
2 changes: 1 addition & 1 deletion impit-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ crate-type = ["cdylib"]
[dependencies]
napi = { version = "=3.10.5", default-features = false, features = ["napi4", "napi5", "async", "web_stream", "tokio_rt"] }
napi-derive = "=3.5.10"
impit = { path="../impit" }
impit = { path="../impit", features = ["http3"] }
rustls = { version="0.23.36" }
tokio = { version="1.41.1", features = ["full"] }
h2 = "0.4.7"
Expand Down
2 changes: 1 addition & 1 deletion impit-python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name = "impit"
crate-type = ["cdylib"]

[dependencies]
impit = { path="../impit" }
impit = { path="../impit", features = ["http3"] }
rustls = { version="0.23.36" }
tokio = { version="1.41.1", features = ["full"] }
h2 = "0.4.7"
Expand Down
15 changes: 11 additions & 4 deletions impit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
# HTTP/3 (QUIC) support, see `ImpitBuilder::with_http3`. Needs the `--cfg reqwest_unstable` rustflag.
# Without it, clients don't set up the unused QUIC endpoint (a UDP socket and a receive buffer of
# at least 2 MB per client).
http3 = ["reqwest/http3", "dep:hickory-proto", "dep:hickory-resolver", "dep:tokio"]

[dependencies]
bytes = "1"
encoding = "0.2.33"
futures-core = "0.3"
hickory-proto = "0.26.1"
hickory-resolver = "0.26.1"
hickory-proto = { version = "0.26.1", optional = true }
hickory-resolver = { version = "0.26.1", optional = true }
log = "0.4.22"
mime = "0.3.17"
reqwest = { version="0.13.1", features = ["json", "gzip", "brotli", "zstd", "deflate", "http3", "cookies", "stream", "socks"] }
reqwest = { version="0.13.1", features = ["json", "gzip", "brotli", "zstd", "deflate", "cookies", "stream", "socks"] }
rustls = { version="0.23.36", features=["impit"] }
lol_html = "2.7.2"
thiserror = "2.0.12"
tokio = { version="1.40.0", features = ["full"] }
tokio = { version="1.40.0", features = ["full"], optional = true }
url = "2.5.2"
rustls-platform-verifier = "0.6"
webpki-root-certs = "1.0.10"
Expand All @@ -27,4 +33,5 @@ rand = "0.9"
uuid = { version = "1", features = ["v4"] }

[dev-dependencies]
tokio = { version="1.40.0", features = ["full"] }
futures-util = "0.3"
5 changes: 2 additions & 3 deletions impit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use reqwest::cookie::Jar;
async fn main() {
let impit = Impit::<Jar>::builder()
.with_browser(Browser::Firefox)
.with_http3()
.build()
.unwrap();

Expand Down Expand Up @@ -54,5 +53,5 @@ h2 = { git="https://github.com/apify/h2.git" }

Without the patched dependencies, the project won't build.

Note that you also have to build your project with `rustflags = "--cfg reqwest_unstable"`, otherwise, the build will also fail.
This is because `impit` uses unstable features of `reqwest` (namely `http3` support), which are not available in the stable version of the library.
HTTP/3 support is behind the `http3` feature (`impit = { ..., features = ["http3"] }`). With it, you also have to build your project with `rustflags = "--cfg reqwest_unstable"`, otherwise, the build will fail.
This is because HTTP/3 support in `reqwest` is unstable, so it's not available in the stable version of the library.
1 change: 0 additions & 1 deletion impit/examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use impit::{fingerprint::database as fingerprints, impit::Impit};
async fn main() {
let impit = Impit::<Jar>::builder()
.with_fingerprint(fingerprints::firefox_144::fingerprint())
.with_http3()
.build()
.unwrap();

Expand Down
47 changes: 45 additions & 2 deletions impit/src/impit.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#[cfg(feature = "http3")]
use tokio::sync::RwLock;

use log::debug;
use reqwest::{cookie::CookieStore, header::HeaderMap, Method, Response, Version};
use std::{fmt::Debug, net::IpAddr, sync::Arc, time::Duration};
use url::Url;

#[cfg(feature = "http3")]
use crate::http3::H3Engine;
use crate::{
errors::{ErrorContext, ImpitError},
fingerprint::BrowserFingerprint,
http3::H3Engine,
http_headers::HttpHeadersBuilder,
request::{ImpitBody, ImpitRequest, RequestOptions},
tls,
Expand All @@ -23,6 +25,7 @@ pub struct Impit<CookieStoreImpl: CookieStore + 'static> {
pub(self) base_client: reqwest::Client,
pub(self) h3_client: Option<reqwest::Client>,
pub(self) vanilla_client: Option<reqwest::Client>,
#[cfg(feature = "http3")]
h3_engine: Arc<RwLock<Option<H3Engine>>>,
config: ImpitBuilder<CookieStoreImpl>,
}
Expand Down Expand Up @@ -72,7 +75,6 @@ pub enum RedirectBehavior {
/// .with_ignore_tls_errors(true)
/// .with_proxy("http://localhost:8080".to_string())
/// .with_default_timeout(Duration::from_secs(10))
/// .with_http3()
/// .build()
/// .unwrap();
///
Expand Down Expand Up @@ -175,7 +177,10 @@ impl<CookieStoreImpl: CookieStore + 'static> ImpitBuilder<CookieStoreImpl> {
/// To enforce HTTP/3 usage, use the `http3_prior_knowledge` option in the `RequestOptions` struct when
/// making the request.
///
/// Requires the `http3` feature of this crate (and the `--cfg reqwest_unstable` rustflag).
///
/// Note that this feature is experimental and may not work as expected with all servers.
#[cfg(feature = "http3")]
pub fn with_http3(mut self) -> Self {
self.max_http_version = Version::HTTP_3;
self
Expand Down Expand Up @@ -286,6 +291,7 @@ impl<CookieStoreImpl: CookieStore + 'static> Impit<CookieStoreImpl> {
client = client.cookie_provider(cookie_provider.clone());
}

#[cfg(feature = "http3")]
if config.max_http_version == Version::HTTP_3 {
client = client.http3_prior_knowledge();
}
Expand Down Expand Up @@ -359,6 +365,7 @@ impl<CookieStoreImpl: CookieStore + 'static> Impit<CookieStoreImpl> {
h3_client,
vanilla_client,
config,
#[cfg(feature = "http3")]
h3_engine: Arc::new(RwLock::new(None)),
})
}
Expand All @@ -379,6 +386,12 @@ impl<CookieStoreImpl: CookieStore + 'static> Impit<CookieStoreImpl> {
}
}

#[cfg(not(feature = "http3"))]
async fn should_use_h3(&self, _host: &str) -> bool {
false
}

#[cfg(feature = "http3")]
async fn should_use_h3(&self, host: &str) -> bool {
if self.config.max_http_version < Version::HTTP_3 {
debug!("HTTP/3 is disabled, falling back to TCP-based requests.");
Expand Down Expand Up @@ -527,6 +540,7 @@ impl<CookieStoreImpl: CookieStore + 'static> Impit<CookieStoreImpl> {
}
};

#[cfg(feature = "http3")]
if !h3 {
let engine_guard = self.h3_engine.read().await;
if let Some(h3_engine) = engine_guard.as_ref() {
Expand Down Expand Up @@ -700,3 +714,32 @@ impl<CookieStoreImpl: CookieStore + 'static> Impit<CookieStoreImpl> {
}
}
}

#[cfg(test)]
mod tests {
use reqwest::cookie::Jar;

use super::Impit;
use crate::{errors::ImpitError, request::RequestOptions};

async fn get_with_http3_prior_knowledge(impit: &Impit<Jar>) -> Result<(), ImpitError> {
let options = RequestOptions {
http3_prior_knowledge: true,
..Default::default()
};
impit
.get("https://example.com".to_string(), None, Some(options))
.await
.map(|_| ())
}

#[tokio::test]
async fn http3_is_off_by_default() {
let impit = Impit::<Jar>::builder().build().unwrap();
assert!(impit.h3_client.is_none());
assert!(matches!(
get_with_http3_prior_knowledge(&impit).await,
Err(ImpitError::Http3Disabled)
));
}
}
7 changes: 4 additions & 3 deletions impit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
//! async fn main() {
//! let impit = Impit::<Jar>::builder()
//! .with_fingerprint(fingerprints::firefox_144::fingerprint())
//! .with_http3()
//! .build()
//! .unwrap();
//!
Expand Down Expand Up @@ -53,14 +52,16 @@
//!
//! Without the patched dependencies, the project won't build.
//!
//! Note that you also have to build your project with `rustflags = "--cfg reqwest_unstable"`, otherwise, the build will also fail.
//! This is because `impit` uses unstable features of `reqwest` (namely `http3` support), which are not available in the stable version of the library.
//! HTTP/3 support is behind the `http3` feature (`impit = { ..., features = ["http3"] }`). With it, you also have to build your project
//! with `rustflags = "--cfg reqwest_unstable"`, otherwise, the build will fail.
//! This is because HTTP/3 support in `reqwest` is unstable, so it's not available in the stable version of the library.

#![deny(unused_crate_dependencies)]
mod http_headers;
mod response_parsing;
mod tls;

#[cfg(feature = "http3")]
pub(crate) mod http3;

/// Main module that contains the `Impit` struct and its methods.
Expand Down
Loading