Skip to content

Commit

Permalink
[refactor] Delegating building descriptors to the Policy impl
Browse files Browse the repository at this point in the history
Signed-off-by: dd di cesare <[email protected]>
  • Loading branch information
didierofrivia committed Aug 7, 2024
1 parent e3d5862 commit 65a1ea7
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 129 deletions.
130 changes: 129 additions & 1 deletion src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use crate::glob::GlobPattern;
use crate::policy_index::PolicyIndex;
use log::warn;
use log::{debug, warn};
use proxy_wasm::traits::{Context};
use serde::Deserialize;
use crate::envoy::{RateLimitDescriptor, RateLimitDescriptor_Entry};
use crate::utils::tokenize_with_escaping;
use crate::filter::http_context::{Filter};

#[derive(Deserialize, Debug, Clone)]
pub struct SelectorItem {
Expand Down Expand Up @@ -126,6 +130,130 @@ impl Policy {
rules,
}
}

pub fn build_descriptors(
&self,
filter: &Filter,
) -> protobuf::RepeatedField<RateLimitDescriptor> {
self.rules
.iter()
.filter(|rule: &&Rule| self.filter_rule_by_conditions(filter, &rule.conditions))
// Mapping 1 Rule -> 1 Descriptor
// Filter out empty descriptors
.filter_map(|rule| self.build_single_descriptor(filter, &rule.data))
.collect()
}

fn filter_rule_by_conditions(&self, filter: &Filter, conditions: &[Condition]) -> bool {
if conditions.is_empty() {
// no conditions means matching all the requests.
return true;
}

conditions
.iter()
.any(|condition| self.condition_applies(filter, condition))
}

fn condition_applies(&self, filter: &Filter, condition: &Condition) -> bool {
condition
.all_of
.iter()
.all(|pattern_expression| self.pattern_expression_applies(filter, pattern_expression))
}

fn pattern_expression_applies(&self, filter: &Filter, p_e: &PatternExpression, ) -> bool {
let attribute_path = tokenize_with_escaping(&p_e.selector, '.', '\\');
// convert a Vec<String> to Vec<&str>
// attribute_path.iter().map(AsRef::as_ref).collect()
let attribute_value = match filter
.get_property(attribute_path.iter().map(AsRef::as_ref).collect())
{
None => {
debug!(
"#{} pattern_expression_applies: selector not found: {}, defaulting to ``",
filter.context_id, p_e.selector
);
"".to_string()
}
// TODO(eastizle): not all fields are strings
// https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
Some(attribute_bytes) => match String::from_utf8(attribute_bytes) {
Err(e) => {
debug!(
"#{} pattern_expression_applies: failed to parse selector value: {}, error: {}",
filter.context_id, p_e.selector, e
);
return false;
}
Ok(attribute_value) => attribute_value,
},
};
p_e.operator
.eval(p_e.value.as_str(), attribute_value.as_str())
}

fn build_single_descriptor(&self, filter: &Filter, data_list: &[DataItem]) -> Option<RateLimitDescriptor> {
let mut entries = ::protobuf::RepeatedField::default();

// iterate over data items to allow any data item to skip the entire descriptor
for data in data_list.iter() {
match &data.item {
DataType::Static(static_item) => {
let mut descriptor_entry = RateLimitDescriptor_Entry::new();
descriptor_entry.set_key(static_item.key.to_owned());
descriptor_entry.set_value(static_item.value.to_owned());
entries.push(descriptor_entry);
}
DataType::Selector(selector_item) => {
let descriptor_key = match &selector_item.key {
None => selector_item.selector.to_owned(),
Some(key) => key.to_owned(),
};

let attribute_path = tokenize_with_escaping(&selector_item.selector, '.', '\\');
// convert a Vec<String> to Vec<&str>
// attribute_path.iter().map(AsRef::as_ref).collect()
let value = match filter
.get_property(attribute_path.iter().map(AsRef::as_ref).collect())
{
None => {
debug!(
"#{} build_single_descriptor: selector not found: {}",
filter.context_id, selector_item.selector
);
match &selector_item.default {
None => return None, // skipping the entire descriptor
Some(default_value) => default_value.clone(),
}
}
// TODO(eastizle): not all fields are strings
// https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
Some(attribute_bytes) => match String::from_utf8(attribute_bytes) {
Err(e) => {
debug!(
"#{} build_single_descriptor: failed to parse selector value: {}, error: {}",
filter.context_id, selector_item.selector, e
);
return None;
}
Ok(attribute_value) => attribute_value,
},
};
let mut descriptor_entry = RateLimitDescriptor_Entry::new();
descriptor_entry.set_key(descriptor_key);
descriptor_entry.set_value(value);
entries.push(descriptor_entry);
}
}
}

let mut res = RateLimitDescriptor::new();
res.set_entries(entries);
Some(res)
}


}

pub struct FilterConfig {
Expand Down
2 changes: 1 addition & 1 deletion src/filter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod http_context;
pub(crate) mod http_context;
mod root_context;

#[cfg_attr(
Expand Down
130 changes: 3 additions & 127 deletions src/filter/http_context.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use crate::configuration::{
Condition, DataItem, DataType, FailureMode, FilterConfig, PatternExpression, Policy,
Rule,
FailureMode, FilterConfig, Policy,
};
use crate::envoy::{
RateLimitDescriptor, RateLimitDescriptor_Entry, RateLimitRequest, RateLimitResponse,
RateLimitRequest, RateLimitResponse,
RateLimitResponse_Code,
};
use crate::filter::http_context::TracingHeader::{Baggage, Traceparent, Tracestate};
use crate::utils::tokenize_with_escaping;
use log::{debug, warn};
use protobuf::Message;
use proxy_wasm::traits::{Context, HttpContext};
Expand Down Expand Up @@ -61,7 +59,7 @@ impl Filter {
}

fn process_rate_limit_policy(&self, rlp: &Policy) -> Action {
let descriptors = self.build_descriptors(rlp);
let descriptors = rlp.build_descriptors(self);
if descriptors.is_empty() {
debug!(
"#{} process_rate_limit_policy: empty descriptors",
Expand Down Expand Up @@ -108,128 +106,6 @@ impl Filter {
}
}

fn build_descriptors(
&self,
rlp: &Policy,
) -> protobuf::RepeatedField<RateLimitDescriptor> {
rlp.rules
.iter()
.filter(|rule: &&Rule| self.filter_rule_by_conditions(&rule.conditions))
// Mapping 1 Rule -> 1 Descriptor
// Filter out empty descriptors
.filter_map(|rule| self.build_single_descriptor(&rule.data))
.collect()
}

fn filter_rule_by_conditions(&self, conditions: &[Condition]) -> bool {
if conditions.is_empty() {
// no conditions is equivalent to matching all the requests.
return true;
}

conditions
.iter()
.any(|condition| self.condition_applies(condition))
}

fn condition_applies(&self, condition: &Condition) -> bool {
condition
.all_of
.iter()
.all(|pattern_expression| self.pattern_expression_applies(pattern_expression))
}

fn pattern_expression_applies(&self, p_e: &PatternExpression) -> bool {
let attribute_path = tokenize_with_escaping(&p_e.selector, '.', '\\');
// convert a Vec<String> to Vec<&str>
// attribute_path.iter().map(AsRef::as_ref).collect()
let attribute_value = match self
.get_property(attribute_path.iter().map(AsRef::as_ref).collect())
{
None => {
debug!(
"#{} pattern_expression_applies: selector not found: {}, defaulting to ``",
self.context_id, p_e.selector
);
"".to_string()
}
// TODO(eastizle): not all fields are strings
// https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
Some(attribute_bytes) => match String::from_utf8(attribute_bytes) {
Err(e) => {
debug!(
"#{} pattern_expression_applies: failed to parse selector value: {}, error: {}",
self.context_id, p_e.selector, e
);
return false;
}
Ok(attribute_value) => attribute_value,
},
};
p_e.operator
.eval(p_e.value.as_str(), attribute_value.as_str())
}

fn build_single_descriptor(&self, data_list: &[DataItem]) -> Option<RateLimitDescriptor> {
let mut entries = ::protobuf::RepeatedField::default();

// iterate over data items to allow any data item to skip the entire descriptor
for data in data_list.iter() {
match &data.item {
DataType::Static(static_item) => {
let mut descriptor_entry = RateLimitDescriptor_Entry::new();
descriptor_entry.set_key(static_item.key.to_owned());
descriptor_entry.set_value(static_item.value.to_owned());
entries.push(descriptor_entry);
}
DataType::Selector(selector_item) => {
let descriptor_key = match &selector_item.key {
None => selector_item.selector.to_owned(),
Some(key) => key.to_owned(),
};

let attribute_path = tokenize_with_escaping(&selector_item.selector, '.', '\\');
// convert a Vec<String> to Vec<&str>
// attribute_path.iter().map(AsRef::as_ref).collect()
let value = match self
.get_property(attribute_path.iter().map(AsRef::as_ref).collect())
{
None => {
debug!(
"#{} build_single_descriptor: selector not found: {}",
self.context_id, selector_item.selector
);
match &selector_item.default {
None => return None, // skipping the entire descriptor
Some(default_value) => default_value.clone(),
}
}
// TODO(eastizle): not all fields are strings
// https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
Some(attribute_bytes) => match String::from_utf8(attribute_bytes) {
Err(e) => {
debug!(
"#{} build_single_descriptor: failed to parse selector value: {}, error: {}",
self.context_id, selector_item.selector, e
);
return None;
}
Ok(attribute_value) => attribute_value,
},
};
let mut descriptor_entry = RateLimitDescriptor_Entry::new();
descriptor_entry.set_key(descriptor_key);
descriptor_entry.set_value(value);
entries.push(descriptor_entry);
}
}
}

let mut res = RateLimitDescriptor::new();
res.set_entries(entries);
Some(res)
}

fn handle_error_on_grpc_response(&self) {
match &self.config.failure_mode {
FailureMode::Deny => {
Expand Down

0 comments on commit 65a1ea7

Please sign in to comment.