use crate::{
_AFTER_CLOSE_TIMEOUT_MS,
collections::{MaybeUninitSlice, ShortBoxSliceU16},
futures::Sleep,
misc::{ConnectionState, Either},
stream::{BufStreamReader, StreamCommon, StreamReader},
sync::{Arc, AtomicU8, AtomicWaker},
tls::{
TlsMode, TlsStreamBridge, TlsStreamBridgeData,
key_schedule::KeyScheduleRead,
misc::read_after_handshake_data,
protocol::{alert::Alert, key_update::KeyUpdate, new_session_ticket::NewSessionTicket},
},
};
use alloc::boxed::Box;
use core::{
future::poll_fn,
hint::cold_path,
num::NonZeroUsize,
pin::{Pin, pin},
sync::atomic::Ordering,
task::{Poll, ready},
time::Duration,
};
#[derive(Debug)]
pub struct TlsStreamReader<SR, TM, const IS_CLIENT: bool> {
connection_state: Arc<AtomicU8>,
ksr: KeyScheduleRead,
max_fragment_length: u16,
new_session_ticket: Option<NewSessionTicket<ShortBoxSliceU16<u8>>>,
plaintext_consumed: usize,
plaintext_len: usize,
reader_buffer: BufStreamReader,
reader_waker: Arc<AtomicWaker>,
stream_bridge: TlsStreamBridge<IS_CLIENT>,
stream_reader: SR,
timer: Pin<Box<Sleep>>,
_tm: TM,
}
impl<SR, TM, const IS_CLIENT: bool> TlsStreamReader<SR, TM, IS_CLIENT> {
#[inline]
pub(crate) fn new(
connection_state: Arc<AtomicU8>,
ksr: KeyScheduleRead,
max_fragment_length: u16,
new_session_ticket: Option<NewSessionTicket<ShortBoxSliceU16<u8>>>,
plaintext_consumed: usize,
plaintext_len: usize,
reader_buffer: BufStreamReader,
reader_waker: Arc<AtomicWaker>,
stream_bridge: TlsStreamBridge<IS_CLIENT>,
stream_reader: SR,
_tm: TM,
) -> crate::Result<Self> {
Ok(Self {
connection_state,
ksr,
max_fragment_length,
new_session_ticket,
plaintext_consumed,
plaintext_len,
reader_buffer,
reader_waker,
stream_bridge,
stream_reader,
timer: Box::pin(Sleep::new(Duration::from_millis(_AFTER_CLOSE_TIMEOUT_MS))?),
_tm,
})
}
#[inline]
pub fn close_abruptly(&self) {
self.connection_state.store(ConnectionState::ClosedAbruptly.into(), Ordering::Relaxed);
}
#[inline]
pub fn connection_state(&self) -> ConnectionState {
self.connection_state.load(Ordering::Relaxed).into()
}
#[inline]
pub fn send_close_notify(&self) -> crate::Result<()> {
self.connection_state.store(ConnectionState::WriteClosed.into(), Ordering::Relaxed);
self.stream_bridge.update(TlsStreamBridgeData::new(Either::Left(Alert::close_notify())));
Ok(())
}
#[cfg(any(feature = "http2", feature = "web-socket"))]
#[inline]
pub(crate) const fn connection_state_raw(&self) -> &Arc<AtomicU8> {
&self.connection_state
}
}
impl<SR, TM, const IS_CLIENT: bool> StreamCommon for TlsStreamReader<SR, TM, IS_CLIENT> {}
impl<SR, TM, const IS_CLIENT: bool> StreamReader for TlsStreamReader<SR, TM, IS_CLIENT>
where
SR: StreamReader,
TM: TlsMode,
{
#[inline]
async fn read(&mut self, bytes: MaybeUninitSlice<'_, u8>) -> crate::Result<Option<NonZeroUsize>> {
let Self {
connection_state,
ksr,
max_fragment_length,
new_session_ticket,
plaintext_consumed,
plaintext_len,
reader_buffer,
reader_waker,
stream_bridge,
stream_reader,
timer,
_tm,
} = self;
let mut read_fut = pin!(async {
if TM::TY.is_plain_text() {
return stream_reader.read(bytes).await;
}
read_after_handshake_data::<_, _, IS_CLIENT>(
(&*stream_bridge, &*connection_state),
bytes,
ksr,
*max_fragment_length,
new_session_ticket,
plaintext_consumed,
plaintext_len,
reader_buffer,
stream_reader,
alert_cb,
closed_conn_cb,
key_update_cb,
)
.await
});
poll_fn(|cx| match read_fut.as_mut().poll(cx) {
Poll::Ready(res) => Poll::Ready(res),
Poll::Pending => {
reader_waker.register(cx.waker());
let current_state = connection_state.load(Ordering::Relaxed);
match ConnectionState::from(current_state) {
ConnectionState::Draining | ConnectionState::Open => Poll::Pending,
ConnectionState::ClosedAbruptly
| ConnectionState::ClosedGracefully
| ConnectionState::ReadClosed => {
cold_path();
Poll::Ready(Ok(None))
}
ConnectionState::WriteClosed => {
cold_path();
let _rslt = ready!(timer.as_mut().poll(cx));
connection_state.store(ConnectionState::ClosedGracefully.into(), Ordering::Relaxed);
Poll::Ready(Ok(None))
}
}
}
})
.await
}
}
async fn alert_cb<SR, const IS_CLIENT: bool>(
aux: &mut (&TlsStreamBridge<IS_CLIENT>, &Arc<AtomicU8>),
alert: Alert,
_: &mut SR,
) -> crate::Result<bool> {
if alert.is_close_notify() {
aux.1.store(ConnectionState::ReadClosed.into(), Ordering::Relaxed);
aux.0.update(TlsStreamBridgeData::new(Either::Left(alert)));
} else {
aux.1.store(ConnectionState::ClosedAbruptly.into(), Ordering::Relaxed);
}
Ok(true)
}
fn closed_conn_cb<const IS_CLIENT: bool>(aux: &mut (&TlsStreamBridge<IS_CLIENT>, &Arc<AtomicU8>)) {
aux.1.store(ConnectionState::ClosedAbruptly.into(), Ordering::Relaxed);
}
async fn key_update_cb<SR, const IS_CLIENT: bool>(
aux: &mut (&TlsStreamBridge<IS_CLIENT>, &Arc<AtomicU8>),
key_update: Option<KeyUpdate>,
_: &mut SR,
) -> crate::Result<()> {
if let Some(elem) = key_update {
aux.0.update(TlsStreamBridgeData::new(Either::Right(elem)));
}
Ok(())
}