-
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
de91c7a
commit 3c6e4e5
Showing
16 changed files
with
1,173 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
use crate::{ | ||
certs::CryptoSource, | ||
error::*, | ||
log::*, | ||
name_exp::{ByteName, ServerName}, | ||
AppConfig, AppConfigList, | ||
}; | ||
use derive_builder::Builder; | ||
use rustc_hash::FxHashMap as HashMap; | ||
use std::borrow::Cow; | ||
|
||
use super::upstream::PathManager; | ||
|
||
/// Struct serving information to route incoming connections, like server name to be handled and tls certs/keys settings. | ||
#[derive(Builder)] | ||
pub struct BackendApp<T> | ||
where | ||
T: CryptoSource, | ||
{ | ||
#[builder(setter(into))] | ||
/// backend application name, e.g., app1 | ||
pub app_name: String, | ||
#[builder(setter(custom))] | ||
/// server name, e.g., example.com, in [[ServerName]] object | ||
pub server_name: ServerName, | ||
/// struct of reverse proxy serving incoming request | ||
pub path_manager: PathManager, | ||
/// tls settings: https redirection with 30x | ||
#[builder(default)] | ||
pub https_redirection: Option<bool>, | ||
/// TLS settings: source meta for server cert, key, client ca cert | ||
#[builder(default)] | ||
pub crypto_source: Option<T>, | ||
} | ||
impl<'a, T> BackendAppBuilder<T> | ||
where | ||
T: CryptoSource, | ||
{ | ||
pub fn server_name(&mut self, server_name: impl Into<Cow<'a, str>>) -> &mut Self { | ||
self.server_name = Some(server_name.to_server_name()); | ||
self | ||
} | ||
} | ||
|
||
/// HashMap and some meta information for multiple Backend structs. | ||
pub struct BackendAppManager<T> | ||
where | ||
T: CryptoSource, | ||
{ | ||
/// HashMap of Backend structs, key is server name | ||
pub apps: HashMap<ServerName, BackendApp<T>>, | ||
/// for plaintext http | ||
pub default_server_name: Option<ServerName>, | ||
} | ||
|
||
impl<T> Default for BackendAppManager<T> | ||
where | ||
T: CryptoSource, | ||
{ | ||
fn default() -> Self { | ||
Self { | ||
apps: HashMap::<ServerName, BackendApp<T>>::default(), | ||
default_server_name: None, | ||
} | ||
} | ||
} | ||
|
||
impl<T> TryFrom<&AppConfig<T>> for BackendApp<T> | ||
where | ||
T: CryptoSource + Clone, | ||
{ | ||
type Error = RpxyError; | ||
|
||
fn try_from(app_config: &AppConfig<T>) -> Result<Self, Self::Error> { | ||
let mut backend_builder = BackendAppBuilder::default(); | ||
let path_manager = PathManager::try_from(app_config)?; | ||
backend_builder | ||
.app_name(app_config.app_name.clone()) | ||
.server_name(app_config.server_name.clone()) | ||
.path_manager(path_manager); | ||
// TLS settings and build backend instance | ||
let backend = if app_config.tls.is_none() { | ||
backend_builder.build()? | ||
} else { | ||
let tls = app_config.tls.as_ref().unwrap(); | ||
backend_builder | ||
.https_redirection(Some(tls.https_redirection)) | ||
.crypto_source(Some(tls.inner.clone())) | ||
.build()? | ||
}; | ||
Ok(backend) | ||
} | ||
} | ||
|
||
impl<T> TryFrom<&AppConfigList<T>> for BackendAppManager<T> | ||
where | ||
T: CryptoSource + Clone, | ||
{ | ||
type Error = RpxyError; | ||
|
||
fn try_from(config_list: &AppConfigList<T>) -> Result<Self, Self::Error> { | ||
let mut manager = Self::default(); | ||
for app_config in config_list.inner.iter() { | ||
let backend: BackendApp<T> = BackendApp::try_from(app_config)?; | ||
manager | ||
.apps | ||
.insert(app_config.server_name.clone().to_server_name(), backend); | ||
|
||
info!( | ||
"Registering application {} ({})", | ||
&app_config.server_name, &app_config.app_name | ||
); | ||
} | ||
|
||
// default backend application for plaintext http requests | ||
if let Some(default_app_name) = &config_list.default_app { | ||
let default_server_name = manager | ||
.apps | ||
.iter() | ||
.filter(|(_k, v)| &v.app_name == default_app_name) | ||
.map(|(_, v)| v.server_name.clone()) | ||
.collect::<Vec<_>>(); | ||
|
||
if !default_server_name.is_empty() { | ||
info!( | ||
"Serving plaintext http for requests to unconfigured server_name by app {} (server_name: {}).", | ||
&default_app_name, | ||
(&default_server_name[0]).try_into().unwrap_or_else(|_| "".to_string()) | ||
); | ||
|
||
manager.default_server_name = Some(default_server_name[0].clone()); | ||
} | ||
} | ||
Ok(manager) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
#[cfg(feature = "sticky-cookie")] | ||
pub use super::{ | ||
load_balance_sticky::{LoadBalanceSticky, LoadBalanceStickyBuilder}, | ||
sticky_cookie::StickyCookie, | ||
}; | ||
use derive_builder::Builder; | ||
use rand::Rng; | ||
use std::sync::{ | ||
atomic::{AtomicUsize, Ordering}, | ||
Arc, | ||
}; | ||
|
||
/// Constants to specify a load balance option | ||
pub mod load_balance_options { | ||
pub const FIX_TO_FIRST: &str = "none"; | ||
pub const ROUND_ROBIN: &str = "round_robin"; | ||
pub const RANDOM: &str = "random"; | ||
#[cfg(feature = "sticky-cookie")] | ||
pub const STICKY_ROUND_ROBIN: &str = "sticky"; | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
/// Pointer to upstream serving the incoming request. | ||
/// If 'sticky cookie'-based LB is enabled and cookie must be updated/created, the new cookie is also given. | ||
pub struct PointerToUpstream { | ||
pub ptr: usize, | ||
pub context: Option<LoadBalanceContext>, | ||
} | ||
/// Trait for LB | ||
pub(super) trait LoadBalanceWithPointer { | ||
fn get_ptr(&self, req_info: Option<&LoadBalanceContext>) -> PointerToUpstream; | ||
} | ||
|
||
#[derive(Debug, Clone, Builder)] | ||
/// Round Robin LB object as a pointer to the current serving upstream destination | ||
pub struct LoadBalanceRoundRobin { | ||
#[builder(default)] | ||
/// Pointer to the index of the last served upstream destination | ||
ptr: Arc<AtomicUsize>, | ||
#[builder(setter(custom), default)] | ||
/// Number of upstream destinations | ||
num_upstreams: usize, | ||
} | ||
impl LoadBalanceRoundRobinBuilder { | ||
pub fn num_upstreams(&mut self, v: &usize) -> &mut Self { | ||
self.num_upstreams = Some(*v); | ||
self | ||
} | ||
} | ||
impl LoadBalanceWithPointer for LoadBalanceRoundRobin { | ||
/// Increment the count of upstream served up to the max value | ||
fn get_ptr(&self, _info: Option<&LoadBalanceContext>) -> PointerToUpstream { | ||
// Get a current count of upstream served | ||
let current_ptr = self.ptr.load(Ordering::Relaxed); | ||
|
||
let ptr = if current_ptr < self.num_upstreams - 1 { | ||
self.ptr.fetch_add(1, Ordering::Relaxed) | ||
} else { | ||
// Clear the counter | ||
self.ptr.fetch_and(0, Ordering::Relaxed) | ||
}; | ||
PointerToUpstream { ptr, context: None } | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Builder)] | ||
/// Random LB object to keep the object of random pools | ||
pub struct LoadBalanceRandom { | ||
#[builder(setter(custom), default)] | ||
/// Number of upstream destinations | ||
num_upstreams: usize, | ||
} | ||
impl LoadBalanceRandomBuilder { | ||
pub fn num_upstreams(&mut self, v: &usize) -> &mut Self { | ||
self.num_upstreams = Some(*v); | ||
self | ||
} | ||
} | ||
impl LoadBalanceWithPointer for LoadBalanceRandom { | ||
/// Returns the random index within the range | ||
fn get_ptr(&self, _info: Option<&LoadBalanceContext>) -> PointerToUpstream { | ||
let mut rng = rand::thread_rng(); | ||
let ptr = rng.gen_range(0..self.num_upstreams); | ||
PointerToUpstream { ptr, context: None } | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
/// Load Balancing Option | ||
pub enum LoadBalance { | ||
/// Fix to the first upstream. Use if only one upstream destination is specified | ||
FixToFirst, | ||
/// Randomly chose one upstream server | ||
Random(LoadBalanceRandom), | ||
/// Simple round robin without session persistance | ||
RoundRobin(LoadBalanceRoundRobin), | ||
#[cfg(feature = "sticky-cookie")] | ||
/// Round robin with session persistance using cookie | ||
StickyRoundRobin(LoadBalanceSticky), | ||
} | ||
impl Default for LoadBalance { | ||
fn default() -> Self { | ||
Self::FixToFirst | ||
} | ||
} | ||
|
||
impl LoadBalance { | ||
/// Get the index of the upstream serving the incoming request | ||
pub fn get_context(&self, _context_to_lb: &Option<LoadBalanceContext>) -> PointerToUpstream { | ||
match self { | ||
LoadBalance::FixToFirst => PointerToUpstream { | ||
ptr: 0usize, | ||
context: None, | ||
}, | ||
LoadBalance::RoundRobin(ptr) => ptr.get_ptr(None), | ||
LoadBalance::Random(ptr) => ptr.get_ptr(None), | ||
#[cfg(feature = "sticky-cookie")] | ||
LoadBalance::StickyRoundRobin(ptr) => { | ||
// Generate new context if sticky round robin is enabled. | ||
ptr.get_ptr(_context_to_lb.as_ref()) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
/// Struct to handle the sticky cookie string, | ||
/// - passed from Rp module (http handler) to LB module, manipulated from req, only StickyCookieValue exists. | ||
/// - passed from LB module to Rp module (http handler), will be inserted into res, StickyCookieValue and Info exist. | ||
pub struct LoadBalanceContext { | ||
#[cfg(feature = "sticky-cookie")] | ||
pub sticky_cookie: StickyCookie, | ||
#[cfg(not(feature = "sticky-cookie"))] | ||
pub sticky_cookie: (), | ||
} |
Oops, something went wrong.