wip: support rustls-0.23 for http1.1 and 1.2
This commit is contained in:
parent
2f9f0a1122
commit
0c6f3edf18
16 changed files with 80 additions and 393 deletions
|
|
@ -13,7 +13,8 @@ publish.workspace = true
|
||||||
# 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", "cache", "rustls-backend"]
|
# default = ["http3-quinn", "cache", "rustls-backend"]
|
||||||
|
default = ["cache", "rustls-backend"]
|
||||||
http3-quinn = ["rpxy-lib/http3-quinn"]
|
http3-quinn = ["rpxy-lib/http3-quinn"]
|
||||||
http3-s2n = ["rpxy-lib/http3-s2n"]
|
http3-s2n = ["rpxy-lib/http3-s2n"]
|
||||||
native-tls-backend = ["rpxy-lib/native-tls-backend"]
|
native-tls-backend = ["rpxy-lib/native-tls-backend"]
|
||||||
|
|
@ -30,7 +31,6 @@ mimalloc = { version = "*", default-features = false }
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
serde = { version = "1.0.202", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.202", default-features = false, features = ["derive"] }
|
||||||
derive_builder = "0.20.0"
|
|
||||||
tokio = { version = "1.37.0", default-features = false, features = [
|
tokio = { version = "1.37.0", default-features = false, features = [
|
||||||
"net",
|
"net",
|
||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
|
|
@ -39,7 +39,6 @@ tokio = { version = "1.37.0", default-features = false, features = [
|
||||||
"macros",
|
"macros",
|
||||||
] }
|
] }
|
||||||
async-trait = "0.1.80"
|
async-trait = "0.1.80"
|
||||||
rustls-pemfile = "1.0.4"
|
|
||||||
|
|
||||||
|
|
||||||
# config
|
# config
|
||||||
|
|
|
||||||
|
|
@ -1,183 +0,0 @@
|
||||||
use crate::log::*;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use derive_builder::Builder;
|
|
||||||
use rpxy_lib::{
|
|
||||||
reexports::{Certificate, PrivateKey},
|
|
||||||
CertsAndKeys, CryptoSource,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
io::{self, BufReader, Cursor, Read},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Builder, Debug, Clone)]
|
|
||||||
/// Crypto-related file reader implementing certs::CryptoRead trait
|
|
||||||
pub struct CryptoFileSource {
|
|
||||||
#[builder(setter(custom))]
|
|
||||||
/// Always exist
|
|
||||||
pub tls_cert_path: PathBuf,
|
|
||||||
|
|
||||||
#[builder(setter(custom))]
|
|
||||||
/// Always exist
|
|
||||||
pub tls_cert_key_path: PathBuf,
|
|
||||||
|
|
||||||
#[builder(setter(custom), default)]
|
|
||||||
/// This may not exist
|
|
||||||
pub client_ca_cert_path: Option<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CryptoFileSourceBuilder {
|
|
||||||
pub fn tls_cert_path<T: AsRef<Path>>(&mut self, v: T) -> &mut Self {
|
|
||||||
self.tls_cert_path = Some(v.as_ref().to_path_buf());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn tls_cert_key_path<T: AsRef<Path>>(&mut self, v: T) -> &mut Self {
|
|
||||||
self.tls_cert_key_path = Some(v.as_ref().to_path_buf());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn client_ca_cert_path<T: AsRef<Path>>(&mut self, v: Option<T>) -> &mut Self {
|
|
||||||
self.client_ca_cert_path = Some(v.map(|p| p.as_ref().to_path_buf()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CryptoSource for CryptoFileSource {
|
|
||||||
type Error = io::Error;
|
|
||||||
/// read crypto materials from source
|
|
||||||
async fn read(&self) -> Result<CertsAndKeys, Self::Error> {
|
|
||||||
read_certs_and_keys(
|
|
||||||
&self.tls_cert_path,
|
|
||||||
&self.tls_cert_key_path,
|
|
||||||
self.client_ca_cert_path.as_ref(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/// Returns true when mutual tls is enabled
|
|
||||||
fn is_mutual_tls(&self) -> bool {
|
|
||||||
self.client_ca_cert_path.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read certificates and private keys from file
|
|
||||||
fn read_certs_and_keys(
|
|
||||||
cert_path: &PathBuf,
|
|
||||||
cert_key_path: &PathBuf,
|
|
||||||
client_ca_cert_path: Option<&PathBuf>,
|
|
||||||
) -> Result<CertsAndKeys, io::Error> {
|
|
||||||
debug!("Read TLS server certificates and private key");
|
|
||||||
|
|
||||||
let certs: Vec<_> = {
|
|
||||||
let certs_path_str = cert_path.display().to_string();
|
|
||||||
let mut reader = BufReader::new(
|
|
||||||
File::open(cert_path)
|
|
||||||
.map_err(|e| io::Error::new(e.kind(), format!("Unable to load the certificates [{certs_path_str}]: {e}")))?,
|
|
||||||
);
|
|
||||||
rustls_pemfile::certs(&mut reader)
|
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Unable to parse the certificates"))?
|
|
||||||
}
|
|
||||||
.drain(..)
|
|
||||||
.map(Certificate)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let cert_keys: Vec<_> = {
|
|
||||||
let cert_key_path_str = cert_key_path.display().to_string();
|
|
||||||
let encoded_keys = {
|
|
||||||
let mut encoded_keys = vec![];
|
|
||||||
File::open(cert_key_path)
|
|
||||||
.map_err(|e| {
|
|
||||||
io::Error::new(
|
|
||||||
e.kind(),
|
|
||||||
format!("Unable to load the certificate keys [{cert_key_path_str}]: {e}"),
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
.read_to_end(&mut encoded_keys)?;
|
|
||||||
encoded_keys
|
|
||||||
};
|
|
||||||
let mut reader = Cursor::new(encoded_keys);
|
|
||||||
let pkcs8_keys = rustls_pemfile::pkcs8_private_keys(&mut reader).map_err(|_| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"Unable to parse the certificates private keys (PKCS8)",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
reader.set_position(0);
|
|
||||||
let mut rsa_keys = rustls_pemfile::rsa_private_keys(&mut reader)?;
|
|
||||||
let mut keys = pkcs8_keys;
|
|
||||||
keys.append(&mut rsa_keys);
|
|
||||||
if keys.is_empty() {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"No private keys found - Make sure that they are in PKCS#8/PEM format",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
keys.drain(..).map(PrivateKey).collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
let client_ca_certs = if let Some(path) = client_ca_cert_path {
|
|
||||||
debug!("Read CA certificates for client authentication");
|
|
||||||
// Reads client certificate and returns client
|
|
||||||
let certs: Vec<_> = {
|
|
||||||
let certs_path_str = path.display().to_string();
|
|
||||||
let mut reader = BufReader::new(File::open(path).map_err(|e| {
|
|
||||||
io::Error::new(
|
|
||||||
e.kind(),
|
|
||||||
format!("Unable to load the client certificates [{certs_path_str}]: {e}"),
|
|
||||||
)
|
|
||||||
})?);
|
|
||||||
rustls_pemfile::certs(&mut reader)
|
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Unable to parse the client certificates"))?
|
|
||||||
}
|
|
||||||
.drain(..)
|
|
||||||
.map(Certificate)
|
|
||||||
.collect();
|
|
||||||
Some(certs)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(CertsAndKeys {
|
|
||||||
certs,
|
|
||||||
cert_keys,
|
|
||||||
client_ca_certs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
#[tokio::test]
|
|
||||||
async fn read_server_crt_key_files() {
|
|
||||||
let tls_cert_path = "../example-certs/server.crt";
|
|
||||||
let tls_cert_key_path = "../example-certs/server.key";
|
|
||||||
let crypto_file_source = CryptoFileSourceBuilder::default()
|
|
||||||
.tls_cert_key_path(tls_cert_key_path)
|
|
||||||
.tls_cert_path(tls_cert_path)
|
|
||||||
.build();
|
|
||||||
assert!(crypto_file_source.is_ok());
|
|
||||||
|
|
||||||
let crypto_file_source = crypto_file_source.unwrap();
|
|
||||||
let crypto_elem = crypto_file_source.read().await;
|
|
||||||
assert!(crypto_elem.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn read_server_crt_key_files_with_client_ca_crt() {
|
|
||||||
let tls_cert_path = "../example-certs/server.crt";
|
|
||||||
let tls_cert_key_path = "../example-certs/server.key";
|
|
||||||
let client_ca_cert_path = Some("../example-certs/client.ca.crt");
|
|
||||||
let crypto_file_source = CryptoFileSourceBuilder::default()
|
|
||||||
.tls_cert_key_path(tls_cert_key_path)
|
|
||||||
.tls_cert_path(tls_cert_path)
|
|
||||||
.client_ca_cert_path(client_ca_cert_path)
|
|
||||||
.build();
|
|
||||||
assert!(crypto_file_source.is_ok());
|
|
||||||
|
|
||||||
let crypto_file_source = crypto_file_source.unwrap();
|
|
||||||
let crypto_elem = crypto_file_source.read().await;
|
|
||||||
assert!(crypto_elem.is_ok());
|
|
||||||
|
|
||||||
let crypto_elem = crypto_elem.unwrap();
|
|
||||||
assert!(crypto_elem.client_ca_certs.is_some());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
use super::toml::ConfigToml;
|
use super::toml::ConfigToml;
|
||||||
use crate::{
|
use crate::error::{anyhow, ensure};
|
||||||
cert_file_reader::CryptoFileSource,
|
|
||||||
error::{anyhow, ensure},
|
|
||||||
};
|
|
||||||
use clap::{Arg, ArgAction};
|
use clap::{Arg, ArgAction};
|
||||||
use hot_reload::{ReloaderReceiver, ReloaderService};
|
use hot_reload::{ReloaderReceiver, ReloaderService};
|
||||||
use rpxy_certs::{build_cert_reloader, CryptoFileSourceBuilder, CryptoReloader, ServerCryptoBase};
|
use rpxy_certs::{build_cert_reloader, CryptoFileSourceBuilder, CryptoReloader, ServerCryptoBase};
|
||||||
|
|
@ -43,7 +40,7 @@ pub fn parse_opts() -> Result<Opts, anyhow::Error> {
|
||||||
Ok(Opts { config_file_path, watch })
|
Ok(Opts { config_file_path, watch })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_settings(config: &ConfigToml) -> std::result::Result<(ProxyConfig, AppConfigList<CryptoFileSource>), anyhow::Error> {
|
pub fn build_settings(config: &ConfigToml) -> std::result::Result<(ProxyConfig, AppConfigList), anyhow::Error> {
|
||||||
// build proxy config
|
// build proxy config
|
||||||
let proxy_config: ProxyConfig = config.try_into()?;
|
let proxy_config: ProxyConfig = config.try_into()?;
|
||||||
|
|
||||||
|
|
@ -74,7 +71,7 @@ pub fn build_settings(config: &ConfigToml) -> std::result::Result<(ProxyConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
// build applications
|
// build applications
|
||||||
let mut app_config_list_inner = Vec::<AppConfig<CryptoFileSource>>::new();
|
let mut app_config_list_inner = Vec::<AppConfig>::new();
|
||||||
|
|
||||||
// let mut backends = Backends::new();
|
// let mut backends = Backends::new();
|
||||||
for (app_name, app) in apps.0.iter() {
|
for (app_name, app) in apps.0.iter() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
cert_file_reader::{CryptoFileSource, CryptoFileSourceBuilder},
|
|
||||||
constants::*,
|
constants::*,
|
||||||
error::{anyhow, ensure},
|
error::{anyhow, ensure},
|
||||||
};
|
};
|
||||||
|
|
@ -214,7 +213,7 @@ impl ConfigToml {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
pub fn build_app_config(&self, app_name: &str) -> std::result::Result<AppConfig<CryptoFileSource>, anyhow::Error> {
|
pub fn build_app_config(&self, app_name: &str) -> std::result::Result<AppConfig, anyhow::Error> {
|
||||||
let server_name_string = self.server_name.as_ref().ok_or(anyhow!("Missing server_name"))?;
|
let server_name_string = self.server_name.as_ref().ok_or(anyhow!("Missing server_name"))?;
|
||||||
|
|
||||||
// reverse proxy settings
|
// reverse proxy settings
|
||||||
|
|
@ -224,11 +223,6 @@ impl Application {
|
||||||
let tls_config = if self.tls.is_some() {
|
let tls_config = if self.tls.is_some() {
|
||||||
let tls = self.tls.as_ref().unwrap();
|
let tls = self.tls.as_ref().unwrap();
|
||||||
ensure!(tls.tls_cert_key_path.is_some() && tls.tls_cert_path.is_some());
|
ensure!(tls.tls_cert_key_path.is_some() && tls.tls_cert_path.is_some());
|
||||||
let inner = CryptoFileSourceBuilder::default()
|
|
||||||
.tls_cert_path(tls.tls_cert_path.as_ref().unwrap())
|
|
||||||
.tls_cert_key_path(tls.tls_cert_key_path.as_ref().unwrap())
|
|
||||||
.client_ca_cert_path(tls.client_ca_cert_path.as_deref())
|
|
||||||
.build()?;
|
|
||||||
|
|
||||||
let https_redirection = if tls.https_redirection.is_none() {
|
let https_redirection = if tls.https_redirection.is_none() {
|
||||||
true // Default true
|
true // Default true
|
||||||
|
|
@ -236,10 +230,7 @@ impl Application {
|
||||||
tls.https_redirection.unwrap()
|
tls.https_redirection.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(TlsConfig {
|
Some(TlsConfig { https_redirection })
|
||||||
inner,
|
|
||||||
https_redirection,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
mod cert_file_reader;
|
|
||||||
mod config;
|
mod config;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod error;
|
mod error;
|
||||||
|
|
@ -141,7 +140,7 @@ async fn rpxy_service_with_watcher(
|
||||||
/// Wrapper of entry point for rpxy service with certificate management service
|
/// Wrapper of entry point for rpxy service with certificate management service
|
||||||
async fn rpxy_entrypoint(
|
async fn rpxy_entrypoint(
|
||||||
proxy_config: &rpxy_lib::ProxyConfig,
|
proxy_config: &rpxy_lib::ProxyConfig,
|
||||||
app_config_list: &rpxy_lib::AppConfigList<cert_file_reader::CryptoFileSource>,
|
app_config_list: &rpxy_lib::AppConfigList,
|
||||||
cert_service_and_rx: Option<&(
|
cert_service_and_rx: Option<&(
|
||||||
ReloaderService<rpxy_certs::CryptoReloader, rpxy_certs::ServerCryptoBase>,
|
ReloaderService<rpxy_certs::CryptoReloader, rpxy_certs::ServerCryptoBase>,
|
||||||
ReloaderReceiver<rpxy_certs::ServerCryptoBase>,
|
ReloaderReceiver<rpxy_certs::ServerCryptoBase>,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ publish.workspace = true
|
||||||
# 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", "cache", "rustls-backend"]
|
default = ["sticky-cookie", "cache", "rustls-backend"]
|
||||||
|
# default = ["http3-quinn", "sticky-cookie", "cache", "rustls-backend"]
|
||||||
http3-quinn = ["socket2", "quinn", "h3", "h3-quinn", "rpxy-certs/http3"]
|
http3-quinn = ["socket2", "quinn", "h3", "h3-quinn", "rpxy-certs/http3"]
|
||||||
http3-s2n = [
|
http3-s2n = [
|
||||||
"h3",
|
"h3",
|
||||||
|
|
@ -73,8 +74,8 @@ hyper-rustls = { version = "0.27.1", default-features = false, features = [
|
||||||
# tls and cert management for server
|
# tls and cert management for server
|
||||||
rpxy-certs = { path = "../rpxy-certs/", default-features = false }
|
rpxy-certs = { path = "../rpxy-certs/", default-features = false }
|
||||||
hot_reload = "0.1.5"
|
hot_reload = "0.1.5"
|
||||||
rustls = { version = "0.21.12", default-features = false }
|
rustls = { version = "0.23.8", default-features = false }
|
||||||
tokio-rustls = { version = "0.24.1", features = ["early-data"] }
|
tokio-rustls = { version = "0.26.0", features = ["early-data"] }
|
||||||
webpki = "0.22.4"
|
webpki = "0.22.4"
|
||||||
x509-parser = "0.16.0"
|
x509-parser = "0.16.0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto::CryptoSource,
|
|
||||||
error::*,
|
error::*,
|
||||||
log::*,
|
log::*,
|
||||||
name_exp::{ByteName, ServerName},
|
name_exp::{ByteName, ServerName},
|
||||||
|
|
@ -13,10 +12,7 @@ use super::upstream::PathManager;
|
||||||
|
|
||||||
/// Struct serving information to route incoming connections, like server name to be handled and tls certs/keys settings.
|
/// Struct serving information to route incoming connections, like server name to be handled and tls certs/keys settings.
|
||||||
#[derive(Builder)]
|
#[derive(Builder)]
|
||||||
pub struct BackendApp<T>
|
pub struct BackendApp {
|
||||||
where
|
|
||||||
T: CryptoSource,
|
|
||||||
{
|
|
||||||
#[builder(setter(into))]
|
#[builder(setter(into))]
|
||||||
/// backend application name, e.g., app1
|
/// backend application name, e.g., app1
|
||||||
pub app_name: String,
|
pub app_name: String,
|
||||||
|
|
@ -28,50 +24,27 @@ where
|
||||||
/// tls settings: https redirection with 30x
|
/// tls settings: https redirection with 30x
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
pub https_redirection: Option<bool>,
|
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>
|
impl<'a> BackendAppBuilder {
|
||||||
where
|
|
||||||
T: CryptoSource,
|
|
||||||
{
|
|
||||||
pub fn server_name(&mut self, server_name: impl Into<Cow<'a, str>>) -> &mut Self {
|
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.server_name = Some(server_name.to_server_name());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
/// HashMap and some meta information for multiple Backend structs.
|
/// HashMap and some meta information for multiple Backend structs.
|
||||||
pub struct BackendAppManager<T>
|
pub struct BackendAppManager {
|
||||||
where
|
|
||||||
T: CryptoSource,
|
|
||||||
{
|
|
||||||
/// HashMap of Backend structs, key is server name
|
/// HashMap of Backend structs, key is server name
|
||||||
pub apps: HashMap<ServerName, BackendApp<T>>,
|
pub apps: HashMap<ServerName, BackendApp>,
|
||||||
/// for plaintext http
|
/// for plaintext http
|
||||||
pub default_server_name: Option<ServerName>,
|
pub default_server_name: Option<ServerName>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for BackendAppManager<T>
|
impl TryFrom<&AppConfig> for BackendApp {
|
||||||
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;
|
type Error = RpxyError;
|
||||||
|
|
||||||
fn try_from(app_config: &AppConfig<T>) -> Result<Self, Self::Error> {
|
fn try_from(app_config: &AppConfig) -> Result<Self, Self::Error> {
|
||||||
let mut backend_builder = BackendAppBuilder::default();
|
let mut backend_builder = BackendAppBuilder::default();
|
||||||
let path_manager = PathManager::try_from(app_config)?;
|
let path_manager = PathManager::try_from(app_config)?;
|
||||||
backend_builder
|
backend_builder
|
||||||
|
|
@ -83,28 +56,20 @@ where
|
||||||
backend_builder.build()?
|
backend_builder.build()?
|
||||||
} else {
|
} else {
|
||||||
let tls = app_config.tls.as_ref().unwrap();
|
let tls = app_config.tls.as_ref().unwrap();
|
||||||
backend_builder
|
backend_builder.https_redirection(Some(tls.https_redirection)).build()?
|
||||||
.https_redirection(Some(tls.https_redirection))
|
|
||||||
.crypto_source(Some(tls.inner.clone()))
|
|
||||||
.build()?
|
|
||||||
};
|
};
|
||||||
Ok(backend)
|
Ok(backend)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TryFrom<&AppConfigList<T>> for BackendAppManager<T>
|
impl TryFrom<&AppConfigList> for BackendAppManager {
|
||||||
where
|
|
||||||
T: CryptoSource + Clone,
|
|
||||||
{
|
|
||||||
type Error = RpxyError;
|
type Error = RpxyError;
|
||||||
|
|
||||||
fn try_from(config_list: &AppConfigList<T>) -> Result<Self, Self::Error> {
|
fn try_from(config_list: &AppConfigList) -> Result<Self, Self::Error> {
|
||||||
let mut manager = Self::default();
|
let mut manager = Self::default();
|
||||||
for app_config in config_list.inner.iter() {
|
for app_config in config_list.inner.iter() {
|
||||||
let backend: BackendApp<T> = BackendApp::try_from(app_config)?;
|
let backend: BackendApp = BackendApp::try_from(app_config)?;
|
||||||
manager
|
manager.apps.insert(app_config.server_name.clone().to_server_name(), backend);
|
||||||
.apps
|
|
||||||
.insert(app_config.server_name.clone().to_server_name(), backend);
|
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Registering application {} ({})",
|
"Registering application {} ({})",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use super::load_balance::{
|
||||||
// use super::{BytesName, LbContext, PathNameBytesExp, UpstreamOption};
|
// use super::{BytesName, LbContext, PathNameBytesExp, UpstreamOption};
|
||||||
use super::upstream_opts::UpstreamOption;
|
use super::upstream_opts::UpstreamOption;
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto::CryptoSource,
|
|
||||||
error::RpxyError,
|
error::RpxyError,
|
||||||
globals::{AppConfig, UpstreamUri},
|
globals::{AppConfig, UpstreamUri},
|
||||||
log::*,
|
log::*,
|
||||||
|
|
@ -28,12 +27,9 @@ pub struct PathManager {
|
||||||
inner: HashMap<PathName, UpstreamCandidates>,
|
inner: HashMap<PathName, UpstreamCandidates>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TryFrom<&AppConfig<T>> for PathManager
|
impl TryFrom<&AppConfig> for PathManager {
|
||||||
where
|
|
||||||
T: CryptoSource,
|
|
||||||
{
|
|
||||||
type Error = RpxyError;
|
type Error = RpxyError;
|
||||||
fn try_from(app_config: &AppConfig<T>) -> Result<Self, Self::Error> {
|
fn try_from(app_config: &AppConfig) -> Result<Self, Self::Error> {
|
||||||
let mut inner: HashMap<PathName, UpstreamCandidates> = HashMap::default();
|
let mut inner: HashMap<PathName, UpstreamCandidates> = HashMap::default();
|
||||||
|
|
||||||
app_config.reverse_proxy.iter().for_each(|rpc| {
|
app_config.reverse_proxy.iter().for_each(|rpc| {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,10 @@
|
||||||
pub const RESPONSE_HEADER_SERVER: &str = "rpxy";
|
pub const RESPONSE_HEADER_SERVER: &str = "rpxy";
|
||||||
// pub const LISTEN_ADDRESSES_V4: &[&str] = &["0.0.0.0"];
|
|
||||||
// pub const LISTEN_ADDRESSES_V6: &[&str] = &["[::]"];
|
|
||||||
pub const TCP_LISTEN_BACKLOG: u32 = 1024;
|
pub const TCP_LISTEN_BACKLOG: u32 = 1024;
|
||||||
// pub const HTTP_LISTEN_PORT: u16 = 8080;
|
|
||||||
// pub const HTTPS_LISTEN_PORT: u16 = 8443;
|
|
||||||
pub const PROXY_IDLE_TIMEOUT_SEC: u64 = 20;
|
pub const PROXY_IDLE_TIMEOUT_SEC: u64 = 20;
|
||||||
pub const UPSTREAM_IDLE_TIMEOUT_SEC: u64 = 20;
|
pub const UPSTREAM_IDLE_TIMEOUT_SEC: u64 = 20;
|
||||||
pub const TLS_HANDSHAKE_TIMEOUT_SEC: u64 = 15; // default as with firefox browser
|
pub const TLS_HANDSHAKE_TIMEOUT_SEC: u64 = 15; // default as with firefox browser
|
||||||
pub const MAX_CLIENTS: usize = 512;
|
pub const MAX_CLIENTS: usize = 512;
|
||||||
pub const MAX_CONCURRENT_STREAMS: u32 = 64;
|
pub const MAX_CONCURRENT_STREAMS: u32 = 64;
|
||||||
pub const CERTS_WATCH_DELAY_SECS: u32 = 60;
|
|
||||||
pub const LOAD_CERTS_ONLY_WHEN_UPDATED: bool = true;
|
|
||||||
|
|
||||||
// #[cfg(feature = "http3")]
|
|
||||||
// pub const H3_RESPONSE_BUF_SIZE: usize = 65_536; // 64KB
|
|
||||||
// #[cfg(feature = "http3")]
|
|
||||||
// pub const H3_REQUEST_BUF_SIZE: usize = 65_536; // 64KB // handled by quinn
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ pub enum RpxyError {
|
||||||
// certificate reloader errors
|
// certificate reloader errors
|
||||||
#[error("No certificate reloader when building a proxy for TLS")]
|
#[error("No certificate reloader when building a proxy for TLS")]
|
||||||
NoCertificateReloader,
|
NoCertificateReloader,
|
||||||
#[error("Certificate reload error: {0}")]
|
// #[error("Certificate reload error: {0}")]
|
||||||
CertificateReloadError(#[from] hot_reload::ReloaderError<crate::crypto::ServerCryptoBase>),
|
// CertificateReloadError(#[from] hot_reload::ReloaderError<crate::crypto::ServerCryptoBase>),
|
||||||
|
|
||||||
// backend errors
|
// backend errors
|
||||||
#[error("Invalid reverse proxy setting")]
|
#[error("Invalid reverse proxy setting")]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
use crate::{
|
use crate::{constants::*, count::RequestCount};
|
||||||
constants::*,
|
|
||||||
count::RequestCount,
|
|
||||||
crypto::{CryptoSource, ServerCryptoBase},
|
|
||||||
};
|
|
||||||
use hot_reload::ReloaderReceiver;
|
use hot_reload::ReloaderReceiver;
|
||||||
|
use rpxy_certs::ServerCryptoBase;
|
||||||
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||||
|
|
||||||
/// Global object containing proxy configurations and shared object like counters.
|
/// Global object containing proxy configurations and shared object like counters.
|
||||||
|
|
@ -17,10 +14,8 @@ pub struct Globals {
|
||||||
pub runtime_handle: tokio::runtime::Handle,
|
pub runtime_handle: tokio::runtime::Handle,
|
||||||
/// Shared context - Notify object to stop async tasks
|
/// Shared context - Notify object to stop async tasks
|
||||||
pub term_notify: Option<Arc<tokio::sync::Notify>>,
|
pub term_notify: Option<Arc<tokio::sync::Notify>>,
|
||||||
/// Shared context - Certificate reloader service receiver
|
|
||||||
pub cert_reloader_rx: Option<ReloaderReceiver<ServerCryptoBase>>,
|
|
||||||
/// Shared context - Certificate reloader service receiver // TODO: newer one
|
/// Shared context - Certificate reloader service receiver // TODO: newer one
|
||||||
pub cert_reloader_rx_new: Option<ReloaderReceiver<rpxy_certs::ServerCryptoBase>>,
|
pub cert_reloader_rx: Option<ReloaderReceiver<ServerCryptoBase>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration parameters for proxy transport and request handlers
|
/// Configuration parameters for proxy transport and request handlers
|
||||||
|
|
@ -129,24 +124,18 @@ impl Default for ProxyConfig {
|
||||||
|
|
||||||
/// Configuration parameters for backend applications
|
/// Configuration parameters for backend applications
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub struct AppConfigList<T>
|
pub struct AppConfigList {
|
||||||
where
|
pub inner: Vec<AppConfig>,
|
||||||
T: CryptoSource,
|
|
||||||
{
|
|
||||||
pub inner: Vec<AppConfig<T>>,
|
|
||||||
pub default_app: Option<String>,
|
pub default_app: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration parameters for single backend application
|
/// Configuration parameters for single backend application
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub struct AppConfig<T>
|
pub struct AppConfig {
|
||||||
where
|
|
||||||
T: CryptoSource,
|
|
||||||
{
|
|
||||||
pub app_name: String,
|
pub app_name: String,
|
||||||
pub server_name: String,
|
pub server_name: String,
|
||||||
pub reverse_proxy: Vec<ReverseProxyConfig>,
|
pub reverse_proxy: Vec<ReverseProxyConfig>,
|
||||||
pub tls: Option<TlsConfig<T>>,
|
pub tls: Option<TlsConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration parameters for single reverse proxy corresponding to the path
|
/// Configuration parameters for single reverse proxy corresponding to the path
|
||||||
|
|
@ -167,10 +156,6 @@ pub struct UpstreamUri {
|
||||||
|
|
||||||
/// Configuration parameters on TLS for a single backend application
|
/// Configuration parameters on TLS for a single backend application
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub struct TlsConfig<T>
|
pub struct TlsConfig {
|
||||||
where
|
|
||||||
T: CryptoSource,
|
|
||||||
{
|
|
||||||
pub inner: T,
|
|
||||||
pub https_redirection: bool,
|
pub https_redirection: bool,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
mod backend;
|
mod backend;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod count;
|
mod count;
|
||||||
mod crypto;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod forwarder;
|
mod forwarder;
|
||||||
mod globals;
|
mod globals;
|
||||||
|
|
@ -12,8 +11,13 @@ mod name_exp;
|
||||||
mod proxy;
|
mod proxy;
|
||||||
/* ------------------------------------------------ */
|
/* ------------------------------------------------ */
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto::build_cert_reloader, error::*, forwarder::Forwarder, globals::Globals, log::*,
|
// crypto::build_cert_reloader,
|
||||||
message_handler::HttpMessageHandlerBuilder, proxy::Proxy,
|
error::*,
|
||||||
|
forwarder::Forwarder,
|
||||||
|
globals::Globals,
|
||||||
|
log::*,
|
||||||
|
message_handler::HttpMessageHandlerBuilder,
|
||||||
|
proxy::Proxy,
|
||||||
};
|
};
|
||||||
use futures::future::select_all;
|
use futures::future::select_all;
|
||||||
use hot_reload::ReloaderReceiver;
|
use hot_reload::ReloaderReceiver;
|
||||||
|
|
@ -21,26 +25,19 @@ use rpxy_certs::ServerCryptoBase;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/* ------------------------------------------------ */
|
/* ------------------------------------------------ */
|
||||||
pub use crate::{
|
pub use crate::globals::{AppConfig, AppConfigList, ProxyConfig, ReverseProxyConfig, TlsConfig, UpstreamUri};
|
||||||
crypto::{CertsAndKeys, CryptoSource},
|
|
||||||
globals::{AppConfig, AppConfigList, ProxyConfig, ReverseProxyConfig, TlsConfig, UpstreamUri},
|
|
||||||
};
|
|
||||||
pub mod reexports {
|
pub mod reexports {
|
||||||
pub use hyper::Uri;
|
pub use hyper::Uri;
|
||||||
pub use rustls::{Certificate, PrivateKey};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Entrypoint that creates and spawns tasks of reverse proxy services
|
/// Entrypoint that creates and spawns tasks of reverse proxy services
|
||||||
pub async fn entrypoint<T>(
|
pub async fn entrypoint(
|
||||||
proxy_config: &ProxyConfig,
|
proxy_config: &ProxyConfig,
|
||||||
app_config_list: &AppConfigList<T>,
|
app_config_list: &AppConfigList,
|
||||||
cert_rx: Option<&ReloaderReceiver<ServerCryptoBase>>, // TODO:
|
cert_rx: Option<&ReloaderReceiver<ServerCryptoBase>>, // TODO:
|
||||||
runtime_handle: &tokio::runtime::Handle,
|
runtime_handle: &tokio::runtime::Handle,
|
||||||
term_notify: Option<Arc<tokio::sync::Notify>>,
|
term_notify: Option<Arc<tokio::sync::Notify>>,
|
||||||
) -> RpxyResult<()>
|
) -> RpxyResult<()> {
|
||||||
where
|
|
||||||
T: CryptoSource + Clone + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
#[cfg(all(feature = "http3-quinn", feature = "http3-s2n"))]
|
#[cfg(all(feature = "http3-quinn", feature = "http3-s2n"))]
|
||||||
warn!("Both \"http3-quinn\" and \"http3-s2n\" features are enabled. \"http3-quinn\" will be used");
|
warn!("Both \"http3-quinn\" and \"http3-s2n\" features are enabled. \"http3-quinn\" will be used");
|
||||||
|
|
||||||
|
|
@ -82,26 +79,16 @@ where
|
||||||
// 1. build backends, and make it contained in Arc
|
// 1. build backends, and make it contained in Arc
|
||||||
let app_manager = Arc::new(backend::BackendAppManager::try_from(app_config_list)?);
|
let app_manager = Arc::new(backend::BackendAppManager::try_from(app_config_list)?);
|
||||||
|
|
||||||
// 2. build crypto reloader service
|
// 2. build global shared context
|
||||||
let (cert_reloader_service, cert_reloader_rx) = match proxy_config.https_port {
|
|
||||||
Some(_) => {
|
|
||||||
let (s, r) = build_cert_reloader(&app_manager).await?;
|
|
||||||
(Some(s), Some(r))
|
|
||||||
}
|
|
||||||
None => (None, None),
|
|
||||||
};
|
|
||||||
|
|
||||||
// 3. build global shared context
|
|
||||||
let globals = Arc::new(Globals {
|
let globals = Arc::new(Globals {
|
||||||
proxy_config: proxy_config.clone(),
|
proxy_config: proxy_config.clone(),
|
||||||
request_count: Default::default(),
|
request_count: Default::default(),
|
||||||
runtime_handle: runtime_handle.clone(),
|
runtime_handle: runtime_handle.clone(),
|
||||||
term_notify: term_notify.clone(),
|
term_notify: term_notify.clone(),
|
||||||
cert_reloader_rx: cert_reloader_rx.clone(),
|
cert_reloader_rx: cert_rx.cloned(),
|
||||||
cert_reloader_rx_new: cert_rx.cloned(), // TODO: newer one
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4. build message handler containing Arc-ed http_client and backends, and make it contained in Arc as well
|
// 3. build message handler containing Arc-ed http_client and backends, and make it contained in Arc as well
|
||||||
let forwarder = Arc::new(Forwarder::try_new(&globals).await?);
|
let forwarder = Arc::new(Forwarder::try_new(&globals).await?);
|
||||||
let message_handler = Arc::new(
|
let message_handler = Arc::new(
|
||||||
HttpMessageHandlerBuilder::default()
|
HttpMessageHandlerBuilder::default()
|
||||||
|
|
@ -111,7 +98,7 @@ where
|
||||||
.build()?,
|
.build()?,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 5. spawn each proxy for a given socket with copied Arc-ed message_handler.
|
// 4. spawn each proxy for a given socket with copied Arc-ed message_handler.
|
||||||
// build hyper connection builder shared with proxy instances
|
// build hyper connection builder shared with proxy instances
|
||||||
let connection_builder = proxy::connection_builder(&globals);
|
let connection_builder = proxy::connection_builder(&globals);
|
||||||
|
|
||||||
|
|
@ -132,23 +119,9 @@ where
|
||||||
globals.runtime_handle.spawn(async move { proxy.start().await })
|
globals.runtime_handle.spawn(async move { proxy.start().await })
|
||||||
});
|
});
|
||||||
|
|
||||||
// wait for all future
|
|
||||||
match cert_reloader_service {
|
|
||||||
Some(cert_service) => {
|
|
||||||
tokio::select! {
|
|
||||||
_ = cert_service.start() => {
|
|
||||||
error!("Certificate reloader service got down");
|
|
||||||
}
|
|
||||||
_ = select_all(futures_iter) => {
|
|
||||||
error!("Some proxy services are down");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if let (Ok(Err(e)), _, _) = select_all(futures_iter).await {
|
if let (Ok(Err(e)), _, _) = select_all(futures_iter).await {
|
||||||
error!("Some proxy services are down: {}", e);
|
error!("Some proxy services are down: {}", e);
|
||||||
}
|
return Err(e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{BackendAppManager, LoadBalanceContext},
|
backend::{BackendAppManager, LoadBalanceContext},
|
||||||
crypto::CryptoSource,
|
|
||||||
error::*,
|
error::*,
|
||||||
forwarder::{ForwardRequest, Forwarder},
|
forwarder::{ForwardRequest, Forwarder},
|
||||||
globals::Globals,
|
globals::Globals,
|
||||||
|
|
@ -34,20 +33,18 @@ pub(super) struct HandlerContext {
|
||||||
#[derive(Clone, Builder)]
|
#[derive(Clone, Builder)]
|
||||||
/// HTTP message handler for requests from clients and responses from backend applications,
|
/// HTTP message handler for requests from clients and responses from backend applications,
|
||||||
/// responsible to manipulate and forward messages to upstream backends and downstream clients.
|
/// responsible to manipulate and forward messages to upstream backends and downstream clients.
|
||||||
pub struct HttpMessageHandler<U, C>
|
pub struct HttpMessageHandler<C>
|
||||||
where
|
where
|
||||||
C: Send + Sync + Connect + Clone + 'static,
|
C: Send + Sync + Connect + Clone + 'static,
|
||||||
U: CryptoSource + Clone,
|
|
||||||
{
|
{
|
||||||
forwarder: Arc<Forwarder<C>>,
|
forwarder: Arc<Forwarder<C>>,
|
||||||
pub(super) globals: Arc<Globals>,
|
pub(super) globals: Arc<Globals>,
|
||||||
app_manager: Arc<BackendAppManager<U>>,
|
app_manager: Arc<BackendAppManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<U, C> HttpMessageHandler<U, C>
|
impl<C> HttpMessageHandler<C>
|
||||||
where
|
where
|
||||||
C: Send + Sync + Connect + Clone + 'static,
|
C: Send + Sync + Connect + Clone + 'static,
|
||||||
U: CryptoSource + Clone,
|
|
||||||
{
|
{
|
||||||
/// Handle incoming request message from a client.
|
/// Handle incoming request message from a client.
|
||||||
/// Responsible to passthrough responses from backend applications or generate synthetic error responses.
|
/// Responsible to passthrough responses from backend applications or generate synthetic error responses.
|
||||||
|
|
@ -64,14 +61,7 @@ where
|
||||||
log_data.client_addr(&client_addr);
|
log_data.client_addr(&client_addr);
|
||||||
|
|
||||||
let http_result = self
|
let http_result = self
|
||||||
.handle_request_inner(
|
.handle_request_inner(&mut log_data, req, client_addr, listen_addr, tls_enabled, tls_server_name)
|
||||||
&mut log_data,
|
|
||||||
req,
|
|
||||||
client_addr,
|
|
||||||
listen_addr,
|
|
||||||
tls_enabled,
|
|
||||||
tls_server_name,
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// passthrough or synthetic response
|
// passthrough or synthetic response
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,15 @@ use crate::{
|
||||||
backend::{BackendApp, UpstreamCandidates},
|
backend::{BackendApp, UpstreamCandidates},
|
||||||
constants::RESPONSE_HEADER_SERVER,
|
constants::RESPONSE_HEADER_SERVER,
|
||||||
log::*,
|
log::*,
|
||||||
CryptoSource,
|
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, ensure, Result};
|
use anyhow::{anyhow, ensure, Result};
|
||||||
use http::{header, HeaderValue, Request, Response, Uri};
|
use http::{header, HeaderValue, Request, Response, Uri};
|
||||||
use hyper_util::client::legacy::connect::Connect;
|
use hyper_util::client::legacy::connect::Connect;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
impl<U, C> HttpMessageHandler<U, C>
|
impl<C> HttpMessageHandler<C>
|
||||||
where
|
where
|
||||||
C: Send + Sync + Connect + Clone + 'static,
|
C: Send + Sync + Connect + Clone + 'static,
|
||||||
U: CryptoSource + Clone,
|
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
// Functions to generate messages
|
// Functions to generate messages
|
||||||
|
|
@ -21,7 +19,7 @@ where
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
/// Manipulate a response message sent from a backend application to forward downstream to a client.
|
/// Manipulate a response message sent from a backend application to forward downstream to a client.
|
||||||
pub(super) fn generate_response_forwarded<B>(&self, response: &mut Response<B>, backend_app: &BackendApp<U>) -> Result<()> {
|
pub(super) fn generate_response_forwarded<B>(&self, response: &mut Response<B>, backend_app: &BackendApp) -> Result<()> {
|
||||||
let headers = response.headers_mut();
|
let headers = response.headers_mut();
|
||||||
remove_connection_header(headers);
|
remove_connection_header(headers);
|
||||||
remove_hop_header(headers);
|
remove_hop_header(headers);
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,16 @@ mod proxy_quic_s2n;
|
||||||
use crate::{
|
use crate::{
|
||||||
globals::Globals,
|
globals::Globals,
|
||||||
hyper_ext::rt::{LocalExecutor, TokioTimer},
|
hyper_ext::rt::{LocalExecutor, TokioTimer},
|
||||||
|
name_exp::ServerName,
|
||||||
};
|
};
|
||||||
use hyper_util::server::{self, conn::auto::Builder as ConnectionBuilder};
|
use hyper_util::server::{self, conn::auto::Builder as ConnectionBuilder};
|
||||||
|
use rustc_hash::FxHashMap as HashMap;
|
||||||
|
use rustls::ServerConfig;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// SNI to ServerConfig map type
|
||||||
|
pub type SniServerCryptoMap = HashMap<ServerName, Arc<ServerConfig>>;
|
||||||
|
|
||||||
pub(crate) use proxy_main::Proxy;
|
pub(crate) use proxy_main::Proxy;
|
||||||
|
|
||||||
/// build connection builder shared with proxy instances
|
/// build connection builder shared with proxy instances
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use super::socket::bind_tcp_socket;
|
use super::socket::bind_tcp_socket;
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::TLS_HANDSHAKE_TIMEOUT_SEC,
|
constants::TLS_HANDSHAKE_TIMEOUT_SEC,
|
||||||
crypto::{CryptoSource, ServerCrypto, SniServerCryptoMap},
|
|
||||||
error::*,
|
error::*,
|
||||||
globals::Globals,
|
globals::Globals,
|
||||||
hyper_ext::{
|
hyper_ext::{
|
||||||
|
|
@ -20,14 +19,15 @@ use hyper::{
|
||||||
service::service_fn,
|
service::service_fn,
|
||||||
};
|
};
|
||||||
use hyper_util::{client::legacy::connect::Connect, rt::TokioIo, server::conn::auto::Builder as ConnectionBuilder};
|
use hyper_util::{client::legacy::connect::Connect, rt::TokioIo, server::conn::auto::Builder as ConnectionBuilder};
|
||||||
|
use rpxy_certs::ServerCrypto;
|
||||||
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
|
|
||||||
/// Wrapper function to handle request for HTTP/1.1 and HTTP/2
|
/// Wrapper function to handle request for HTTP/1.1 and HTTP/2
|
||||||
/// HTTP/3 is handled in proxy_h3.rs which directly calls the message handler
|
/// HTTP/3 is handled in proxy_h3.rs which directly calls the message handler
|
||||||
async fn serve_request<U, T>(
|
async fn serve_request<T>(
|
||||||
req: Request<Incoming>,
|
req: Request<Incoming>,
|
||||||
handler: Arc<HttpMessageHandler<U, T>>,
|
handler: Arc<HttpMessageHandler<T>>,
|
||||||
client_addr: SocketAddr,
|
client_addr: SocketAddr,
|
||||||
listen_addr: SocketAddr,
|
listen_addr: SocketAddr,
|
||||||
tls_enabled: bool,
|
tls_enabled: bool,
|
||||||
|
|
@ -35,7 +35,6 @@ async fn serve_request<U, T>(
|
||||||
) -> RpxyResult<Response<ResponseBody>>
|
) -> RpxyResult<Response<ResponseBody>>
|
||||||
where
|
where
|
||||||
T: Send + Sync + Connect + Clone,
|
T: Send + Sync + Connect + Clone,
|
||||||
U: CryptoSource + Clone,
|
|
||||||
{
|
{
|
||||||
handler
|
handler
|
||||||
.handle_request(
|
.handle_request(
|
||||||
|
|
@ -50,10 +49,9 @@ where
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
/// Proxy main object responsible to serve requests received from clients at the given socket address.
|
/// Proxy main object responsible to serve requests received from clients at the given socket address.
|
||||||
pub(crate) struct Proxy<U, T, E = LocalExecutor>
|
pub(crate) struct Proxy<T, E = LocalExecutor>
|
||||||
where
|
where
|
||||||
T: Send + Sync + Connect + Clone + 'static,
|
T: Send + Sync + Connect + Clone + 'static,
|
||||||
U: CryptoSource + Clone + Sync + Send + 'static,
|
|
||||||
{
|
{
|
||||||
/// global context shared among async tasks
|
/// global context shared among async tasks
|
||||||
pub globals: Arc<Globals>,
|
pub globals: Arc<Globals>,
|
||||||
|
|
@ -64,13 +62,12 @@ where
|
||||||
/// hyper connection builder serving http request
|
/// hyper connection builder serving http request
|
||||||
pub connection_builder: Arc<ConnectionBuilder<E>>,
|
pub connection_builder: Arc<ConnectionBuilder<E>>,
|
||||||
/// message handler serving incoming http request
|
/// message handler serving incoming http request
|
||||||
pub message_handler: Arc<HttpMessageHandler<U, T>>,
|
pub message_handler: Arc<HttpMessageHandler<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<U, T> Proxy<U, T>
|
impl<T> Proxy<T>
|
||||||
where
|
where
|
||||||
T: Send + Sync + Connect + Clone + 'static,
|
T: Send + Sync + Connect + Clone + 'static,
|
||||||
U: CryptoSource + Clone + Sync + Send + 'static,
|
|
||||||
{
|
{
|
||||||
/// Serves requests from clients
|
/// Serves requests from clients
|
||||||
fn serve_connection<I>(&self, stream: I, peer_addr: SocketAddr, tls_server_name: Option<ServerName>)
|
fn serve_connection<I>(&self, stream: I, peer_addr: SocketAddr, tls_server_name: Option<ServerName>)
|
||||||
|
|
@ -164,15 +161,11 @@ where
|
||||||
let Some(mut server_crypto_rx) = self.globals.cert_reloader_rx.clone() else {
|
let Some(mut server_crypto_rx) = self.globals.cert_reloader_rx.clone() else {
|
||||||
return Err(RpxyError::NoCertificateReloader);
|
return Err(RpxyError::NoCertificateReloader);
|
||||||
};
|
};
|
||||||
// TODO: newer one
|
|
||||||
let Some(mut server_crypto_rx_new) = self.globals.cert_reloader_rx_new.clone() else {
|
|
||||||
return Err(RpxyError::NoCertificateReloader);
|
|
||||||
};
|
|
||||||
let tcp_socket = bind_tcp_socket(&self.listening_on)?;
|
let tcp_socket = bind_tcp_socket(&self.listening_on)?;
|
||||||
let tcp_listener = tcp_socket.listen(self.globals.proxy_config.tcp_listen_backlog)?;
|
let tcp_listener = tcp_socket.listen(self.globals.proxy_config.tcp_listen_backlog)?;
|
||||||
info!("Start TCP proxy serving with HTTPS request for configured host names");
|
info!("Start TCP proxy serving with HTTPS request for configured host names");
|
||||||
|
|
||||||
let mut server_crypto_map: Option<Arc<SniServerCryptoMap>> = None;
|
let mut server_crypto_map: Option<Arc<super::SniServerCryptoMap>> = None;
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
tcp_cnx = tcp_listener.accept().fuse() => {
|
tcp_cnx = tcp_listener.accept().fuse() => {
|
||||||
|
|
@ -234,28 +227,16 @@ where
|
||||||
error!("Reloader is broken");
|
error!("Reloader is broken");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let cert_keys_map = server_crypto_rx.borrow().clone().unwrap();
|
let server_crypto_base = server_crypto_rx.borrow().clone().unwrap();
|
||||||
let Some(server_crypto): Option<Arc<ServerCrypto>> = (&cert_keys_map).try_into().ok() else {
|
let Some(server_config): Option<Arc<ServerCrypto>> = (&server_crypto_base).try_into().ok() else {
|
||||||
error!("Failed to update server crypto");
|
error!("Failed to update server crypto");
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
server_crypto_map = Some(server_crypto.inner_local_map.clone());
|
let map = server_config.individual_config_map.clone().iter().map(|(k,v)| {
|
||||||
}
|
let server_name = ServerName::from(k.as_slice());
|
||||||
// TODO: newer one
|
(server_name, v.clone())
|
||||||
_ = server_crypto_rx_new.changed().fuse() => {
|
}).collect::<rustc_hash::FxHashMap<_,_>>();
|
||||||
if server_crypto_rx_new.borrow().is_none() {
|
server_crypto_map = Some(Arc::new(map));
|
||||||
error!("Reloader is broken");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let cert_keys_map = server_crypto_rx_new.borrow().clone().unwrap();
|
|
||||||
// let Some(server_crypto) = cert_keys_map.try_into().ok() else {
|
|
||||||
// break;
|
|
||||||
// };
|
|
||||||
// let Some(server_crypto): Option<Arc<ServerCrypto>> = (&cert_keys_map).try_into().ok() else {
|
|
||||||
// error!("Failed to update server crypto");
|
|
||||||
// break;
|
|
||||||
// };
|
|
||||||
// server_crypto_map = Some(server_crypto.inner_local_map.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue