use ahash::AHashMap;
use async_trait::async_trait;
use bytes::Bytes;
use fancy_log::{LogLevel, log};
use crate::layers::l7::container::Container;
use crate::resources::kv::KvStore;
use super::hijack::{self, Hijacker};
#[async_trait]
pub trait TemplateContext: Send {
async fn get(&mut self, key: &str) -> String;
}
pub struct SimpleContext<'a> {
pub kv: &'a mut KvStore,
pub payloads: Option<&'a AHashMap<String, Bytes>>,
}
#[async_trait]
impl<'a> TemplateContext for SimpleContext<'a> {
async fn get(&mut self, key: &str) -> String {
if let Some(payloads) = self.payloads {
let mut hijacker = hijack::l4p::L4PlusHijacker {
kv: self.kv,
payloads,
};
if hijacker.can_handle(key) {
match hijacker.resolve(key).await {
Ok(value) => return value,
Err(e) => {
log(
LogLevel::Warn,
&format!("⚠ L4+ Hijacking failed for '{key}': {e}, trying KV fallback"),
);
}
}
}
}
if let Some(value) = self.kv.get(key) {
value.clone()
} else {
log(
LogLevel::Warn,
&format!("⚠ Template key '{key}' not found in KV Store, keeping original: {{{{{key}}}}}"),
);
format!("{{{{{key}}}}}") }
}
}
pub struct L7Context<'a> {
pub container: &'a mut Container,
}
#[async_trait]
impl<'a> TemplateContext for L7Context<'a> {
async fn get(&mut self, key: &str) -> String {
let mut hijacker = hijack::l7_http::HttpHijacker {
container: self.container,
};
if hijacker.can_handle(key) {
match hijacker.resolve(key).await {
Ok(value) => return value,
Err(e) => {
log(
LogLevel::Warn,
&format!("⚠ Hijacking failed for '{key}': {e}, trying KV fallback"),
);
}
}
}
if let Some(value) = self.container.kv.get(key) {
value.clone()
} else {
log(
LogLevel::Warn,
&format!("⚠ Template key '{key}' not found, keeping original: {{{{{key}}}}}"),
);
format!("{{{{{key}}}}}") }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_simple_context_found() {
let mut kv = KvStore::new();
kv.insert("key".to_string(), "value".to_string());
let mut context = SimpleContext {
kv: &mut kv,
payloads: None,
};
let result = context.get("key").await;
assert_eq!(result, "value");
}
#[tokio::test]
async fn test_simple_context_not_found() {
let mut kv = KvStore::new();
let mut context = SimpleContext {
kv: &mut kv,
payloads: None,
};
let result = context.get("missing").await;
assert_eq!(result, "{{missing}}");
}
#[tokio::test]
async fn test_l4p_hijacking() {
let mut kv = KvStore::new();
let mut payloads = AHashMap::new();
let data = vec![0xDE, 0xAD, 0xBE, 0xEF];
payloads.insert("tls.clienthello".to_string(), Bytes::from(data));
let mut context = SimpleContext {
kv: &mut kv,
payloads: Some(&payloads),
};
let result = context.get("tls.clienthello").await;
assert_eq!(result, "deadbeef");
assert_eq!(kv.get("tls.clienthello"), Some(&"deadbeef".to_string()));
}
}