use crate::control::{ControlState, PreferredEnd};
use bytes::Bytes;
use monoloop_contracts::{ConnectionId, ConnectorError, ConnectorErrorKind};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot, Mutex};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConnectionEnd {
pub connection_id: ConnectionId,
pub kind: ConnectionEndKind,
pub initiated_by: EndInitiator,
pub bytes_accepted: u64,
pub bytes_received: u64,
pub safe_transport_error: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConnectionEndKind {
RemoteEof,
Cancelled,
Terminated,
TransportFailure,
LocalShutdown,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EndInitiator {
LocalControl,
Remote,
LocalTransport,
}
#[derive(Clone, Debug)]
pub struct RawInputHandle {
connection_id: ConnectionId,
tx: mpsc::Sender<RawInputMessage>,
finished: Arc<AtomicBool>,
control: Arc<ControlState>,
max_chunk_bytes: usize,
}
#[derive(Debug)]
pub enum RawInputMessage {
Bytes(Bytes),
Finish,
}
impl RawInputHandle {
pub fn new(
connection_id: ConnectionId,
tx: mpsc::Sender<RawInputMessage>,
control: Arc<ControlState>,
max_chunk_bytes: usize,
) -> Self {
Self {
connection_id,
tx,
finished: Arc::new(AtomicBool::new(false)),
control,
max_chunk_bytes,
}
}
pub async fn send(&self, bytes: Bytes) -> Result<(), ConnectorError> {
if let Some(err) = self.control_interrupt() {
return Err(err);
}
if self.finished.load(Ordering::SeqCst) || self.control.is_terminal() {
return Err(ConnectorError::new(
ConnectorErrorKind::WriteFailed,
"send after finish or terminal",
)
.with_connection_id(self.connection_id.as_str()));
}
if bytes.len() > self.max_chunk_bytes {
return Err(ConnectorError::resource("chunk exceeds max_chunk_bytes")
.with_connection_id(self.connection_id.as_str()));
}
self.tx
.send(RawInputMessage::Bytes(bytes))
.await
.map_err(|_| {
ConnectorError::new(ConnectorErrorKind::WriteFailed, "input channel closed")
.with_connection_id(self.connection_id.as_str())
})
}
pub async fn finish(&self) -> Result<(), ConnectorError> {
if let Some(err) = self.control_interrupt() {
return Err(err);
}
if self.finished.swap(true, Ordering::SeqCst) {
return Err(ConnectorError::new(
ConnectorErrorKind::WriteFailed,
"input already finished",
)
.with_connection_id(self.connection_id.as_str()));
}
self.tx.send(RawInputMessage::Finish).await.map_err(|_| {
ConnectorError::new(ConnectorErrorKind::WriteFailed, "input channel closed")
.with_connection_id(self.connection_id.as_str())
})
}
fn control_interrupt(&self) -> Option<ConnectorError> {
if self.control.terminate_requested() {
Some(ConnectorError::terminated().with_connection_id(self.connection_id.as_str()))
} else if self.control.cancel_requested() {
Some(ConnectorError::cancelled().with_connection_id(self.connection_id.as_str()))
} else {
None
}
}
}
#[derive(Debug)]
pub struct RawOutputHandle {
connection_id: ConnectionId,
rx: Mutex<mpsc::Receiver<Bytes>>,
control: Arc<ControlState>,
}
impl RawOutputHandle {
pub fn new(
connection_id: ConnectionId,
rx: mpsc::Receiver<Bytes>,
control: Arc<ControlState>,
) -> Self {
Self {
connection_id,
rx: Mutex::new(rx),
control,
}
}
pub async fn receive(&self) -> Result<Option<Bytes>, ConnectorError> {
if let Some(err) = self.control_interrupt_if_no_data() {
let mut guard = self.rx.lock().await;
match guard.try_recv() {
Ok(bytes) => return Ok(Some(bytes)),
Err(mpsc::error::TryRecvError::Empty) => return Err(err),
Err(mpsc::error::TryRecvError::Disconnected) => return Ok(None),
}
}
let mut guard = self.rx.lock().await;
tokio::select! {
biased;
msg = guard.recv() => Ok(msg),
_ = self.wait_interrupt() => {
match guard.try_recv() {
Ok(bytes) => Ok(Some(bytes)),
Err(mpsc::error::TryRecvError::Empty) => {
Err(self.control_interrupt_if_no_data().unwrap_or_else(|| {
ConnectorError::cancelled()
.with_connection_id(self.connection_id.as_str())
}))
}
Err(mpsc::error::TryRecvError::Disconnected) => Ok(None),
}
}
}
}
async fn wait_interrupt(&self) {
loop {
if self.control.cancel_requested()
|| self.control.terminate_requested()
|| self.control.is_terminal()
{
return;
}
self.control.notify().notified().await;
}
}
fn control_interrupt_if_no_data(&self) -> Option<ConnectorError> {
if self.control.terminate_requested() {
Some(ConnectorError::terminated().with_connection_id(self.connection_id.as_str()))
} else if self.control.cancel_requested() {
Some(ConnectorError::cancelled().with_connection_id(self.connection_id.as_str()))
} else {
None
}
}
}
#[derive(Debug)]
pub struct ConnectionCompletionHandle {
rx: Mutex<Option<oneshot::Receiver<ConnectionEnd>>>,
}
impl ConnectionCompletionHandle {
pub fn new(rx: oneshot::Receiver<ConnectionEnd>) -> Self {
Self {
rx: Mutex::new(Some(rx)),
}
}
pub async fn wait(self) -> ConnectionEnd {
let mut guard = self.rx.lock().await;
let rx = guard
.take()
.expect("ConnectionCompletionHandle polled twice");
match rx.await {
Ok(end) => end,
Err(_) => ConnectionEnd {
connection_id: ConnectionId::new("unknown"),
kind: ConnectionEndKind::TransportFailure,
initiated_by: EndInitiator::LocalTransport,
bytes_accepted: 0,
bytes_received: 0,
safe_transport_error: Some("completion channel dropped".into()),
},
}
}
}
pub struct ConnectionOwner {
pub connection_id: ConnectionId,
pub control: Arc<ControlState>,
pub bytes_accepted: u64,
pub bytes_received: u64,
end_tx: Option<oneshot::Sender<ConnectionEnd>>,
}
impl ConnectionOwner {
pub fn new(
connection_id: ConnectionId,
control: Arc<ControlState>,
end_tx: oneshot::Sender<ConnectionEnd>,
) -> Self {
Self {
connection_id,
control,
bytes_accepted: 0,
bytes_received: 0,
end_tx: Some(end_tx),
}
}
pub fn finish(
&mut self,
observed: ConnectionEndKind,
initiated_by: EndInitiator,
safe_error: Option<String>,
) {
if self.control.is_terminal() {
return;
}
let kind = match self.control.preferred_end_kind() {
Some(PreferredEnd::Terminated) => ConnectionEndKind::Terminated,
Some(PreferredEnd::Cancelled) => ConnectionEndKind::Cancelled,
None => observed,
};
let initiated = match kind {
ConnectionEndKind::Cancelled | ConnectionEndKind::Terminated => {
EndInitiator::LocalControl
}
_ => initiated_by,
};
self.control.mark_terminal();
if let Some(tx) = self.end_tx.take() {
let _ = tx.send(ConnectionEnd {
connection_id: self.connection_id.clone(),
kind,
initiated_by: initiated,
bytes_accepted: self.bytes_accepted,
bytes_received: self.bytes_received,
safe_transport_error: safe_error,
});
}
}
}