From bc03ad4850bf76321fabffe1404b272cb7962452 Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Fri, 9 Aug 2024 14:54:37 +0100 Subject: [PATCH 1/4] Move attribute parsing to attribute trait Signed-off-by: Adam Cattermole --- src/attribute.rs | 118 +++++++++++++++++++++++++++++++++++++++++++ src/configuration.rs | 62 ++++++----------------- src/lib.rs | 1 + src/policy.rs | 27 +--------- 4 files changed, 136 insertions(+), 72 deletions(-) create mode 100644 src/attribute.rs diff --git a/src/attribute.rs b/src/attribute.rs new file mode 100644 index 00000000..9d3a3511 --- /dev/null +++ b/src/attribute.rs @@ -0,0 +1,118 @@ +use crate::configuration::Path; +use crate::filter::http_context::Filter; +use chrono::{DateTime, FixedOffset}; +use proxy_wasm::traits::Context; + +pub trait Attribute { + fn parse(raw_attribute: Vec) -> Result + where + Self: Sized; +} + +impl Attribute for String { + fn parse(raw_attribute: Vec) -> Result { + String::from_utf8(raw_attribute).map_err(|err| { + format!( + "parse: failed to parse selector String value, error: {}", + err + ) + }) + } +} + +impl Attribute for i64 { + fn parse(raw_attribute: Vec) -> Result { + if raw_attribute.len() != 8 { + return Err(format!( + "Int value expected to be 8 bytes, but got {}", + raw_attribute.len() + )); + } + Ok(i64::from_le_bytes( + raw_attribute[..8] + .try_into() + .expect("This has to be 8 bytes long!"), + )) + } +} + +impl Attribute for u64 { + fn parse(raw_attribute: Vec) -> Result { + if raw_attribute.len() != 8 { + return Err(format!( + "UInt value expected to be 8 bytes, but got {}", + raw_attribute.len() + )); + } + Ok(u64::from_le_bytes( + raw_attribute[..8] + .try_into() + .expect("This has to be 8 bytes long!"), + )) + } +} + +impl Attribute for f64 { + fn parse(raw_attribute: Vec) -> Result { + if raw_attribute.len() != 8 { + return Err(format!( + "Float value expected to be 8 bytes, but got {}", + raw_attribute.len() + )); + } + Ok(f64::from_le_bytes( + raw_attribute[..8] + .try_into() + .expect("This has to be 8 bytes long!"), + )) + } +} + +impl Attribute for Vec { + fn parse(raw_attribute: Vec) -> Result { + Ok(raw_attribute) + } +} + +impl Attribute for bool { + fn parse(raw_attribute: Vec) -> Result { + if raw_attribute.len() != 1 { + return Err(format!( + "Bool value expected to be 1 byte, but got {}", + raw_attribute.len() + )); + } + Ok(raw_attribute[0] & 1 == 1) + } +} + +impl Attribute for DateTime { + fn parse(raw_attribute: Vec) -> Result { + if raw_attribute.len() != 8 { + return Err(format!( + "parse: Timestamp expected to be 8 bytes, but got {}", + raw_attribute.len() + )); + } + + let nanos = i64::from_le_bytes( + raw_attribute.as_slice()[..8] + .try_into() + .expect("This has to be 8 bytes long!"), + ); + Ok(DateTime::from_timestamp_nanos(nanos).into()) + } +} + +pub fn get_attribute(f: &Filter, attr: &str) -> Result +where + T: Attribute, +{ + match f.get_property(Path::from(attr).tokens()) { + None => Err(format!( + "#{} get_attribute: not found: {}", + f.context_id, attr + )), + Some(attribute_bytes) => T::parse(attribute_bytes), + } +} diff --git a/src/configuration.rs b/src/configuration.rs index 7c815943..d7495ef0 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,13 +1,15 @@ -use crate::policy::Policy; -use crate::policy_index::PolicyIndex; +use std::cell::OnceCell; +use std::fmt::{Debug, Display, Formatter}; +use std::sync::Arc; + use cel_interpreter::objects::ValueType; use cel_interpreter::{Context, Expression, Value}; use cel_parser::{Atom, RelationOp}; -use chrono::{DateTime, FixedOffset}; use serde::Deserialize; -use std::cell::OnceCell; -use std::fmt::{Debug, Display, Formatter}; -use std::sync::Arc; + +use crate::attribute::Attribute; +use crate::policy::Policy; +use crate::policy_index::PolicyIndex; #[derive(Deserialize, Debug, Clone)] pub struct SelectorItem { @@ -168,47 +170,13 @@ impl PatternExpression { pub fn eval(&self, raw_attribute: Vec) -> Result { let cel_type = &self.compiled.get().unwrap().cel_type; let value = match cel_type { - ValueType::String => - Value::String(String::from_utf8(raw_attribute).map_err(|err| format!( - "pattern_expression_applies: failed to parse selector String value: {}, error: {}", - self.selector, err))?.into()), - ValueType::Int => { - if raw_attribute.len() != 8 { - return Err(format!("Int value expected to be 8 bytes, but got {}", raw_attribute.len())); - } - Value::Int(i64::from_le_bytes(raw_attribute[..8].try_into().expect("This has to be 8 bytes long!"))) - }, - ValueType::UInt => { - { - if raw_attribute.len() != 8 { - return Err(format!("UInt value expected to be 8 bytes, but got {}", raw_attribute.len())); - } - Value::UInt(u64::from_le_bytes(raw_attribute[..8].try_into().expect("This has to be 8 bytes long!"))) - } - }, - ValueType::Float => { - if raw_attribute.len() != 8 { - return Err(format!("Float value expected to be 8 bytes, but got {}", raw_attribute.len())); - } - Value::Float(f64::from_le_bytes(raw_attribute[..8].try_into().expect("This has to be 8 bytes long!"))) - }, - ValueType::Bytes => Value::Bytes(raw_attribute.into()), - ValueType::Bool => { - if raw_attribute.len() != 1 { - return Err(format!("Bool value expected to be 1 byte, but got {}", raw_attribute.len())); - } - Value::Bool(raw_attribute[0] & 1 == 1) - } - ValueType::Timestamp => { - { - if raw_attribute.len() != 8 { - return Err(format!("Timestamp expected to be 8 bytes, but got {}", raw_attribute.len())); - } - let nanos = i64::from_le_bytes(raw_attribute[..8].try_into().expect("This has to be 8 bytes long!")); - let time: DateTime = DateTime::from_timestamp_nanos(nanos).into(); - Value::Timestamp(time) - } - } + ValueType::String => Value::String(Arc::new(Attribute::parse(raw_attribute)?)), + ValueType::Int => Value::Int(Attribute::parse(raw_attribute)?), + ValueType::UInt => Value::UInt(Attribute::parse(raw_attribute)?), + ValueType::Float => Value::Float(Attribute::parse(raw_attribute)?), + ValueType::Bytes => Value::Bytes(Arc::new(Attribute::parse(raw_attribute)?)), + ValueType::Bool => Value::Bool(Attribute::parse(raw_attribute)?), + ValueType::Timestamp => Value::Timestamp(Attribute::parse(raw_attribute)?), // todo: Impl support for parsing these two types… Tho List/Map of what? // ValueType::List => {} // ValueType::Map => {} diff --git a/src/lib.rs b/src/lib.rs index 417ec5f5..8ee6c311 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod attribute; mod configuration; mod envoy; mod filter; diff --git a/src/policy.rs b/src/policy.rs index aa46a38d..cea00daf 100644 --- a/src/policy.rs +++ b/src/policy.rs @@ -1,3 +1,4 @@ +use crate::attribute::get_attribute; use crate::configuration::{DataItem, DataType, PatternExpression}; use crate::envoy::{RateLimitDescriptor, RateLimitDescriptor_Entry}; use crate::filter::http_context::Filter; @@ -125,31 +126,7 @@ impl Policy { Some(key) => key.to_owned(), }; - let attribute_path = selector_item.path(); - let value = match filter.get_property(attribute_path.tokens()) { - None => { - debug!( - "#{} build_single_descriptor: selector not found: {}", - filter.context_id, attribute_path - ); - 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, attribute_path, e - ); - return None; - } - Ok(attribute_value) => attribute_value, - }, - }; + let value = get_attribute(filter, selector_item.selector.as_str()).ok()?; let mut descriptor_entry = RateLimitDescriptor_Entry::new(); descriptor_entry.set_key(descriptor_key); descriptor_entry.set_value(value); From 40ecfafe5e06c814cbe886a7c2865b5099c8ab54 Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Fri, 9 Aug 2024 15:14:35 +0100 Subject: [PATCH 2/4] Use default value if present Signed-off-by: Adam Cattermole --- src/policy.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/policy.rs b/src/policy.rs index cea00daf..7b894c16 100644 --- a/src/policy.rs +++ b/src/policy.rs @@ -1,4 +1,4 @@ -use crate::attribute::get_attribute; +use crate::attribute::Attribute; use crate::configuration::{DataItem, DataType, PatternExpression}; use crate::envoy::{RateLimitDescriptor, RateLimitDescriptor_Entry}; use crate::filter::http_context::Filter; @@ -126,7 +126,25 @@ impl Policy { Some(key) => key.to_owned(), }; - let value = get_attribute(filter, selector_item.selector.as_str()).ok()?; + let attribute_path = selector_item.path(); + let value = match filter.get_property(attribute_path.tokens()) { + None => { + debug!( + "#{} build_single_descriptor: selector not found: {}", + filter.context_id, attribute_path + ); + 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) => Attribute::parse(attribute_bytes) + .inspect_err(|e| debug!("#{} build_single_descriptor: failed to parse selector value: {}, error: {}", + filter.context_id, attribute_path, e)) + .ok()?, + }; let mut descriptor_entry = RateLimitDescriptor_Entry::new(); descriptor_entry.set_key(descriptor_key); descriptor_entry.set_value(value); From d044a3e1f5c5aac6af5678f08fab8316020960dd Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Fri, 9 Aug 2024 15:18:02 +0100 Subject: [PATCH 3/4] Set allow(dead_code) on get_attribute Signed-off-by: Adam Cattermole --- src/attribute.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/attribute.rs b/src/attribute.rs index 9d3a3511..a5853a9f 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -104,6 +104,7 @@ impl Attribute for DateTime { } } +#[allow(dead_code)] pub fn get_attribute(f: &Filter, attr: &str) -> Result where T: Attribute, From e5a20deb563dedb6747bc7053e2f983e7fd2dcc0 Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Tue, 13 Aug 2024 10:40:03 +0100 Subject: [PATCH 4/4] Update log messages Signed-off-by: Adam Cattermole --- src/attribute.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/attribute.rs b/src/attribute.rs index a5853a9f..aada6176 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -24,7 +24,7 @@ impl Attribute for i64 { fn parse(raw_attribute: Vec) -> Result { if raw_attribute.len() != 8 { return Err(format!( - "Int value expected to be 8 bytes, but got {}", + "parse: Int value expected to be 8 bytes, but got {}", raw_attribute.len() )); } @@ -40,7 +40,7 @@ impl Attribute for u64 { fn parse(raw_attribute: Vec) -> Result { if raw_attribute.len() != 8 { return Err(format!( - "UInt value expected to be 8 bytes, but got {}", + "parse: UInt value expected to be 8 bytes, but got {}", raw_attribute.len() )); } @@ -56,7 +56,7 @@ impl Attribute for f64 { fn parse(raw_attribute: Vec) -> Result { if raw_attribute.len() != 8 { return Err(format!( - "Float value expected to be 8 bytes, but got {}", + "parse: Float value expected to be 8 bytes, but got {}", raw_attribute.len() )); } @@ -78,7 +78,7 @@ impl Attribute for bool { fn parse(raw_attribute: Vec) -> Result { if raw_attribute.len() != 1 { return Err(format!( - "Bool value expected to be 1 byte, but got {}", + "parse: Bool value expected to be 1 byte, but got {}", raw_attribute.len() )); }