Skip to main content

headless_engine/network/
fingerprint.rs

1use reqwest::header::{
2    HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, DNT,
3    UPGRADE_INSECURE_REQUESTS, USER_AGENT,
4};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8pub enum DeviceProfile {
9    #[default]
10    ChromeWindows,
11    ChromeLinux,
12    SafariMac,
13    SafariIos,
14    ChromeAndroid,
15}
16
17pub struct Fingerprint {
18    pub profile: DeviceProfile,
19    pub user_agent: &'static str,
20    pub platform: &'static str,
21    pub screen_width: u32,
22    pub screen_height: u32,
23    pub is_mobile: bool,
24}
25
26impl Fingerprint {
27    pub fn for_profile(profile: DeviceProfile) -> Self {
28        match profile {
29            DeviceProfile::ChromeWindows => Self {
30                profile,
31                user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
32                platform: "Win32",
33                screen_width: 1920,
34                screen_height: 1080,
35                is_mobile: false,
36            },
37            DeviceProfile::ChromeLinux => Self {
38                profile,
39                user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
40                platform: "Linux x86_64",
41                screen_width: 1920,
42                screen_height: 1080,
43                is_mobile: false,
44            },
45            DeviceProfile::SafariMac => Self {
46                profile,
47                user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15",
48                platform: "MacIntel",
49                screen_width: 2560,
50                screen_height: 1440,
51                is_mobile: false,
52            },
53            DeviceProfile::SafariIos => Self {
54                profile,
55                user_agent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Mobile/15E148 Safari/604.1",
56                platform: "iPhone",
57                screen_width: 393,
58                screen_height: 852,
59                is_mobile: true,
60            },
61            DeviceProfile::ChromeAndroid => Self {
62                profile,
63                user_agent: "Mozilla/5.0 (Linux; Android 14; Pixel 8 Build/UD1A.230803.022) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.122 Mobile Safari/537.36",
64                platform: "Linux armv8l",
65                screen_width: 412,
66                screen_height: 915,
67                is_mobile: true,
68            },
69        }
70    }
71
72    pub fn build_headers(&self) -> HeaderMap {
73        let mut headers = HeaderMap::new();
74
75        headers.insert(USER_AGENT, HeaderValue::from_static(self.user_agent));
76        headers.insert(
77            ACCEPT,
78            HeaderValue::from_static(
79                "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
80            ),
81        );
82        headers.insert(ACCEPT_LANGUAGE, HeaderValue::from_static("en-US,en;q=0.9"));
83        headers.insert(
84            ACCEPT_ENCODING,
85            HeaderValue::from_static("gzip, deflate, br"),
86        );
87        headers.insert(UPGRADE_INSECURE_REQUESTS, HeaderValue::from_static("1"));
88        headers.insert(DNT, HeaderValue::from_static("1"));
89
90        match self.profile {
91            DeviceProfile::ChromeWindows => {
92                headers.insert(
93                    "sec-ch-ua",
94                    HeaderValue::from_static(
95                        "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"",
96                    ),
97                );
98                headers.insert("sec-ch-ua-mobile", HeaderValue::from_static("?0"));
99                headers.insert(
100                    "sec-ch-ua-platform",
101                    HeaderValue::from_static("\"Windows\""),
102                );
103                headers.insert("sec-fetch-dest", HeaderValue::from_static("document"));
104                headers.insert("sec-fetch-mode", HeaderValue::from_static("navigate"));
105                headers.insert("sec-fetch-site", HeaderValue::from_static("none"));
106                headers.insert("sec-fetch-user", HeaderValue::from_static("?1"));
107            }
108            DeviceProfile::ChromeLinux => {
109                headers.insert(
110                    "sec-ch-ua",
111                    HeaderValue::from_static(
112                        "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"",
113                    ),
114                );
115                headers.insert("sec-ch-ua-mobile", HeaderValue::from_static("?0"));
116                headers.insert("sec-ch-ua-platform", HeaderValue::from_static("\"Linux\""));
117                headers.insert("sec-fetch-dest", HeaderValue::from_static("document"));
118                headers.insert("sec-fetch-mode", HeaderValue::from_static("navigate"));
119                headers.insert("sec-fetch-site", HeaderValue::from_static("none"));
120                headers.insert("sec-fetch-user", HeaderValue::from_static("?1"));
121            }
122            DeviceProfile::ChromeAndroid => {
123                headers.insert(
124                    "sec-ch-ua",
125                    HeaderValue::from_static(
126                        "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"",
127                    ),
128                );
129                headers.insert("sec-ch-ua-mobile", HeaderValue::from_static("?1"));
130                headers.insert(
131                    "sec-ch-ua-platform",
132                    HeaderValue::from_static("\"Android\""),
133                );
134                headers.insert("sec-fetch-dest", HeaderValue::from_static("document"));
135                headers.insert("sec-fetch-mode", HeaderValue::from_static("navigate"));
136                headers.insert("sec-fetch-site", HeaderValue::from_static("none"));
137                headers.insert("sec-fetch-user", HeaderValue::from_static("?1"));
138            }
139            DeviceProfile::SafariMac | DeviceProfile::SafariIos => {
140                // Safari does not send Sec-CH-UA headers
141            }
142        }
143
144        headers
145    }
146}