From 1665df92748a4256fc2a3b73d354e7275c992a7b Mon Sep 17 00:00:00 2001 From: Andrey Ermilov Date: Sun, 27 Aug 2023 16:03:08 +0200 Subject: [PATCH 1/2] refactor(config): Add trait Configurable --- hitbox-tower/src/configurable.rs | 29 +++++++++++ hitbox-tower/src/configuration/endpoint.rs | 13 +++-- hitbox-tower/src/layer.rs | 58 +++++++++++----------- hitbox-tower/src/lib.rs | 3 ++ hitbox-tower/src/service.rs | 33 ++++++------ 5 files changed, 87 insertions(+), 49 deletions(-) create mode 100644 hitbox-tower/src/configurable.rs diff --git a/hitbox-tower/src/configurable.rs b/hitbox-tower/src/configurable.rs new file mode 100644 index 0000000..a7b9f05 --- /dev/null +++ b/hitbox-tower/src/configurable.rs @@ -0,0 +1,29 @@ +use hitbox::policy::PolicyConfig; +use hitbox::predicate::Predicate; +use hitbox::Extractor; +use hitbox_http::{CacheableHttpRequest, CacheableHttpResponse}; + +type RequestPredicate = + Box> + Send + Sync>; + +type ResponsePredicate = + Box> + Send + Sync>; + +type RequestExtractor = + Box> + Send + Sync>; + +pub trait Configurable { + fn request_predicates(&self) -> RequestPredicate + where + ReqBody: Send + 'static; + + fn response_predicates(&self) -> ResponsePredicate + where + ResBody: Send + 'static; + + fn extractors(&self) -> RequestExtractor + where + ReqBody: Send + 'static; + + fn policy(&self) -> PolicyConfig; +} diff --git a/hitbox-tower/src/configuration/endpoint.rs b/hitbox-tower/src/configuration/endpoint.rs index f7a3d59..dca5375 100644 --- a/hitbox-tower/src/configuration/endpoint.rs +++ b/hitbox-tower/src/configuration/endpoint.rs @@ -1,4 +1,5 @@ use crate::configuration::{RequestExtractor, RequestPredicate, ResponsePredicate}; +use crate::Configurable; use hitbox::policy::PolicyConfig; use hitbox::predicate::Predicate; use hitbox::Extractor; @@ -31,8 +32,10 @@ impl EndpointConfig { policy: Default::default(), } } +} - pub(crate) fn request_predicates( +impl Configurable for EndpointConfig { + fn request_predicates( &self, ) -> Box> + Send + Sync> where @@ -54,7 +57,7 @@ impl EndpointConfig { }) } - pub(crate) fn response_predicates( + fn response_predicates( &self, ) -> Box> + Send + Sync> where @@ -68,7 +71,7 @@ impl EndpointConfig { }) } - pub(crate) fn extractors( + fn extractors( &self, ) -> Box> + Send + Sync> where @@ -84,6 +87,10 @@ impl EndpointConfig { RequestExtractor::Header { key } => Box::new(inner.header(key.to_string())), }) } + + fn policy(&self) -> PolicyConfig { + self.policy.clone() + } } impl Default for EndpointConfig { diff --git a/hitbox-tower/src/layer.rs b/hitbox-tower/src/layer.rs index 65d39ba..05f1fcb 100644 --- a/hitbox-tower/src/layer.rs +++ b/hitbox-tower/src/layer.rs @@ -15,26 +15,26 @@ use crate::service::CacheService; #[derive(Clone)] pub struct Cache { pub backend: Arc, - pub endpoint_config: Arc, + pub configuration: Arc, } impl Cache { pub fn new(backend: B) -> Cache { Cache { backend: Arc::new(backend), - endpoint_config: Arc::new(Default::default()), + configuration: Arc::new(Default::default()), } } } impl Layer for Cache { - type Service = CacheService; + type Service = CacheService; fn layer(&self, upstream: S) -> Self::Service { CacheService::new( upstream, Arc::clone(&self.backend), - Arc::clone(&self.endpoint_config), + Arc::clone(&self.configuration), ) } } @@ -47,7 +47,7 @@ impl Cache { pub struct CacheBuilder { backend: Option, - endpoint_config: EndpointConfig, + configuration: EndpointConfig, } impl CacheBuilder @@ -57,73 +57,73 @@ where pub fn backend(self, backend: NB) -> CacheBuilder { CacheBuilder { backend: Some(backend), - endpoint_config: self.endpoint_config, + configuration: self.configuration, } } pub fn enable(self, policy: EnabledCacheConfig) -> Self { - let endpoint_config = EndpointConfig { - request_predicates: self.endpoint_config.request_predicates, - response_predicates: self.endpoint_config.response_predicates, - extractors: self.endpoint_config.extractors, + let configuration = EndpointConfig { + request_predicates: self.configuration.request_predicates, + response_predicates: self.configuration.response_predicates, + extractors: self.configuration.extractors, policy: PolicyConfig::Enabled(policy), }; CacheBuilder { backend: self.backend, - endpoint_config, + configuration, } } pub fn disable(self) -> Self { CacheBuilder { backend: self.backend, - endpoint_config: self.endpoint_config, + configuration: self.configuration, } } pub fn request(self, predicates: RequestPredicateBuilder) -> Self { - let endpoint_config = EndpointConfig { + let configuration = EndpointConfig { request_predicates: predicates.build(), - response_predicates: self.endpoint_config.response_predicates, - extractors: self.endpoint_config.extractors, - policy: self.endpoint_config.policy, + response_predicates: self.configuration.response_predicates, + extractors: self.configuration.extractors, + policy: self.configuration.policy, }; CacheBuilder { backend: self.backend, - endpoint_config, + configuration, } } pub fn response(self, predicates: ResponsePredicateBuilder) -> Self { - let endpoint_config = EndpointConfig { - request_predicates: self.endpoint_config.request_predicates, + let configuration = EndpointConfig { + request_predicates: self.configuration.request_predicates, response_predicates: predicates.build(), - extractors: self.endpoint_config.extractors, - policy: self.endpoint_config.policy, + extractors: self.configuration.extractors, + policy: self.configuration.policy, }; CacheBuilder { backend: self.backend, - endpoint_config, + configuration, } } pub fn cache_key(self, extractors: ExtractorBuilder) -> Self { - let endpoint_config = EndpointConfig { - request_predicates: self.endpoint_config.request_predicates, - response_predicates: self.endpoint_config.response_predicates, + let configuration = EndpointConfig { + request_predicates: self.configuration.request_predicates, + response_predicates: self.configuration.response_predicates, extractors: extractors.build(), - policy: self.endpoint_config.policy, + policy: self.configuration.policy, }; CacheBuilder { backend: self.backend, - endpoint_config, + configuration, } } pub fn build(self) -> Cache { Cache { backend: Arc::new(self.backend.expect("Please add some cache backend")), - endpoint_config: Arc::new(self.endpoint_config), + configuration: Arc::new(self.configuration), } } } @@ -132,7 +132,7 @@ impl Default for CacheBuilder { fn default() -> Self { Self { backend: None, - endpoint_config: Default::default(), + configuration: Default::default(), } } } diff --git a/hitbox-tower/src/lib.rs b/hitbox-tower/src/lib.rs index cd6503e..4ff2110 100644 --- a/hitbox-tower/src/lib.rs +++ b/hitbox-tower/src/lib.rs @@ -1,7 +1,10 @@ +pub mod configurable; pub mod configuration; pub mod future; pub mod layer; pub mod service; +pub use crate::configuration::EndpointConfig; pub use ::http::{Method, StatusCode}; +pub use configurable::Configurable; pub use layer::Cache; diff --git a/hitbox-tower/src/service.rs b/hitbox-tower/src/service.rs index d93620b..c58e958 100644 --- a/hitbox-tower/src/service.rs +++ b/hitbox-tower/src/service.rs @@ -1,4 +1,4 @@ -use crate::configuration::EndpointConfig; +use crate::Configurable; use std::{fmt::Debug, sync::Arc}; use hitbox::{backend::CacheBackend, fsm::CacheFuture}; @@ -9,23 +9,23 @@ use tower::Service; use crate::future::Transformer; -pub struct CacheService { +pub struct CacheService { upstream: S, backend: Arc, - endpoint_config: Arc, + configuration: Arc, } -impl CacheService { - pub fn new(upstream: S, backend: Arc, endpoint_config: Arc) -> Self { +impl CacheService { + pub fn new(upstream: S, backend: Arc, configuration: Arc) -> Self { CacheService { upstream, backend, - endpoint_config, + configuration, } } } -impl Clone for CacheService +impl Clone for CacheService where S: Clone, B: Clone, @@ -33,17 +33,18 @@ where fn clone(&self) -> Self { Self { upstream: self.upstream.clone(), - backend: Arc::clone(&self.backend), - endpoint_config: Arc::clone(&self.endpoint_config), + backend: self.backend.clone(), + configuration: self.configuration.clone(), } } } -impl Service> for CacheService +impl Service> for CacheService where S: Service, Response = Response> + Clone + Send + 'static, B: CacheBackend + Clone + Send + Sync + 'static, S::Future: Send, + C: Configurable, // debug bounds ReqBody: Debug + HttpBody + Send + 'static, @@ -69,18 +70,16 @@ where } fn call(&mut self, req: Request) -> Self::Future { - //dbg!(&req); - let transformer = Transformer::new(self.upstream.clone()); - let config = &self.endpoint_config; + let configuration = &self.configuration; CacheFuture::new( self.backend.clone(), CacheableHttpRequest::from_request(req), transformer, - Arc::new(config.request_predicates()), - Arc::new(config.response_predicates()), - Arc::new(config.extractors()), - Arc::new(config.policy.clone()), //TODO: remove clone + Arc::new(configuration.request_predicates()), + Arc::new(configuration.response_predicates()), + Arc::new(configuration.extractors()), + Arc::new(configuration.policy()), ) } } From 07d84821303ccd0c44e2170b00d3a82637bdaf79 Mon Sep 17 00:00:00 2001 From: Andrey Ermilov Date: Sun, 27 Aug 2023 17:56:29 +0200 Subject: [PATCH 2/2] refactor(config): layer uses generic Configurable --- examples/examples/axum.rs | 29 +++-- hitbox-tower/src/configuration/builder.rs | 88 ++++++++++++++ hitbox-tower/src/configuration/endpoint.rs | 47 +++++++- .../src/configuration/extractors/request.rs | 2 +- hitbox-tower/src/configuration/mod.rs | 1 + .../src/configuration/predicates/request.rs | 2 +- .../src/configuration/predicates/response.rs | 2 +- hitbox-tower/src/layer.rs | 107 +++++------------- hitbox-tower/src/service.rs | 5 +- hitbox/src/policy.rs | 5 +- 10 files changed, 194 insertions(+), 94 deletions(-) create mode 100644 hitbox-tower/src/configuration/builder.rs diff --git a/examples/examples/axum.rs b/examples/examples/axum.rs index f9602f5..8db2503 100644 --- a/examples/examples/axum.rs +++ b/examples/examples/axum.rs @@ -6,7 +6,7 @@ use hitbox_tower::{ }; use hitbox_redis::RedisBackend; -use hitbox_tower::Cache; +use hitbox_tower::{Cache, EndpointConfig}; async fn handler_result(Path(_name): Path) -> Result { //dbg!("axum::handler_result"); @@ -41,9 +41,12 @@ async fn main() { .finish(); tracing::subscriber::set_global_default(subscriber).unwrap(); - let backend = RedisBackend::new().unwrap(); - let json_cache = Cache::builder() - .backend(backend) + let redis_backend = RedisBackend::new().unwrap(); + let inmemory_backend = hitbox_stretto::StrettoBackend::builder(10) + .finalize() + .unwrap(); + + let json_config = EndpointConfig::builder() .request( request::method(Method::GET) .query("cache", "true") @@ -53,14 +56,22 @@ async fn main() { .response(response::status_code(StatusCode::OK)) .cache_key(extractor::method().query("cache").path("/{path}*")) .build(); - let backend = hitbox_stretto::StrettoBackend::builder(10) - .finalize() - .unwrap(); - let health_check = Cache::builder() - .backend(backend) // FIX: it should work withod backend + + let health_config = EndpointConfig::builder() .request(request::path("/health").method(Method::GET)) .disable() .build(); + + let json_cache = Cache::builder() + .backend(redis_backend) + .config(json_config) + .build(); + + let health_check = Cache::builder() + .backend(inmemory_backend) // FIX: it should work withod backend + .config(health_config) + .build(); + let app = Router::new() .route("/greet/:name/", get(handler_result)) .route("/", get(handler)) diff --git a/hitbox-tower/src/configuration/builder.rs b/hitbox-tower/src/configuration/builder.rs new file mode 100644 index 0000000..ef7b887 --- /dev/null +++ b/hitbox-tower/src/configuration/builder.rs @@ -0,0 +1,88 @@ +use crate::configuration::{ + ExtractorBuilder, RequestExtractor, RequestPredicate, RequestPredicateBuilder, + ResponsePredicate, ResponsePredicateBuilder, +}; +use crate::EndpointConfig; +use hitbox::policy::PolicyConfig; + +#[derive(Debug)] +pub struct EndpointConfigBuilder { + pub request_predicates: Vec, + pub response_predicates: Vec, + pub extractors: Vec, + pub policy: PolicyConfig, +} + +impl EndpointConfigBuilder { + pub fn new() -> Self { + Self { + request_predicates: Vec::new(), + response_predicates: Vec::new(), + extractors: Vec::new(), + policy: Default::default(), + } + } + + pub fn disable(self) -> Self { + Self { + request_predicates: self.request_predicates, + response_predicates: self.response_predicates, + extractors: self.extractors, + policy: PolicyConfig::Disabled, + } + } + + pub fn request(self, predicates: RequestPredicateBuilder) -> Self { + Self { + request_predicates: predicates.build(), + response_predicates: self.response_predicates, + extractors: self.extractors, + policy: self.policy, + } + } + + pub fn response(self, predicates: ResponsePredicateBuilder) -> Self { + Self { + request_predicates: self.request_predicates, + response_predicates: predicates.build(), + extractors: self.extractors, + policy: self.policy, + } + } + + pub fn cache_key(self, extractors: ExtractorBuilder) -> Self { + Self { + request_predicates: self.request_predicates, + response_predicates: self.response_predicates, + extractors: extractors.build(), + policy: self.policy, + } + } + + pub fn build(self) -> EndpointConfig { + EndpointConfig { + request_predicates: self.request_predicates, + response_predicates: self.response_predicates, + extractors: self.extractors, + policy: self.policy, + } + } +} + +impl Default for EndpointConfigBuilder { + fn default() -> Self { + Self { + request_predicates: Vec::new(), + response_predicates: vec![ResponsePredicate::StatusCode { + code: http::StatusCode::OK, + }], + extractors: vec![ + RequestExtractor::Path { + path: String::from("{path}*"), + }, + RequestExtractor::Method, + ], + policy: Default::default(), + } + } +} diff --git a/hitbox-tower/src/configuration/endpoint.rs b/hitbox-tower/src/configuration/endpoint.rs index dca5375..7f6a3ec 100644 --- a/hitbox-tower/src/configuration/endpoint.rs +++ b/hitbox-tower/src/configuration/endpoint.rs @@ -1,4 +1,6 @@ -use crate::configuration::{RequestExtractor, RequestPredicate, ResponsePredicate}; +use crate::configuration::{ + builder::EndpointConfigBuilder, RequestExtractor, RequestPredicate, ResponsePredicate, +}; use crate::Configurable; use hitbox::policy::PolicyConfig; use hitbox::predicate::Predicate; @@ -14,8 +16,9 @@ use hitbox_http::predicates::{ }; use hitbox_http::{CacheableHttpRequest, CacheableHttpResponse}; use serde::{Deserialize, Serialize}; +use std::sync::Arc; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct EndpointConfig { pub request_predicates: Vec, pub response_predicates: Vec, @@ -32,6 +35,10 @@ impl EndpointConfig { policy: Default::default(), } } + + pub fn builder() -> EndpointConfigBuilder { + EndpointConfigBuilder::new() + } } impl Configurable for EndpointConfig { @@ -93,6 +100,42 @@ impl Configurable for EndpointConfig { } } +impl Configurable for Arc +where + C: Configurable, +{ + fn request_predicates( + &self, + ) -> Box> + Send + Sync> + where + ReqBody: Send + 'static, + { + self.as_ref().request_predicates() + } + + fn response_predicates( + &self, + ) -> Box> + Send + Sync> + where + ResBody: Send + 'static, + { + self.as_ref().response_predicates() + } + + fn extractors( + &self, + ) -> Box> + Send + Sync> + where + ReqBody: Send + 'static, + { + self.as_ref().extractors() + } + + fn policy(&self) -> PolicyConfig { + self.as_ref().policy() + } +} + impl Default for EndpointConfig { fn default() -> Self { Self { diff --git a/hitbox-tower/src/configuration/extractors/request.rs b/hitbox-tower/src/configuration/extractors/request.rs index 11fc3ec..0a02971 100644 --- a/hitbox-tower/src/configuration/extractors/request.rs +++ b/hitbox-tower/src/configuration/extractors/request.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum RequestExtractor { Path { path: String }, Method, diff --git a/hitbox-tower/src/configuration/mod.rs b/hitbox-tower/src/configuration/mod.rs index ea25e19..d7ac6c0 100644 --- a/hitbox-tower/src/configuration/mod.rs +++ b/hitbox-tower/src/configuration/mod.rs @@ -1,3 +1,4 @@ +pub mod builder; mod endpoint; mod extractors; mod predicates; diff --git a/hitbox-tower/src/configuration/predicates/request.rs b/hitbox-tower/src/configuration/predicates/request.rs index 368dfa0..fe749fb 100644 --- a/hitbox-tower/src/configuration/predicates/request.rs +++ b/hitbox-tower/src/configuration/predicates/request.rs @@ -1,7 +1,7 @@ use crate::configuration::serializers::method; use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum RequestPredicate { Path { path: String, diff --git a/hitbox-tower/src/configuration/predicates/response.rs b/hitbox-tower/src/configuration/predicates/response.rs index 4637cd1..e75f0e4 100644 --- a/hitbox-tower/src/configuration/predicates/response.rs +++ b/hitbox-tower/src/configuration/predicates/response.rs @@ -1,7 +1,7 @@ use crate::configuration::serializers::status_code; use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ResponsePredicate { #[serde(with = "status_code")] StatusCode { code: http::StatusCode }, diff --git a/hitbox-tower/src/layer.rs b/hitbox-tower/src/layer.rs index 05f1fcb..2ad27fb 100644 --- a/hitbox-tower/src/layer.rs +++ b/hitbox-tower/src/layer.rs @@ -1,134 +1,87 @@ -use crate::configuration::{ - EndpointConfig, ExtractorBuilder, RequestPredicateBuilder, ResponsePredicateBuilder, -}; +use crate::configuration::EndpointConfig; use std::sync::Arc; -use hitbox::{ - backend::CacheBackend, - policy::{EnabledCacheConfig, PolicyConfig}, -}; +use hitbox::backend::CacheBackend; use hitbox_stretto::StrettoBackend; use tower::Layer; use crate::service::CacheService; #[derive(Clone)] -pub struct Cache { +pub struct Cache { pub backend: Arc, - pub configuration: Arc, + pub configuration: C, } -impl Cache { - pub fn new(backend: B) -> Cache { +impl Cache +where + C: Default, +{ + pub fn new(backend: B) -> Cache { Cache { backend: Arc::new(backend), - configuration: Arc::new(Default::default()), + configuration: Default::default(), } } } -impl Layer for Cache { - type Service = CacheService; +impl Layer for Cache +where + C: Clone, +{ + type Service = CacheService; fn layer(&self, upstream: S) -> Self::Service { CacheService::new( upstream, Arc::clone(&self.backend), - Arc::clone(&self.configuration), + self.configuration.clone(), ) } } -impl Cache { - pub fn builder() -> CacheBuilder { +impl Cache { + pub fn builder() -> CacheBuilder { CacheBuilder::default() } } -pub struct CacheBuilder { +pub struct CacheBuilder { backend: Option, - configuration: EndpointConfig, + configuration: C, } -impl CacheBuilder +impl CacheBuilder where B: CacheBackend, + C: Default, { - pub fn backend(self, backend: NB) -> CacheBuilder { + pub fn backend(self, backend: NB) -> CacheBuilder { CacheBuilder { backend: Some(backend), configuration: self.configuration, } } - pub fn enable(self, policy: EnabledCacheConfig) -> Self { - let configuration = EndpointConfig { - request_predicates: self.configuration.request_predicates, - response_predicates: self.configuration.response_predicates, - extractors: self.configuration.extractors, - policy: PolicyConfig::Enabled(policy), - }; - CacheBuilder { - backend: self.backend, - configuration, - } - } - - pub fn disable(self) -> Self { - CacheBuilder { - backend: self.backend, - configuration: self.configuration, - } - } - - pub fn request(self, predicates: RequestPredicateBuilder) -> Self { - let configuration = EndpointConfig { - request_predicates: predicates.build(), - response_predicates: self.configuration.response_predicates, - extractors: self.configuration.extractors, - policy: self.configuration.policy, - }; - CacheBuilder { - backend: self.backend, - configuration, - } - } - - pub fn response(self, predicates: ResponsePredicateBuilder) -> Self { - let configuration = EndpointConfig { - request_predicates: self.configuration.request_predicates, - response_predicates: predicates.build(), - extractors: self.configuration.extractors, - policy: self.configuration.policy, - }; - CacheBuilder { - backend: self.backend, - configuration, - } - } - - pub fn cache_key(self, extractors: ExtractorBuilder) -> Self { - let configuration = EndpointConfig { - request_predicates: self.configuration.request_predicates, - response_predicates: self.configuration.response_predicates, - extractors: extractors.build(), - policy: self.configuration.policy, - }; + pub fn config(self, configuration: NC) -> CacheBuilder { CacheBuilder { backend: self.backend, configuration, } } - pub fn build(self) -> Cache { + pub fn build(self) -> Cache { Cache { backend: Arc::new(self.backend.expect("Please add some cache backend")), - configuration: Arc::new(self.configuration), + configuration: self.configuration, } } } -impl Default for CacheBuilder { +impl Default for CacheBuilder +where + C: Default, +{ fn default() -> Self { Self { backend: None, diff --git a/hitbox-tower/src/service.rs b/hitbox-tower/src/service.rs index c58e958..f88866e 100644 --- a/hitbox-tower/src/service.rs +++ b/hitbox-tower/src/service.rs @@ -12,11 +12,11 @@ use crate::future::Transformer; pub struct CacheService { upstream: S, backend: Arc, - configuration: Arc, + configuration: C, } impl CacheService { - pub fn new(upstream: S, backend: Arc, configuration: Arc) -> Self { + pub fn new(upstream: S, backend: Arc, configuration: C) -> Self { CacheService { upstream, backend, @@ -29,6 +29,7 @@ impl Clone for CacheService where S: Clone, B: Clone, + C: Clone, { fn clone(&self) -> Self { Self { diff --git a/hitbox/src/policy.rs b/hitbox/src/policy.rs index 449ae61..1c5ff2c 100644 --- a/hitbox/src/policy.rs +++ b/hitbox/src/policy.rs @@ -14,6 +14,9 @@ pub enum PolicyConfig { impl Default for PolicyConfig { fn default() -> Self { - Self::Enabled(Default::default()) + Self::Enabled(EnabledCacheConfig { + ttl: Some(5), + stale_cache: None, + }) } }