use rama_core::extensions::{Extension, Extensions};
use rama_crypto::pki_types::{CertificateDer, PrivateKeyDer};
use rama_utils::{collections::smallvec::SmallVec, macros::generate_set_and_with};
use crate::{
ApplicationProtocol, KeyLogIntent, ProtocolVersion, TlsAlpn, TlsKeyLog, TlsSupportedVersions,
};
use rama_net::address::Host;
#[derive(Debug, Default)]
pub struct TlsClientConfig(Extensions);
impl TlsClientConfig {
#[must_use]
pub fn new() -> Self {
Self(Extensions::new())
}
pub fn default_http() -> Self {
Self::new()
.with_alpn_http_auto()
.with_keylog(KeyLogIntent::Environment)
}
pub fn write_to(&self, extensions: &Extensions) {
extensions.extend(&self.0);
}
generate_set_and_with! {
pub fn alpn(mut self, protocols: SmallVec<[ApplicationProtocol; 2]>) -> Self {
self.0.insert(TlsAlpn(protocols));
self
}
}
generate_set_and_with! {
pub fn alpn_http_auto(mut self) -> Self {
self.0.insert(TlsAlpn::http_auto());
self
}
}
generate_set_and_with! {
pub fn alpn_http_1(mut self) -> Self {
self.0.insert(TlsAlpn::http_1());
self
}
}
generate_set_and_with! {
pub fn alpn_http_2(mut self) -> Self {
self.0.insert(TlsAlpn::http_2());
self
}
}
generate_set_and_with! {
pub fn server_name(mut self, server_name: Host) -> Self {
self.0.insert(TlsServerName(server_name));
self
}
}
generate_set_and_with! {
pub fn server_verify(mut self, mode: ServerVerifyMode) -> Self {
self.0.insert(TlsServerVerify(mode));
self
}
}
generate_set_and_with! {
pub fn supported_versions(mut self, versions: Vec<ProtocolVersion>) -> Self {
self.0.insert(TlsSupportedVersions(versions));
self
}
}
generate_set_and_with! {
pub fn keylog(mut self, intent: KeyLogIntent) -> Self {
self.0.insert(TlsKeyLog(intent));
self
}
}
generate_set_and_with! {
pub fn client_auth(mut self, client_auth: ClientAuth) -> Self {
self.0.insert(TlsClientAuth(client_auth));
self
}
}
generate_set_and_with! {
pub fn store_server_cert_chain(mut self, store: bool) -> Self {
self.0.insert(TlsStoreServerCertChain(store));
self
}
}
pub fn as_extensions(&self) -> &Extensions {
&self.0
}
#[doc(hidden)]
pub fn insert<T: Extension>(&self, piece: T) {
self.0.insert(piece);
}
}
impl Clone for TlsClientConfig {
fn clone(&self) -> Self {
let clone = Self::new();
clone.as_extensions().extend(self.as_extensions());
clone
}
}
#[derive(Debug, Clone, Extension)]
#[extension(tags(tls))]
pub struct TlsServerName(pub Host);
#[derive(Debug, Clone, Extension)]
#[extension(tags(tls))]
pub struct TlsServerVerify(pub ServerVerifyMode);
#[derive(Debug, Clone, Extension)]
#[extension(tags(tls))]
pub struct TlsClientAuth(pub ClientAuth);
#[derive(Debug, Clone, Extension)]
#[extension(tags(tls))]
pub struct TlsStoreServerCertChain(pub bool);
#[derive(Debug, Clone)]
pub enum ClientAuth {
SelfSigned,
Single(ClientAuthData),
}
#[derive(Debug)]
pub struct ClientAuthData {
pub private_key: PrivateKeyDer<'static>,
pub cert_chain: Vec<CertificateDer<'static>>,
}
impl Clone for ClientAuthData {
fn clone(&self) -> Self {
Self {
private_key: self.private_key.clone_key(),
cert_chain: self.cert_chain.clone(),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ServerVerifyMode {
#[default]
Auto,
Disable,
}
#[cfg(test)]
mod tests {
use super::*;
use rama_core::extensions::Extensions;
use rama_utils::collections::smallvec::smallvec;
#[test]
fn pieces_layer_newest_wins_per_type() {
let ext = Extensions::new();
ext.insert(TlsAlpn::http_1());
ext.insert(TlsStoreServerCertChain(true));
ext.insert(TlsAlpn::http_auto());
assert_eq!(
ext.get_ref::<TlsAlpn>().map(|a| a.0.clone()),
Some(smallvec![
ApplicationProtocol::HTTP_2,
ApplicationProtocol::HTTP_11
]),
);
assert_eq!(
ext.get_ref::<TlsStoreServerCertChain>().map(|g| g.0),
Some(true),
);
assert!(ext.get_ref::<TlsServerVerify>().is_none());
}
#[test]
fn config_setters_write_to_bag() {
let config = TlsClientConfig::new()
.with_alpn_http_auto()
.with_server_verify(ServerVerifyMode::Disable);
let bag = Extensions::new();
config.write_to(&bag);
assert_eq!(
bag.get_ref::<TlsAlpn>().map(|a| a.0.clone()),
Some(smallvec![
ApplicationProtocol::HTTP_2,
ApplicationProtocol::HTTP_11
]),
);
assert_eq!(
bag.get_ref::<TlsServerVerify>().map(|v| v.0),
Some(ServerVerifyMode::Disable),
);
}
}