feat: separated feature cache
This commit is contained in:
parent
8ecc83fe78
commit
32b173966c
11 changed files with 76 additions and 36 deletions
2
.github/workflows/docker_build_push.yml
vendored
2
.github/workflows/docker_build_push.yml
vendored
|
|
@ -42,7 +42,7 @@ jobs:
|
||||||
- target: "s2n"
|
- target: "s2n"
|
||||||
dockerfile: ./docker/Dockerfile
|
dockerfile: ./docker/Dockerfile
|
||||||
build-args: |
|
build-args: |
|
||||||
"CARGO_FEATURES=--no-default-features --features http3-s2n"
|
"CARGO_FEATURES=--no-default-features --features http3-s2n cache"
|
||||||
"ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++"
|
"ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++"
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
tags-suffix: "-s2n"
|
tags-suffix: "-s2n"
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@ publish = false
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["http3-quinn"]
|
default = ["http3-quinn", "cache"]
|
||||||
http3-quinn = ["rpxy-lib/http3-quinn"]
|
http3-quinn = ["rpxy-lib/http3-quinn"]
|
||||||
http3-s2n = ["rpxy-lib/http3-s2n"]
|
http3-s2n = ["rpxy-lib/http3-s2n"]
|
||||||
|
cache = ["rpxy-lib/cache"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [
|
rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
use rpxy_lib::{reexports::Uri, AppConfig, ProxyConfig, ReverseProxyConfig, TlsConfig, UpstreamUri};
|
use rpxy_lib::{reexports::Uri, AppConfig, ProxyConfig, ReverseProxyConfig, TlsConfig, UpstreamUri};
|
||||||
use rustc_hash::FxHashMap as HashMap;
|
use rustc_hash::FxHashMap as HashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{fs, net::SocketAddr, path::PathBuf};
|
use std::{fs, net::SocketAddr};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)]
|
#[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)]
|
||||||
pub struct ConfigToml {
|
pub struct ConfigToml {
|
||||||
|
|
@ -32,6 +32,7 @@ pub struct Http3Option {
|
||||||
pub max_idle_timeout: Option<u64>,
|
pub max_idle_timeout: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
#[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)]
|
#[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)]
|
||||||
pub struct CacheOption {
|
pub struct CacheOption {
|
||||||
pub cache_dir: Option<String>,
|
pub cache_dir: Option<String>,
|
||||||
|
|
@ -43,6 +44,7 @@ pub struct CacheOption {
|
||||||
pub struct Experimental {
|
pub struct Experimental {
|
||||||
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
||||||
pub h3: Option<Http3Option>,
|
pub h3: Option<Http3Option>,
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
pub cache: Option<CacheOption>,
|
pub cache: Option<CacheOption>,
|
||||||
pub ignore_sni_consistency: Option<bool>,
|
pub ignore_sni_consistency: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
@ -169,20 +171,19 @@ impl TryInto<ProxyConfig> for &ConfigToml {
|
||||||
proxy_config.sni_consistency = !ignore;
|
proxy_config.sni_consistency = !ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
if let Some(cache_option) = &exp.cache {
|
if let Some(cache_option) = &exp.cache {
|
||||||
proxy_config.cache_enabled = true;
|
proxy_config.cache_enabled = true;
|
||||||
proxy_config.cache_dir = match &cache_option.cache_dir {
|
proxy_config.cache_dir = match &cache_option.cache_dir {
|
||||||
Some(cache_dir) => Some(PathBuf::from(cache_dir)),
|
Some(cache_dir) => Some(std::path::PathBuf::from(cache_dir)),
|
||||||
None => Some(PathBuf::from(CACHE_DIR)),
|
None => Some(std::path::PathBuf::from(CACHE_DIR)),
|
||||||
};
|
|
||||||
proxy_config.cache_max_entry = match &cache_option.max_cache_entry {
|
|
||||||
Some(num) => Some(*num),
|
|
||||||
None => Some(MAX_CACHE_ENTRY),
|
|
||||||
};
|
|
||||||
proxy_config.cache_max_each_size = match &cache_option.max_cache_each_size {
|
|
||||||
Some(num) => Some(*num),
|
|
||||||
None => Some(MAX_CACHE_EACH_SIZE),
|
|
||||||
};
|
};
|
||||||
|
if let Some(num) = cache_option.max_cache_entry {
|
||||||
|
proxy_config.cache_max_entry = num;
|
||||||
|
}
|
||||||
|
if let Some(num) = cache_option.max_cache_each_size {
|
||||||
|
proxy_config.cache_max_each_size = num;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@ pub const LISTEN_ADDRESSES_V4: &[&str] = &["0.0.0.0"];
|
||||||
pub const LISTEN_ADDRESSES_V6: &[&str] = &["[::]"];
|
pub const LISTEN_ADDRESSES_V6: &[&str] = &["[::]"];
|
||||||
pub const CONFIG_WATCH_DELAY_SECS: u32 = 20;
|
pub const CONFIG_WATCH_DELAY_SECS: u32 = 20;
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
// Cache directory
|
// Cache directory
|
||||||
pub const CACHE_DIR: &str = "./cache";
|
pub const CACHE_DIR: &str = "./cache";
|
||||||
// # of entries in cache
|
|
||||||
pub const MAX_CACHE_ENTRY: usize = 1_000;
|
|
||||||
// max size for each file in bytes
|
|
||||||
pub const MAX_CACHE_EACH_SIZE: usize = 65_535;
|
|
||||||
|
|
||||||
// TODO: max cache size in total
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,11 @@ publish = false
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["http3-quinn", "sticky-cookie"]
|
default = ["http3-quinn", "sticky-cookie", "cache"]
|
||||||
http3-quinn = ["quinn", "h3", "h3-quinn", "socket2"]
|
http3-quinn = ["quinn", "h3", "h3-quinn", "socket2"]
|
||||||
http3-s2n = ["h3", "s2n-quic", "s2n-quic-rustls", "s2n-quic-h3"]
|
http3-s2n = ["h3", "s2n-quic", "s2n-quic-rustls", "s2n-quic-h3"]
|
||||||
sticky-cookie = ["base64", "sha2", "chrono"]
|
sticky-cookie = ["base64", "sha2", "chrono"]
|
||||||
|
cache = ["http-cache-semantics", "lru"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
@ -74,8 +75,8 @@ s2n-quic-h3 = { path = "../submodules/s2n-quic/quic/s2n-quic-h3/", optional = tr
|
||||||
s2n-quic-rustls = { path = "../submodules/s2n-quic/quic/s2n-quic-rustls/", optional = true }
|
s2n-quic-rustls = { path = "../submodules/s2n-quic/quic/s2n-quic-rustls/", optional = true }
|
||||||
|
|
||||||
# cache
|
# cache
|
||||||
http-cache-semantics = { path = "../submodules/rusty-http-cache-semantics/" }
|
http-cache-semantics = { path = "../submodules/rusty-http-cache-semantics/", optional = true }
|
||||||
lru = { version = "0.11.0" }
|
lru = { version = "0.11.0", optional = true }
|
||||||
|
|
||||||
# cookie handling for sticky cookie
|
# cookie handling for sticky cookie
|
||||||
chrono = { version = "0.4.26", default-features = false, features = [
|
chrono = { version = "0.4.26", default-features = false, features = [
|
||||||
|
|
|
||||||
|
|
@ -31,3 +31,12 @@ pub mod H3 {
|
||||||
#[cfg(feature = "sticky-cookie")]
|
#[cfg(feature = "sticky-cookie")]
|
||||||
/// For load-balancing with sticky cookie
|
/// For load-balancing with sticky cookie
|
||||||
pub const STICKY_COOKIE_NAME: &str = "rpxy_srv_id";
|
pub const STICKY_COOKIE_NAME: &str = "rpxy_srv_id";
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
// # of entries in cache
|
||||||
|
pub const MAX_CACHE_ENTRY: usize = 1_000;
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
// max size for each file in bytes
|
||||||
|
pub const MAX_CACHE_EACH_SIZE: usize = 65_535;
|
||||||
|
|
||||||
|
// TODO: max cache size in total
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ use crate::{
|
||||||
utils::{BytesName, PathNameBytesExp},
|
utils::{BytesName, PathNameBytesExp},
|
||||||
};
|
};
|
||||||
use rustc_hash::FxHashMap as HashMap;
|
use rustc_hash::FxHashMap as HashMap;
|
||||||
|
use std::net::SocketAddr;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
};
|
};
|
||||||
use std::{net::SocketAddr, path::PathBuf};
|
|
||||||
use tokio::time::Duration;
|
use tokio::time::Duration;
|
||||||
|
|
||||||
/// Global object containing proxy configurations and shared object like counters.
|
/// Global object containing proxy configurations and shared object like counters.
|
||||||
|
|
@ -53,10 +53,14 @@ pub struct ProxyConfig {
|
||||||
// experimentals
|
// experimentals
|
||||||
pub sni_consistency: bool, // Handler
|
pub sni_consistency: bool, // Handler
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
pub cache_enabled: bool,
|
pub cache_enabled: bool,
|
||||||
pub cache_dir: Option<PathBuf>,
|
#[cfg(feature = "cache")]
|
||||||
pub cache_max_entry: Option<usize>,
|
pub cache_dir: Option<std::path::PathBuf>,
|
||||||
pub cache_max_each_size: Option<usize>,
|
#[cfg(feature = "cache")]
|
||||||
|
pub cache_max_entry: usize,
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
pub cache_max_each_size: usize,
|
||||||
|
|
||||||
// All need to make packet acceptor
|
// All need to make packet acceptor
|
||||||
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
||||||
|
|
@ -93,10 +97,14 @@ impl Default for ProxyConfig {
|
||||||
|
|
||||||
sni_consistency: true,
|
sni_consistency: true,
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
cache_enabled: false,
|
cache_enabled: false,
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
cache_dir: None,
|
cache_dir: None,
|
||||||
cache_max_entry: None,
|
#[cfg(feature = "cache")]
|
||||||
cache_max_each_size: None,
|
cache_max_entry: MAX_CACHE_ENTRY,
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
cache_max_each_size: MAX_CACHE_EACH_SIZE,
|
||||||
|
|
||||||
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
||||||
http3: false,
|
http3: false,
|
||||||
|
|
|
||||||
|
|
@ -121,14 +121,14 @@ impl RpxyCache {
|
||||||
let path = globals.proxy_config.cache_dir.as_ref().unwrap();
|
let path = globals.proxy_config.cache_dir.as_ref().unwrap();
|
||||||
let cache_file_manager = Arc::new(RwLock::new(CacheFileManager::new(path, &globals.runtime_handle).await));
|
let cache_file_manager = Arc::new(RwLock::new(CacheFileManager::new(path, &globals.runtime_handle).await));
|
||||||
let inner = Arc::new(Mutex::new(LruCache::new(
|
let inner = Arc::new(Mutex::new(LruCache::new(
|
||||||
std::num::NonZeroUsize::new(globals.proxy_config.cache_max_entry.unwrap()).unwrap(),
|
std::num::NonZeroUsize::new(globals.proxy_config.cache_max_entry).unwrap(),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
cache_file_manager,
|
cache_file_manager,
|
||||||
inner,
|
inner,
|
||||||
runtime_handle: globals.runtime_handle.clone(),
|
runtime_handle: globals.runtime_handle.clone(),
|
||||||
max_each_size: globals.proxy_config.cache_max_each_size.unwrap(),
|
max_each_size: globals.proxy_config.cache_max_each_size,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
use super::cache::{get_policy_if_cacheable, RpxyCache};
|
use super::cache::{get_policy_if_cacheable, RpxyCache};
|
||||||
use crate::{error::RpxyError, globals::Globals, log::*, CryptoSource};
|
#[cfg(feature = "cache")]
|
||||||
|
use crate::log::*;
|
||||||
|
use crate::{error::RpxyError, globals::Globals, CryptoSource};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
use derive_builder::Builder;
|
|
||||||
use hyper::{
|
use hyper::{
|
||||||
body::{Body, HttpBody},
|
body::{Body, HttpBody},
|
||||||
client::{connect::Connect, HttpConnector},
|
client::{connect::Connect, HttpConnector},
|
||||||
|
|
@ -11,6 +14,8 @@ use hyper::{
|
||||||
};
|
};
|
||||||
use hyper_rustls::HttpsConnector;
|
use hyper_rustls::HttpsConnector;
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
/// Build synthetic request to cache
|
||||||
fn build_synth_req_for_cache<T>(req: &Request<T>) -> Request<()> {
|
fn build_synth_req_for_cache<T>(req: &Request<T>) -> Request<()> {
|
||||||
let mut builder = Request::builder()
|
let mut builder = Request::builder()
|
||||||
.method(req.method())
|
.method(req.method())
|
||||||
|
|
@ -30,12 +35,12 @@ pub trait ForwardRequest<B> {
|
||||||
async fn request(&self, req: Request<B>) -> Result<Response<Body>, Self::Error>;
|
async fn request(&self, req: Request<B>) -> Result<Response<Body>, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Builder, Clone)]
|
|
||||||
/// Forwarder struct responsible to cache handling
|
/// Forwarder struct responsible to cache handling
|
||||||
pub struct Forwarder<C, B = Body>
|
pub struct Forwarder<C, B = Body>
|
||||||
where
|
where
|
||||||
C: Connect + Clone + Sync + Send + 'static,
|
C: Connect + Clone + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
cache: Option<RpxyCache>,
|
cache: Option<RpxyCache>,
|
||||||
inner: Client<C, B>,
|
inner: Client<C, B>,
|
||||||
inner_h2: Client<C, B>, // `h2c` or http/2-only client is defined separately
|
inner_h2: Client<C, B>, // `h2c` or http/2-only client is defined separately
|
||||||
|
|
@ -50,6 +55,8 @@ where
|
||||||
C: Connect + Clone + Sync + Send + 'static,
|
C: Connect + Clone + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
type Error = RpxyError;
|
type Error = RpxyError;
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
async fn request(&self, req: Request<B>) -> Result<Response<Body>, Self::Error> {
|
async fn request(&self, req: Request<B>) -> Result<Response<Body>, Self::Error> {
|
||||||
let mut synth_req = None;
|
let mut synth_req = None;
|
||||||
if self.cache.is_some() {
|
if self.cache.is_some() {
|
||||||
|
|
@ -99,10 +106,19 @@ where
|
||||||
// res
|
// res
|
||||||
Ok(Response::from_parts(parts, Body::from(aggregated)))
|
Ok(Response::from_parts(parts, Body::from(aggregated)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "cache"))]
|
||||||
|
async fn request(&self, req: Request<B>) -> Result<Response<Body>, Self::Error> {
|
||||||
|
match req.version() {
|
||||||
|
Version::HTTP_2 => self.inner_h2.request(req).await.map_err(RpxyError::Hyper), // handles `h2c` requests
|
||||||
|
_ => self.inner.request(req).await.map_err(RpxyError::Hyper),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Forwarder<HttpsConnector<HttpConnector>, Body> {
|
impl Forwarder<HttpsConnector<HttpConnector>, Body> {
|
||||||
pub async fn new<T: CryptoSource>(globals: &std::sync::Arc<Globals<T>>) -> Self {
|
/// Build forwarder
|
||||||
|
pub async fn new<T: CryptoSource>(_globals: &std::sync::Arc<Globals<T>>) -> Self {
|
||||||
// let connector = TrustDnsResolver::default().into_rustls_webpki_https_connector();
|
// let connector = TrustDnsResolver::default().into_rustls_webpki_https_connector();
|
||||||
let connector = hyper_rustls::HttpsConnectorBuilder::new()
|
let connector = hyper_rustls::HttpsConnectorBuilder::new()
|
||||||
.with_webpki_roots()
|
.with_webpki_roots()
|
||||||
|
|
@ -119,7 +135,12 @@ impl Forwarder<HttpsConnector<HttpConnector>, Body> {
|
||||||
let inner = Client::builder().build::<_, Body>(connector);
|
let inner = Client::builder().build::<_, Body>(connector);
|
||||||
let inner_h2 = Client::builder().http2_only(true).build::<_, Body>(connector_h2);
|
let inner_h2 = Client::builder().http2_only(true).build::<_, Body>(connector_h2);
|
||||||
|
|
||||||
let cache = RpxyCache::new(globals).await;
|
#[cfg(feature = "cache")]
|
||||||
|
{
|
||||||
|
let cache = RpxyCache::new(_globals).await;
|
||||||
Self { inner, inner_h2, cache }
|
Self { inner, inner_h2, cache }
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "cache"))]
|
||||||
|
Self { inner, inner_h2 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
mod cache;
|
mod cache;
|
||||||
mod forwarder;
|
mod forwarder;
|
||||||
mod handler_main;
|
mod handler_main;
|
||||||
|
|
|
||||||
|
|
@ -60,11 +60,14 @@ where
|
||||||
if !proxy_config.sni_consistency {
|
if !proxy_config.sni_consistency {
|
||||||
info!("Ignore consistency between TLS SNI and Host header (or Request line). Note it violates RFC.");
|
info!("Ignore consistency between TLS SNI and Host header (or Request line). Note it violates RFC.");
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
if proxy_config.cache_enabled {
|
if proxy_config.cache_enabled {
|
||||||
info!(
|
info!(
|
||||||
"Cache is enabled: cache dir = {:?}",
|
"Cache is enabled: cache dir = {:?}",
|
||||||
proxy_config.cache_dir.as_ref().unwrap()
|
proxy_config.cache_dir.as_ref().unwrap()
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
info!("Cache is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
// build global
|
// build global
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue