opentele2-browser
Browser HTTP header and fingerprint generation from the Apify fingerprint datapoints, based on
browserforge. The crate embeds the three Bayesian networks of apify_fingerprint_datapoints
0.15.0 and samples them as browserforge does, with two deliberate differences: every random draw
comes from the seedable Rng, and every set operation keeps first-seen order instead of Python’s
hash order, so a seed reproduces its output on any platform. Python’s opentele2.browser and
Node’s generateHeaders and generateFingerprint give the same bytes for the same seed.
docs.rs · crates.io · Apache-2.0
[dependencies]
opentele2-browser = "2.0.0"The crate carries Apache-2.0, the licence of the datapoints it embeds and of browserforge; it has no features.
Entry points
pub fn generate_headers(seed: Option<&str>, options: &HeaderOptions) -> Result<Headers>;
pub fn generate_fingerprint(seed: Option<&str>, options: &FingerprintOptions) -> Result<Fingerprint>;One set of headers, or one fingerprint with its headers, drawn from Rng::from_seed(seed), or
from operating system entropy when seed is None.
use opentele2_browser::{Browser, FingerprintOptions, HeaderOptions, Screen};
fn main() -> opentele2_browser::Result<()> {
let options = HeaderOptions {
browsers: vec![Browser::new("chrome")],
os: vec!["windows".to_owned()],
..HeaderOptions::default()
};
let headers = opentele2_browser::generate_headers(Some("my-id"), &options)?;
println!("{:?}", headers.get("User-Agent"));
let fingerprint = opentele2_browser::generate_fingerprint(
Some("my-id"),
&FingerprintOptions {
screen: Some(Screen { max_width: Some(1920), ..Screen::default() }),
header: options,
..FingerprintOptions::default()
},
)?;
println!("{}", fingerprint.to_json()?);
Ok(())
}Headers
pub const SUPPORTED_BROWSERS: [&str; 4] = ["chrome", "firefox", "safari", "edge"];
pub const SUPPORTED_OPERATING_SYSTEMS: [&str; 5] = ["windows", "macos", "linux", "android", "ios"];
pub const SUPPORTED_DEVICES: [&str; 2] = ["desktop", "mobile"];
pub const MAX_LOCALES: usize = 10;
pub type Headers = OrderedMap<String>;
pub enum HttpVersion { One, #[default] Two }
pub struct Browser {
pub name: String,
pub min_version: Option<u32>,
pub max_version: Option<u32>,
pub http_version: HttpVersion,
}
impl Browser {
pub fn new(name: impl Into<String>) -> Self;
}
pub struct HeaderOptions {
pub browsers: Vec<Browser>,
pub os: Vec<String>,
pub devices: Vec<String>,
pub locales: Vec<String>,
pub http_version: HttpVersion,
pub strict: bool,
}
impl HeaderOptions {
pub fn with_http_version(self, http_version: HttpVersion) -> Self;
}
pub struct HeaderOverrides {
pub user_agent: Vec<String>,
pub request_dependent_headers: Headers,
// ...
}
pub struct HeaderGenerator { /* ... */ }
impl HeaderGenerator {
pub fn new(options: HeaderOptions) -> Result<Self>;
pub fn options(&self) -> &HeaderOptions;
pub fn generate(&self, rng: &mut Rng, overrides: &HeaderOverrides) -> Result<Headers>;
}
pub fn get_user_agent(headers: &Headers) -> Option<&str>;
pub fn browser_of(user_agent: &str) -> Option<&'static str>;
pub fn pascalize(name: &str) -> String;HeaderOptions::default() is browserforge’s: every browser, operating system and device, en-US
and HTTP/2. HeaderGenerator::new checks the options (a minimum version above the maximum, more
than ten locales); generate draws one set of headers under the per-call overrides.
Fingerprints
pub struct Screen {
pub min_width: Option<u32>,
pub max_width: Option<u32>,
pub min_height: Option<u32>,
pub max_height: Option<u32>,
}
pub struct FingerprintOptions {
pub screen: Option<Screen>,
pub strict: bool,
pub mock_webrtc: bool,
pub slim: bool,
pub header: HeaderOptions,
}
pub struct FingerprintOverrides {
pub screen: Option<Screen>,
pub strict: Option<bool>,
pub mock_webrtc: Option<bool>,
pub slim: Option<bool>,
pub header: HeaderOverrides,
}
pub struct FingerprintGenerator { /* ... */ }
impl FingerprintGenerator {
pub fn new(options: FingerprintOptions) -> Result<Self>;
pub fn options(&self) -> &FingerprintOptions;
pub fn generate(&self, rng: &mut Rng, overrides: &FingerprintOverrides) -> Result<Fingerprint>;
}
pub struct Fingerprint {
pub screen: ScreenFingerprint,
pub navigator: NavigatorFingerprint,
pub headers: Headers,
pub video_codecs: Value,
pub audio_codecs: Value,
pub plugins_data: Value,
pub battery: Value,
pub video_card: Option<VideoCard>,
pub multimedia_devices: Value,
pub fonts: Value,
pub mock_webrtc: bool,
pub slim: bool,
pub strict: bool,
}
impl Fingerprint {
pub fn to_json(&self) -> Result<String>;
}to_json serialises a fingerprint with browserforge’s keys (videoCodecs, mockWebRTC, …),
as Python’s Fingerprint.dumps() does.
Rng
pub struct Rng { /* xoshiro256** state */ }
impl Rng {
pub fn from_seed(seed: &str) -> Self;
pub fn from_entropy() -> Result<Self>;
pub fn from_state(state: [u64; 4]) -> Self;
pub fn next_u64(&mut self) -> u64;
pub fn next_f64(&mut self) -> f64;
}The xoshiro256** generator behind every random choice: from_seed uses the SHA-256 digest of the
seed as its state, from_entropy the operating system’s entropy.
Errors
pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
TooRestrictive,
UnknownNode(String),
Zip(String),
Network(String),
InvalidOptions(String),
NoHeaders,
MissingUserAgent,
NoFingerprint,
UnknownBrowser(String),
Data(String),
Entropy(getrandom::Error),
}| Variant | Raised when |
|---|---|
TooRestrictive | The constraints leave some network node without any possible value. |
InvalidOptions | Options browserforge rejects, such as a minimum version above the maximum or more than ten locales. |
NoHeaders | No input sample satisfies the options, even after the permitted relaxations. |
NoFingerprint | The fingerprint network has no sample for the generated User-Agent (and, when strict, the screen). |
Entropy | The operating system could not provide entropy. |
The lower-level network module (BayesianNetwork, header_network, fingerprint_network,
input_network) exposes the embedded networks themselves.