From fc8821669fe7041d9ec1893cf1093b88048d7cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Mon, 5 Oct 2026 17:04:34 +0200 Subject: [PATCH 1/3] feat: send HTTP/2 `HEADER_TABLE_SIZE` from browser fingerprints Adds `header_table_size` to `Http2Fingerprint` and sets it to 65536 for the Chrome and Firefox profiles, matching what both browsers send in their SETTINGS frame. reqwest is patched to the commit exposing `http2_header_table_size` (seanmonstar/reqwest#3105) until it is released, and the hyper-util and tower-http forks are bumped to their synced heads to satisfy its requirements. Related to #385 --- Cargo.lock | 24 +++++++++++------------ Cargo.toml | 5 +++-- impit/src/fingerprint/database/chrome.rs | 8 ++++++++ impit/src/fingerprint/database/firefox.rs | 3 +++ impit/src/fingerprint/database/okhttp.rs | 1 + impit/src/fingerprint/database/safari.rs | 1 + impit/src/fingerprint/mod.rs | 1 + impit/src/impit.rs | 4 ++++ 8 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fca162c..04a5df9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,9 +244,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.22.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bitflags" @@ -1127,9 +1127,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -1141,7 +1141,6 @@ dependencies = [ "httparse", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1165,8 +1164,8 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.20" -source = "git+https://github.com/apify/hyper-util?rev=9b7795dfd7158fc55e7c84b65bf1dae1d2dea67d#9b7795dfd7158fc55e7c84b65bf1dae1d2dea67d" +version = "0.1.21" +source = "git+https://github.com/apify/hyper-util?rev=401e6fb99bb6bd4956a0fafca39f4b7b37be8a0a#401e6fb99bb6bd4956a0fafca39f4b7b37be8a0a" dependencies = [ "base64", "bytes", @@ -1174,6 +1173,7 @@ dependencies = [ "futures-util", "http", "http-body", + "httparse", "hyper", "ipnet", "libc", @@ -2176,9 +2176,8 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +version = "0.13.5" +source = "git+https://github.com/seanmonstar/reqwest?rev=2230ed3945fb2908692b88dad1dcd06db9fb2ca6#2230ed3945fb2908692b88dad1dcd06db9fb2ca6" dependencies = [ "base64", "bytes", @@ -2795,8 +2794,8 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" -source = "git+https://github.com/apify/tower-http?rev=f9efc0d9193e774d33aedc1022b922efefc22052#f9efc0d9193e774d33aedc1022b922efefc22052" +version = "0.7.1" +source = "git+https://github.com/apify/tower-http?rev=4d96dbd81f86559c38cf76b8ce671089544a26e3#4d96dbd81f86559c38cf76b8ce671089544a26e3" dependencies = [ "async-compression", "bitflags", @@ -2806,6 +2805,7 @@ dependencies = [ "http", "http-body", "http-body-util", + "percent-encoding", "pin-project-lite", "tokio", "tokio-util", diff --git a/Cargo.toml b/Cargo.toml index d8ee6645..e1cc3d1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,9 @@ members = [ [patch.crates-io] h2 = { git = "https://github.com/apify/h2", rev = "7f393a728a8db07cabb1b78d2094772b33943b9a" } rustls = { git = "https://github.com/apify/rustls", rev="23b2c17427c095b768e22ccf0dadb97266860cf1" } -tower-http = { git = "https://github.com/apify/tower-http", rev="f9efc0d9193e774d33aedc1022b922efefc22052" } -hyper-util = { git = "https://github.com/apify/hyper-util", rev="9b7795dfd7158fc55e7c84b65bf1dae1d2dea67d" } +tower-http = { git = "https://github.com/apify/tower-http", rev="4d96dbd81f86559c38cf76b8ce671089544a26e3" } +hyper-util = { git = "https://github.com/apify/hyper-util", rev="401e6fb99bb6bd4956a0fafca39f4b7b37be8a0a" } +reqwest = { git = "https://github.com/seanmonstar/reqwest", rev="2230ed3945fb2908692b88dad1dcd06db9fb2ca6" } [profile.release] strip = true # Automatically strip symbols from the binary. diff --git a/impit/src/fingerprint/database/chrome.rs b/impit/src/fingerprint/database/chrome.rs index 4fb5c675..c130b6eb 100644 --- a/impit/src/fingerprint/database/chrome.rs +++ b/impit/src/fingerprint/database/chrome.rs @@ -137,6 +137,7 @@ pub mod chrome_151 { // 15663105, which is what the capture shows on the wire. initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } @@ -278,6 +279,7 @@ pub mod chrome_142 { initial_stream_window_size: Some(6_291_456), initial_connection_window_size: Some(15_663_105), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } @@ -419,6 +421,7 @@ pub mod chrome_136 { initial_stream_window_size: Some(6_291_456), initial_connection_window_size: Some(15_663_105), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } @@ -554,6 +557,7 @@ pub mod chrome_133 { initial_stream_window_size: Some(6_291_456), initial_connection_window_size: Some(15_663_105), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } @@ -689,6 +693,7 @@ pub mod chrome_124 { initial_stream_window_size: Some(6_291_456), initial_connection_window_size: Some(15_663_105), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } @@ -827,6 +832,7 @@ pub mod chrome_131 { initial_stream_window_size: Some(6_291_456), initial_connection_window_size: Some(15_663_105), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } @@ -959,6 +965,7 @@ pub mod chrome_100 { initial_stream_window_size: Some(6_291_456), initial_connection_window_size: Some(15_663_105), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } @@ -1260,6 +1267,7 @@ pub mod chrome_125 { initial_stream_window_size: Some(6_291_456), initial_connection_window_size: Some(15_663_105), max_header_list_size: Some(262_144), + header_table_size: Some(65_536), } } diff --git a/impit/src/fingerprint/database/firefox.rs b/impit/src/fingerprint/database/firefox.rs index 5ccc8561..16661400 100644 --- a/impit/src/fingerprint/database/firefox.rs +++ b/impit/src/fingerprint/database/firefox.rs @@ -123,6 +123,7 @@ pub mod firefox_128 { initial_stream_window_size: Some(131_072), initial_connection_window_size: Some(12_517_377), max_header_list_size: None, + header_table_size: Some(65_536), } } @@ -264,6 +265,7 @@ pub mod firefox_133 { initial_stream_window_size: Some(131_072), initial_connection_window_size: Some(12_517_377), max_header_list_size: None, + header_table_size: Some(65_536), } } @@ -410,6 +412,7 @@ pub mod firefox_135 { initial_stream_window_size: Some(131_072), initial_connection_window_size: Some(12_517_377), max_header_list_size: None, + header_table_size: Some(65_536), } } diff --git a/impit/src/fingerprint/database/okhttp.rs b/impit/src/fingerprint/database/okhttp.rs index f5dd00df..66a15c06 100644 --- a/impit/src/fingerprint/database/okhttp.rs +++ b/impit/src/fingerprint/database/okhttp.rs @@ -37,6 +37,7 @@ fn shared_http2_fingerprint() -> Http2Fingerprint { // Connection window is also increased to 16 MiB via WINDOW_UPDATE initial_connection_window_size: Some(16_777_216), max_header_list_size: None, + header_table_size: None, } } diff --git a/impit/src/fingerprint/database/safari.rs b/impit/src/fingerprint/database/safari.rs index 23b926bd..bfd49ed0 100644 --- a/impit/src/fingerprint/database/safari.rs +++ b/impit/src/fingerprint/database/safari.rs @@ -132,6 +132,7 @@ pub mod ios_18 { // 65_535 (h2 default) + 10_420_225 WINDOW_UPDATE = 10_485_760. initial_connection_window_size: Some(10_485_760), max_header_list_size: None, + header_table_size: None, } } diff --git a/impit/src/fingerprint/mod.rs b/impit/src/fingerprint/mod.rs index 7b249263..c582d29c 100644 --- a/impit/src/fingerprint/mod.rs +++ b/impit/src/fingerprint/mod.rs @@ -105,6 +105,7 @@ pub struct Http2Fingerprint { pub initial_stream_window_size: Option, pub initial_connection_window_size: Option, pub max_header_list_size: Option, + pub header_table_size: Option, } /// TLS extensions configuration. diff --git a/impit/src/impit.rs b/impit/src/impit.rs index 4c45610c..5b5d83cc 100644 --- a/impit/src/impit.rs +++ b/impit/src/impit.rs @@ -258,6 +258,10 @@ impl Impit { if let Some(max_size) = fingerprint.http2.max_header_list_size { client = client.http2_max_header_list_size(max_size); } + + if let Some(size) = fingerprint.http2.header_table_size { + client = client.http2_header_table_size(size); + } } if config.max_http_version == Version::HTTP_3 { From 3ec6792b7e8ae69efe1ef78f2585dbb3646766f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Mon, 5 Oct 2026 17:05:28 +0200 Subject: [PATCH 2/3] fix: pass the full connection window size in older Chrome and Firefox fingerprints `initial_connection_window_size` is the target window, h2 subtracts the default 65535 to get the WINDOW_UPDATE increment. The older Chrome and Firefox profiles passed the on-wire increment instead, so they emitted 15597570 and 12451842 rather than the 15663105 and 12517377 the browsers send. chrome151 and ios_18 already use the full window size. Related to #385 --- impit/src/fingerprint/database/chrome.rs | 14 +++++++------- impit/src/fingerprint/database/firefox.rs | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/impit/src/fingerprint/database/chrome.rs b/impit/src/fingerprint/database/chrome.rs index c130b6eb..582d1b42 100644 --- a/impit/src/fingerprint/database/chrome.rs +++ b/impit/src/fingerprint/database/chrome.rs @@ -277,7 +277,7 @@ pub mod chrome_142 { ":status".to_string(), ], initial_stream_window_size: Some(6_291_456), - initial_connection_window_size: Some(15_663_105), + initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), } @@ -419,7 +419,7 @@ pub mod chrome_136 { ":status".to_string(), ], initial_stream_window_size: Some(6_291_456), - initial_connection_window_size: Some(15_663_105), + initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), } @@ -555,7 +555,7 @@ pub mod chrome_133 { ":status".to_string(), ], initial_stream_window_size: Some(6_291_456), - initial_connection_window_size: Some(15_663_105), + initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), } @@ -691,7 +691,7 @@ pub mod chrome_124 { ":status".to_string(), ], initial_stream_window_size: Some(6_291_456), - initial_connection_window_size: Some(15_663_105), + initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), } @@ -830,7 +830,7 @@ pub mod chrome_131 { ":status".to_string(), ], initial_stream_window_size: Some(6_291_456), - initial_connection_window_size: Some(15_663_105), + initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), } @@ -963,7 +963,7 @@ pub mod chrome_100 { ":status".to_string(), ], initial_stream_window_size: Some(6_291_456), - initial_connection_window_size: Some(15_663_105), + initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), } @@ -1265,7 +1265,7 @@ pub mod chrome_125 { ":status".to_string(), ], initial_stream_window_size: Some(6_291_456), - initial_connection_window_size: Some(15_663_105), + initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), } diff --git a/impit/src/fingerprint/database/firefox.rs b/impit/src/fingerprint/database/firefox.rs index 16661400..d35553b6 100644 --- a/impit/src/fingerprint/database/firefox.rs +++ b/impit/src/fingerprint/database/firefox.rs @@ -121,7 +121,7 @@ pub mod firefox_128 { ":status".to_string(), ], initial_stream_window_size: Some(131_072), - initial_connection_window_size: Some(12_517_377), + initial_connection_window_size: Some(12_582_912), max_header_list_size: None, header_table_size: Some(65_536), } @@ -263,7 +263,7 @@ pub mod firefox_133 { ":status".to_string(), ], initial_stream_window_size: Some(131_072), - initial_connection_window_size: Some(12_517_377), + initial_connection_window_size: Some(12_582_912), max_header_list_size: None, header_table_size: Some(65_536), } @@ -410,7 +410,7 @@ pub mod firefox_135 { ":status".to_string(), ], initial_stream_window_size: Some(131_072), - initial_connection_window_size: Some(12_517_377), + initial_connection_window_size: Some(12_582_912), max_header_list_size: None, header_table_size: Some(65_536), } From da7b971381345ac38b8b987cab50f81be140367e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Tue, 6 Oct 2026 11:32:11 +0200 Subject: [PATCH 3/3] feat: send HTTP/2 `MAX_CONCURRENT_STREAMS` from browser fingerprints Chrome 100 to 116 send 1000 and iOS sends 100, using the `http2_max_concurrent_streams` from the same reqwest change. Related to #385 --- impit/src/fingerprint/database/chrome.rs | 14 +++++++++++--- impit/src/fingerprint/database/firefox.rs | 3 +++ impit/src/fingerprint/database/okhttp.rs | 1 + impit/src/fingerprint/database/safari.rs | 1 + impit/src/fingerprint/mod.rs | 1 + impit/src/impit.rs | 4 ++++ 6 files changed, 21 insertions(+), 3 deletions(-) diff --git a/impit/src/fingerprint/database/chrome.rs b/impit/src/fingerprint/database/chrome.rs index 582d1b42..55dfc332 100644 --- a/impit/src/fingerprint/database/chrome.rs +++ b/impit/src/fingerprint/database/chrome.rs @@ -138,6 +138,7 @@ pub mod chrome_151 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -280,6 +281,7 @@ pub mod chrome_142 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -422,6 +424,7 @@ pub mod chrome_136 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -558,6 +561,7 @@ pub mod chrome_133 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -694,6 +698,7 @@ pub mod chrome_124 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -833,6 +838,7 @@ pub mod chrome_131 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -966,6 +972,7 @@ pub mod chrome_100 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: Some(1_000), } } @@ -1064,7 +1071,7 @@ pub mod chrome_107 { "Chrome", "107", chrome_100::tls_fingerprint(), - chrome_100::http2_fingerprint(), // TODO Chrome 107 uses different HTTP/2 settings + chrome_100::http2_fingerprint(), headers(), ) } @@ -1097,7 +1104,7 @@ pub mod chrome_110 { "Chrome", "110", chrome_100::tls_fingerprint(), - chrome_100::http2_fingerprint(), // TODO Chrome 110 uses different HTTP/2 settings + chrome_100::http2_fingerprint(), headers(), ) } @@ -1130,7 +1137,7 @@ pub mod chrome_116 { "Chrome", "116", chrome_100::tls_fingerprint(), - chrome_100::http2_fingerprint(), // TODO Chrome 116 uses different HTTP/2 settings + chrome_100::http2_fingerprint(), headers(), ) } @@ -1268,6 +1275,7 @@ pub mod chrome_125 { initial_connection_window_size: Some(15_728_640), max_header_list_size: Some(262_144), header_table_size: Some(65_536), + max_concurrent_streams: None, } } diff --git a/impit/src/fingerprint/database/firefox.rs b/impit/src/fingerprint/database/firefox.rs index d35553b6..4bbf89fe 100644 --- a/impit/src/fingerprint/database/firefox.rs +++ b/impit/src/fingerprint/database/firefox.rs @@ -124,6 +124,7 @@ pub mod firefox_128 { initial_connection_window_size: Some(12_582_912), max_header_list_size: None, header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -266,6 +267,7 @@ pub mod firefox_133 { initial_connection_window_size: Some(12_582_912), max_header_list_size: None, header_table_size: Some(65_536), + max_concurrent_streams: None, } } @@ -413,6 +415,7 @@ pub mod firefox_135 { initial_connection_window_size: Some(12_582_912), max_header_list_size: None, header_table_size: Some(65_536), + max_concurrent_streams: None, } } diff --git a/impit/src/fingerprint/database/okhttp.rs b/impit/src/fingerprint/database/okhttp.rs index 66a15c06..869aeff7 100644 --- a/impit/src/fingerprint/database/okhttp.rs +++ b/impit/src/fingerprint/database/okhttp.rs @@ -38,6 +38,7 @@ fn shared_http2_fingerprint() -> Http2Fingerprint { initial_connection_window_size: Some(16_777_216), max_header_list_size: None, header_table_size: None, + max_concurrent_streams: None, } } diff --git a/impit/src/fingerprint/database/safari.rs b/impit/src/fingerprint/database/safari.rs index bfd49ed0..9d900625 100644 --- a/impit/src/fingerprint/database/safari.rs +++ b/impit/src/fingerprint/database/safari.rs @@ -133,6 +133,7 @@ pub mod ios_18 { initial_connection_window_size: Some(10_485_760), max_header_list_size: None, header_table_size: None, + max_concurrent_streams: Some(100), } } diff --git a/impit/src/fingerprint/mod.rs b/impit/src/fingerprint/mod.rs index c582d29c..9d10309e 100644 --- a/impit/src/fingerprint/mod.rs +++ b/impit/src/fingerprint/mod.rs @@ -106,6 +106,7 @@ pub struct Http2Fingerprint { pub initial_connection_window_size: Option, pub max_header_list_size: Option, pub header_table_size: Option, + pub max_concurrent_streams: Option, } /// TLS extensions configuration. diff --git a/impit/src/impit.rs b/impit/src/impit.rs index 5b5d83cc..4609ccef 100644 --- a/impit/src/impit.rs +++ b/impit/src/impit.rs @@ -262,6 +262,10 @@ impl Impit { if let Some(size) = fingerprint.http2.header_table_size { client = client.http2_header_table_size(size); } + + if let Some(max) = fingerprint.http2.max_concurrent_streams { + client = client.http2_max_concurrent_streams(max); + } } if config.max_http_version == Version::HTTP_3 {