use bytes::Bytes;
use compio_io::{AsyncRead, AsyncWrite};
use monocoque_core::rt::TcpStream;
use smallvec::SmallVec;
use std::io;
use tracing::{debug, trace};
use crate::base::SocketBase;
use crate::{handshake::perform_handshake_with_options, session::SocketType};
use monocoque_core::endpoint::Endpoint;
use monocoque_core::options::SocketOptions;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RepState {
AwaitingRequest,
ReadyToReply,
}
pub struct RepSocket<S = TcpStream>
where
S: AsyncRead + AsyncWrite + Unpin,
{
base: SocketBase<S>,
frames: SmallVec<[Bytes; 4]>,
state: RepState,
}
impl<S> RepSocket<S>
where
S: AsyncRead + AsyncWrite + Unpin,
{
pub async fn new(stream: S) -> io::Result<Self> {
Self::with_options(stream, SocketOptions::default()).await
}
pub async fn with_options(mut stream: S, options: SocketOptions) -> io::Result<Self> {
debug!("[REP] Creating new direct REP socket");
debug!("[REP] Performing ZMTP handshake...");
let handshake_result = perform_handshake_with_options(
&mut stream,
SocketType::Rep,
None,
Some(options.handshake_timeout),
&options,
)
.await
.map_err(|e| io::Error::other(format!("Handshake failed: {}", e)))?;
debug!(
peer_identity = ?handshake_result.peer_identity,
peer_socket_type = ?handshake_result.peer_socket_type,
"[REP] Handshake complete"
);
debug!("[REP] Socket initialized");
let mut base = SocketBase::new(stream, SocketType::Rep, options);
base.curve_cipher = handshake_result.curve_cipher;
Ok(Self {
base,
frames: SmallVec::new(),
state: RepState::AwaitingRequest,
})
}
pub async fn recv(&mut self) -> io::Result<Option<Vec<Bytes>>> {
if self.state != RepState::AwaitingRequest {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Cannot recv while in ReadyToReply state - must call send() first",
));
}
trace!("[REP] Waiting for request");
loop {
loop {
match self.base.process_frame()? {
crate::base::FrameResult::NeedMore => break,
crate::base::FrameResult::CommandHandled => {
if !self.base.send_buffer.is_empty() {
self.base.flush_send_buffer().await?;
}
}
crate::base::FrameResult::Data(more, payload) => {
self.frames.push(payload);
if !more {
let msg: Vec<Bytes> = self.frames.drain(..).collect();
trace!("[REP] Received {} frames", msg.len());
self.state = RepState::ReadyToReply;
return Ok(Some(msg));
}
}
}
}
let n = self.base.read_raw().await?;
if n == 0 {
trace!("[REP] Connection closed");
return Ok(None);
}
if self.base.check_heartbeat()? {
self.base.flush_send_buffer().await?;
}
}
}
pub async fn send(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
if self.state != RepState::ReadyToReply {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Cannot send while awaiting request - must call recv() first",
));
}
trace!("[REP] Sending {} frames", msg.len());
self.base.encode_message_to_write_buf(&msg)?;
self.base.write_from_buf().await?;
self.state = RepState::AwaitingRequest;
trace!("[REP] Reply sent successfully");
Ok(())
}
pub async fn close(self) -> io::Result<()> {
trace!("[REP] Closing socket");
Ok(())
}
pub const fn options(&self) -> &SocketOptions {
&self.base.options
}
pub fn options_mut(&mut self) -> &mut SocketOptions {
&mut self.base.options
}
pub fn set_options(&mut self, options: SocketOptions) {
self.base.set_options(options);
}
pub const fn state(&self) -> RepState {
self.state
}
#[inline]
pub const fn socket_type(&self) -> SocketType {
SocketType::Rep
}
#[inline]
pub fn last_endpoint(&self) -> Option<&Endpoint> {
self.base.last_endpoint()
}
#[inline]
pub fn has_more(&self) -> bool {
self.base.has_more()
}
#[inline]
pub fn events(&self) -> u32 {
self.base.events()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rep_state_machine() {
use bytes::Bytes;
use monocoque_core::rt::TcpListener;
monocoque_core::rt::LocalRuntime::new()
.unwrap()
.block_on(async {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let client_task = monocoque_core::rt::spawn(async move {
monocoque_core::rt::sleep(std::time::Duration::from_millis(10)).await;
let stream = monocoque_core::rt::TcpStream::connect(addr).await.unwrap();
let mut req = crate::req::ReqSocket::new(stream).await.unwrap();
req.send(vec![Bytes::from("test")]).await.unwrap();
let reply = req.recv().await.unwrap();
assert!(reply.is_some());
req
});
let (server_stream, _) = listener.accept().await.unwrap();
let mut rep = RepSocket::new(server_stream).await.unwrap();
assert_eq!(rep.state(), RepState::AwaitingRequest);
let msg = rep.recv().await.unwrap();
assert!(msg.is_some());
assert_eq!(rep.state(), RepState::ReadyToReply);
rep.send(msg.unwrap()).await.unwrap();
assert_eq!(rep.state(), RepState::AwaitingRequest);
monocoque_core::rt::join(client_task).await;
});
}
}
impl RepSocket<TcpStream> {
pub async fn from_tcp(stream: TcpStream) -> io::Result<Self> {
Self::from_tcp_with_options(stream, SocketOptions::default()).await
}
pub async fn from_tcp_with_options(
stream: TcpStream,
options: SocketOptions,
) -> io::Result<Self> {
crate::utils::configure_tcp_stream(&stream, &options, "REP")?;
Self::with_options(stream, options).await
}
}
crate::impl_socket_trait!(RepSocket<S>, SocketType::Rep);