rama_ua/profile/http.rs
1use rama_http_types::{
2 HeaderName, Method, Version,
3 conn::StreamDependencyParams,
4 proto::{
5 h1::Http1HeaderMap,
6 h2::{PseudoHeaderOrder, frame::SettingsConfig},
7 },
8};
9use rama_net::fingerprint::{HttpRequestInput, Ja4H, Ja4HComputeError};
10use serde::{Deserialize, Serialize};
11use std::sync::Arc;
12
13/// Marker header name for custom headers.
14///
15/// Header value: `x-rama-custom-header-marker`
16///
17/// This is used to identify in the [`HttpHeadersProfile`]
18/// the initial location of custom headers, which is also
19/// by the [`UserAgentEmulateRequestModifier`] used to place
20/// any original request headers that were not present in the
21/// [`HttpHeadersProfile`] (also called base headers).
22///
23/// If this header is not present in the [`HttpHeadersProfile`]
24/// then it will be assumed that remaining headers are to be
25/// put as the final headers in the request header map.
26///
27/// [`HttpHeadersProfile`]: crate::profile::HttpHeadersProfile
28/// [`UserAgentEmulateHttpRequestModifier`]: crate::emulate::UserAgentEmulateHttpRequestModifier
29pub static CUSTOM_HEADER_MARKER: HeaderName =
30 HeaderName::from_static("x-rama-custom-header-marker");
31
32#[derive(Debug, Clone)]
33/// A User Agent (UA) profile for HTTP.
34///
35/// This profile contains the HTTP profiles for
36/// [`Http1`][`Http1Profile`] and [`Http2`][`Http2Profile`].
37///
38/// [`Http1Profile`]: crate::profile::Http1Profile
39/// [`Http2Profile`]: crate::profile::Http2Profile
40pub struct HttpProfile {
41 /// The HTTP/1.1 profile.
42 pub h1: Arc<Http1Profile>,
43 /// The HTTP/2 profile.
44 pub h2: Arc<Http2Profile>,
45}
46
47impl HttpProfile {
48 /// Compute the [`Ja4H`] (hash) for the h1 navigate headers in this [`HttpProfile`].
49 ///
50 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
51 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
52 pub fn ja4h_h1_navigate(&self, method: Option<Method>) -> Result<Ja4H, Ja4HComputeError> {
53 Ja4H::compute(HttpRequestInput {
54 header_map: self.h1.headers.navigate.clone(),
55 http_method: method.unwrap_or(Method::GET),
56 version: Version::HTTP_11,
57 })
58 }
59
60 /// Compute the [`Ja4H`] (hash) for the h1 fetch headers in this [`HttpProfile`], if such headers are available for fetch.
61 ///
62 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
63 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
64 pub fn ja4h_h1_fetch(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
65 self.h1.headers.fetch.clone().map(|header_map| {
66 Ja4H::compute(HttpRequestInput {
67 header_map,
68 http_method: method.unwrap_or(Method::GET),
69 version: Version::HTTP_11,
70 })
71 })
72 }
73
74 /// Compute the [`Ja4H`] (hash) for the h1 xhr headers in this [`HttpProfile`], if such headers are available for xhr.
75 ///
76 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
77 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
78 pub fn ja4h_h1_xhr(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
79 self.h1.headers.xhr.clone().map(|header_map| {
80 Ja4H::compute(HttpRequestInput {
81 header_map,
82 http_method: method.unwrap_or(Method::GET),
83 version: Version::HTTP_11,
84 })
85 })
86 }
87
88 /// Compute the [`Ja4H`] (hash) for the h1 form headers in this [`HttpProfile`], if such headers are available for form.
89 ///
90 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
91 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
92 pub fn ja4h_h1_form(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
93 self.h1.headers.form.clone().map(|header_map| {
94 Ja4H::compute(HttpRequestInput {
95 header_map,
96 http_method: method.unwrap_or(Method::GET),
97 version: Version::HTTP_11,
98 })
99 })
100 }
101
102 /// Compute the [`Ja4H`] (hash) for the h2 navigate headers in this [`HttpProfile`].
103 ///
104 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
105 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
106 pub fn ja4h_h2_navigate(&self, method: Option<Method>) -> Result<Ja4H, Ja4HComputeError> {
107 Ja4H::compute(HttpRequestInput {
108 header_map: self.h2.headers.navigate.clone(),
109 http_method: method.unwrap_or(Method::GET),
110 version: Version::HTTP_2,
111 })
112 }
113
114 /// Compute the [`Ja4H`] (hash) for the h2 fetch headers in this [`HttpProfile`], if such headers are available for fetch.
115 ///
116 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
117 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
118 pub fn ja4h_h2_fetch(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
119 self.h2.headers.fetch.clone().map(|header_map| {
120 Ja4H::compute(HttpRequestInput {
121 header_map,
122 http_method: method.unwrap_or(Method::GET),
123 version: Version::HTTP_2,
124 })
125 })
126 }
127
128 /// Compute the [`Ja4H`] (hash) for the h2 xhr headers in this [`HttpProfile`], if such headers are available for xhr.
129 ///
130 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
131 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
132 pub fn ja4h_h2_xhr(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
133 self.h2.headers.xhr.clone().map(|header_map| {
134 Ja4H::compute(HttpRequestInput {
135 header_map,
136 http_method: method.unwrap_or(Method::GET),
137 version: Version::HTTP_2,
138 })
139 })
140 }
141
142 /// Compute the [`Ja4H`] (hash) for the h2 form headers in this [`HttpProfile`], if such headers are available for form.
143 ///
144 /// As specified by <https://blog.foxio.io/ja4%2B-network-fingerprinting>
145 /// and reference implementations found at <https://github.com/FoxIO-LLC/ja4>.
146 pub fn ja4h_h2_form(&self, method: Option<Method>) -> Option<Result<Ja4H, Ja4HComputeError>> {
147 self.h2.headers.form.clone().map(|header_map| {
148 Ja4H::compute(HttpRequestInput {
149 header_map,
150 http_method: method.unwrap_or(Method::GET),
151 version: Version::HTTP_2,
152 })
153 })
154 }
155}
156
157impl<'de> Deserialize<'de> for HttpProfile {
158 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
159 where
160 D: serde::Deserializer<'de>,
161 {
162 let input = HttpProfileDeserialize::deserialize(deserializer)?;
163 Ok(Self {
164 h1: Arc::new(input.h1),
165 h2: Arc::new(input.h2),
166 })
167 }
168}
169
170impl Serialize for HttpProfile {
171 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
172 where
173 S: serde::Serializer,
174 {
175 HttpProfileSerialize {
176 h1: self.h1.as_ref(),
177 h2: self.h2.as_ref(),
178 }
179 .serialize(serializer)
180 }
181}
182
183#[derive(Debug, Serialize)]
184struct HttpProfileSerialize<'a> {
185 h1: &'a Http1Profile,
186 h2: &'a Http2Profile,
187}
188
189#[derive(Debug, Deserialize)]
190struct HttpProfileDeserialize {
191 h1: Http1Profile,
192 h2: Http2Profile,
193}
194
195#[derive(Debug, Deserialize, Serialize)]
196pub struct HttpHeadersProfile {
197 /// The headers to be used for navigation requests.
198 ///
199 /// A navigation request is the regular request that a user-agent
200 /// makes automatically or on behalf of the user, but that is not
201 /// triggered directly by a script.
202 pub navigate: Http1HeaderMap,
203 /// The headers to be used for fetch requests.
204 ///
205 /// A fetch request is a request made by a script to retrieve a resource from a server,
206 /// using the [`fetch`][`fetch`] API.
207 ///
208 /// In case the user-agent does not support the [`fetch`][`fetch`] API,
209 /// then it is recommended to try to use the `xhr` headers,
210 /// and as a final fallback use the `navigate` headers.
211 ///
212 /// [`fetch`]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
213 pub fetch: Option<Http1HeaderMap>,
214 /// The headers to be used for XMLHttpRequest requests.
215 ///
216 /// An [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
217 /// is a request made by a script to retrieve a resource from a server.
218 ///
219 /// In case the user-agent does not support the [`XMLHttpRequest`][`XMLHttpRequest`] API,
220 /// then it is recommended to try to use the `fetch` headers,
221 /// and as a final fallback use the `navigate` headers.
222 pub xhr: Option<Http1HeaderMap>,
223 /// The headers to be used for form submissions.
224 ///
225 /// A form submission is a request made by a script to submit a form to a server.
226 ///
227 /// In case the user-agent does not support forms (e.g. because it does not handle html forms),
228 /// then it is recommended to try to use the `fetch` headers and any fallbacks that the latter may have.
229 pub form: Option<Http1HeaderMap>,
230}
231
232#[derive(Debug, Deserialize, Serialize)]
233/// The HTTP/1.1 profile.
234///
235/// This profile contains the headers and settings for the HTTP/1.1 protocol.
236pub struct Http1Profile {
237 /// The (base) headers to be used for the HTTP/1.1 profile.
238 pub headers: HttpHeadersProfile,
239 /// The settings for the HTTP/1.1 profile.
240 pub settings: Http1Settings,
241}
242
243#[derive(Debug, Deserialize, Serialize, Default)]
244/// The settings for the HTTP/1.1 profile.
245pub struct Http1Settings {
246 /// Whether to enforce title case the headers.
247 pub title_case_headers: bool,
248}
249
250#[derive(Debug, Deserialize, Serialize)]
251/// The HTTP/2 profile.
252///
253/// This profile contains the headers and settings for the HTTP/2 protocol.
254pub struct Http2Profile {
255 /// The headers to be used for the HTTP/2 profile.
256 pub headers: HttpHeadersProfile,
257 /// The settings for the HTTP/2 profile.
258 pub settings: Http2Settings,
259}
260
261#[derive(Debug, Clone, Deserialize, Serialize, Default)]
262/// The settings for the HTTP/2 profile.
263pub struct Http2Settings {
264 /// The pseudo headers to be used for the HTTP/2 profile.
265 ///
266 /// See [`PseudoHeader`] for more details.
267 pub http_pseudo_headers: Option<PseudoHeaderOrder>,
268
269 /// The (initial) h2 settings to be used for the HTTP/2 profile.
270 ///
271 /// See [`SettingsConfig`] for more details.
272 pub initial_config: Option<SettingsConfig>,
273
274 /// The priority settings to be used for the HTTP/2 profile.
275 ///
276 /// See [`StreamDependencyParams`] for more details.
277 pub priority_header: Option<StreamDependencyParams>,
278}