diff --git a/.cargo/config.toml b/.cargo/config.toml index e69de29b..424ebfbf 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.wasm32-wasip1] +rustflags = ["-C", "link-arg=--allow-undefined"] diff --git a/src/data/cel.rs b/src/data/cel.rs index 4e3fc748..63abaddc 100644 --- a/src/data/cel.rs +++ b/src/data/cel.rs @@ -155,7 +155,7 @@ impl Expression { }) .collect(); - attributes.sort_by(|a, b| a.path.tokens().len().cmp(&b.path.tokens().len())); + attributes.sort_by_key(|a| a.path.tokens().len()); Ok(Self { attributes, diff --git a/src/filter/kuadrant_filter.rs b/src/filter/kuadrant_filter.rs index 32832448..7415d313 100644 --- a/src/filter/kuadrant_filter.rs +++ b/src/filter/kuadrant_filter.rs @@ -1,5 +1,6 @@ use crate::kuadrant::{Pipeline, PipelineFactory, PipelineState, ReqRespCtx}; use crate::metrics::METRICS; +use proxy_wasm::hostcalls; use proxy_wasm::traits::{Context, HttpContext}; use proxy_wasm::types::Action; use std::ops::Not; @@ -122,7 +123,15 @@ impl HttpContext for KuadrantFilter { Err(e) => { error!("#{} failed to build pipeline: {:?}", self.context_id, e); METRICS.errors().increment(); - // todo(adam-cattermole): we should deny the request + #[allow(clippy::panic)] + hostcalls::send_http_response(500, Default::default(), Some(b"Internal Server Error.\n")) + .unwrap_or_else(|err| { + error!( + "#{} CRITICAL: Failed to send error response: {:?}. WASM runtime is in an invalid state", + self.context_id, err + ); + panic!("CRITICAL: Failed to send HTTP reply after pipeline build failure"); + }); Action::Continue } } diff --git a/src/kuadrant/pipeline/executor.rs b/src/kuadrant/pipeline/executor.rs index 29460bcf..b4155563 100644 --- a/src/kuadrant/pipeline/executor.rs +++ b/src/kuadrant/pipeline/executor.rs @@ -97,7 +97,7 @@ impl Pipeline { } pub fn eval(mut self) -> PipelineState { - let tasks_to_process: Vec<_> = self.task_queue.drain(..).collect(); + let tasks_to_process: Vec<_> = std::mem::take(&mut self.task_queue); for task in tasks_to_process { if task diff --git a/src/kuadrant/pipeline/factory.rs b/src/kuadrant/pipeline/factory.rs index 44fa7958..6755e1f5 100644 --- a/src/kuadrant/pipeline/factory.rs +++ b/src/kuadrant/pipeline/factory.rs @@ -82,7 +82,7 @@ impl TryFrom for PipelineFactory { let blueprint = Rc::new(blueprint); for hostname in &config_action_set.route_rule_conditions.hostnames { - let key = reverse_subdomain(hostname); + let key = reverse_subdomain(&hostname.to_ascii_lowercase()); index.map_with_default( key, |blueprints| blueprints.push(Rc::clone(&blueprint)), @@ -189,7 +189,7 @@ impl PipelineFactory { match ctx.get_attribute::("request.host") { Ok(AttributeState::Available(Some(host))) => { let split_host = host.split_once(':').map_or(host.as_str(), |(h, _)| h); - Ok(split_host.to_owned()) + Ok(split_host.to_ascii_lowercase()) } Ok(AttributeState::Available(None)) => Err(BuildError::EvaluationError( "hostname not found".to_string(), @@ -585,6 +585,39 @@ mod tests { assert_eq!(factory.request_data.len(), 1); } + #[test] + fn build_matches_hostname_case_insensitively() { + let config = build_test_config(vec!["Example.COM".to_string()], vec![], "test-service"); + let factory = PipelineFactory::try_from(config).unwrap(); + + let mock_host = MockWasmHost::new() + .with_property("request.host".into(), "example.com".as_bytes().to_vec()); + let ctx = ReqRespCtx::new(Arc::new(mock_host)); + assert!(factory.build(ctx).unwrap().is_some()); + } + + #[test] + fn build_matches_mixed_case_request_hostname() { + let config = build_test_config(vec!["api.example.com".to_string()], vec![], "test-service"); + let factory = PipelineFactory::try_from(config).unwrap(); + + let mock_host = MockWasmHost::new() + .with_property("request.host".into(), "API.Example.COM".as_bytes().to_vec()); + let ctx = ReqRespCtx::new(Arc::new(mock_host)); + assert!(factory.build(ctx).unwrap().is_some()); + } + + #[test] + fn build_matches_wildcard_case_insensitively() { + let config = build_test_config(vec!["*.Example.COM".to_string()], vec![], "test-service"); + let factory = PipelineFactory::try_from(config).unwrap(); + + let mock_host = MockWasmHost::new() + .with_property("request.host".into(), "API.example.com".as_bytes().to_vec()); + let ctx = ReqRespCtx::new(Arc::new(mock_host)); + assert!(factory.build(ctx).unwrap().is_some()); + } + #[test] fn factory_handles_multiple_hostnames_for_same_action_set() { let config = build_test_config( diff --git a/src/kuadrant/pipeline/tasks/token_usage/event_parser.rs b/src/kuadrant/pipeline/tasks/token_usage/event_parser.rs index 1663d126..ef878eb5 100644 --- a/src/kuadrant/pipeline/tasks/token_usage/event_parser.rs +++ b/src/kuadrant/pipeline/tasks/token_usage/event_parser.rs @@ -85,11 +85,7 @@ impl EventBuilder { } self.event.data.push_str(val); } - "id" => { - if !val.contains('\u{0000}') { - self.event.id = val.to_string() - } - } + "id" if !val.contains('\u{0000}') => self.event.id = val.to_string(), "retry" => { if let Ok(val) = val.parse::() { self.event.retry = Some(Duration::from_millis(val))