Skip to content

Commit

Permalink
[OPTIMIZATION] backend pool matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
ismoilovdevml committed Oct 15, 2023
1 parent 4a9f0c4 commit 237b3c6
Showing 1 changed file with 39 additions and 63 deletions.
102 changes: 39 additions & 63 deletions src/backend_pool_matcher.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashMap, iter::FromIterator, ops::Deref, str::FromStr};
use std::{collections::HashMap, ops::Deref, str::FromStr};

use hyper::{header::HOST, Body, Method, Request};
use pom::parser::*;
Expand All @@ -8,89 +8,65 @@ use regex::Regex;
#[derive(Debug)]
pub struct ComparableRegex(Regex);

/// neat trick for making all functions of the internal type available
impl Deref for ComparableRegex {
type Target = Regex;
type Target = Regex;

fn deref(&self) -> &Self::Target {
&self.0
}
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl PartialEq for ComparableRegex {
fn eq(&self, other: &Self) -> bool {
self.0.as_str().eq(other.0.as_str())
}
fn eq(&self, other: &Self) -> bool {
self.0.as_str().eq(other.0.as_str())
}
}

impl ComparableRegex {
pub fn new(regex: &str) -> Result<ComparableRegex, regex::Error> {
Ok(ComparableRegex(Regex::new(regex)?))
}
pub fn new(regex: &str) -> Result<ComparableRegex, regex::Error> {
Ok(ComparableRegex(Regex::new(regex)?))
}
}

#[derive(Debug, PartialEq)]
pub enum BackendPoolMatcher {
Host(String),
HostRegexp(ComparableRegex),
Method(Method),
Path(String),
PathRegexp(ComparableRegex),
Query(String, String),
And(Box<BackendPoolMatcher>, Box<BackendPoolMatcher>),
Or(Box<BackendPoolMatcher>, Box<BackendPoolMatcher>),
Host(String),
HostRegexp(ComparableRegex),
Method(Method),
Path(String),
PathRegexp(ComparableRegex),
Query(String, String),
And(Box<BackendPoolMatcher>, Box<BackendPoolMatcher>),
Or(Box<BackendPoolMatcher>, Box<BackendPoolMatcher>),
}

impl From<String> for BackendPoolMatcher {
fn from(str: String) -> Self {
let chars: Vec<char> = str.chars().collect();
let result = parser().parse(&chars).unwrap();
result
let chars: Vec<char> = str.chars().collect();
let result = parser().parse(&chars).unwrap();
result
}
}

impl BackendPoolMatcher {
/// Returns true if the BackendPoolMatcher is statisfied by the given request
///
/// # Arguments
///
/// * `request` - A hyper http request
///
/// # Examples
///
/// ```
/// let request = Request::builder().uri("https://google.de").body(Body::empty());
/// let matcher = BackendPoolMatcher::Host("google.de".into());
///
/// assert_eq!(matcher.matches(&request), true);
/// ```
pub fn matches(&self, request: &Request<Body>) -> bool {
match self {
BackendPoolMatcher::Host(host) => request.headers().get(HOST).map(|h| h == host).unwrap_or(false),
BackendPoolMatcher::HostRegexp(host_regex) => request
.headers()
.get(HOST)
.and_then(|h| Some(host_regex.is_match(h.to_str().ok()?)))
.unwrap_or(false),
BackendPoolMatcher::Method(method) => request.method() == method,
BackendPoolMatcher::Path(path) => request.uri().path() == path,
BackendPoolMatcher::PathRegexp(path_regex) => path_regex.is_match(request.uri().path()),
BackendPoolMatcher::Query(key, value) => {
let query_params: HashMap<String, String> = request
.uri()
.query()
.map(|v| url::form_urlencoded::parse(v.as_bytes()).into_owned().collect())
.unwrap_or_else(HashMap::new);

query_params
.get(key)
.map(|sent_value| sent_value == value)
.unwrap_or(false)
}
BackendPoolMatcher::And(left, right) => left.matches(request) && right.matches(request),
BackendPoolMatcher::Or(left, right) => left.matches(request) || right.matches(request),
/// Simplified matches function
pub fn matches(&self, request: &Request<Body>) -> bool {
match self {
BackendPoolMatcher::Host(host) => request.headers().get(HOST).map_or(false, |h| h == host),
BackendPoolMatcher::HostRegexp(host_regex) => request.headers().get(HOST).map_or(false, |h| host_regex.is_match(h.to_str().unwrap_or(""))),
BackendPoolMatcher::Method(method) => request.method() == method,
BackendPoolMatcher::Path(path) => request.uri().path() == path,
BackendPoolMatcher::PathRegexp(path_regex) => path_regex.is_match(request.uri().path()),
BackendPoolMatcher::Query(key, value) => {
request.uri().query().map_or(false, |v| {
let query_params: HashMap<_, _> = url::form_urlencoded::parse(v.as_bytes()).into_owned().collect();
query_params.get(key).map_or(false, |sent_value| sent_value == value)
})
}
BackendPoolMatcher::And(left, right) => left.matches(request) && right.matches(request),
BackendPoolMatcher::Or(left, right) => left.matches(request) || right.matches(request),
}
}
}
}

/// A PEG parser for generating BackendPoolMatcher rules
Expand Down

0 comments on commit 237b3c6

Please sign in to comment.