Skip to main content

IdentitySession

Struct IdentitySession 

Source
pub struct IdentitySession { /* private fields */ }
Expand description

A rustenium browser session with an identity applied.

Implementations§

Source§

impl IdentitySession

Source

pub async fn launch( config: impl Into<IdentityConfig>, ) -> Result<Self, IdentityError>

Launch a new Chromium instance from the given config. Applies all CDP emulation overrides and registers the stealth bootstrap script before returning.

Examples found in repository?
examples/launch.rs (line 32)
18async fn main() -> Result<(), Box<dyn std::error::Error>> {
19    let identity = match std::env::args().nth(1) {
20        Some(arg) => {
21            let id: u64 = arg.parse()?;
22            preset::get_by_id(id)?
23        }
24        None => preset::random(),
25    };
26
27    println!(
28        "Launching preset #{:?}: {:?} {} / {:?} / {}",
29        identity.id, identity.os, identity.os_version, identity.browser, identity.gpu.webgl_renderer
30    );
31
32    let _session = IdentitySession::launch(identity).await?;
33
34    println!("Browser is up with the identity applied.");
35    println!("Visit a detection site in the opened window (e.g. creepjs / bot.sannysoft.com).");
36    println!("Press Ctrl-C to quit.");
37
38    // Hold the process (and therefore the browser) open until interrupted.
39    tokio::signal::ctrl_c().await?;
40    println!("\nShutting down.");
41    Ok(())
42}
More examples
Hide additional examples
examples/verify.rs (line 23)
12async fn main() -> Result<(), Box<dyn std::error::Error>> {
13    let identity = match std::env::args().nth(1) {
14        Some(arg) => preset::get_by_id(arg.parse()?)?,
15        None => preset::random(),
16    };
17    let url = std::env::args()
18        .nth(2)
19        .unwrap_or_else(|| "https://abrahamjuliot.github.io/creepjs/".to_string());
20
21    println!("preset #{:?}: {:?} / {:?}", identity.id, identity.os, identity.browser);
22
23    let mut session = IdentitySession::launch(identity).await?;
24    println!("launched, navigating to {url} ...");
25
26    // Collect page errors before anything else runs.
27    session.browser().add_init_script(rustenium::cdp::target_manager::InitScript {
28        page: Some(r#"window.__errs=[];
29            addEventListener('error', e => __errs.push('ERR ' + (e.message||e)));
30            addEventListener('unhandledrejection', e => __errs.push('REJ ' + (e.reason && (e.reason.message||e.reason))));
31        "#.into()),
32        worker: None,
33    }).await;
34
35    session.browser_mut().navigate(&url).await?;
36    println!("navigated; waiting for CreepJS to finish computing");
37
38    // CreepJS takes a while; poll for the widget rather than guessing a duration.
39    let mut rendered = false;
40    for _ in 0..40 {
41        tokio::time::sleep(Duration::from_secs(2)).await;
42        let probe = session
43            .browser_mut()
44            .evaluate_script("!!document.querySelector('.stealth-rating')", false)
45            .await;
46        if let Ok(v) = probe {
47            if format!("{:?}", v).contains("true") {
48                rendered = true;
49                break;
50            }
51        }
52        print!(".");
53    }
54    println!();
55    if !rendered {
56        println!("WARNING: stealth widget never rendered — page may be blocked or hung");
57    }
58    tokio::time::sleep(Duration::from_secs(3)).await;
59
60    let expr = r#"(() => {
61        const pct = (s) => { const e=document.querySelector(s); return e ? (e.textContent.match(/^\s*(\d+%)/)||['','?'])[1] : 'MISSING'; };
62        const flagged = [...document.querySelectorAll('#fingerprint-data span.hash')]
63            .map(e => [e.parentElement.textContent.trim().slice(0,12), e.className.replace('hash','').trim()])
64            .filter(([,c]) => c).map(([n,c]) => n+'='+c);
65        return 'stealth=' + pct('.stealth-rating') + ' headless=' + pct('.headless-rating')
66             + ' hashes=' + document.querySelectorAll('span.hash').length
67             + ' flagged=' + (flagged.join(',') || 'NONE');
68    })()"#;
69
70    match session.browser_mut().evaluate_script(expr, false).await {
71        Ok(v) => println!("\n=== CreepJS ===\n{:?}", v.result.value),
72        Err(e) => println!("\nevaluate failed: {e:?}"),
73    }
74
75    session.close().await;
76    Ok(())
77}
Source

pub fn browser(&self) -> &ChromeBrowser

Access the underlying rustenium ChromeBrowser.

Examples found in repository?
examples/verify.rs (line 27)
12async fn main() -> Result<(), Box<dyn std::error::Error>> {
13    let identity = match std::env::args().nth(1) {
14        Some(arg) => preset::get_by_id(arg.parse()?)?,
15        None => preset::random(),
16    };
17    let url = std::env::args()
18        .nth(2)
19        .unwrap_or_else(|| "https://abrahamjuliot.github.io/creepjs/".to_string());
20
21    println!("preset #{:?}: {:?} / {:?}", identity.id, identity.os, identity.browser);
22
23    let mut session = IdentitySession::launch(identity).await?;
24    println!("launched, navigating to {url} ...");
25
26    // Collect page errors before anything else runs.
27    session.browser().add_init_script(rustenium::cdp::target_manager::InitScript {
28        page: Some(r#"window.__errs=[];
29            addEventListener('error', e => __errs.push('ERR ' + (e.message||e)));
30            addEventListener('unhandledrejection', e => __errs.push('REJ ' + (e.reason && (e.reason.message||e.reason))));
31        "#.into()),
32        worker: None,
33    }).await;
34
35    session.browser_mut().navigate(&url).await?;
36    println!("navigated; waiting for CreepJS to finish computing");
37
38    // CreepJS takes a while; poll for the widget rather than guessing a duration.
39    let mut rendered = false;
40    for _ in 0..40 {
41        tokio::time::sleep(Duration::from_secs(2)).await;
42        let probe = session
43            .browser_mut()
44            .evaluate_script("!!document.querySelector('.stealth-rating')", false)
45            .await;
46        if let Ok(v) = probe {
47            if format!("{:?}", v).contains("true") {
48                rendered = true;
49                break;
50            }
51        }
52        print!(".");
53    }
54    println!();
55    if !rendered {
56        println!("WARNING: stealth widget never rendered — page may be blocked or hung");
57    }
58    tokio::time::sleep(Duration::from_secs(3)).await;
59
60    let expr = r#"(() => {
61        const pct = (s) => { const e=document.querySelector(s); return e ? (e.textContent.match(/^\s*(\d+%)/)||['','?'])[1] : 'MISSING'; };
62        const flagged = [...document.querySelectorAll('#fingerprint-data span.hash')]
63            .map(e => [e.parentElement.textContent.trim().slice(0,12), e.className.replace('hash','').trim()])
64            .filter(([,c]) => c).map(([n,c]) => n+'='+c);
65        return 'stealth=' + pct('.stealth-rating') + ' headless=' + pct('.headless-rating')
66             + ' hashes=' + document.querySelectorAll('span.hash').length
67             + ' flagged=' + (flagged.join(',') || 'NONE');
68    })()"#;
69
70    match session.browser_mut().evaluate_script(expr, false).await {
71        Ok(v) => println!("\n=== CreepJS ===\n{:?}", v.result.value),
72        Err(e) => println!("\nevaluate failed: {e:?}"),
73    }
74
75    session.close().await;
76    Ok(())
77}
Source

pub fn browser_mut(&mut self) -> &mut ChromeBrowser

Mutable access to the underlying rustenium ChromeBrowser.

Examples found in repository?
examples/verify.rs (line 35)
12async fn main() -> Result<(), Box<dyn std::error::Error>> {
13    let identity = match std::env::args().nth(1) {
14        Some(arg) => preset::get_by_id(arg.parse()?)?,
15        None => preset::random(),
16    };
17    let url = std::env::args()
18        .nth(2)
19        .unwrap_or_else(|| "https://abrahamjuliot.github.io/creepjs/".to_string());
20
21    println!("preset #{:?}: {:?} / {:?}", identity.id, identity.os, identity.browser);
22
23    let mut session = IdentitySession::launch(identity).await?;
24    println!("launched, navigating to {url} ...");
25
26    // Collect page errors before anything else runs.
27    session.browser().add_init_script(rustenium::cdp::target_manager::InitScript {
28        page: Some(r#"window.__errs=[];
29            addEventListener('error', e => __errs.push('ERR ' + (e.message||e)));
30            addEventListener('unhandledrejection', e => __errs.push('REJ ' + (e.reason && (e.reason.message||e.reason))));
31        "#.into()),
32        worker: None,
33    }).await;
34
35    session.browser_mut().navigate(&url).await?;
36    println!("navigated; waiting for CreepJS to finish computing");
37
38    // CreepJS takes a while; poll for the widget rather than guessing a duration.
39    let mut rendered = false;
40    for _ in 0..40 {
41        tokio::time::sleep(Duration::from_secs(2)).await;
42        let probe = session
43            .browser_mut()
44            .evaluate_script("!!document.querySelector('.stealth-rating')", false)
45            .await;
46        if let Ok(v) = probe {
47            if format!("{:?}", v).contains("true") {
48                rendered = true;
49                break;
50            }
51        }
52        print!(".");
53    }
54    println!();
55    if !rendered {
56        println!("WARNING: stealth widget never rendered — page may be blocked or hung");
57    }
58    tokio::time::sleep(Duration::from_secs(3)).await;
59
60    let expr = r#"(() => {
61        const pct = (s) => { const e=document.querySelector(s); return e ? (e.textContent.match(/^\s*(\d+%)/)||['','?'])[1] : 'MISSING'; };
62        const flagged = [...document.querySelectorAll('#fingerprint-data span.hash')]
63            .map(e => [e.parentElement.textContent.trim().slice(0,12), e.className.replace('hash','').trim()])
64            .filter(([,c]) => c).map(([n,c]) => n+'='+c);
65        return 'stealth=' + pct('.stealth-rating') + ' headless=' + pct('.headless-rating')
66             + ' hashes=' + document.querySelectorAll('span.hash').length
67             + ' flagged=' + (flagged.join(',') || 'NONE');
68    })()"#;
69
70    match session.browser_mut().evaluate_script(expr, false).await {
71        Ok(v) => println!("\n=== CreepJS ===\n{:?}", v.result.value),
72        Err(e) => println!("\nevaluate failed: {e:?}"),
73    }
74
75    session.close().await;
76    Ok(())
77}
Source

pub fn identity(&self) -> &Identity

Get the identity.

Source

pub async fn close(self) -> bool

Examples found in repository?
examples/verify.rs (line 75)
12async fn main() -> Result<(), Box<dyn std::error::Error>> {
13    let identity = match std::env::args().nth(1) {
14        Some(arg) => preset::get_by_id(arg.parse()?)?,
15        None => preset::random(),
16    };
17    let url = std::env::args()
18        .nth(2)
19        .unwrap_or_else(|| "https://abrahamjuliot.github.io/creepjs/".to_string());
20
21    println!("preset #{:?}: {:?} / {:?}", identity.id, identity.os, identity.browser);
22
23    let mut session = IdentitySession::launch(identity).await?;
24    println!("launched, navigating to {url} ...");
25
26    // Collect page errors before anything else runs.
27    session.browser().add_init_script(rustenium::cdp::target_manager::InitScript {
28        page: Some(r#"window.__errs=[];
29            addEventListener('error', e => __errs.push('ERR ' + (e.message||e)));
30            addEventListener('unhandledrejection', e => __errs.push('REJ ' + (e.reason && (e.reason.message||e.reason))));
31        "#.into()),
32        worker: None,
33    }).await;
34
35    session.browser_mut().navigate(&url).await?;
36    println!("navigated; waiting for CreepJS to finish computing");
37
38    // CreepJS takes a while; poll for the widget rather than guessing a duration.
39    let mut rendered = false;
40    for _ in 0..40 {
41        tokio::time::sleep(Duration::from_secs(2)).await;
42        let probe = session
43            .browser_mut()
44            .evaluate_script("!!document.querySelector('.stealth-rating')", false)
45            .await;
46        if let Ok(v) = probe {
47            if format!("{:?}", v).contains("true") {
48                rendered = true;
49                break;
50            }
51        }
52        print!(".");
53    }
54    println!();
55    if !rendered {
56        println!("WARNING: stealth widget never rendered — page may be blocked or hung");
57    }
58    tokio::time::sleep(Duration::from_secs(3)).await;
59
60    let expr = r#"(() => {
61        const pct = (s) => { const e=document.querySelector(s); return e ? (e.textContent.match(/^\s*(\d+%)/)||['','?'])[1] : 'MISSING'; };
62        const flagged = [...document.querySelectorAll('#fingerprint-data span.hash')]
63            .map(e => [e.parentElement.textContent.trim().slice(0,12), e.className.replace('hash','').trim()])
64            .filter(([,c]) => c).map(([n,c]) => n+'='+c);
65        return 'stealth=' + pct('.stealth-rating') + ' headless=' + pct('.headless-rating')
66             + ' hashes=' + document.querySelectorAll('span.hash').length
67             + ' flagged=' + (flagged.join(',') || 'NONE');
68    })()"#;
69
70    match session.browser_mut().evaluate_script(expr, false).await {
71        Ok(v) => println!("\n=== CreepJS ===\n{:?}", v.result.value),
72        Err(e) => println!("\nevaluate failed: {e:?}"),
73    }
74
75    session.close().await;
76    Ok(())
77}

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more