mod quic_sender;
mod tcp_sender;
mod tcp_tls_sender;
mod websocket_sender;
mod websocket_tls_sender;
pub use quic_sender::QuicSender;
pub use tcp_sender::TcpSender;
pub use tcp_tls_sender::TcpTlsSender;
pub use websocket_sender::WebSocketSender;
pub use websocket_tls_sender::WebSocketTlsSender;
use crate::IggyError;
use crate::alloc::buffer::PooledBuffer;
use compio::BufResult;
use compio::buf::IoBufMut;
use compio::io::{AsyncReadExt, AsyncWriteExt};
use compio::net::TcpStream;
use compio::quic::{RecvStream, SendStream};
use compio::tls::TlsStream;
use std::future::Future;
#[cfg(unix)]
use std::os::fd::{AsFd, OwnedFd};
use tracing::debug;
#[cfg(unix)]
use tracing::error;
macro_rules! forward_async_methods {
(
$(
async fn $method_name:ident
$(<$($generic:ident $(: $bound:path)?),+>)?
(
&mut self $(, $arg:ident : $arg_ty:ty )*
) -> $ret:ty ;
)*
) => {
$(
pub async fn $method_name
$(<$($generic $(: $bound)?),+>)?
(&mut self, $( $arg: $arg_ty ),* ) -> $ret {
match self {
Self::Tcp(d) => d.$method_name$(::<$($generic),+>)?($( $arg ),*).await,
Self::TcpTls(s) => s.$method_name$(::<$($generic),+>)?($( $arg ),*).await,
Self::Quic(s) => s.$method_name$(::<$($generic),+>)?($( $arg ),*).await,
Self::WebSocket(s) => s.$method_name$(::<$($generic),+>)?($( $arg ),*).await,
Self::WebSocketTls(s) => s.$method_name$(::<$($generic),+>)?($( $arg ),*).await,
}
}
)*
}
}
pub trait Sender {
fn read<B: IoBufMut>(&mut self, buffer: B) -> impl Future<Output = (Result<(), IggyError>, B)>;
fn send_empty_ok_response(&mut self) -> impl Future<Output = Result<(), IggyError>>;
fn send_ok_response(&mut self, payload: &[u8]) -> impl Future<Output = Result<(), IggyError>>;
fn send_ok_response_vectored(
&mut self,
length: &[u8],
slices: Vec<PooledBuffer>,
) -> impl Future<Output = Result<(), IggyError>>;
fn send_error_response(
&mut self,
error: IggyError,
) -> impl Future<Output = Result<(), IggyError>>;
fn shutdown(&mut self) -> impl Future<Output = Result<(), IggyError>>;
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum SenderKind {
Tcp(TcpSender),
TcpTls(TcpTlsSender),
Quic(QuicSender),
WebSocket(WebSocketSender),
WebSocketTls(WebSocketTlsSender),
}
impl SenderKind {
pub fn get_tcp_sender(stream: TcpStream) -> Self {
Self::Tcp(TcpSender {
stream: Some(stream),
})
}
pub fn get_tcp_tls_sender(stream: TlsStream<TcpStream>) -> Self {
Self::TcpTls(TcpTlsSender { stream })
}
pub fn get_quic_sender(send_stream: SendStream, recv_stream: RecvStream) -> Self {
Self::Quic(QuicSender {
send: send_stream,
recv: recv_stream,
})
}
pub fn get_websocket_sender(stream: WebSocketSender) -> Self {
Self::WebSocket(stream)
}
pub fn get_websocket_tls_sender(stream: WebSocketTlsSender) -> Self {
Self::WebSocketTls(stream)
}
#[cfg(unix)]
pub fn take_and_migrate_tcp(&mut self) -> Option<OwnedFd> {
match self {
SenderKind::Tcp(tcp_sender) => {
let stream = tcp_sender.stream.take()?;
let poll_fd = stream.into_poll_fd().ok()?;
let raw_fd = poll_fd.as_fd();
let Ok(owned_fd) = nix::unistd::dup(raw_fd) else {
error!("Failed to dup fd");
return None;
};
Some(owned_fd)
}
_ => None,
}
}
forward_async_methods! {
async fn read<B: IoBufMut>(&mut self, buffer: B) -> (Result<(), IggyError>, B);
async fn send_empty_ok_response(&mut self) -> Result<(), IggyError>;
async fn send_ok_response(&mut self, payload: &[u8]) -> Result<(), IggyError>;
async fn send_ok_response_vectored(&mut self, length: &[u8], slices: Vec<PooledBuffer>) -> Result<(), IggyError>;
async fn send_error_response(&mut self, error: IggyError) -> Result<(), IggyError>;
async fn shutdown(&mut self) -> Result<(), IggyError>;
}
}
const STATUS_OK: &[u8] = &[0; 4];
pub(crate) async fn read<T, B>(stream: &mut T, buffer: B) -> (Result<(), IggyError>, B)
where
T: AsyncReadExt + AsyncWriteExt + Unpin,
B: IoBufMut,
{
let BufResult(result, buffer) = stream.read_exact(buffer).await;
match (result, buffer) {
(Ok(_), buffer) => (Ok(()), buffer),
(Err(e), buffer) => {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
(Err(IggyError::ConnectionClosed), buffer)
} else {
(Err(IggyError::TcpError), buffer)
}
}
}
}
pub(crate) async fn send_empty_ok_response<T>(stream: &mut T) -> Result<(), IggyError>
where
T: AsyncReadExt + AsyncWriteExt + Unpin,
{
send_ok_response(stream, &[]).await
}
pub(crate) async fn send_ok_response<T>(stream: &mut T, payload: &[u8]) -> Result<(), IggyError>
where
T: AsyncReadExt + AsyncWriteExt + Unpin,
{
send_response(stream, STATUS_OK, payload).await
}
pub(crate) async fn send_ok_response_vectored<T>(
stream: &mut T,
length: &[u8],
slices: Vec<PooledBuffer>,
) -> Result<(), IggyError>
where
T: AsyncReadExt + AsyncWriteExt + Unpin,
{
send_response_vectored(stream, STATUS_OK, length, slices).await
}
pub(crate) async fn send_error_response<T>(
stream: &mut T,
error: IggyError,
) -> Result<(), IggyError>
where
T: AsyncReadExt + AsyncWriteExt + Unpin,
{
send_response(stream, &error.as_code().to_le_bytes(), &[]).await
}
pub(crate) async fn send_response<T>(
stream: &mut T,
status: &[u8],
payload: &[u8],
) -> Result<(), IggyError>
where
T: AsyncReadExt + AsyncWriteExt + Unpin,
{
debug!(
"Sending response of len: {} with status: {:?}...",
payload.len(),
status
);
let length = (payload.len() as u32).to_le_bytes();
stream
.write_all([status, &length, payload].concat())
.await
.0
.map_err(|_| IggyError::TcpError)?;
debug!("Sent response with status: {:?}", status);
Ok(())
}
pub(crate) async fn send_response_vectored<T>(
stream: &mut T,
status: &[u8],
length: &[u8],
mut slices: Vec<PooledBuffer>,
) -> Result<(), IggyError>
where
T: AsyncReadExt + AsyncWriteExt + Unpin,
{
let resp_status = u32::from_le_bytes(status.try_into().unwrap());
debug!(
"Sending vectored response of len: {} with status: {:?}...",
slices.len(),
resp_status
);
let status = PooledBuffer::from(status);
let length = PooledBuffer::from(length);
slices.splice(0..0, [status, length]);
stream
.write_vectored_all(slices)
.await
.0
.map_err(|_| IggyError::TcpError)?;
debug!("Sent response with status: {:?}", resp_status);
Ok(())
}