use crate::akamai::AkamaiFingerprint;
use crate::akamai_extractor::extract_akamai_fingerprint;
use crate::http2_parser::{Http2ParseError, 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>, Http2ParseError> {
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 {
match self.parser.parse_frames_with_offset(frame_data) {
Ok((frames, bytes_consumed)) => {
if !frames.is_empty() {
self.parsed_offset = start_offset.saturating_add(bytes_consumed);
if let Some(fingerprint) = extract_akamai_fingerprint(&frames) {
self.fingerprint = Some(fingerprint.clone());
return Ok(Some(fingerprint));
}
}
}
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()
}
}