use crate::profile::ChromeProfile;
use http::header::{HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, USER_AGENT};
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequestContext {
Navigate,
Xhr,
Form,
Iframe,
NoCorsScript,
NoCorsStyle,
NoCorsImage,
NoCorsFont,
NoCorsMedia,
Worker,
ServiceWorker,
Prefetch,
}
pub fn inject_chrome_headers(
headers: &mut HeaderMap,
profile: &ChromeProfile,
sec_fetch_site: &str,
is_initial_navigation: bool,
context: RequestContext,
accept_ch: bool,
referer: Option<&str>,
) {
if let Ok(val) = HeaderValue::from_str(&profile.headers.sec_ch_ua) {
headers.insert("sec-ch-ua", val);
}
headers.insert("sec-ch-ua-mobile", HeaderValue::from_static("?0"));
if let Ok(val) = HeaderValue::from_str(&profile.headers.sec_ch_ua_platform) {
headers.insert("sec-ch-ua-platform", val);
}
if accept_ch {
if let Ok(val) = HeaderValue::from_str(&profile.headers.sec_ch_ua_platform_version) {
headers.insert("sec-ch-ua-platform-version", val);
}
}
headers.insert("upgrade-insecure-requests", HeaderValue::from_static("1"));
if let Ok(val) = HeaderValue::from_str(&profile.headers.user_agent) {
headers.insert(USER_AGENT, val);
}
if let Ok(val) = HeaderValue::from_str(sec_fetch_site) {
headers.insert("sec-fetch-site", val);
}
let (mode, dest, accept_val) = match context {
RequestContext::Navigate | RequestContext::Form => ("navigate", "document", "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"),
RequestContext::Iframe => ("navigate", "iframe", "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"),
RequestContext::Xhr => ("cors", "empty", "*/*"),
RequestContext::NoCorsScript => ("no-cors", "script", "*/*"),
RequestContext::NoCorsStyle => ("no-cors", "style", "text/css,*/*;q=0.1"),
RequestContext::NoCorsImage => ("no-cors", "image", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"),
RequestContext::NoCorsFont => ("no-cors", "font", "*/*"),
RequestContext::NoCorsMedia => ("no-cors", "video", "*/*"),
RequestContext::Worker => ("same-origin", "worker", "*/*"),
RequestContext::ServiceWorker => ("same-origin", "serviceworker", "*/*"),
RequestContext::Prefetch => ("no-cors", "empty", "*/*"),
};
headers.insert(ACCEPT, HeaderValue::from_static(accept_val));
headers.insert("sec-fetch-mode", HeaderValue::from_static(mode));
if is_initial_navigation
&& (context == RequestContext::Navigate
|| context == RequestContext::Form
|| context == RequestContext::Iframe)
{
headers.insert("sec-fetch-user", HeaderValue::from_static("?1"));
}
headers.insert("sec-fetch-dest", HeaderValue::from_static(dest));
if let Some(r) = referer {
if let Ok(val) = HeaderValue::from_str(r) {
headers.insert(http::header::REFERER, val);
}
}
let encoding = if profile.headers.zstd_encoding {
"gzip, deflate, br, zstd"
} else {
"gzip, deflate, br"
};
headers.insert(ACCEPT_ENCODING, HeaderValue::from_static(encoding));
if let Ok(val) = HeaderValue::from_str(&profile.headers.accept_language) {
headers.insert(ACCEPT_LANGUAGE, val);
}
if profile.headers.include_priority_header {
headers.insert("priority", HeaderValue::from_static("u=0, i"));
}
for (name, value) in headers.iter_mut() {
if name == "cookie" || name == "authorization" {
value.set_sensitive(true);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::profile::chrome_134::chrome_134_windows_x64;
#[test]
fn test_inject_chrome_headers_context_mapping() {
let profile = chrome_134_windows_x64();
let mut headers = HeaderMap::new();
inject_chrome_headers(
&mut headers,
&profile,
"same-origin",
true,
RequestContext::Navigate,
false,
None,
);
assert_eq!(
headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
"document"
);
assert_eq!(
headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
"navigate"
);
assert_eq!(
headers.get("sec-fetch-user").unwrap().to_str().unwrap(),
"?1"
);
assert!(headers.get("sec-ch-ua-platform-version").is_none());
let mut headers = HeaderMap::new();
inject_chrome_headers(
&mut headers,
&profile,
"cross-site",
false,
RequestContext::Xhr,
true,
None,
);
assert_eq!(
headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
"empty"
);
assert_eq!(
headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
"cors"
);
assert!(headers.get("sec-fetch-user").is_none());
assert_eq!(
headers
.get("sec-ch-ua-platform-version")
.unwrap()
.to_str()
.unwrap(),
"\"15.0.0\""
);
let mut headers = HeaderMap::new();
inject_chrome_headers(
&mut headers,
&profile,
"same-site",
false,
RequestContext::NoCorsImage,
false,
None,
);
assert_eq!(
headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
"image"
);
assert_eq!(
headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
"no-cors"
);
assert!(headers
.get("accept")
.unwrap()
.to_str()
.unwrap()
.contains("image/avif"));
let mut headers = HeaderMap::new();
inject_chrome_headers(
&mut headers,
&profile,
"same-origin",
false,
RequestContext::ServiceWorker,
false,
None,
);
assert_eq!(
headers.get("sec-fetch-dest").unwrap().to_str().unwrap(),
"serviceworker"
);
assert_eq!(
headers.get("sec-fetch-mode").unwrap().to_str().unwrap(),
"same-origin"
);
}
#[test]
fn test_inject_sensitive_headers_marked_properly() {
let profile = chrome_134_windows_x64();
let mut headers = HeaderMap::new();
headers.insert("cookie", HeaderValue::from_static("session=123"));
headers.insert("authorization", HeaderValue::from_static("Bearer token"));
headers.insert("host", HeaderValue::from_static("example.com"));
inject_chrome_headers(
&mut headers,
&profile,
"none",
true,
RequestContext::Navigate,
false,
None,
);
assert!(headers.get("cookie").unwrap().is_sensitive());
assert!(headers.get("authorization").unwrap().is_sensitive());
assert!(!headers.get("host").unwrap().is_sensitive());
}
}