#![doc(
html_favicon_url = "https://raw.githubusercontent.com/plabayo/rama/main/docs/img/rama_logo.svg"
)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/plabayo/rama/main/docs/img/rama_logo.svg"
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, allow(clippy::float_cmp))]
use std::borrow::Cow;
use rama_core::extensions::Extension;
use rama_utils::collections::smallvec::{SmallVec, smallvec};
mod enums;
pub use enums::{
ApplicationProtocol, CertificateCompressionAlgorithm, CipherSuite, CompressionAlgorithm,
ECPointFormat, ExtensionId, ProtocolVersion, SignatureScheme, SupportedGroup,
};
pub mod client;
pub mod fingerprint;
pub mod keylog;
pub mod server;
#[cfg(feature = "http")]
mod http;
#[cfg(feature = "dial9")]
mod dial9;
#[derive(Clone, Debug, Extension)]
#[extension(tags(tls))]
pub struct TlsAlpn(pub SmallVec<[ApplicationProtocol; 2]>);
impl TlsAlpn {
#[must_use]
pub fn http_auto() -> Self {
Self(smallvec![
ApplicationProtocol::HTTP_2,
ApplicationProtocol::HTTP_11,
])
}
#[must_use]
pub fn http_1() -> Self {
Self(smallvec![ApplicationProtocol::HTTP_11])
}
#[must_use]
pub fn http_2() -> Self {
Self(smallvec![ApplicationProtocol::HTTP_2])
}
}
#[derive(Debug, Clone, Extension)]
#[extension(tags(tls))]
pub struct TlsKeyLog(pub KeyLogIntent);
#[derive(Debug, Clone, Extension)]
#[extension(tags(tls))]
pub struct TlsSupportedVersions(pub Vec<ProtocolVersion>);
#[derive(Debug, Clone, Extension)]
#[extension(tags(tls))]
pub struct TlsTunnel {
pub server_identity: Option<rama_net::address::Host>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TlsTunnelMode<'a> {
Plain,
Tls(Option<&'a rama_net::address::Host>),
}
#[must_use]
pub fn resolve_tls_tunnel<'a>(
tunnel: Option<&'a TlsTunnel>,
fallback_server_identity: Option<&'a rama_net::address::Host>,
) -> TlsTunnelMode<'a> {
match tunnel {
Some(tunnel) => {
TlsTunnelMode::Tls(tunnel.server_identity.as_ref().or(fallback_server_identity))
}
None => fallback_server_identity.map_or(TlsTunnelMode::Plain, |identity| {
TlsTunnelMode::Tls(Some(identity))
}),
}
}
#[cfg(test)]
mod tunnel_tests {
use super::*;
use rama_net::address::Host;
#[test]
fn hardcoded_identity_enables_tls_without_context() {
let identity = Host::from_static("example.com");
assert_eq!(
resolve_tls_tunnel(None, Some(&identity)),
TlsTunnelMode::Tls(Some(&identity))
);
}
#[test]
fn empty_context_falls_back_to_hardcoded_identity() {
let tunnel = TlsTunnel {
server_identity: None,
};
let identity = Host::from_static("example.com");
assert_eq!(
resolve_tls_tunnel(Some(&tunnel), Some(&identity)),
TlsTunnelMode::Tls(Some(&identity))
);
}
#[test]
fn empty_context_without_fallback_still_requests_tls() {
let tunnel = TlsTunnel {
server_identity: None,
};
assert_eq!(
resolve_tls_tunnel(Some(&tunnel), None),
TlsTunnelMode::Tls(None)
);
}
}
#[derive(Debug, Clone, Default, Extension)]
#[extension(tags(tls))]
pub struct SecureTransport {
client_hello: Option<client::ClientHello>,
}
impl SecureTransport {
#[must_use]
pub fn with_client_hello(hello: client::ClientHello) -> Self {
Self {
client_hello: Some(hello),
}
}
#[must_use]
pub fn client_hello(&self) -> Option<&client::ClientHello> {
self.client_hello.as_ref()
}
}
#[derive(Debug, Clone, Default)]
pub enum KeyLogIntent {
#[default]
Environment,
Disabled,
File(String),
Custom(std::sync::Arc<dyn keylog::KeyLogSink>),
}
impl KeyLogIntent {
#[must_use]
pub fn env_file_path() -> Option<String> {
std::env::var("SSLKEYLOGFILE").ok()
}
#[must_use]
pub fn file_path(&self) -> Option<Cow<'_, str>> {
match self {
Self::Disabled | Self::Custom(_) => None,
Self::Environment => Self::env_file_path().map(Into::into),
Self::File(keylog_filename) => Some(keylog_filename.into()),
}
}
}