use std::cell::RefCell;
use std::rc::Rc;
use dope_session::{CodecLayer, WriteBufArr};
use crate::state::TlsState;
use crate::wire_bufs::WireBufs;
thread_local! {
static CURRENT_SERVER_CONFIG: RefCell<Option<Rc<shin::server::Config>>> =
const { RefCell::new(None) };
}
pub struct TlsConnState {
pub tls: Option<TlsState>,
pub bufs: WireBufs,
}
impl Default for TlsConnState {
fn default() -> Self {
let tls = TlsCodec::current_server_config().map(|cfg| TlsState::new_server((*cfg).clone()));
let mut bufs = WireBufs::new();
bufs.close = tls.is_none();
Self { tls, bufs }
}
}
pub struct TlsCodec;
impl TlsCodec {
pub fn install_server_config(cfg: shin::server::Config) {
CURRENT_SERVER_CONFIG.with(|slot| {
*slot.borrow_mut() = Some(Rc::new(cfg));
});
}
fn current_server_config() -> Option<Rc<shin::server::Config>> {
CURRENT_SERVER_CONFIG.with(|slot| slot.borrow().clone())
}
}
impl CodecLayer for TlsCodec {
type ConnState = TlsConnState;
type WriteBuf = WriteBufArr;
const IS_PASSTHROUGH: bool = false;
#[inline(always)]
fn is_active(state: &Self::ConnState) -> bool {
state.tls.as_ref().is_some_and(|s| s.is_established())
}
fn process_inbound(state: &mut Self::ConnState, wire_in: &[u8]) {
let Some(tls) = state.tls.as_mut() else {
state.bufs.close = true;
return;
};
if !tls.try_read_tcp(wire_in) {
state.bufs.close = true;
return;
}
while let Some(chunk) = tls.pull_app() {
state.bufs.plaintext.extend_from_slice(&chunk);
}
tls.pull_send_into(&mut state.bufs.wire);
if tls.is_closed() {
state.bufs.close = true;
}
}
fn process_outbound(state: &mut Self::ConnState, plaintext_in: &[u8]) {
let Some(tls) = state.tls.as_mut() else {
state.bufs.close = true;
return;
};
if !tls.is_established() {
return;
}
if tls.write_app(plaintext_in).is_err() {
state.bufs.close = true;
return;
}
tls.pull_send_into(&mut state.bufs.wire);
}
#[inline(always)]
fn wire_slice(state: &Self::ConnState) -> &[u8] {
state.bufs.wire_slice()
}
#[inline(always)]
fn take_plaintext(state: &mut Self::ConnState) -> Vec<u8> {
state.bufs.take_plaintext()
}
#[inline(always)]
fn install_plaintext(state: &mut Self::ConnState, plaintext: Vec<u8>) {
state.bufs.install_plaintext(plaintext);
}
fn take_inflight(state: &mut Self::ConnState) -> Option<(*const u8, u32)> {
state.bufs.take_inflight()
}
fn consume_inflight(state: &mut Self::ConnState, n: usize) -> bool {
state.bufs.consume_inflight(n)
}
#[inline(always)]
fn has_inflight(state: &Self::ConnState) -> bool {
state.bufs.has_inflight()
}
#[inline(always)]
fn close_pending(state: &Self::ConnState) -> bool {
state.bufs.close_pending()
}
}