monocoque/zmq/rep.rs
1//! REP socket implementation.
2
3use super::common::channel_to_io_error;
4use bytes::Bytes;
5use monocoque_core::monitor::{SocketEventSender, SocketMonitor, create_monitor};
6use monocoque_core::options::SocketOptions;
7use monocoque_core::rt::{TcpListener, TcpStream};
8use monocoque_zmtp::SocketType;
9use monocoque_zmtp::rep::RepSocket as InternalRep;
10use std::io;
11
12/// A REP socket for synchronous reply patterns.
13///
14/// REP sockets enforce strict alternation between receive and send:
15/// - Must call `recv()` to get a request
16/// - Must call `send()` to reply before next `recv()`
17/// - Automatically handles routing envelopes
18///
19/// They're used for:
20/// - Synchronous RPC servers
21/// - Request-reply protocols
22/// - Service endpoints
23///
24/// ## ZeroMQ Compatibility
25///
26/// Compatible with `zmq::REQ` and `zmq::REP` sockets from libzmq.
27///
28/// ## Example
29///
30/// ```rust,no_run
31/// use monocoque::zmq::RepSocket;
32/// use monocoque_core::rt::TcpListener;
33/// use bytes::Bytes;
34///
35/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
36/// // Bind and accept
37/// let listener = TcpListener::bind("127.0.0.1:5555").await?;
38/// let (stream, _) = listener.accept().await?;
39/// let mut socket = RepSocket::from_tcp(stream).await?;
40///
41/// loop {
42/// // Receive request
43/// if let Ok(Some(request)) = socket.recv().await {
44/// println!("Got request: {:?}", request);
45///
46/// // Send reply
47/// socket.send(vec![Bytes::from("REPLY")]).await?;
48/// }
49/// }
50/// # }
51/// ```
52pub struct RepSocket<S = TcpStream>
53where
54 S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
55{
56 inner: InternalRep<S>,
57 monitor: Option<SocketEventSender>,
58}
59
60impl RepSocket {
61 /// Bind to `addr`, accept one connection, and return a ready REP socket.
62 ///
63 /// Returns the `TcpListener` so the caller can accept further connections.
64 ///
65 /// # Example
66 ///
67 /// ```rust,no_run
68 /// use monocoque::zmq::RepSocket;
69 /// use bytes::Bytes;
70 ///
71 /// # async fn example() -> std::io::Result<()> {
72 /// let (_listener, mut socket) = RepSocket::bind("127.0.0.1:5555").await?;
73 /// if let Ok(Some(req)) = socket.recv().await {
74 /// socket.send(vec![Bytes::from("PONG")]).await?;
75 /// }
76 /// # Ok(())
77 /// # }
78 /// ```
79 pub async fn bind(
80 addr: impl monocoque_core::rt::ToSocketAddrs,
81 ) -> io::Result<(TcpListener, Self)> {
82 let listener = TcpListener::bind(addr).await?;
83 let (stream, _) = listener.accept().await?;
84 let socket = Self::from_tcp(stream).await?;
85 Ok((listener, socket))
86 }
87
88 /// Bind with custom socket options.
89 pub async fn bind_with_options(
90 addr: impl monocoque_core::rt::ToSocketAddrs,
91 options: SocketOptions,
92 ) -> io::Result<(TcpListener, Self)> {
93 let listener = TcpListener::bind(addr).await?;
94 let (stream, _) = listener.accept().await?;
95 let socket = Self::from_tcp_with_options(stream, options).await?;
96 Ok((listener, socket))
97 }
98
99 /// Create a REP socket from an existing TCP stream.
100 ///
101 /// Create a REP socket from a TCP stream with TCP_NODELAY enabled.
102 pub async fn from_tcp(stream: TcpStream) -> io::Result<Self> {
103 Ok(Self {
104 inner: InternalRep::from_tcp(stream).await?,
105 monitor: None,
106 })
107 }
108
109 /// Create a REP socket from a TCP stream with custom options.
110 pub async fn from_tcp_with_options(
111 stream: TcpStream,
112 options: monocoque_core::options::SocketOptions,
113 ) -> io::Result<Self> {
114 Ok(Self {
115 inner: InternalRep::with_options(stream, options).await?,
116 monitor: None,
117 })
118 }
119
120 /// Create a REP socket from any stream with custom options.
121 pub async fn with_options<Stream>(
122 stream: Stream,
123 options: monocoque_core::options::SocketOptions,
124 ) -> io::Result<RepSocket<Stream>>
125 where
126 Stream: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
127 {
128 Ok(RepSocket {
129 inner: InternalRep::with_options(stream, options).await?,
130 monitor: None,
131 })
132 }
133}
134
135// Generic impl - works with any stream type
136impl<S> RepSocket<S>
137where
138 S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
139{
140 /// Enable monitoring for this socket.
141 ///
142 /// Returns a receiver for socket lifecycle events.
143 pub fn monitor(&mut self) -> SocketMonitor {
144 let (sender, receiver) = create_monitor();
145 self.monitor = Some(sender);
146 receiver
147 }
148
149 /// Receive a request message.
150 ///
151 /// This blocks until a request is received. The routing envelope is
152 /// automatically extracted and stored for the subsequent `send()` call.
153 ///
154 /// # Returns
155 ///
156 /// - `Some(msg)` - Received a request (content only, envelope stripped)
157 /// - `None` - Connection closed gracefully or error occurred
158 ///
159 /// # Example
160 ///
161 /// ```rust,no_run
162 /// use monocoque::zmq::RepSocket;
163 ///
164 /// # async fn example(socket: &mut RepSocket) -> std::io::Result<()> {
165 /// if let Ok(Some(request)) = socket.recv().await {
166 /// for (i, frame) in request.iter().enumerate() {
167 /// println!("Frame {}: {:?}", i, frame);
168 /// }
169 /// }
170 /// # Ok(())
171 /// # }
172 /// ```
173 pub async fn recv(&mut self) -> io::Result<Option<Vec<Bytes>>> {
174 self.inner.recv().await
175 }
176
177 /// Receive a request body into a caller-provided buffer, reusing its
178 /// allocation.
179 ///
180 /// Allocation-free counterpart to [`recv`](Self::recv): the routing envelope
181 /// is stashed for the reply and the body is moved into `out`. Returns
182 /// `Ok(true)` on a request, `Ok(false)` on EOF.
183 pub async fn recv_into(&mut self, out: &mut Vec<Bytes>) -> io::Result<bool> {
184 self.inner.recv_into(out).await
185 }
186
187 /// Get the socket type.
188 ///
189 /// # ZeroMQ Compatibility
190 ///
191 /// Corresponds to `ZMQ_TYPE` (16) option.
192 #[inline]
193 pub const fn socket_type() -> SocketType {
194 SocketType::Rep
195 }
196
197 /// Get the endpoint this socket is connected/bound to, if available.
198 ///
199 /// Returns `None` if the socket was created from a raw stream.
200 ///
201 /// # ZeroMQ Compatibility
202 ///
203 /// Corresponds to `ZMQ_LAST_ENDPOINT` (32) option.
204 #[inline]
205 pub fn last_endpoint(&self) -> Option<&monocoque_core::endpoint::Endpoint> {
206 self.inner.last_endpoint()
207 }
208
209 /// Check if the last received message has more frames coming.
210 ///
211 /// Returns `true` if there are more frames in the current multipart message.
212 ///\n /// # ZeroMQ Compatibility
213 ///
214 /// Corresponds to `ZMQ_RCVMORE` (13) option.
215 #[inline]
216 pub fn has_more(&self) -> bool {
217 self.inner.has_more()
218 }
219
220 /// Get the event state of the socket.
221 ///
222 /// Returns a bitmask indicating ready-to-receive and ready-to-send states.
223 ///
224 /// # Returns
225 ///
226 /// - `1` (POLLIN) - Socket is ready to receive
227 /// - `2` (POLLOUT) - Socket is ready to send
228 /// - `3` (POLLIN | POLLOUT) - Socket is ready for both
229 ///
230 /// # ZeroMQ Compatibility
231 ///
232 /// Corresponds to `ZMQ_EVENTS` (15) option.
233 #[inline]
234 pub fn events(&self) -> u32 {
235 self.inner.events()
236 }
237
238 /// Send a reply message.
239 ///
240 /// This must be called after `recv()` and automatically uses the stored
241 /// routing envelope from the request.
242 ///
243 /// # Arguments
244 ///
245 /// * `msg` - Vector of message frames (parts) to send as reply
246 ///
247 /// # Errors
248 ///
249 /// Returns an error if:
250 /// - Called without first calling `recv()`
251 /// - The underlying connection is closed
252 ///
253 /// # Example
254 ///
255 /// ```rust,no_run
256 /// use monocoque::zmq::RepSocket;
257 /// use bytes::Bytes;
258 ///
259 /// # async fn example(socket: &mut RepSocket) -> std::io::Result<()> {
260 /// // Send single-part reply
261 /// socket.send(vec![Bytes::from("OK")]).await?;
262 ///
263 /// // Send multi-part reply
264 /// socket.send(vec![
265 /// Bytes::from("Status: OK"),
266 /// Bytes::from("Data: ..."),
267 /// ]).await?;
268 /// # Ok(())
269 /// # }
270 /// ```
271 pub async fn send(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
272 channel_to_io_error(self.inner.send(msg).await)
273 }
274
275 /// Get a mutable reference to this socket's options.
276 #[inline]
277 pub fn options_mut(&mut self) -> &mut SocketOptions {
278 self.inner.options_mut()
279 }
280}
281
282// Unix-specific impl for IPC support
283#[cfg(unix)]
284impl RepSocket<monocoque_core::rt::UnixStream> {
285 /// Create a REP socket from an existing Unix domain socket stream (IPC).
286 pub async fn from_unix_stream(stream: monocoque_core::rt::UnixStream) -> io::Result<Self> {
287 Ok(Self {
288 inner: InternalRep::new(stream).await?,
289 monitor: None,
290 })
291 }
292
293 /// Create a REP socket from an existing Unix stream with custom options.
294 ///
295 /// This method provides full control over socket behavior through SocketOptions.
296 pub async fn from_unix_stream_with_options(
297 stream: monocoque_core::rt::UnixStream,
298 options: monocoque_core::options::SocketOptions,
299 ) -> io::Result<Self> {
300 Ok(Self {
301 inner: InternalRep::with_options(stream, options).await?,
302 monitor: None,
303 })
304 }
305}