From 2a22347703a7a4d8ceab8e8f5f35c0d18dfd12bd Mon Sep 17 00:00:00 2001 From: Andrey Ermilov Date: Sun, 13 Aug 2023 18:28:35 +0200 Subject: [PATCH 1/6] feat(config): Add dummy backend --- examples/examples/axum.rs | 1 - hitbox-tower/Cargo.toml | 1 + hitbox-tower/src/dummy.rs | 36 ++++++++++++++++++++++++++++++++++++ hitbox-tower/src/future.rs | 4 +--- hitbox-tower/src/layer.rs | 11 +++++------ hitbox-tower/src/lib.rs | 1 + hitbox-tower/src/service.rs | 23 ++++------------------- 7 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 hitbox-tower/src/dummy.rs diff --git a/examples/examples/axum.rs b/examples/examples/axum.rs index 3542c2d..63108c2 100644 --- a/examples/examples/axum.rs +++ b/examples/examples/axum.rs @@ -42,7 +42,6 @@ async fn main() { let inmemory = StrettoBackend::builder(2 ^ 16) .finalize() .unwrap(); - // build our application with a single route let app = Router::new() .route("/greet/:name/", get(handler_result)) .route("/", get(handler)) diff --git a/hitbox-tower/Cargo.toml b/hitbox-tower/Cargo.toml index ae2f333..e570278 100644 --- a/hitbox-tower/Cargo.toml +++ b/hitbox-tower/Cargo.toml @@ -23,4 +23,5 @@ serde = "1" futures = "0.3" pin-project = "1" chrono = "0.4" +axum = "0.6" bytes = "1" diff --git a/hitbox-tower/src/dummy.rs b/hitbox-tower/src/dummy.rs new file mode 100644 index 0000000..d64cf48 --- /dev/null +++ b/hitbox-tower/src/dummy.rs @@ -0,0 +1,36 @@ +use axum::async_trait; +use hitbox_backend::{BackendResult, CacheBackend, CacheableResponse, CachedValue, DeleteStatus}; + +pub struct DummyBackend; + +#[async_trait] +impl CacheBackend for DummyBackend { + async fn get(&self, _key: String) -> BackendResult>> + where + T: CacheableResponse, + ::Cached: serde::de::DeserializeOwned, + { + unreachable!() + } + + async fn set( + &self, + _key: String, + _value: &CachedValue, + _ttl: Option, + ) -> BackendResult<()> + where + T: CacheableResponse + Send, + T::Cached: serde::Serialize + Send + Sync, + { + unreachable!() + } + + async fn delete(&self, _key: String) -> BackendResult { + unreachable!() + } + + async fn start(&self) -> BackendResult<()> { + unreachable!() + } +} diff --git a/hitbox-tower/src/future.rs b/hitbox-tower/src/future.rs index bd573bd..d605d0c 100644 --- a/hitbox-tower/src/future.rs +++ b/hitbox-tower/src/future.rs @@ -4,12 +4,10 @@ use std::{ task::{Context, Poll}, }; -use bytes::Bytes; -use futures::{future::BoxFuture, Future, FutureExt}; +use futures::{future::BoxFuture, Future}; use hitbox::fsm::Transform; use hitbox_http::{CacheableHttpRequest, CacheableHttpResponse, FromBytes}; use http::{Request, Response}; -use hyper::Body; use pin_project::pin_project; use tower::Service; diff --git a/hitbox-tower/src/layer.rs b/hitbox-tower/src/layer.rs index be8eb99..9c32ff9 100644 --- a/hitbox-tower/src/layer.rs +++ b/hitbox-tower/src/layer.rs @@ -1,10 +1,9 @@ -use std::{marker::PhantomData, sync::Arc}; +use std::sync::Arc; use hitbox::backend::CacheBackend; -use hitbox_redis::RedisBackend; use tower::Layer; -use crate::service::CacheService; +use crate::{service::CacheService, dummy::DummyBackend}; #[derive(Clone)] pub struct Cache { @@ -27,9 +26,9 @@ impl Layer for Cache { } } -impl Cache { - pub fn builder() -> CacheBuilder { - CacheBuilder::::default() +impl Cache { + pub fn builder() -> CacheBuilder { + CacheBuilder::::default() } } diff --git a/hitbox-tower/src/lib.rs b/hitbox-tower/src/lib.rs index 8e6217c..587f819 100644 --- a/hitbox-tower/src/lib.rs +++ b/hitbox-tower/src/lib.rs @@ -1,3 +1,4 @@ +pub mod dummy; pub mod future; pub mod layer; pub mod service; diff --git a/hitbox-tower/src/service.rs b/hitbox-tower/src/service.rs index 89e9d87..0169b17 100644 --- a/hitbox-tower/src/service.rs +++ b/hitbox-tower/src/service.rs @@ -1,32 +1,17 @@ -use std::{fmt::Debug, marker::PhantomData, pin::Pin, sync::Arc}; +use std::{fmt::Debug, sync::Arc}; -use bytes::Bytes; -use chrono::{Duration, Utc}; -use futures::{ - future::{BoxFuture, Map}, - Future, FutureExt, -}; -use hitbox::{ - backend::{BackendError, CacheBackend}, - fsm::{CacheFuture, Transform}, - Cacheable, CachedValue, -}; -use hitbox_backend::CacheableResponse; +use hitbox::{backend::CacheBackend, fsm::CacheFuture}; use hitbox_http::{ extractors::NeutralExtractor, extractors::{method::MethodExtractor, path::PathExtractor}, predicates::{query::QueryPredicate, NeutralPredicate, NeutralResponsePredicate}, - CacheableHttpRequest, CacheableHttpResponse, FromBytes, SerializableHttpResponse, + CacheableHttpRequest, CacheableHttpResponse, FromBytes, }; use http::{Request, Response}; use hyper::body::{Body, HttpBody}; -use serde::{de::DeserializeOwned, Serialize}; use tower::Service; -use hitbox::fsm::CacheFuture3; -use tracing::log::warn; - -use crate::future::{Transformer, UpstreamFuture}; +use crate::future::Transformer; pub struct CacheService { upstream: S, From 8a135c20c1a96a84e0afb439f3c0e2dd7a139cd3 Mon Sep 17 00:00:00 2001 From: Andrey Ermilov Date: Sun, 13 Aug 2023 23:50:49 +0200 Subject: [PATCH 2/6] feat(config): Add predicates to layer --- examples/examples/axum.rs | 29 +++++++++++++++++++++++++++-- hitbox-tower/src/layer.rs | 35 ++++++++++++++++++++++++----------- hitbox/src/lib.rs | 2 +- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/examples/examples/axum.rs b/examples/examples/axum.rs index 63108c2..c77532f 100644 --- a/examples/examples/axum.rs +++ b/examples/examples/axum.rs @@ -42,14 +42,39 @@ async fn main() { let inmemory = StrettoBackend::builder(2 ^ 16) .finalize() .unwrap(); + let request_predicate = predicate::RequestBuilder::new() + .query("cache", "true") + .build(); + let response_predicate = predicate::ResponseBuilder::new() + .status_code(200) + .body(operations::NE(operations::EmptyVec)) + .build(); + let cache_key = CacheKeyBuilder::new() + .path(Full) + .method() + .build(); + let endpoint_config = Config::builder() + .request_predicate(response_predicate) + .response_predicate(response_predicate) + .cache_key(cache_key) + .build(); let app = Router::new() .route("/greet/:name/", get(handler_result)) .route("/", get(handler)) .route("/json/", get(handler_json)) .layer( ServiceBuilder::new() - .layer(Cache::builder().backend(inmemory).build()) - .layer(Cache::builder().backend(backend).build()), + .layer( + Cache::builder() + .config(config) + .backend(inmemory) + .build() + ) + .layer( + Cache::builder() + .config(config) + .backend(backend) + .build()), ); // run it with hyper on localhost:3000 diff --git a/hitbox-tower/src/layer.rs b/hitbox-tower/src/layer.rs index 9c32ff9..daf7790 100644 --- a/hitbox-tower/src/layer.rs +++ b/hitbox-tower/src/layer.rs @@ -1,24 +1,37 @@ use std::sync::Arc; -use hitbox::backend::CacheBackend; +use hitbox::{backend::CacheBackend, predicates::Predicate, Extractor}; +use hitbox_http::{ + extractors::NeutralExtractor, + extractors::{method::MethodExtractor, path::PathExtractor}, + predicates::{query::QueryPredicate, NeutralPredicate, NeutralResponsePredicate}, + CacheableHttpRequest, CacheableHttpResponse, FromBytes, +}; +use http::{Request, Response}; use tower::Layer; -use crate::{service::CacheService, dummy::DummyBackend}; +use crate::{dummy::DummyBackend, service::CacheService}; #[derive(Clone)] -pub struct Cache { +pub struct Cache { pub backend: Arc, + request_predicates: Arc + Send + Sync>, + response_predicates: Arc + Send + Sync>, + key_extractors: Arc + Send + Sync>, } -impl Cache { - pub fn new(backend: B) -> Cache { +impl Cache { + pub fn new(backend: B) -> Cache { Cache { backend: Arc::new(backend), + request_predicates: Arc::new(NeutralPredicate::new()), + response_predicates: Arc::new(NeutralResponsePredicate::new()), + key_extractors: Arc::new(NeutralExtractor::new()), } } } -impl Layer for Cache { +impl Layer for Cache { type Service = CacheService; fn layer(&self, upstream: S) -> Self::Service { @@ -26,17 +39,17 @@ impl Layer for Cache { } } -impl Cache { - pub fn builder() -> CacheBuilder { +impl Cache { + pub fn builder() -> CacheBuilder { CacheBuilder::::default() } } -pub struct CacheBuilder { +pub struct CacheBuilder { backend: Option, } -impl CacheBuilder +impl CacheBuilder where B: CacheBackend, { @@ -46,7 +59,7 @@ where } } - pub fn build(self) -> Cache { + pub fn build(self) -> Cache { Cache { backend: Arc::new(self.backend.expect("Please add some cache backend")), } diff --git a/hitbox/src/lib.rs b/hitbox/src/lib.rs index b20580d..e1be892 100644 --- a/hitbox/src/lib.rs +++ b/hitbox/src/lib.rs @@ -72,10 +72,10 @@ pub mod fsm; #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] pub mod metrics; pub mod predicates; - pub use cache::Cacheable; pub use error::CacheError; pub use hitbox_backend::{CachePolicy, CacheState, CacheableResponse, CachedValue}; +pub use cache::Extractor; /// The `hitbox` prelude. pub mod prelude { From 77a64bd280f9cddb5ce3d5fdc88a70fd173b542f Mon Sep 17 00:00:00 2001 From: Andrey Ermilov Date: Mon, 14 Aug 2023 00:42:42 +0200 Subject: [PATCH 3/6] feat(config): Add endpoint config --- examples/examples/axum.rs | 36 ++++++++++++++++++------------------ hitbox-tower/src/config.rs | 11 +++++++++++ hitbox-tower/src/layer.rs | 26 ++++++++++---------------- hitbox-tower/src/lib.rs | 1 + hitbox-tower/src/service.rs | 19 ++++++++++++------- 5 files changed, 52 insertions(+), 41 deletions(-) create mode 100644 hitbox-tower/src/config.rs diff --git a/examples/examples/axum.rs b/examples/examples/axum.rs index c77532f..9660f75 100644 --- a/examples/examples/axum.rs +++ b/examples/examples/axum.rs @@ -42,22 +42,22 @@ async fn main() { let inmemory = StrettoBackend::builder(2 ^ 16) .finalize() .unwrap(); - let request_predicate = predicate::RequestBuilder::new() - .query("cache", "true") - .build(); - let response_predicate = predicate::ResponseBuilder::new() - .status_code(200) - .body(operations::NE(operations::EmptyVec)) - .build(); - let cache_key = CacheKeyBuilder::new() - .path(Full) - .method() - .build(); - let endpoint_config = Config::builder() - .request_predicate(response_predicate) - .response_predicate(response_predicate) - .cache_key(cache_key) - .build(); + //let request_predicate = predicate::RequestBuilder::new() + //.query("cache", "true") + //.build(); + //let response_predicate = predicate::ResponseBuilder::new() + //.status_code(200) + //.body(operations::NE(operations::EmptyVec)) + //.build(); + //let cache_key = CacheKeyBuilder::new() + //.path(Full) + //.method() + //.build(); + //let endpoint_config = Config::builder() + //.request_predicate(response_predicate) + //.response_predicate(response_predicate) + //.cache_key(cache_key) + //.build(); let app = Router::new() .route("/greet/:name/", get(handler_result)) .route("/", get(handler)) @@ -66,13 +66,13 @@ async fn main() { ServiceBuilder::new() .layer( Cache::builder() - .config(config) + //.config(config) .backend(inmemory) .build() ) .layer( Cache::builder() - .config(config) + //.config(config) .backend(backend) .build()), ); diff --git a/hitbox-tower/src/config.rs b/hitbox-tower/src/config.rs new file mode 100644 index 0000000..af3d94a --- /dev/null +++ b/hitbox-tower/src/config.rs @@ -0,0 +1,11 @@ +#[derive(Clone)] +pub struct Config { + pub query: (String, String), +} + +impl Config { + pub fn new() -> Self { + Self { query: (String::from("key"), String::from("value")) } + } +} + diff --git a/hitbox-tower/src/layer.rs b/hitbox-tower/src/layer.rs index daf7790..d2ccd83 100644 --- a/hitbox-tower/src/layer.rs +++ b/hitbox-tower/src/layer.rs @@ -13,43 +13,37 @@ use tower::Layer; use crate::{dummy::DummyBackend, service::CacheService}; #[derive(Clone)] -pub struct Cache { +pub struct Cache { pub backend: Arc, - request_predicates: Arc + Send + Sync>, - response_predicates: Arc + Send + Sync>, - key_extractors: Arc + Send + Sync>, } -impl Cache { - pub fn new(backend: B) -> Cache { +impl Cache { + pub fn new(backend: B) -> Cache { Cache { backend: Arc::new(backend), - request_predicates: Arc::new(NeutralPredicate::new()), - response_predicates: Arc::new(NeutralResponsePredicate::new()), - key_extractors: Arc::new(NeutralExtractor::new()), } } } -impl Layer for Cache { +impl Layer for Cache { type Service = CacheService; fn layer(&self, upstream: S) -> Self::Service { - CacheService::new(upstream, Arc::clone(&self.backend)) + CacheService::new(upstream, Arc::clone(&self.backend), crate::config::Config::new()) } } -impl Cache { - pub fn builder() -> CacheBuilder { +impl Cache { + pub fn builder() -> CacheBuilder { CacheBuilder::::default() } } -pub struct CacheBuilder { +pub struct CacheBuilder { backend: Option, } -impl CacheBuilder +impl CacheBuilder where B: CacheBackend, { @@ -59,7 +53,7 @@ where } } - pub fn build(self) -> Cache { + pub fn build(self) -> Cache { Cache { backend: Arc::new(self.backend.expect("Please add some cache backend")), } diff --git a/hitbox-tower/src/lib.rs b/hitbox-tower/src/lib.rs index 587f819..9610bed 100644 --- a/hitbox-tower/src/lib.rs +++ b/hitbox-tower/src/lib.rs @@ -2,5 +2,6 @@ pub mod dummy; pub mod future; pub mod layer; pub mod service; +pub mod config; pub use layer::Cache; diff --git a/hitbox-tower/src/service.rs b/hitbox-tower/src/service.rs index 0169b17..25eca86 100644 --- a/hitbox-tower/src/service.rs +++ b/hitbox-tower/src/service.rs @@ -1,3 +1,4 @@ +use crate::config::Config; use std::{fmt::Debug, sync::Arc}; use hitbox::{backend::CacheBackend, fsm::CacheFuture}; @@ -16,11 +17,12 @@ use crate::future::Transformer; pub struct CacheService { upstream: S, backend: Arc, + config: Config, } impl CacheService { - pub fn new(upstream: S, backend: Arc) -> Self { - CacheService { upstream, backend } + pub fn new(upstream: S, backend: Arc, config: Config) -> Self { + CacheService { upstream, backend, config } } } @@ -33,6 +35,7 @@ where Self { upstream: self.upstream.clone(), backend: Arc::clone(&self.backend), + config: self.config.clone(), } } } @@ -70,15 +73,17 @@ where dbg!(&req); let transformer = Transformer::new(self.upstream.clone()); + let config = self.config.clone(); + let request_predicate = NeutralPredicate::new().query(config.query.0, config.query.1); + let response_predicate = NeutralResponsePredicate::new(); + let extractor = NeutralExtractor::new().method().path("/{path}*"); CacheFuture::new( self.backend.clone(), CacheableHttpRequest::from_request(req), transformer, - Arc::new(Box::new( - NeutralPredicate::new().query("cache".to_owned(), "true".to_owned()), - )), - Arc::new(NeutralResponsePredicate::new()), - Arc::new(NeutralExtractor::new().method().path("/{path}*")), + Arc::new(request_predicate), + Arc::new(response_predicate), + Arc::new(extractor), ) } } From ee60cc74cb84906795c0bf48d848d703352f870e Mon Sep 17 00:00:00 2001 From: Andrey Ermilov Date: Mon, 14 Aug 2023 21:00:05 +0200 Subject: [PATCH 4/6] feat(config): Intermediate result --- examples/examples/axum.rs | 47 ++++++++++++++--------------- examples/examples/tower.rs | 2 +- hitbox-http/src/lib.rs | 2 +- hitbox-http/src/predicates/query.rs | 6 ++++ hitbox-tower/src/config.rs | 35 +++++++++++++++++---- hitbox-tower/src/layer.rs | 26 ++++++++++------ hitbox-tower/src/lib.rs | 2 +- hitbox-tower/src/service.rs | 19 +++++++----- hitbox/src/fsm/future.rs | 11 ++++--- hitbox/src/lib.rs | 2 +- 10 files changed, 96 insertions(+), 56 deletions(-) diff --git a/examples/examples/axum.rs b/examples/examples/axum.rs index 9660f75..778fe85 100644 --- a/examples/examples/axum.rs +++ b/examples/examples/axum.rs @@ -39,42 +39,41 @@ async fn main() { tracing::subscriber::set_global_default(subscriber).unwrap(); let backend = RedisBackend::new().unwrap(); - let inmemory = StrettoBackend::builder(2 ^ 16) - .finalize() - .unwrap(); + let inmemory = StrettoBackend::builder(2 ^ 16).finalize().unwrap(); //let request_predicate = predicate::RequestBuilder::new() - //.query("cache", "true") - //.build(); + //.query("cache", "true") + //.build(); //let response_predicate = predicate::ResponseBuilder::new() - //.status_code(200) - //.body(operations::NE(operations::EmptyVec)) - //.build(); + //.status_code(200) + //.body(operations::NE(operations::EmptyVec)) + //.build(); //let cache_key = CacheKeyBuilder::new() - //.path(Full) - //.method() - //.build(); + //.path(Full) + //.method() + //.build(); //let endpoint_config = Config::builder() - //.request_predicate(response_predicate) - //.response_predicate(response_predicate) - //.cache_key(cache_key) - //.build(); + //.request_predicate(response_predicate) + //.response_predicate(response_predicate) + //.cache_key(cache_key) + //.build(); let app = Router::new() .route("/greet/:name/", get(handler_result)) .route("/", get(handler)) .route("/json/", get(handler_json)) .layer( ServiceBuilder::new() + //.layer( + //Cache::builder() + ////.config(config) + //.backend(inmemory) + //.build(), + //) .layer( Cache::builder() - //.config(config) - .backend(inmemory) - .build() - ) - .layer( - Cache::builder() - //.config(config) - .backend(backend) - .build()), + //.config(config) + .backend(backend) + .build(), + ), ); // run it with hyper on localhost:3000 diff --git a/examples/examples/tower.rs b/examples/examples/tower.rs index 33b49d5..e4a8e65 100644 --- a/examples/examples/tower.rs +++ b/examples/examples/tower.rs @@ -1,6 +1,6 @@ -use hitbox_stretto::StrettoBackend; use hitbox_redis::RedisBackend; use hitbox_stretto::builder::StrettoBackendBuilder; +use hitbox_stretto::StrettoBackend; use hitbox_tower::Cache; use hyper::{Body, Server}; use std::{convert::Infallible, net::SocketAddr}; diff --git a/hitbox-http/src/lib.rs b/hitbox-http/src/lib.rs index 3403d5c..a762625 100644 --- a/hitbox-http/src/lib.rs +++ b/hitbox-http/src/lib.rs @@ -1,7 +1,7 @@ mod body; pub mod extractors; pub mod predicates; -mod query; +pub mod query; mod request; mod response; diff --git a/hitbox-http/src/predicates/query.rs b/hitbox-http/src/predicates/query.rs index 73b91e4..432226b 100644 --- a/hitbox-http/src/predicates/query.rs +++ b/hitbox-http/src/predicates/query.rs @@ -9,6 +9,12 @@ pub struct Query

{ inner: P, } +impl

Query

{ + pub fn new(name: String, value: crate::query::Value, operation: Operation, inner: P) -> Self { + Self { name, value, operation, inner } + } +} + pub trait QueryPredicate: Sized { fn query(self, name: String, value: String) -> Query; } diff --git a/hitbox-tower/src/config.rs b/hitbox-tower/src/config.rs index af3d94a..769a6b2 100644 --- a/hitbox-tower/src/config.rs +++ b/hitbox-tower/src/config.rs @@ -1,11 +1,34 @@ -#[derive(Clone)] -pub struct Config { - pub query: (String, String), +use hitbox_http::CacheableHttpRequest; +use hitbox::predicates::Predicate; +use hitbox_http::predicates::{NeutralPredicate, query::QueryPredicate}; + +pub enum RequestPredicate { + Query { key: String, value: String }, +} + +pub struct EndpointConfig { + pub request_predicates: Vec, } -impl Config { - pub fn new() -> Self { - Self { query: (String::from("key"), String::from("value")) } +impl EndpointConfig { + pub fn create(&self) -> Box> + Send + Sync> + where + ReqBody: Send + 'static, + { + let acc_predicate = Box::new(NeutralPredicate::new()); + self + .request_predicates + .iter() + .rfold(acc_predicate, |inner, predicate| match predicate { + RequestPredicate::Query { key, value } => Box::new(inner.query(key.clone(), value.clone())) + }) } } +impl Default for EndpointConfig { + fn default() -> Self { + Self { + request_predicates: vec![ RequestPredicate::Query { key: String::from("balala"), value: String::from("true") }], + } + } +} diff --git a/hitbox-tower/src/layer.rs b/hitbox-tower/src/layer.rs index d2ccd83..cf3ca3a 100644 --- a/hitbox-tower/src/layer.rs +++ b/hitbox-tower/src/layer.rs @@ -1,13 +1,7 @@ +use crate::config::EndpointConfig; use std::sync::Arc; -use hitbox::{backend::CacheBackend, predicates::Predicate, Extractor}; -use hitbox_http::{ - extractors::NeutralExtractor, - extractors::{method::MethodExtractor, path::PathExtractor}, - predicates::{query::QueryPredicate, NeutralPredicate, NeutralResponsePredicate}, - CacheableHttpRequest, CacheableHttpResponse, FromBytes, -}; -use http::{Request, Response}; +use hitbox::backend::CacheBackend; use tower::Layer; use crate::{dummy::DummyBackend, service::CacheService}; @@ -15,12 +9,14 @@ use crate::{dummy::DummyBackend, service::CacheService}; #[derive(Clone)] pub struct Cache { pub backend: Arc, + pub endpoint_config: Arc, } impl Cache { pub fn new(backend: B) -> Cache { Cache { backend: Arc::new(backend), + endpoint_config: Arc::new(Default::default()), } } } @@ -29,7 +25,11 @@ impl Layer for Cache { type Service = CacheService; fn layer(&self, upstream: S) -> Self::Service { - CacheService::new(upstream, Arc::clone(&self.backend), crate::config::Config::new()) + CacheService::new( + upstream, + Arc::clone(&self.backend), + Arc::new(Default::default()), + ) } } @@ -41,6 +41,7 @@ impl Cache { pub struct CacheBuilder { backend: Option, + endpoint_config: Option, } impl CacheBuilder @@ -50,18 +51,23 @@ where pub fn backend(self, backend: NB) -> CacheBuilder { CacheBuilder { backend: Some(backend), + endpoint_config: self.endpoint_config, } } pub fn build(self) -> Cache { Cache { backend: Arc::new(self.backend.expect("Please add some cache backend")), + endpoint_config: Arc::new(self.endpoint_config.unwrap_or_default()), } } } impl Default for CacheBuilder { fn default() -> Self { - Self { backend: None } + Self { + backend: None, + endpoint_config: Default::default(), + } } } diff --git a/hitbox-tower/src/lib.rs b/hitbox-tower/src/lib.rs index 9610bed..1313aec 100644 --- a/hitbox-tower/src/lib.rs +++ b/hitbox-tower/src/lib.rs @@ -1,7 +1,7 @@ +pub mod config; pub mod dummy; pub mod future; pub mod layer; pub mod service; -pub mod config; pub use layer::Cache; diff --git a/hitbox-tower/src/service.rs b/hitbox-tower/src/service.rs index 25eca86..6b084ac 100644 --- a/hitbox-tower/src/service.rs +++ b/hitbox-tower/src/service.rs @@ -1,4 +1,4 @@ -use crate::config::Config; +use crate::config::EndpointConfig; use std::{fmt::Debug, sync::Arc}; use hitbox::{backend::CacheBackend, fsm::CacheFuture}; @@ -17,12 +17,16 @@ use crate::future::Transformer; pub struct CacheService { upstream: S, backend: Arc, - config: Config, + endpoint_config: Arc, } impl CacheService { - pub fn new(upstream: S, backend: Arc, config: Config) -> Self { - CacheService { upstream, backend, config } + pub fn new(upstream: S, backend: Arc, endpoint_config: Arc) -> Self { + CacheService { + upstream, + backend, + endpoint_config, + } } } @@ -35,7 +39,7 @@ where Self { upstream: self.upstream.clone(), backend: Arc::clone(&self.backend), - config: self.config.clone(), + endpoint_config: Arc::clone(&self.endpoint_config), } } } @@ -73,15 +77,14 @@ where dbg!(&req); let transformer = Transformer::new(self.upstream.clone()); - let config = self.config.clone(); - let request_predicate = NeutralPredicate::new().query(config.query.0, config.query.1); + let config = &self.endpoint_config; let response_predicate = NeutralResponsePredicate::new(); let extractor = NeutralExtractor::new().method().path("/{path}*"); CacheFuture::new( self.backend.clone(), CacheableHttpRequest::from_request(req), transformer, - Arc::new(request_predicate), + Arc::new(config.create()), Arc::new(response_predicate), Arc::new(extractor), ) diff --git a/hitbox/src/fsm/future.rs b/hitbox/src/fsm/future.rs index 6dbe1c6..c52ed22 100644 --- a/hitbox/src/fsm/future.rs +++ b/hitbox/src/fsm/future.rs @@ -312,6 +312,7 @@ where } => { let policy = ready!(cache_policy_future.poll(cx)); trace!("{policy:?}"); + dbg!(&policy); match policy { crate::cache::CachePolicy::Cacheable { key, request } => { let backend = this.backend.clone(); @@ -372,10 +373,12 @@ where StateProj::UpstreamPolled { upstream_result } => { let upstream_result = upstream_result.take().expect(POLL_AFTER_READY_ERROR); let predicates = this.response_predicates.clone(); - let cache_policy = - Box::pin(async move { upstream_result.cache_policy(predicates).await }); - State::CheckResponseCachePolicy { cache_policy } - // return Poll::Ready(this.transformer.response_transform(upstream_result)); + match this.cache_key { + Some(_cache_key) => State::CheckResponseCachePolicy { + cache_policy: Box::pin(async move { upstream_result.cache_policy(predicates).await }) + }, + None => State::Response { response: Some(upstream_result) } + } } StateProj::CheckResponseCachePolicy { cache_policy } => { let policy = ready!(cache_policy.poll(cx)); diff --git a/hitbox/src/lib.rs b/hitbox/src/lib.rs index e1be892..f63c8e6 100644 --- a/hitbox/src/lib.rs +++ b/hitbox/src/lib.rs @@ -73,9 +73,9 @@ pub mod fsm; pub mod metrics; pub mod predicates; pub use cache::Cacheable; +pub use cache::Extractor; pub use error::CacheError; pub use hitbox_backend::{CachePolicy, CacheState, CacheableResponse, CachedValue}; -pub use cache::Extractor; /// The `hitbox` prelude. pub mod prelude { From 52c156ea4274f3b3525709bad449a2967cc826e6 Mon Sep 17 00:00:00 2001 From: Andrey Ermilov Date: Tue, 15 Aug 2023 20:22:23 +0200 Subject: [PATCH 5/6] feat(config): Add extractors --- hitbox-http/src/predicates/method.rs | 46 +++++++++ hitbox-http/src/predicates/mod.rs | 7 +- hitbox-http/src/predicates/path.rs | 23 ++--- hitbox-http/src/predicates/query.rs | 7 +- hitbox-http/src/predicates/status_code.rs | 46 +++++++++ hitbox-http/src/response.rs | 4 +- hitbox-tower/src/config.rs | 119 ++++++++++++++++++++-- hitbox-tower/src/service.rs | 10 +- 8 files changed, 226 insertions(+), 36 deletions(-) create mode 100644 hitbox-http/src/predicates/method.rs create mode 100644 hitbox-http/src/predicates/status_code.rs diff --git a/hitbox-http/src/predicates/method.rs b/hitbox-http/src/predicates/method.rs new file mode 100644 index 0000000..bcedf3e --- /dev/null +++ b/hitbox-http/src/predicates/method.rs @@ -0,0 +1,46 @@ +use crate::CacheableHttpRequest; +use async_trait::async_trait; +use hitbox::predicates::{Predicate, PredicateResult}; + +pub struct Method

{ + method: http::Method, + inner: P, +} + +pub trait MethodPredicate: Sized { + fn method(self, method: http::Method) -> Method; +} + +impl

MethodPredicate for P +where + P: Predicate, +{ + fn method(self, method: http::Method) -> Method { + Method { + method, + inner: self, + } + } +} + +#[async_trait] +impl Predicate for Method

+where + P: Predicate> + Send + Sync, + ReqBody: Send + 'static, +{ + type Subject = P::Subject; + + async fn check(&self, request: Self::Subject) -> PredicateResult { + match self.inner.check(request).await { + PredicateResult::Cacheable(request) => { + if self.method == request.parts().method { + PredicateResult::Cacheable(request) + } else { + PredicateResult::NonCacheable(request) + } + } + PredicateResult::NonCacheable(request) => PredicateResult::NonCacheable(request), + } + } +} diff --git a/hitbox-http/src/predicates/mod.rs b/hitbox-http/src/predicates/mod.rs index 51634fa..de255c1 100644 --- a/hitbox-http/src/predicates/mod.rs +++ b/hitbox-http/src/predicates/mod.rs @@ -1,7 +1,4 @@ -use std::{ - marker::PhantomData, - sync::{atomic::AtomicPtr, Arc, RwLock}, -}; +use std::{marker::PhantomData, sync::atomic::AtomicPtr}; use async_trait::async_trait; use hitbox_backend::predicates::{Predicate, PredicateResult}; @@ -10,8 +7,10 @@ use crate::{CacheableHttpRequest, CacheableHttpResponse}; pub mod body; pub mod header; +pub mod method; pub mod path; pub mod query; +pub mod status_code; pub struct NeutralPredicate { _req: PhantomData>>, // FIX: NOT HEHE diff --git a/hitbox-http/src/predicates/path.rs b/hitbox-http/src/predicates/path.rs index 10ef311..c644a6d 100644 --- a/hitbox-http/src/predicates/path.rs +++ b/hitbox-http/src/predicates/path.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use crate::CacheableHttpRequest; use actix_router::ResourceDef; use async_trait::async_trait; @@ -11,16 +9,16 @@ pub struct Path

{ } pub trait PathPredicate: Sized { - fn path(self, resource: ResourceDef) -> Path; + fn path(self, resource: String) -> Path; } impl

PathPredicate for P where P: Predicate, { - fn path(self, resource: ResourceDef) -> Path { + fn path(self, resource: String) -> Path { Path { - resource, + resource: ResourceDef::from(resource), inner: self, } } @@ -36,16 +34,13 @@ where async fn check(&self, request: Self::Subject) -> PredicateResult { match self.inner.check(request).await { - PredicateResult::Cacheable(request) => match self.inner.check(request).await { - PredicateResult::Cacheable(request) => { - if self.resource.is_match(request.parts().uri.path()) { - PredicateResult::Cacheable(request) - } else { - PredicateResult::NonCacheable(request) - } + PredicateResult::Cacheable(request) => { + if self.resource.is_match(request.parts().uri.path()) { + PredicateResult::Cacheable(request) + } else { + PredicateResult::NonCacheable(request) } - PredicateResult::NonCacheable(request) => PredicateResult::NonCacheable(request), - }, + } PredicateResult::NonCacheable(request) => PredicateResult::NonCacheable(request), } } diff --git a/hitbox-http/src/predicates/query.rs b/hitbox-http/src/predicates/query.rs index 432226b..be24d23 100644 --- a/hitbox-http/src/predicates/query.rs +++ b/hitbox-http/src/predicates/query.rs @@ -11,7 +11,12 @@ pub struct Query

{ impl

Query

{ pub fn new(name: String, value: crate::query::Value, operation: Operation, inner: P) -> Self { - Self { name, value, operation, inner } + Self { + name, + value, + operation, + inner, + } } } diff --git a/hitbox-http/src/predicates/status_code.rs b/hitbox-http/src/predicates/status_code.rs new file mode 100644 index 0000000..7f7b194 --- /dev/null +++ b/hitbox-http/src/predicates/status_code.rs @@ -0,0 +1,46 @@ +use crate::CacheableHttpResponse; +use async_trait::async_trait; +use hitbox::predicates::{Predicate, PredicateResult}; + +pub struct StatusCode

{ + status_code: http::StatusCode, + inner: P, +} + +pub trait StatusCodePredicate: Sized { + fn status_code(self, status_code: http::StatusCode) -> StatusCode; +} + +impl

StatusCodePredicate for P +where + P: Predicate, +{ + fn status_code(self, status_code: http::StatusCode) -> StatusCode { + StatusCode { + status_code, + inner: self, + } + } +} + +#[async_trait] +impl Predicate for StatusCode

+where + P: Predicate> + Send + Sync, + ReqBody: Send + 'static, +{ + type Subject = P::Subject; + + async fn check(&self, response: Self::Subject) -> PredicateResult { + match self.inner.check(response).await { + PredicateResult::Cacheable(response) => { + if self.status_code == response.parts.status { + PredicateResult::Cacheable(response) + } else { + PredicateResult::NonCacheable(response) + } + } + PredicateResult::NonCacheable(response) => PredicateResult::NonCacheable(response), + } + } +} diff --git a/hitbox-http/src/response.rs b/hitbox-http/src/response.rs index 35f43fd..63e1133 100644 --- a/hitbox-http/src/response.rs +++ b/hitbox-http/src/response.rs @@ -28,8 +28,8 @@ where } pub struct CacheableHttpResponse { - parts: Parts, - body: ResponseBody, + pub parts: Parts, + pub body: ResponseBody, } impl CacheableHttpResponse diff --git a/hitbox-tower/src/config.rs b/hitbox-tower/src/config.rs index 769a6b2..244fc84 100644 --- a/hitbox-tower/src/config.rs +++ b/hitbox-tower/src/config.rs @@ -1,26 +1,118 @@ -use hitbox_http::CacheableHttpRequest; use hitbox::predicates::Predicate; -use hitbox_http::predicates::{NeutralPredicate, query::QueryPredicate}; +use hitbox::Extractor; +use hitbox_http::extractors::NeutralExtractor; +use hitbox_http::extractors::{ + header::HeaderExtractor, method::MethodExtractor, path::PathExtractor, query::QueryExtractor, +}; +use hitbox_http::predicates::{ + header::HeaderPredicate, + method::MethodPredicate, + path::PathPredicate, + //body::BodyPredicate, + query::QueryPredicate, + status_code::StatusCodePredicate, + NeutralPredicate, + NeutralResponsePredicate, +}; +use hitbox_http::{CacheableHttpRequest, CacheableHttpResponse}; pub enum RequestPredicate { + Path { path: String }, + Method { method: http::Method }, + Header { key: String, value: String }, Query { key: String, value: String }, + //Body { statement: String }, +} + +pub enum ResponsePredicate { + StatusCode { code: http::StatusCode }, + //Body { statement: String }, +} + +pub enum RequestExtractor { + Path { path: String }, + Method, + Header { key: String }, + Query { key: String }, + //Body { statement: String }, } pub struct EndpointConfig { pub request_predicates: Vec, + pub response_predicates: Vec, + pub extractors: Vec, } impl EndpointConfig { - pub fn create(&self) -> Box> + Send + Sync> - where - ReqBody: Send + 'static, + pub fn new() -> Self { + Self { + request_predicates: Vec::new(), + response_predicates: Vec::new(), + extractors: Vec::new(), + } + } + + pub fn with_request_predicate(self, predicate: RequestPredicate) -> Self { + self + } + + pub fn with_response_predicate(self, predicate: ResponsePredicate) -> Self { + self + } + + pub fn with_cache_key(self, extractor: RequestExtractor) -> Self { + self + } + + pub(crate) fn request_predicates( + &self, + ) -> Box> + Send + Sync> + where + ReqBody: Send + 'static, { let acc_predicate = Box::new(NeutralPredicate::new()); - self - .request_predicates + self.request_predicates + .iter() + .rfold(acc_predicate, |inner, predicate| match predicate { + RequestPredicate::Path { path } => Box::new(inner.path(path.clone())), + RequestPredicate::Query { key, value } => { + Box::new(inner.query(key.clone(), value.clone())) + } + RequestPredicate::Header { key, value } => { + Box::new(inner.header(key.clone(), value.clone())) + } + RequestPredicate::Method { method } => Box::new(inner.method(method.clone())), + }) + } + + pub(crate) fn response_predicates( + &self, + ) -> Box> + Send + Sync> + where + ResBody: Send + 'static, + { + let acc_predicate = Box::new(NeutralResponsePredicate::new()); + self.response_predicates .iter() .rfold(acc_predicate, |inner, predicate| match predicate { - RequestPredicate::Query { key, value } => Box::new(inner.query(key.clone(), value.clone())) + ResponsePredicate::StatusCode { code } => Box::new(inner.status_code(*code)), + }) + } + + pub(crate) fn extractors( + &self, + ) -> Box> + Send + Sync> + where + ReqBody: Send + 'static, + { + let acc_extractors = Box::new(NeutralExtractor::new()); + self.extractors + .iter() + .rfold(acc_extractors, |inner, extractor| match extractor { + RequestExtractor::Path { path } => Box::new(inner.path(path)), + RequestExtractor::Method => Box::new(inner.method()), + RequestExtractor::Query { key } => Box::new(inner.query(key.to_string())), + RequestExtractor::Header { key } => Box::new(inner.header(key.to_string())), }) } } @@ -28,7 +120,16 @@ impl EndpointConfig { impl Default for EndpointConfig { fn default() -> Self { Self { - request_predicates: vec![ RequestPredicate::Query { key: String::from("balala"), value: String::from("true") }], + request_predicates: Vec::new(), + response_predicates: vec![ResponsePredicate::StatusCode { + code: http::StatusCode::OK, + }], + extractors: vec![ + RequestExtractor::Path { + path: String::from("{path}*"), + }, + RequestExtractor::Method, + ], } } } diff --git a/hitbox-tower/src/service.rs b/hitbox-tower/src/service.rs index 6b084ac..0cd828e 100644 --- a/hitbox-tower/src/service.rs +++ b/hitbox-tower/src/service.rs @@ -5,7 +5,7 @@ use hitbox::{backend::CacheBackend, fsm::CacheFuture}; use hitbox_http::{ extractors::NeutralExtractor, extractors::{method::MethodExtractor, path::PathExtractor}, - predicates::{query::QueryPredicate, NeutralPredicate, NeutralResponsePredicate}, + predicates::NeutralResponsePredicate, CacheableHttpRequest, CacheableHttpResponse, FromBytes, }; use http::{Request, Response}; @@ -78,15 +78,13 @@ where let transformer = Transformer::new(self.upstream.clone()); let config = &self.endpoint_config; - let response_predicate = NeutralResponsePredicate::new(); - let extractor = NeutralExtractor::new().method().path("/{path}*"); CacheFuture::new( self.backend.clone(), CacheableHttpRequest::from_request(req), transformer, - Arc::new(config.create()), - Arc::new(response_predicate), - Arc::new(extractor), + Arc::new(config.request_predicates()), + Arc::new(config.response_predicates()), + Arc::new(config.extractors()), ) } } From 99d7f7fc7c9459bcbfb45a14ec3caecb447f2008 Mon Sep 17 00:00:00 2001 From: Belousov Maksim Date: Wed, 16 Aug 2023 04:01:55 +0400 Subject: [PATCH 6/6] feat(config)!: refactor Cache configuration interface --- examples/examples/axum.rs | 16 +++-- hitbox-tower/src/config.rs | 143 ++++++++++++++++++++++++++++++++++--- hitbox-tower/src/layer.rs | 21 ++++-- 3 files changed, 164 insertions(+), 16 deletions(-) diff --git a/examples/examples/axum.rs b/examples/examples/axum.rs index 778fe85..39d2871 100644 --- a/examples/examples/axum.rs +++ b/examples/examples/axum.rs @@ -56,6 +56,7 @@ async fn main() { //.response_predicate(response_predicate) //.cache_key(cache_key) //.build(); + use hitbox_tower::config::request; let app = Router::new() .route("/greet/:name/", get(handler_result)) .route("/", get(handler)) @@ -63,15 +64,22 @@ async fn main() { .layer( ServiceBuilder::new() //.layer( - //Cache::builder() - ////.config(config) - //.backend(inmemory) - //.build(), + //Cache::builder() + ////.config(config) + //.backend(inmemory) + //.build(), //) .layer( Cache::builder() //.config(config) .backend(backend) + .request( + request::query("cache", "true") + .query("x-cache", "true") + .path("/{path}*"), + ) + // .response(response::header()) + // .key(path("/{}*")) .build(), ), ); diff --git a/hitbox-tower/src/config.rs b/hitbox-tower/src/config.rs index 244fc84..cb1ed0e 100644 --- a/hitbox-tower/src/config.rs +++ b/hitbox-tower/src/config.rs @@ -16,6 +16,7 @@ use hitbox_http::predicates::{ }; use hitbox_http::{CacheableHttpRequest, CacheableHttpResponse}; +#[derive(Debug)] pub enum RequestPredicate { Path { path: String }, Method { method: http::Method }, @@ -24,11 +25,13 @@ pub enum RequestPredicate { //Body { statement: String }, } +#[derive(Debug)] pub enum ResponsePredicate { StatusCode { code: http::StatusCode }, //Body { statement: String }, } +#[derive(Debug)] pub enum RequestExtractor { Path { path: String }, Method, @@ -37,6 +40,7 @@ pub enum RequestExtractor { //Body { statement: String }, } +#[derive(Debug)] pub struct EndpointConfig { pub request_predicates: Vec, pub response_predicates: Vec, @@ -52,6 +56,10 @@ impl EndpointConfig { } } + pub fn builder() -> EndpointConfigBuilder> { + EndpointConfigBuilder::default() + } + pub fn with_request_predicate(self, predicate: RequestPredicate) -> Self { self } @@ -71,17 +79,22 @@ impl EndpointConfig { ReqBody: Send + 'static, { let acc_predicate = Box::new(NeutralPredicate::new()); + dbg!(&self.request_predicates); self.request_predicates .iter() - .rfold(acc_predicate, |inner, predicate| match predicate { - RequestPredicate::Path { path } => Box::new(inner.path(path.clone())), - RequestPredicate::Query { key, value } => { - Box::new(inner.query(key.clone(), value.clone())) + .rfold(acc_predicate, |inner, predicate| { + dbg!("+++++++++++++++++++++++++++++++++++++++++++++++"); + dbg!(&predicate); + match predicate { + RequestPredicate::Path { path } => Box::new(inner.path(path.clone())), + RequestPredicate::Query { key, value } => { + Box::new(inner.query(key.clone(), value.clone())) + } + RequestPredicate::Header { key, value } => { + Box::new(inner.header(key.clone(), value.clone())) + } + RequestPredicate::Method { method } => Box::new(inner.method(method.clone())), } - RequestPredicate::Header { key, value } => { - Box::new(inner.header(key.clone(), value.clone())) - } - RequestPredicate::Method { method } => Box::new(inner.method(method.clone())), }) } @@ -133,3 +146,117 @@ impl Default for EndpointConfig { } } } + +pub struct EndpointConfigBuilder { + request_predicates: RP, +} + +impl Default for EndpointConfigBuilder> { + fn default() -> Self { + EndpointConfigBuilder { + request_predicates: NeutralPredicate::new(), + } + } +} + +// impl

EndpointConfigBuilder

{ +// pub fn request(predicate_builder: RequestPredicateBuilder

) -> EndpointConfigBuilder

{ +// EndpointConfigBuilder { +// request_predicates: predicate_builder.predicate, +// } +// } +// } + +pub struct RequestPredicateBuilder { + predicates: Vec, +} + +impl RequestPredicateBuilder { + pub fn new() -> Self { + RequestPredicateBuilder { + predicates: Vec::new(), + } + } + + pub fn query(mut self, key: &str, value: &str) -> Self { + self.predicates.push(RequestPredicate::Query { + key: key.to_owned(), + value: value.to_owned(), + }); + self + } + + pub fn path(mut self, path: &str) -> Self { + self.predicates.push(RequestPredicate::Path { + path: path.to_owned(), + }); + self + } + + pub fn build(self) -> Vec { + self.predicates + } +} + +pub mod request { + use super::RequestPredicateBuilder; + + pub fn query(key: &str, value: &str) -> RequestPredicateBuilder { + RequestPredicateBuilder::new().query(key, value) + } + + pub fn path(path: &str) -> RequestPredicateBuilder { + RequestPredicateBuilder::new().path(path) + } +} + +// pub struct RequestPredicateBuilder

{ +// predicate: P, +// } +// +// impl Default for RequestPredicateBuilder> { +// fn default() -> Self { +// RequestPredicateBuilder { +// predicate: NeutralPredicate::new(), +// } +// } +// } +// +// use hitbox_http::predicates::query::Query; +// +// impl

RequestPredicateBuilder

+// where +// P: Predicate, +// { +// pub fn query(self, key: &str, value: &str) -> RequestPredicateBuilder> { +// RequestPredicateBuilder { +// predicate: self.predicate.query(key.to_owned(), value.to_owned()), +// } +// } +// } +// +// pub struct Request; +// +// impl Request { +// fn query( +// key: &str, +// value: &str, +// ) -> RequestPredicateBuilder>> +// where +// ReqBody: Send + 'static, +// { +// RequestPredicateBuilder { +// predicate: NeutralPredicate::new().query(key.to_owned(), value.to_owned()), +// } +// } +// } +// +// #[test] +// fn test_endpoint_config_builder() { +// use hyper::Body; +// let config = EndpointConfigBuilder::request( +// Request::query::("hui", "hui") +// .query("pizda", "pizda") +// .query("test", "test"), +// ); +// } diff --git a/hitbox-tower/src/layer.rs b/hitbox-tower/src/layer.rs index cf3ca3a..166901e 100644 --- a/hitbox-tower/src/layer.rs +++ b/hitbox-tower/src/layer.rs @@ -1,4 +1,4 @@ -use crate::config::EndpointConfig; +use crate::config::{EndpointConfig, RequestPredicateBuilder}; use std::sync::Arc; use hitbox::backend::CacheBackend; @@ -28,7 +28,7 @@ impl Layer for Cache { CacheService::new( upstream, Arc::clone(&self.backend), - Arc::new(Default::default()), + Arc::clone(&self.endpoint_config), ) } } @@ -41,7 +41,7 @@ impl Cache { pub struct CacheBuilder { backend: Option, - endpoint_config: Option, + endpoint_config: EndpointConfig, } impl CacheBuilder @@ -55,10 +55,23 @@ where } } + pub fn request(self, predicates: RequestPredicateBuilder) -> Self { + let endpoint_config = EndpointConfig { + request_predicates: predicates.build(), + response_predicates: self.endpoint_config.response_predicates, + extractors: self.endpoint_config.extractors, + }; + CacheBuilder { + backend: self.backend, + endpoint_config, + } + } + pub fn build(self) -> Cache { + dbg!(&self.endpoint_config); Cache { backend: Arc::new(self.backend.expect("Please add some cache backend")), - endpoint_config: Arc::new(self.endpoint_config.unwrap_or_default()), + endpoint_config: Arc::new(self.endpoint_config), } } }