use std::collections::HashMap;
use async_trait::async_trait;
use regex::Regex;
use serde::{Serialize, Deserialize};
use crate::core::{ProxyRequest, HttpMethod, ProxyError};
use super::Predicate;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathPredicateConfig {
pub pattern: String,
}
#[derive(Debug)]
pub struct PathPredicate {
config: PathPredicateConfig,
regex: Regex,
}
impl PathPredicate {
pub fn new(config: PathPredicateConfig) -> Result<Self, ProxyError> {
let regex_pattern = Self::pattern_to_regex(&config.pattern);
let regex = Regex::new(®ex_pattern)
.map_err(|e| ProxyError::RoutingError(format!("Invalid path predicate regex pattern '{}': {}", config.pattern, e)))?;
Ok(Self { config, regex })
}
fn pattern_to_regex(pattern: &str) -> String {
let mut regex_pattern = "^".to_string();
let mut chars = pattern.chars().peekable();
while let Some(c) = chars.next() {
match c {
':' => {
let mut param_name = String::new();
while let Some(&next_char) = chars.peek() {
if next_char.is_alphanumeric() || next_char == '_' {
param_name.push(chars.next().unwrap());
} else {
break;
}
}
regex_pattern.push_str(&format!("([^/]+)"));
},
'*' => {
regex_pattern.push_str("(.*)");
},
'.' | '^' | '$' | '|' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '\\' => {
regex_pattern.push('\\');
regex_pattern.push(c);
},
_ => {
regex_pattern.push(c);
}
}
}
regex_pattern.push('$');
regex_pattern
}
}
#[async_trait]
impl Predicate for PathPredicate {
async fn matches(&self, request: &ProxyRequest) -> bool {
self.regex.is_match(&request.path)
}
fn predicate_type(&self) -> &str {
"path"
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MethodPredicateConfig {
pub methods: Vec<HttpMethod>,
}
#[derive(Debug)]
pub struct MethodPredicate {
config: MethodPredicateConfig,
}
impl MethodPredicate {
pub fn new(config: MethodPredicateConfig) -> Self {
Self { config }
}
}
#[async_trait]
impl Predicate for MethodPredicate {
async fn matches(&self, request: &ProxyRequest) -> bool {
self.config.methods.contains(&request.method)
}
fn predicate_type(&self) -> &str {
"method"
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HeaderPredicateConfig {
pub headers: HashMap<String, String>,
#[serde(default)]
pub exact_match: bool,
}
#[derive(Debug)]
pub struct HeaderPredicate {
config: HeaderPredicateConfig,
}
impl HeaderPredicate {
pub fn new(config: HeaderPredicateConfig) -> Self {
Self { config }
}
}
#[async_trait]
impl Predicate for HeaderPredicate {
async fn matches(&self, request: &ProxyRequest) -> bool {
for (name, expected_value) in &self.config.headers {
if let Some(header_value) = request.headers.get(name) {
if let Ok(actual_value) = header_value.to_str() {
if self.config.exact_match {
if actual_value != expected_value {
return false;
}
} else {
if !actual_value.contains(expected_value) {
return false;
}
}
} else {
return false;
}
} else {
return false;
}
}
true
}
fn predicate_type(&self) -> &str {
"header"
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryPredicateConfig {
pub params: HashMap<String, String>,
#[serde(default)]
pub exact_match: bool,
}
#[derive(Debug)]
pub struct QueryPredicate {
config: QueryPredicateConfig,
}
impl QueryPredicate {
pub fn new(config: QueryPredicateConfig) -> Self {
Self { config }
}
fn parse_query_params(query: &str) -> HashMap<String, String> {
let mut params = HashMap::new();
for pair in query.split('&') {
let mut iter = pair.split('=');
if let (Some(key), Some(value)) = (iter.next(), iter.next()) {
params.insert(key.to_string(), value.to_string());
}
}
params
}
}
#[async_trait]
impl Predicate for QueryPredicate {
async fn matches(&self, request: &ProxyRequest) -> bool {
if self.config.params.is_empty() {
return true;
}
if let Some(query) = &request.query {
let params = Self::parse_query_params(query);
for (name, expected_value) in &self.config.params {
if let Some(actual_value) = params.get(name) {
if self.config.exact_match {
if actual_value != expected_value {
return false;
}
} else {
if !actual_value.contains(expected_value) {
return false;
}
}
} else {
return false;
}
}
true
} else {
false
}
}
fn predicate_type(&self) -> &str {
"query"
}
}