use console::style;
use hyper::{header::HeaderValue, HeaderMap};
use serde::Deserialize;
use indexmap::IndexMap;
pub mod header_operator;
use header_operator::HeaderOperator;
use super::util::fmt_condition_connector;
use crate::rule_set::rule::ConditionKey;
#[derive(Clone, Debug, Deserialize)]
pub struct HeaderConditionStatement {
#[serde(default)]
pub op: Option<HeaderOperator>,
#[serde(default)]
pub value: String,
}
impl std::fmt::Display for HeaderConditionStatement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let op = self.op.clone().unwrap_or_default();
match op {
HeaderOperator::Exists | HeaderOperator::Absent => write!(f, "{}", op),
_ => write!(f, "{}`{}`", op, self.value),
}
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(transparent)]
pub struct Headers(pub IndexMap<ConditionKey, HeaderConditionStatement>);
impl Headers {
pub fn is_match(
&self,
parsed_request_headers: &HeaderMap<HeaderValue>,
rule_idx: usize,
rule_set_idx: usize,
) -> bool {
self.0
.iter()
.all(|(matcher_key, stmt)| {
let op = stmt.op.clone().unwrap_or_default();
match op {
HeaderOperator::Exists => {
return parsed_request_headers.contains_key(matcher_key.as_str())
}
HeaderOperator::Absent => {
return !parsed_request_headers.contains_key(matcher_key.as_str())
}
_ => {}
}
let header_value = match parsed_request_headers.get(matcher_key.as_str()) {
Some(v) => v,
None => return false,
};
let header_str = match header_value.to_str() {
Ok(s) => s,
Err(err) => {
log::error!(
"{} to get request header value by key (rule #{} in rule set #{}):\n`{}`\n({})",
style("failed").red(),
rule_idx + 1,
rule_set_idx + 1,
matcher_key,
err,
);
return true;
}
};
op.to_rule_op().is_match(header_str, &stmt.value)
})
}
pub fn validate(&self) -> bool {
!self.0.is_empty()
}
}
impl std::fmt::Display for Headers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self
.0
.iter()
.map(|(key, stmt)| format!("{}{}", key, stmt))
.collect::<Vec<String>>()
.join(fmt_condition_connector().as_str());
let _ = write!(f, "[headers] {}", s);
Ok(())
}
}
#[cfg(test)]
mod tests;