Skip to content

Commit

Permalink
Wired proper descriptor wiring into context
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Snaps <[email protected]>
  • Loading branch information
alexsnaps committed Dec 3, 2024
1 parent 7cc4ff4 commit 8941898
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 57 deletions.
4 changes: 2 additions & 2 deletions limitador-server/examples/envoy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ static_resources:
rate_limits:
- stage: 0
actions:
- {request_headers: {header_name: "userid", descriptor_key: "user_id"}}
- { request_headers: { header_name: ":method", descriptor_key: "req_method" } }
- { request_headers: { header_name: "userid", descriptor_key: "user_id" } }
- { request_headers: { header_name: ":method", descriptor_key: "descriptors[0]['method']" } }
http_filters:
- name: envoy.filters.http.ratelimit
typed_config:
Expand Down
2 changes: 1 addition & 1 deletion limitador-server/examples/limits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
max_value: 5
seconds: 60
conditions:
- "req_method == 'POST'"
- "descriptors[0]['req.method'] == 'POST'"
variables:
- user_id
8 changes: 4 additions & 4 deletions limitador-server/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ bin/grpcurl -plaintext -d @ 127.0.0.1:18081 envoy.service.ratelimit.v3.RateLimit
{
"entries": [
{
"key": "req_method",
"key": "req.method",
"value": "POST"
},
{
"key": "req_path",
"key": "req.path",
"value": "/"
}
]
Expand All @@ -125,11 +125,11 @@ while :; do bin/grpcurl -plaintext -d @ 127.0.0.1:18081 envoy.service.ratelimit.
{
"entries": [
{
"key": "req_method",
"key": "req.method",
"value": "POST"
},
{
"key": "req_path",
"key": "req.path",
"value": "/"
}
]
Expand Down
4 changes: 2 additions & 2 deletions limitador-server/sandbox/envoy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ static_resources:
- actions:
- request_headers:
header_name: :method
descriptor_key: req_method
descriptor_key: req.method
- request_headers:
header_name: :path
descriptor_key: req_path
descriptor_key: req.path
http_filters:
- name: envoy.filters.http.ratelimit
typed_config:
Expand Down
4 changes: 2 additions & 2 deletions limitador-server/sandbox/envoy2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ static_resources:
- actions:
- request_headers:
header_name: :method
descriptor_key: req_method
descriptor_key: req.method
- request_headers:
header_name: :path
descriptor_key: req_path
descriptor_key: req.path
http_filters:
- name: envoy.filters.http.ratelimit
typed_config:
Expand Down
4 changes: 2 additions & 2 deletions limitador-server/sandbox/envoy3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ static_resources:
- actions:
- request_headers:
header_name: :method
descriptor_key: req_method
descriptor_key: req.method
- request_headers:
header_name: :path
descriptor_key: req_path
descriptor_key: req.path
http_filters:
- name: envoy.filters.http.ratelimit
typed_config:
Expand Down
12 changes: 6 additions & 6 deletions limitador-server/sandbox/limits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
max_value: 10
seconds: 60
conditions:
- "req_method == 'GET'"
- "req_path != '/json'"
- "descriptors[0]['req.method'] == 'GET'"
- "descriptors[0]['req.path'] != '/json'"
variables: []
- namespace: test_namespace
max_value: 5
seconds: 60
conditions:
- "req_method == 'POST'"
- "req_path != '/json'"
- "descriptors[0]['req.method'] == 'POST'"
- "descriptors[0]['req.path'] != '/json'"
variables: []
- namespace: test_namespace
max_value: 50000
seconds: 10
conditions:
- "req_method == 'GET'"
- "req_path == '/json'"
- "descriptors[0]['req.method'] == 'GET'"
- "descriptors[0]['req.path'] == '/json'"
variables: []
4 changes: 2 additions & 2 deletions limitador-server/sandbox/load-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
{
"entries": [
{
"key": "req_method",
"key": "req.method",
"value": "GET"
},
{
"key": "req_path",
"key": "req.path",
"value": "/json"
}
]
Expand Down
2 changes: 1 addition & 1 deletion limitador-server/sandbox/redis-otel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ bin/grpcurl -plaintext -d @ 127.0.0.1:18081 envoy.service.ratelimit.v3.RateLimit
{
"entries": [
{
"key": "req_method",
"key": "req.method",
"value": "POST"
}
]
Expand Down
65 changes: 41 additions & 24 deletions limitador-server/src/envoy_rls/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ use opentelemetry::propagation::Extractor;
use std::collections::HashMap;
use std::sync::Arc;

use limitador::CheckResult;
use tonic::codegen::http::HeaderMap;
use tonic::{transport, transport::Server, Request, Response, Status};
use tracing::Span;
use tracing_opentelemetry::OpenTelemetrySpanExt;

use crate::envoy_rls::server::envoy::config::core::v3::HeaderValue;
use crate::envoy_rls::server::envoy::service::ratelimit::v3::rate_limit_response::Code;
use crate::envoy_rls::server::envoy::service::ratelimit::v3::rate_limit_service_server::{
Expand All @@ -19,6 +13,12 @@ use crate::envoy_rls::server::envoy::service::ratelimit::v3::{
};
use crate::prometheus_metrics::PrometheusMetrics;
use crate::Limiter;
use limitador::limit::Context;
use limitador::CheckResult;
use tonic::codegen::http::HeaderMap;
use tonic::{transport, transport::Server, Request, Response, Status};
use tracing::Span;
use tracing_opentelemetry::OpenTelemetrySpanExt;

include!("envoy_types.rs");

Expand Down Expand Up @@ -72,7 +72,7 @@ impl RateLimitService for MyRateLimiter {
) -> Result<Response<RateLimitResponse>, Status> {
debug!("Request received: {:?}", request);

let mut values: HashMap<String, String> = HashMap::new();
let mut values: Vec<HashMap<String, String>> = Vec::default();
let (metadata, _ext, req) = request.into_parts();
let namespace = req.domain;
let rl_headers = RateLimitRequestHeaders::new(metadata.into_headers());
Expand All @@ -96,9 +96,11 @@ impl RateLimitService for MyRateLimiter {
let namespace = namespace.into();

for descriptor in &req.descriptors {
let mut map = HashMap::default();
for entry in &descriptor.entries {
values.insert(entry.key.clone(), entry.value.clone());
map.insert(entry.key.clone(), entry.value.clone());
}
values.push(map);
}

// "hits_addend" is optional according to the spec, and should default
Expand All @@ -109,7 +111,8 @@ impl RateLimitService for MyRateLimiter {
req.hits_addend
};

let ctx = values.into();
let mut ctx = Context::default();
ctx.list_binding("descriptors".to_string(), values);

let rate_limited_resp = match &*self.limiter {
Limiter::Blocking(limiter) => limiter.check_rate_limited_and_update(
Expand Down Expand Up @@ -255,8 +258,12 @@ mod tests {
namespace,
1,
60,
vec!["req_method == 'GET'".try_into().expect("failed parsing!")],
vec!["app_id".try_into().expect("failed parsing!")],
vec!["descriptors[0]['req.method'] == 'GET'"
.try_into()
.expect("failed parsing!")],
vec!["descriptors[0]['app.id']"
.try_into()
.expect("failed parsing!")],
);

let limiter = RateLimiter::new(10_000);
Expand All @@ -276,11 +283,11 @@ mod tests {
descriptors: vec![RateLimitDescriptor {
entries: vec![
Entry {
key: "req_method".to_string(),
key: "req.method".to_string(),
value: "GET".to_string(),
},
Entry {
key: "app_id".to_string(),
key: "app.id".to_string(),
value: "1".to_string(),
},
],
Expand Down Expand Up @@ -337,7 +344,7 @@ mod tests {
domain: "test_namespace".to_string(),
descriptors: vec![RateLimitDescriptor {
entries: vec![Entry {
key: "req_method".to_string(),
key: "req.method".to_string(),
value: "GET".to_string(),
}],
limit: None,
Expand Down Expand Up @@ -371,7 +378,7 @@ mod tests {
domain: "".to_string(),
descriptors: vec![RateLimitDescriptor {
entries: vec![Entry {
key: "req_method".to_string(),
key: "req.method".to_string(),
value: "GET".to_string(),
}],
limit: None,
Expand Down Expand Up @@ -400,18 +407,24 @@ mod tests {
namespace,
10,
60,
vec!["x == '1'".try_into().expect("failed parsing!")],
vec!["z".try_into().expect("failed parsing!")],
vec!["descriptors[0].x == '1'"
.try_into()
.expect("failed parsing!")],
vec!["descriptors[0].z".try_into().expect("failed parsing!")],
),
Limit::new(
namespace,
0,
60,
vec![
"x == '1'".try_into().expect("failed parsing!"),
"y == '2'".try_into().expect("failed parsing!"),
"descriptors[0].x == '1'"
.try_into()
.expect("failed parsing!"),
"descriptors[1].y == '2'"
.try_into()
.expect("failed parsing!"),
],
vec!["z".try_into().expect("failed parsing!")],
vec!["descriptors[0].z".try_into().expect("failed parsing!")],
),
]
.into_iter()
Expand Down Expand Up @@ -480,8 +493,10 @@ mod tests {
namespace,
10,
60,
vec!["x == '1'".try_into().expect("failed parsing!")],
vec!["y".try_into().expect("failed parsing!")],
vec!["descriptors[0].x == '1'"
.try_into()
.expect("failed parsing!")],
vec!["descriptors[0].y".try_into().expect("failed parsing!")],
);

let limiter = RateLimiter::new(10_000);
Expand Down Expand Up @@ -555,8 +570,10 @@ mod tests {
namespace,
1,
60,
vec!["x == '1'".try_into().expect("failed parsing!")],
vec!["y".try_into().expect("failed parsing!")],
vec!["descriptors[0].x == '1'"
.try_into()
.expect("failed parsing!")],
vec!["descriptors[0].y".try_into().expect("failed parsing!")],
);

let limiter = RateLimiter::new(10_000);
Expand Down
30 changes: 19 additions & 11 deletions limitador-server/src/http_api/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::prometheus_metrics::PrometheusMetrics;
use crate::Limiter;
use actix_web::{http::StatusCode, HttpResponse, HttpResponseBuilder, ResponseError};
use actix_web::{App, HttpServer};
use limitador::limit::Context;
use limitador::CheckResult;
use paperclip::actix::{
api_v2_errors,
Expand Down Expand Up @@ -122,7 +123,8 @@ async fn check(
response_headers: _,
} = request.into_inner();
let namespace = namespace.into();
let ctx = values.into();
let mut ctx = Context::default();
ctx.list_binding("descriptors".to_string(), vec![values]);
let is_rate_limited_result = match state.get_ref().limiter() {
Limiter::Blocking(limiter) => limiter.is_rate_limited(&namespace, &ctx, delta),
Limiter::Async(limiter) => limiter.is_rate_limited(&namespace, &ctx, delta).await,
Expand Down Expand Up @@ -153,7 +155,8 @@ async fn report(
response_headers: _,
} = request.into_inner();
let namespace = namespace.into();
let ctx = values.into();
let mut ctx = Context::default();
ctx.list_binding("descriptors".to_string(), vec![values]);
let update_counters_result = match data.get_ref().limiter() {
Limiter::Blocking(limiter) => limiter.update_counters(&namespace, &ctx, delta),
Limiter::Async(limiter) => limiter.update_counters(&namespace, &ctx, delta).await,
Expand All @@ -178,7 +181,8 @@ async fn check_and_report(
response_headers,
} = request.into_inner();
let namespace = namespace.into();
let ctx = values.into();
let mut ctx = Context::default();
ctx.list_binding("descriptors".to_string(), vec![values]);
let rate_limit_data = data.get_ref();
let rate_limited_and_update_result = match rate_limit_data.limiter() {
Limiter::Blocking(limiter) => limiter.check_rate_limited_and_update(
Expand Down Expand Up @@ -382,8 +386,8 @@ mod tests {

// Prepare values to check
let mut values = HashMap::new();
values.insert("req_method".into(), "GET".into());
values.insert("app_id".into(), "1".into());
values.insert("req.method".into(), "GET".into());
values.insert("req.id".into(), "1".into());
let info = CheckAndReportInfo {
namespace: namespace.into(),
values,
Expand Down Expand Up @@ -433,8 +437,8 @@ mod tests {

// Prepare values to check
let mut values = HashMap::new();
values.insert("req_method".into(), "GET".into());
values.insert("app_id".into(), "1".into());
values.insert("req.method".into(), "GET".into());
values.insert("app.id".into(), "1".into());
let info = CheckAndReportInfo {
namespace: namespace.into(),
values,
Expand Down Expand Up @@ -508,8 +512,8 @@ mod tests {

// Prepare values to check
let mut values = HashMap::new();
values.insert("req_method".into(), "GET".into());
values.insert("app_id".into(), "1".into());
values.insert("req.method".into(), "GET".into());
values.insert("app.id".into(), "1".into());
let info = CheckAndReportInfo {
namespace: namespace.into(),
values,
Expand Down Expand Up @@ -551,8 +555,12 @@ mod tests {
namespace,
max,
60,
vec!["req_method == 'GET'".try_into().expect("failed parsing!")],
vec!["app_id".try_into().expect("failed parsing!")],
vec!["descriptors[0]['req.method'] == 'GET'"
.try_into()
.expect("failed parsing!")],
vec!["descriptors[0]['app.id']"
.try_into()
.expect("failed parsing!")],
);

match &limiter {
Expand Down

0 comments on commit 8941898

Please sign in to comment.