use std::{
io,
net::SocketAddr,
pin::Pin,
sync::Arc,
task::{Context, Poll},
time::Duration,
};
use tokio::{
io::{AsyncRead, AsyncWrite, ReadBuf},
net::TcpStream,
time,
};
use tracing::warn;
use crate::const_config::TLS_HANDSHAKE_TIMEOUT_SECS;
pub(crate) type BoxedWriteHalf = Box<dyn AsyncWrite + Send + Unpin>;
pub(crate) type BoxedReadHalf = Box<dyn AsyncRead + Send + Unpin>;
pub(crate) enum LynnStream {
Plain(TcpStream),
#[cfg(feature = "tls")]
Tls(Box<tokio_rustls::TlsStream<TcpStream>>),
}
impl AsyncRead for LynnStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.get_mut() {
Self::Plain(stream) => Pin::new(stream).poll_read(cx, buf),
#[cfg(feature = "tls")]
Self::Tls(stream) => Pin::new(stream.as_mut()).poll_read(cx, buf),
}
}
}
impl AsyncWrite for LynnStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
match self.get_mut() {
Self::Plain(stream) => Pin::new(stream).poll_write(cx, buf),
#[cfg(feature = "tls")]
Self::Tls(stream) => Pin::new(stream.as_mut()).poll_write(cx, buf),
}
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.get_mut() {
Self::Plain(stream) => Pin::new(stream).poll_flush(cx),
#[cfg(feature = "tls")]
Self::Tls(stream) => Pin::new(stream.as_mut()).poll_flush(cx),
}
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.get_mut() {
Self::Plain(stream) => Pin::new(stream).poll_shutdown(cx),
#[cfg(feature = "tls")]
Self::Tls(stream) => Pin::new(stream.as_mut()).poll_shutdown(cx),
}
}
}
pub(crate) fn split_transport(stream: LynnStream) -> (BoxedReadHalf, BoxedWriteHalf) {
let (read_half, write_half) = tokio::io::split(stream);
(Box::new(read_half), Box::new(write_half))
}
pub(crate) enum StreamAcceptor {
Plain,
#[cfg(feature = "tls")]
Tls(Arc<tokio_rustls::TlsAcceptor>),
}
impl StreamAcceptor {
pub(crate) async fn accept(&self, stream: TcpStream, addr: SocketAddr) -> Option<LynnStream> {
match self {
Self::Plain => Some(LynnStream::Plain(stream)),
#[cfg(feature = "tls")]
Self::Tls(acceptor) => {
let handshake = time::timeout(
Duration::from_secs(TLS_HANDSHAKE_TIMEOUT_SECS),
acceptor.accept(stream),
);
match handshake.await {
Ok(Ok(tls_stream)) => Some(LynnStream::Tls(Box::new(tls_stream.into()))),
Ok(Err(e)) => {
warn!(
"TLS handshake with {} failed: {}, closing connection",
addr, e
);
None
},
Err(_) => {
warn!(
"TLS handshake with {} timed out after {}s, closing connection",
addr, TLS_HANDSHAKE_TIMEOUT_SECS
);
None
},
}
},
}
}
}