use std::{fmt::Debug, io};
use btls::{
error::ErrorStack,
ssl::{self, SslConnectorBuilder},
};
use btls_sys as ffi;
pub use ssl::CertificateCompressionAlgorithm;
#[allow(clippy::type_complexity)]
pub enum Codec {
Pointer(fn(&[u8], &mut dyn io::Write) -> io::Result<()>),
Dynamic(Box<dyn Fn(&[u8], &mut dyn io::Write) -> io::Result<()> + Send + Sync>),
}
pub trait CertificateCompressor: Debug + Sync + Send + 'static {
fn compress(&self) -> Codec;
fn decompress(&self) -> Codec;
fn algorithm(&self) -> CertificateCompressionAlgorithm;
}
struct Compressor<const ALGORITHM: i32> {
compress: Codec,
decompress: Codec,
}
impl Codec {
#[inline]
fn call(&self, input: &[u8], output: &mut dyn io::Write) -> io::Result<()> {
match self {
Codec::Pointer(func) => func(input, output),
Codec::Dynamic(closure) => closure(input, output),
}
}
}
impl<const ALGORITHM: i32> ssl::CertificateCompressor for Compressor<ALGORITHM> {
const ALGORITHM: CertificateCompressionAlgorithm = match ALGORITHM {
ffi::TLSEXT_cert_compression_zlib => CertificateCompressionAlgorithm::ZLIB,
ffi::TLSEXT_cert_compression_brotli => CertificateCompressionAlgorithm::BROTLI,
ffi::TLSEXT_cert_compression_zstd => CertificateCompressionAlgorithm::ZSTD,
_ => unreachable!(),
};
const CAN_COMPRESS: bool = true;
const CAN_DECOMPRESS: bool = true;
#[inline]
fn compress<W>(&self, input: &[u8], output: &mut W) -> io::Result<()>
where
W: io::Write,
{
self.compress.call(input, output)
}
#[inline]
fn decompress<W>(&self, input: &[u8], output: &mut W) -> io::Result<()>
where
W: io::Write,
{
self.decompress.call(input, output)
}
}
pub(super) fn register(
compressor: &dyn CertificateCompressor,
builder: &mut SslConnectorBuilder,
) -> Result<(), ErrorStack> {
match compressor.algorithm() {
CertificateCompressionAlgorithm::ZLIB => {
builder.add_certificate_compression_algorithm(Compressor::<
{ ffi::TLSEXT_cert_compression_zlib },
> {
compress: compressor.compress(),
decompress: compressor.decompress(),
})
}
CertificateCompressionAlgorithm::BROTLI => {
builder.add_certificate_compression_algorithm(Compressor::<
{ ffi::TLSEXT_cert_compression_brotli },
> {
compress: compressor.compress(),
decompress: compressor.decompress(),
})
}
CertificateCompressionAlgorithm::ZSTD => {
builder.add_certificate_compression_algorithm(Compressor::<
{ ffi::TLSEXT_cert_compression_zstd },
> {
compress: compressor.compress(),
decompress: compressor.decompress(),
})
}
_ => unreachable!(),
}
}