Skip to content

Commit

Permalink
wip: refactor whole module in lib
Browse files Browse the repository at this point in the history
  • Loading branch information
junkurihara committed Nov 21, 2023
1 parent 7bc6e30 commit f98c778
Show file tree
Hide file tree
Showing 42 changed files with 943 additions and 531 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]

members = ["rpxy-bin", "rpxy-lib"]
members = ["rpxy-bin", "rpxy-lib", "legacy-lib"]
exclude = ["submodules"]
resolver = "2"

Expand Down
89 changes: 89 additions & 0 deletions legacy-lib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
[package]
name = "rpxy-lib-legacy"
version = "0.6.2"
authors = ["Jun Kurihara"]
homepage = "https://github.com/junkurihara/rust-rpxy"
repository = "https://github.com/junkurihara/rust-rpxy"
license = "MIT"
readme = "../README.md"
edition = "2021"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["http3-s2n", "sticky-cookie", "cache"]
http3-quinn = ["quinn", "h3", "h3-quinn", "socket2"]
http3-s2n = ["h3", "s2n-quic", "s2n-quic-rustls", "s2n-quic-h3"]
sticky-cookie = ["base64", "sha2", "chrono"]
cache = ["http-cache-semantics", "lru"]
native-roots = ["hyper-rustls/native-tokio"]

[dependencies]
rand = "0.8.5"
rustc-hash = "1.1.0"
bytes = "1.5.0"
derive_builder = "0.12.0"
futures = { version = "0.3.29", features = ["alloc", "async-await"] }
tokio = { version = "1.34.0", default-features = false, features = [
"net",
"rt-multi-thread",
"time",
"sync",
"macros",
"fs",
] }
async-trait = "0.1.74"
hot_reload = "0.1.4" # reloading certs

# Error handling
anyhow = "1.0.75"
thiserror = "1.0.50"

# http and tls
http = "1.0.0"
http-body-util = "0.1.0"
hyper = { version = "1.0.1", default-features = false }
hyper-util = { version = "0.1.1", features = ["full"] }
hyper-rustls = { version = "0.24.2", default-features = false, features = [
"tokio-runtime",
"webpki-tokio",
"http1",
"http2",
] }
tokio-rustls = { version = "0.24.1", features = ["early-data"] }
rustls = { version = "0.21.9", default-features = false }
webpki = "0.22.4"
x509-parser = "0.15.1"

# logging
tracing = { version = "0.1.40" }

# http/3
quinn = { version = "0.10.2", optional = true }
h3 = { path = "../submodules/h3/h3/", optional = true }
h3-quinn = { path = "../submodules/h3/h3-quinn/", optional = true }
s2n-quic = { version = "1.31.0", default-features = false, features = [
"provider-tls-rustls",
], optional = true }
s2n-quic-h3 = { path = "../submodules/s2n-quic-h3/", optional = true }
s2n-quic-rustls = { version = "0.31.0", optional = true }
# for UDP socket wit SO_REUSEADDR when h3 with quinn
socket2 = { version = "0.5.5", features = ["all"], optional = true }

# cache
http-cache-semantics = { path = "../submodules/rusty-http-cache-semantics/", optional = true }
lru = { version = "0.12.0", optional = true }

# cookie handling for sticky cookie
chrono = { version = "0.4.31", default-features = false, features = [
"unstable-locales",
"alloc",
"clock",
], optional = true }
base64 = { version = "0.21.5", optional = true }
sha2 = { version = "0.10.8", default-features = false, optional = true }


[dev-dependencies]
# http and tls
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
91 changes: 91 additions & 0 deletions legacy-lib/src/certs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use async_trait::async_trait;
use rustc_hash::FxHashSet as HashSet;
use rustls::{
sign::{any_supported_type, CertifiedKey},
Certificate, OwnedTrustAnchor, PrivateKey,
};
use std::io;
use x509_parser::prelude::*;

#[async_trait]
// Trait to read certs and keys anywhere from KVS, file, sqlite, etc.
pub trait CryptoSource {
type Error;

/// read crypto materials from source
async fn read(&self) -> Result<CertsAndKeys, Self::Error>;

/// Returns true when mutual tls is enabled
fn is_mutual_tls(&self) -> bool;
}

/// Certificates and private keys in rustls loaded from files
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CertsAndKeys {
pub certs: Vec<Certificate>,
pub cert_keys: Vec<PrivateKey>,
pub client_ca_certs: Option<Vec<Certificate>>,
}

impl CertsAndKeys {
pub fn parse_server_certs_and_keys(&self) -> Result<CertifiedKey, anyhow::Error> {
// for (server_name_bytes_exp, certs_and_keys) in self.inner.iter() {
let signing_key = self
.cert_keys
.iter()
.find_map(|k| {
if let Ok(sk) = any_supported_type(k) {
Some(sk)
} else {
None
}
})
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Unable to find a valid certificate and key",
)
})?;
Ok(CertifiedKey::new(self.certs.clone(), signing_key))
}

pub fn parse_client_ca_certs(&self) -> Result<(Vec<OwnedTrustAnchor>, HashSet<Vec<u8>>), anyhow::Error> {
let certs = self.client_ca_certs.as_ref().ok_or(anyhow::anyhow!("No client cert"))?;

let owned_trust_anchors: Vec<_> = certs
.iter()
.map(|v| {
// let trust_anchor = tokio_rustls::webpki::TrustAnchor::try_from_cert_der(&v.0).unwrap();
let trust_anchor = webpki::TrustAnchor::try_from_cert_der(&v.0).unwrap();
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
trust_anchor.subject,
trust_anchor.spki,
trust_anchor.name_constraints,
)
})
.collect();

// TODO: SKID is not used currently
let subject_key_identifiers: HashSet<_> = certs
.iter()
.filter_map(|v| {
// retrieve ca key id (subject key id)
let cert = parse_x509_certificate(&v.0).unwrap().1;
let subject_key_ids = cert
.iter_extensions()
.filter_map(|ext| match ext.parsed_extension() {
ParsedExtension::SubjectKeyIdentifier(skid) => Some(skid),
_ => None,
})
.collect::<Vec<_>>();
if !subject_key_ids.is_empty() {
Some(subject_key_ids[0].0.to_owned())
} else {
None
}
})
.collect();

Ok((owned_trust_anchors, subject_key_identifiers))
}
}
45 changes: 45 additions & 0 deletions legacy-lib/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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 HTTP_LISTEN_PORT: u16 = 8080;
// pub const HTTPS_LISTEN_PORT: u16 = 8443;
pub const PROXY_TIMEOUT_SEC: u64 = 60;
pub const UPSTREAM_TIMEOUT_SEC: u64 = 60;
pub const TLS_HANDSHAKE_TIMEOUT_SEC: u64 = 15; // default as with firefox browser
pub const MAX_CLIENTS: usize = 512;
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)]
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
pub mod H3 {
pub const ALT_SVC_MAX_AGE: u32 = 3600;
pub const REQUEST_MAX_BODY_SIZE: usize = 268_435_456; // 256MB
pub const MAX_CONCURRENT_CONNECTIONS: u32 = 4096;
pub const MAX_CONCURRENT_BIDISTREAM: u32 = 64;
pub const MAX_CONCURRENT_UNISTREAM: u32 = 64;
pub const MAX_IDLE_TIMEOUT: u64 = 10; // secs
}

#[cfg(feature = "sticky-cookie")]
/// For load-balancing with sticky cookie
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;
#[cfg(feature = "cache")]
// on memory cache if less than or equel to
pub const MAX_CACHE_EACH_SIZE_ON_MEMORY: usize = 4_096;

// TODO: max cache size in total
86 changes: 86 additions & 0 deletions legacy-lib/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
pub use anyhow::{anyhow, bail, ensure, Context};
use std::io;
use thiserror::Error;

pub type Result<T> = std::result::Result<T, RpxyError>;

/// Describes things that can go wrong in the Rpxy
#[derive(Debug, Error)]
pub enum RpxyError {
#[error("Proxy build error: {0}")]
ProxyBuild(#[from] crate::proxy::ProxyBuilderError),

#[error("Backend build error: {0}")]
BackendBuild(#[from] crate::backend::BackendBuilderError),

#[error("MessageHandler build error: {0}")]
HandlerBuild(#[from] crate::handler::HttpMessageHandlerBuilderError),

#[error("Config builder error: {0}")]
ConfigBuild(&'static str),

#[error("Http Message Handler Error: {0}")]
Handler(&'static str),

#[error("Cache Error: {0}")]
Cache(&'static str),

#[error("Http Request Message Error: {0}")]
Request(&'static str),

#[error("TCP/UDP Proxy Layer Error: {0}")]
Proxy(String),

#[allow(unused)]
#[error("LoadBalance Layer Error: {0}")]
LoadBalance(String),

#[error("I/O Error: {0}")]
Io(#[from] io::Error),

// #[error("Toml Deserialization Error")]
// TomlDe(#[from] toml::de::Error),
#[cfg(feature = "http3-quinn")]
#[error("Quic Connection Error [quinn]: {0}")]
QuicConn(#[from] quinn::ConnectionError),

#[cfg(feature = "http3-s2n")]
#[error("Quic Connection Error [s2n-quic]: {0}")]
QUicConn(#[from] s2n_quic::connection::Error),

#[cfg(feature = "http3-quinn")]
#[error("H3 Error [quinn]: {0}")]
H3(#[from] h3::Error),

#[cfg(feature = "http3-s2n")]
#[error("H3 Error [s2n-quic]: {0}")]
H3(#[from] s2n_quic_h3::h3::Error),

#[error("rustls Connection Error: {0}")]
Rustls(#[from] rustls::Error),

#[error("Hyper Error: {0}")]
Hyper(#[from] hyper::Error),

#[error("Hyper Http Error: {0}")]
HyperHttp(#[from] hyper::http::Error),

#[error("Hyper Http HeaderValue Error: {0}")]
HyperHeaderValue(#[from] hyper::header::InvalidHeaderValue),

#[error("Hyper Http HeaderName Error: {0}")]
HyperHeaderName(#[from] hyper::header::InvalidHeaderName),

#[error(transparent)]
Other(#[from] anyhow::Error),
}

#[allow(dead_code)]
#[derive(Debug, Error, Clone)]
pub enum ClientCertsError {
#[error("TLS Client Certificate is Required for Given SNI: {0}")]
ClientCertRequired(String),

#[error("Inconsistent TLS Client Certificate for Given SNI: {0}")]
InconsistentClientCert(String),
}
Loading

0 comments on commit f98c778

Please sign in to comment.