use std::{sync::Arc, time::Duration};
use rustls::server::{NoClientAuth, danger::ClientCertVerifier};
use crate::dquic::{
log::{QLog, handy::NoopLogger},
param::{ServerParameters, handy::server_parameters},
stream::{ProductStreamsConcurrencyController, handy::ConsistentConcurrency},
tls::{AuthClient, handy::AcceptAllClientAuther},
token::{TokenProvider, handy::NoopTokenRegistry},
};
#[derive(Clone)]
pub struct ServerQuicConfig {
pub defer_idle_timeout: Duration,
pub stream_strategy_factory: Arc<dyn ProductStreamsConcurrencyController>,
pub qlogger: Arc<dyn QLog + Send + Sync>,
pub enable_0rtt: bool,
pub enable_sslkeylog: bool,
pub parameters: ServerParameters,
pub alpns: Vec<Vec<u8>>,
pub token_provider: Arc<dyn TokenProvider>,
pub backlog: usize,
pub client_auther: Arc<dyn AuthClient>,
pub client_cert_verifier: Arc<dyn ClientCertVerifier>,
pub anti_port_scan: bool,
}
impl Default for ServerQuicConfig {
fn default() -> Self {
Self {
defer_idle_timeout: Duration::ZERO,
stream_strategy_factory: Arc::new(ConsistentConcurrency::new),
qlogger: Arc::new(NoopLogger),
enable_0rtt: false,
enable_sslkeylog: false,
parameters: server_parameters(),
alpns: Vec::new(),
token_provider: Arc::new(NoopTokenRegistry),
backlog: 128,
client_auther: Arc::new(AcceptAllClientAuther),
client_cert_verifier: Arc::new(NoClientAuth),
anti_port_scan: false,
}
}
}
impl std::fmt::Debug for ServerQuicConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ServerQuicConfig")
.field("defer_idle_timeout", &self.defer_idle_timeout)
.field("enable_0rtt", &self.enable_0rtt)
.field("enable_sslkeylog", &self.enable_sslkeylog)
.field("alpns", &self.alpns.len())
.field("backlog", &self.backlog)
.field("anti_port_scan", &self.anti_port_scan)
.finish_non_exhaustive()
}
}
impl PartialEq for ServerQuicConfig {
fn eq(&self, other: &Self) -> bool {
self.defer_idle_timeout == other.defer_idle_timeout
&& self.enable_0rtt == other.enable_0rtt
&& self.enable_sslkeylog == other.enable_sslkeylog
&& Arc::ptr_eq(
&self.stream_strategy_factory,
&other.stream_strategy_factory,
)
&& Arc::ptr_eq(&self.qlogger, &other.qlogger)
&& self.parameters == other.parameters
&& self.alpns == other.alpns
&& Arc::ptr_eq(&self.token_provider, &other.token_provider)
&& self.backlog == other.backlog
&& Arc::ptr_eq(&self.client_auther, &other.client_auther)
&& Arc::ptr_eq(&self.client_cert_verifier, &other.client_cert_verifier)
&& self.anti_port_scan == other.anti_port_scan
}
}
impl ServerQuicConfig {
pub(crate) fn is_compatible_with(&self, other: &Self) -> bool {
self == other
}
pub(crate) fn build_rustls_server_config(
&self,
resolver: crate::dquic::sni::SniCertResolver,
) -> Result<rustls::ServerConfig, crate::dquic::network::BindServerError> {
use snafu::ResultExt;
const TLS13: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13];
let provider = rustls::ServerConfig::builder().crypto_provider().clone();
let builder = rustls::ServerConfig::builder_with_provider(provider)
.with_protocol_versions(TLS13)
.context(crate::dquic::network::bind_server_error::VersionSnafu)?;
let mut tls = builder
.with_client_cert_verifier(self.client_cert_verifier.clone())
.with_cert_resolver(std::sync::Arc::new(resolver));
tls.alpn_protocols.clone_from(&self.alpns);
if self.enable_0rtt {
tls.max_early_data_size = 0xffff_ffff;
}
Ok(tls)
}
}
#[cfg(test)]
mod server_tests {
use std::{
sync::{Arc, Weak},
time::Duration,
};
use crate::dquic::{server::*, sni::SniCertResolver};
#[test]
fn test_server_quic_config_default() {
let cfg = ServerQuicConfig::default();
assert_eq!(cfg.defer_idle_timeout, Duration::ZERO);
assert!(!cfg.enable_0rtt);
assert!(!cfg.enable_sslkeylog);
assert!(cfg.alpns.is_empty());
assert_eq!(cfg.backlog, 128);
assert!(!cfg.anti_port_scan);
assert_eq!(Arc::strong_count(&cfg.stream_strategy_factory), 1);
assert_eq!(Arc::strong_count(&cfg.qlogger), 1);
assert_eq!(Arc::strong_count(&cfg.token_provider), 1);
assert_eq!(Arc::strong_count(&cfg.client_auther), 1);
assert_eq!(Arc::strong_count(&cfg.client_cert_verifier), 1);
}
#[test]
fn test_server_quic_config_partial_eq_same() {
let a = ServerQuicConfig::default();
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn test_server_quic_config_partial_eq_different_alpns() {
let a = ServerQuicConfig::default();
let mut b = a.clone();
b.alpns = vec![b"h3".to_vec()];
assert_ne!(a, b);
}
#[test]
fn test_server_quic_config_partial_eq_different_backlog() {
let a = ServerQuicConfig::default();
let mut b = a.clone();
b.backlog = 256;
assert_ne!(a, b);
}
#[test]
fn test_server_quic_config_partial_eq_different_anti_port_scan() {
let a = ServerQuicConfig::default();
let mut b = a.clone();
b.anti_port_scan = true;
assert_ne!(a, b);
}
#[test]
fn test_server_quic_config_debug_reports_public_value_fields() {
let cfg = ServerQuicConfig {
defer_idle_timeout: Duration::from_secs(3),
enable_0rtt: true,
enable_sslkeylog: true,
alpns: vec![b"h3".to_vec(), b"dhttp".to_vec()],
backlog: 7,
anti_port_scan: true,
..Default::default()
};
let debug = format!("{cfg:?}");
assert!(debug.contains("defer_idle_timeout: 3s"));
assert!(debug.contains("enable_0rtt: true"));
assert!(debug.contains("enable_sslkeylog: true"));
assert!(debug.contains("alpns: 2"));
assert!(debug.contains("backlog: 7"));
assert!(debug.contains("anti_port_scan: true"));
assert!(debug.contains(".."));
}
#[test]
fn test_server_quic_config_partial_eq_different_common_values() {
let a = ServerQuicConfig::default();
let mut b = a.clone();
b.defer_idle_timeout = Duration::from_secs(1);
assert_ne!(a, b);
let mut b = a.clone();
b.enable_0rtt = true;
assert_ne!(a, b);
let mut b = a.clone();
b.enable_sslkeylog = true;
assert_ne!(a, b);
}
#[test]
fn test_server_quic_config_partial_eq_requires_same_trait_object_arcs() {
let a = ServerQuicConfig::default();
let b = ServerQuicConfig::default();
assert_eq!(a.defer_idle_timeout, b.defer_idle_timeout);
assert_eq!(a.enable_0rtt, b.enable_0rtt);
assert_eq!(a.enable_sslkeylog, b.enable_sslkeylog);
assert_eq!(a.parameters, b.parameters);
assert_eq!(a.alpns, b.alpns);
assert_eq!(a.backlog, b.backlog);
assert_eq!(a.anti_port_scan, b.anti_port_scan);
assert!(
!Arc::ptr_eq(&a.stream_strategy_factory, &b.stream_strategy_factory),
"fresh default configs should not share dynamic strategy factories"
);
assert_ne!(a, b);
}
#[test]
fn test_server_quic_config_clone() {
let a = ServerQuicConfig::default();
let b = a.clone();
assert_eq!(a.defer_idle_timeout, b.defer_idle_timeout);
assert_eq!(a.enable_0rtt, b.enable_0rtt);
assert_eq!(a.enable_sslkeylog, b.enable_sslkeylog);
assert_eq!(a.alpns, b.alpns);
assert_eq!(a.backlog, b.backlog);
assert_eq!(a.anti_port_scan, b.anti_port_scan);
assert!(Arc::ptr_eq(
&a.stream_strategy_factory,
&b.stream_strategy_factory,
));
assert!(Arc::ptr_eq(&a.qlogger, &b.qlogger));
assert!(Arc::ptr_eq(&a.token_provider, &b.token_provider));
assert!(Arc::ptr_eq(&a.client_auther, &b.client_auther));
assert!(Arc::ptr_eq(
&a.client_cert_verifier,
&b.client_cert_verifier,
));
assert_eq!(a, b);
}
#[test]
fn test_server_quic_config_mutate_independent_after_clone() {
let mut a = ServerQuicConfig::default();
let b = a.clone();
a.defer_idle_timeout = Duration::from_secs(42);
a.backlog = 256;
assert_eq!(b.defer_idle_timeout, Duration::ZERO);
assert_eq!(b.backlog, 128);
assert_eq!(a.defer_idle_timeout, Duration::from_secs(42));
assert_eq!(a.backlog, 256);
}
#[test]
fn test_server_quic_config_is_compatible_with_same_arc() {
let a = ServerQuicConfig::default();
let b = a.clone();
assert!(a.is_compatible_with(&b));
}
#[test]
fn test_server_quic_config_is_compatible_with_same_values() {
let mut a = ServerQuicConfig::default();
let b = a.clone();
a.defer_idle_timeout = Duration::from_secs(1);
a.defer_idle_timeout = Duration::ZERO;
assert_eq!(a.defer_idle_timeout, b.defer_idle_timeout);
assert!(a.is_compatible_with(&b));
}
#[test]
fn test_server_quic_config_is_compatible_with_different_alpns() {
let mut a = ServerQuicConfig::default();
let b = a.clone();
a.alpns = vec![b"h3".to_vec()];
assert!(!a.is_compatible_with(&b));
}
#[test]
fn test_server_quic_config_is_compatible_with_different_backlog() {
let mut a = ServerQuicConfig::default();
let b = a.clone();
a.backlog = 256;
assert!(!a.is_compatible_with(&b));
}
#[test]
fn test_server_quic_config_is_compatible_with_different_anti_port_scan() {
let mut a = ServerQuicConfig::default();
let b = a.clone();
a.anti_port_scan = true;
assert!(!a.is_compatible_with(&b));
}
#[test]
fn build_rustls_server_config_copies_alpns_and_enables_0rtt() {
let cfg = ServerQuicConfig {
alpns: vec![b"h3".to_vec(), b"dhttp".to_vec()],
enable_0rtt: true,
..Default::default()
};
let tls = cfg
.build_rustls_server_config(SniCertResolver {
registry: Weak::new(),
})
.expect("default verifier should produce a rustls config");
assert_eq!(tls.alpn_protocols, cfg.alpns);
assert_eq!(tls.max_early_data_size, 0xffff_ffff);
}
#[test]
fn build_rustls_server_config_leaves_0rtt_disabled_by_default() {
let cfg = ServerQuicConfig::default();
let tls = cfg
.build_rustls_server_config(SniCertResolver {
registry: Weak::new(),
})
.expect("default verifier should produce a rustls config");
assert_eq!(tls.max_early_data_size, 0);
}
}