diff --git a/Cargo.lock b/Cargo.lock index 997815e..dd82a7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "aralez" -version = "0.95.1" +version = "0.95.2" dependencies = [ "ahash", "anyhow", diff --git a/README.md b/README.md index 7a9b194..9b1ee7b 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Built on Rust, on top of **Cloudflare’s Pingora engine**, **Aralez** delivers | **cache_size_mb** | `50` | Optional. Enable internal cache with size in megabytes of value. Cache is entirely disabled when this line is commented out. | | **cache_enabled** | `bool` | Optional. Disabled by default | | **cache_storage** | `memory, redis://host:port` | Optional Defaults to memory works with Redis, Valkey, Dragonfly (requires redis:// prefix) | +| **cache_item_max_size** | `5` | Optional. Set maximum size for cached items in megabytes. Defaults to 0 , no max size. | | **cache_ttl** | `60` | Optional. Set internal cache with size in megabytes of value. Defaults to 50Mb if cache_enabled: true | | **tcp_keepalive_idle** | `60` | Seconds of inactivity before the kernel starts sending keepalive probes to a downstream client. Defaults to 60 | | **tcp_keepalive_interval** | `10` | Seconds between individual keepalive probes if the client does not respond. Defaults to 10 | diff --git a/etc/main.yaml b/etc/main.yaml index 91873c1..4aab987 100644 --- a/etc/main.yaml +++ b/etc/main.yaml @@ -10,6 +10,7 @@ cache_enabled: true # Optional. Disabled by default cache_storage: memory # memory | redis://host:port . Optional Defaults to memory works with Redis, Valkey, Dragonfly (requires redis:// prefix) cache_size_mb: 50 # Optional. Set internal cache with size in megabytes of value. Defaults to 50Mb if cache_enabled: true cache_ttl: 60 # Optional. Set Default TTL in seconds for cached items, works if cache control headers are not set via upstream. Defaults to 60 if cache_enabled: true +cache_item_max_size: 5 # Optional. Set maximum size for cached items in megabytes. Defaults to 0 , no max size. config_api_enabled: true # Boolean to enable/disable remote config push capability. config_address: 127.0.0.1:3000 # HTTP API address for pushing upstreams.yaml from remote location proxy_address_http: 0.0.0.0:80 # Proxy HTTP bind address diff --git a/src/cache/redis.rs b/src/cache/redis.rs index 6ace973..e145132 100644 --- a/src/cache/redis.rs +++ b/src/cache/redis.rs @@ -170,9 +170,13 @@ impl Storage for RedisCache { let (internal_bytes, header_bytes) = meta.serialize()?; let mut conn = (*self.client).clone(); - let _: RedisResult<()> = conn.set_ex(&internal_key, &internal_bytes, ttl).await; - let _: RedisResult<()> = conn.set_ex(&header_key, &header_bytes, ttl).await; + if let Err(e) = conn.set_ex::<_, _, ()>(&internal_key, &internal_bytes, ttl).await { + log::error!("Failed to cache body to Redis: {}", e); + } + if let Err(e) = conn.set_ex::<_, _, ()>(&header_key, &header_bytes, ttl).await { + log::error!("Failed to cache headers to Redis: {}", e); + } Ok(Box::new(RedisMissHandler { client: self.client.clone(), body_key, diff --git a/src/cache/types.rs b/src/cache/types.rs index 7cd4e80..6b98b41 100644 --- a/src/cache/types.rs +++ b/src/cache/types.rs @@ -21,7 +21,8 @@ pub async fn init_cache(config: Arc) { CACHE_TTL.set(ttl).ok(); if let Some(size) = config.cache_size_mb { let cache_size = size * 1024 * 1024; - drop(EVICTION.set(Manager::new(cache_size))); + let manager = Manager::new(cache_size); + let _ = EVICTION.set(manager); } GLOBAL_STORAGE.set(Box::new(MemCache::new())).ok(); info!("Cache initialized successfully. Storage: {}", cache_storage); diff --git a/src/core/proxyhttp.rs b/src/core/proxyhttp.rs index b68d368..43ff8f2 100644 --- a/src/core/proxyhttp.rs +++ b/src/core/proxyhttp.rs @@ -13,7 +13,7 @@ use pingora::ErrorSource::Upstream; use pingora::http::{Method, RequestHeader, ResponseHeader, StatusCode}; use pingora::prelude::*; use pingora_cache::cache_control::CacheControl; -use pingora_cache::{CacheKey, CacheMetaDefaults, RespCacheable}; +use pingora_cache::{CacheKey, CacheMetaDefaults, NoCacheReason, RespCacheable}; use pingora_core::listeners::ALPN; use pingora_core::prelude::HttpPeer; use pingora_proxy::{ProxyHttp, Session}; @@ -89,6 +89,22 @@ impl ProxyHttp for LB { Ok(()) } fn response_cache_filter(&self, _session: &Session, resp: &ResponseHeader, _ctx: &mut Self::CTX) -> Result { + if let Some(item_size) = self.config.cache_item_max_size { + if let Some(content_length_str) = resp.headers.get("content-length") { + match content_length_str.to_str().ok().and_then(|s| s.parse::().ok()) { + Some(content_length) => { + if content_length > item_size { + return Ok(RespCacheable::Uncacheable(NoCacheReason::Custom("Response too large"))); + } + } + None => { + return Ok(RespCacheable::Uncacheable(NoCacheReason::Custom("Response too large"))); + } + } + } else { + return Ok(RespCacheable::Uncacheable(NoCacheReason::Custom("Response too large"))); + } + }; let cc = CacheControl::from_resp_headers(resp); let defaults = CacheMetaDefaults::new( |status| { diff --git a/src/utils/parceyaml.rs b/src/utils/parceyaml.rs index fabb3d5..b630f62 100644 --- a/src/utils/parceyaml.rs +++ b/src/utils/parceyaml.rs @@ -281,7 +281,9 @@ pub fn parce_main_config(path: &str) -> AppConfig { } else if let Ok(jwt_key) = env::var("JWT_KEY") { cfo.master_key = Some(jwt_key); }; - + if let Some(cs) = cfo.cache_item_max_size { + cfo.cache_item_max_size = Some(cs * 1024 * 1024); + } // log_builder(&cfo, &cfo.log_file); cfo.hc_method = cfo.hc_method.to_uppercase(); if let Some((ip, port_str)) = cfo.config_address.rsplit_once(':') { diff --git a/src/utils/types.rs b/src/utils/types.rs index dedb611..5c5051b 100644 --- a/src/utils/types.rs +++ b/src/utils/types.rs @@ -115,6 +115,7 @@ pub struct AppConfig { pub cache_enabled: Option, pub cache_storage: Option, pub cache_size_mb: Option, + pub cache_item_max_size: Option, pub cache_ttl: Option, pub access_log: Option, pub pid_file: Option,