use crate::akamai::AkamaiFingerprint;
use crate::akamai_extractor::extract_akamai_fingerprint;
use crate::error::HuginnNetHttpError;
use crate::http2_parser::{Http2Parser, HTTP2_CONNECTION_PREFACE};
pub struct Http2FingerprintExtractor {
parser: Http2Parser<'static>,
buffer: Vec<u8>,
parsed_offset: usize,
fingerprint: Option<AkamaiFingerprint>,
}
impl Http2FingerprintExtractor {
#[must_use]
pub fn new() -> Self {
Self {
parser: Http2Parser::new(),
buffer: Vec::with_capacity(64 * 1024),
parsed_offset: 0,
fingerprint: None,
}
}
pub fn add_bytes(
&mut self,
data: &[u8],
) -> Result<Option<AkamaiFingerprint>, HuginnNetHttpError> {
if self.fingerprint.is_some() {
return Ok(None);
}
self.buffer.extend_from_slice(data);
let start_offset =
if self.parsed_offset == 0 && self.buffer.starts_with(HTTP2_CONNECTION_PREFACE) {
HTTP2_CONNECTION_PREFACE.len()
} else {
self.parsed_offset
};
let frame_data = &self.buffer[start_offset..];
if frame_data.len() >= 9 {
let (frames, bytes_consumed) = self
.parser
.parse_frames_with_offset(frame_data)
.map_err(|e| HuginnNetHttpError::Parse(e.to_string()))?;
if !frames.is_empty() {
self.parsed_offset = start_offset.saturating_add(bytes_consumed);
match extract_akamai_fingerprint(&frames) {
Ok(fingerprint) => {
self.fingerprint = Some(fingerprint.clone());
return Ok(Some(fingerprint));
}
Err(HuginnNetHttpError::NoSettingsFrame) => {}
Err(e) => return Err(e),
}
}
}
Ok(None)
}
#[must_use]
pub fn get_fingerprint(&self) -> Option<&AkamaiFingerprint> {
self.fingerprint.as_ref()
}
#[must_use]
pub fn fingerprint_extracted(&self) -> bool {
self.fingerprint.is_some()
}
pub fn reset(&mut self) {
self.buffer.clear();
self.parsed_offset = 0;
self.fingerprint = None;
}
}
impl Default for Http2FingerprintExtractor {
fn default() -> Self {
Self::new()
}
}