use rama_core::extensions::Extension;
use rama_http::fingerprint::{HttpRequestInput, Ja4H, Ja4HComputeError};
use rama_http::{
HeaderMap, HeaderName, Method, Version,
proto::h2::{PseudoHeaderOrder, frame::EarlyFrameCapture},
};
use serde::{Deserialize, Serialize};
pub static CUSTOM_HEADER_MARKER: HeaderName =
HeaderName::from_static("x-rama-custom-header-marker");
#[derive(Debug, Extension)]
#[extension(tags(ua, http))]
pub struct HttpProfile {
pub h1: Http1Profile,
pub h2: Http2Profile,
}
impl HttpProfile {
pub fn ja4h_h1_navigate(&self, method: Option<Method>) -> Result<Ja4H, Ja4HComputeError> {
Ja4H::compute(HttpRequestInput {
header_map: self.h1.headers.navigate.clone(),
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_11,
})
}
#[must_use]
pub fn ja4h_h1_fetch(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
self.h1.headers.fetch.clone().map(|header_map| {
Ja4H::compute(HttpRequestInput {
header_map,
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_11,
})
})
}
#[must_use]
pub fn ja4h_h1_xhr(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
self.h1.headers.xhr.clone().map(|header_map| {
Ja4H::compute(HttpRequestInput {
header_map,
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_11,
})
})
}
#[must_use]
pub fn ja4h_h1_form(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
self.h1.headers.form.clone().map(|header_map| {
Ja4H::compute(HttpRequestInput {
header_map,
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_11,
})
})
}
pub fn ja4h_h2_navigate(&self, method: Option<Method>) -> Result<Ja4H, Ja4HComputeError> {
Ja4H::compute(HttpRequestInput {
header_map: self.h2.headers.navigate.clone(),
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_2,
})
}
#[must_use]
pub fn ja4h_h2_fetch(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
self.h2.headers.fetch.clone().map(|header_map| {
Ja4H::compute(HttpRequestInput {
header_map,
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_2,
})
})
}
#[must_use]
pub fn ja4h_h2_xhr(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
self.h2.headers.xhr.clone().map(|header_map| {
Ja4H::compute(HttpRequestInput {
header_map,
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_2,
})
})
}
#[must_use]
pub fn ja4h_h2_form(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
self.h2.headers.form.clone().map(|header_map| {
Ja4H::compute(HttpRequestInput {
header_map,
http_method: method.unwrap_or(Method::GET),
version: Version::HTTP_2,
})
})
}
}
impl<'de> Deserialize<'de> for HttpProfile {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let HttpProfileDeserialize { h1, h2 } = HttpProfileDeserialize::deserialize(deserializer)?;
Ok(Self { h1, h2 })
}
}
impl Serialize for HttpProfile {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
HttpProfileSerialize {
h1: &self.h1,
h2: &self.h2,
}
.serialize(serializer)
}
}
#[derive(Debug, Serialize)]
struct HttpProfileSerialize<'a> {
h1: &'a Http1Profile,
h2: &'a Http2Profile,
}
#[derive(Debug, Deserialize)]
struct HttpProfileDeserialize {
h1: Http1Profile,
h2: Http2Profile,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct HttpHeadersProfile {
pub navigate: HeaderMap,
pub fetch: Option<HeaderMap>,
pub xhr: Option<HeaderMap>,
pub form: Option<HeaderMap>,
pub ws: Option<HeaderMap>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Http1Profile {
pub headers: HttpHeadersProfile,
pub settings: Http1Settings,
}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct Http1Settings {
pub title_case_headers: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Http2Profile {
pub headers: HttpHeadersProfile,
pub settings: Http2Settings,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct Http2Settings {
pub http_pseudo_headers: Option<PseudoHeaderOrder>,
pub early_frames: Option<EarlyFrameCapture>,
}