Overview of primp-h2
mainprimp-h2 is a fork of the original h2 HTTP/2 client and server library. It is part of the PRIMP ecosystem, designed to provide specialized HTTP/2 capabilities.repository·main·Indexed 20 days ago
https://github.com/deedy5/primpAn HTTP client with browser impersonation capabilities designed to bypass fingerprinting-based restrictions by mimicking specific browser and OS profiles. It provides both Rust and Python APIs, supporting synchronous and asynchronous operations. The ecosystem includes primp-h2, a fork of the h2 HTTP/2 client and server library, and supports profiles for Chrome, Safari, Edge, Firefox, and Opera.
primp-h2 is a fork of the original h2 HTTP/2 client and server library. It is part of the PRIMP ecosystem, designed to provide specialized HTTP/2 capabilities.rustls-provider-test crate is an unpublished workspace crate used to host integration tests for various cryptography providers and their associated machinery. Its primary purpose is to allow testing without introducing heavy dependencies into the main rustls crate.When using impersonation, primp modifies the following layers of the network request:
User-Agent, sec-ch-ua, Accept, Accept-Language, Accept-Encoding, sec-fetch-*, etc.gzip, brotli, and zstd per browser profile.If you provide a list to dns_resolver, primp will attempt to use the resolvers in the order they are listed. The first resolver that succeeds will be used. This is useful for attempting secure protocols like DoH or DoT first, then falling back to the system resolver or plain DNS if they fail.
# Try DoH first, fall back to system
client = primp.Client(dns_resolver=["doh://cloudflare-dns.com/dns-query", "system"])
# Try DoH first, fall back to plain DNS
client = primp.Client(dns_resolver=["doh://cloudflare-dns.com/dns-query", "1.1.1.1"])All exceptions in primp derive from PrimpError. Understanding the hierarchy allows you to catch specific errors (like ConnectError) or broad categories (like RequestError) depending on your error handling strategy.
Hierarchy:
PrimpError (base exception)BuilderError: Errors during client or request construction (e.g., invalid URLs).RequestError: Generic request/network errors.ConnectError: Connection-level issues (DNS, proxy, SSL, network).TimeoutError: Request or connection timeouts.StatusError: HTTP 4xx or 5xx responses (includes status_code).RedirectError: Exceeding the maximum number of redirects.BodyError: Errors during body or stream I/O.DecodeError: Errors decoding content (e.g., gzip, deflate, zstd).UpgradeError: Protocol upgrade failures.PrimpError (base exception)
├── BuilderError # Client/request builder errors
├── RequestError # Generic request errors
│ ├── ConnectError # Connection errors (DNS, proxy, SSL)
│ └── TimeoutError # Request timeout
├── StatusError # HTTP 4xx/5xx (has status_code attribute)
├── RedirectError # Too many redirects
├── BodyError # Body/stream errors
├── DecodeError # Content decoding errors
└── UpgradeError # Protocol upgrade errorsTo avoid detection, you can configure the client to mimic specific browser and OS fingerprints using the impersonate and impersonate_os arguments in the Client constructor.
Supported Browser Profiles (impersonate):
chrome_144, chrome_145, chrome_146, chromesafari_18.5, safari_26, safari_26.3, safariedge_144, edge_145, edge_146, edgefirefox_140, firefox_146, firefox_147, firefox_148, firefoxopera_126, opera_127, opera_128, opera_129, operarandomSupported OS Profiles (impersonate_os):
android, ios, linux, macos, windows, randomTo use primp in your Rust project, add it to your Cargo.toml dependencies file.
[dependencies]
primp = "1"You can create an HTTP client that impersonates a specific web browser using Client::builder() and the .impersonate() method. This allows your requests to mimic the TLS and HTTP/2 fingerprints of real browsers.
use primp::{Client, Impersonate};
#[tokio::main]
async fn main() -> Result<(), primp::Error> {
let client = Client::builder()
.impersonate(Impersonate::ChromeV146)
.build()?;
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
println!("Body: {}", resp.text().await?);
Ok(())
}When using streaming responses, always use a context manager to prevent resource leaks. If you do not use a context manager, you must manually call .close() (sync) or .aclose() (async) on the response object.
Recommended Pattern:
with primp.get(url, stream=True) as resp:
for chunk in resp.iter_bytes(65536):
process(chunk)Manual Pattern (Avoid if possible):
resp = primp.get(url, stream=True)
try:
for chunk in resp.iter_bytes():
process(chunk)
finally:
resp.close()# Good
with primp.get(url, stream=True) as resp:
for chunk in resp.iter_bytes(65536): # 64KB chunks
process(chunk)
# Avoid — remember to call resp.close()
resp = primp.get(url, stream=True)
for chunk in resp.iter_bytes():
process(chunk)
resp.close()If you need to build primp from the source repository, follow these steps using maturin:
maturin and run maturin develop -r to build and install the package in development mode.git clone https://github.com/deedy5/primp.git && cd primp/crates/primp-python
python -m venv .venv && source .venv/bin/activate
pip install maturin && maturin develop -rThe AsyncClient is an asynchronous HTTP client designed to impersonate web browsers. It is best used within an async with context manager to ensure proper resource management. All request methods (GET, POST, etc.) return awaitable futures that must be awaited.
import asyncio
import primp
async def main():
async with primp.AsyncClient(impersonate="chrome_146") as client:
resp = await client.get("https://httpbin.org/get")
print(resp.text)
asyncio.run(main())You can benchmark primp against other Python HTTP clients including aiohttp, curl_cffi, httpx, pycurl, and requests. Note that the server response used in this benchmark is gzipped.
To execute the benchmark, run the following command from the benchmark directory:
python run.py