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 /// Get the socket type.
178 ///
179 /// # ZeroMQ Compatibility
180 ///
181 /// Corresponds to `ZMQ_TYPE` (16) option.
182 #[inline]
183 pub const fn socket_type() -> SocketType {
184 SocketType::Rep
185 }
186
187 /// Get the endpoint this socket is connected/bound to, if available.
188 ///
189 /// Returns `None` if the socket was created from a raw stream.
190 ///
191 /// # ZeroMQ Compatibility
192 ///
193 /// Corresponds to `ZMQ_LAST_ENDPOINT` (32) option.
194 #[inline]
195 pub fn last_endpoint(&self) -> Option<&monocoque_core::endpoint::Endpoint> {
196 self.inner.last_endpoint()
197 }
198
199 /// Check if the last received message has more frames coming.
200 ///
201 /// Returns `true` if there are more frames in the current multipart message.
202 ///\n /// # ZeroMQ Compatibility
203 ///
204 /// Corresponds to `ZMQ_RCVMORE` (13) option.
205 #[inline]
206 pub fn has_more(&self) -> bool {
207 self.inner.has_more()
208 }
209
210 /// Get the event state of the socket.
211 ///
212 /// Returns a bitmask indicating ready-to-receive and ready-to-send states.
213 ///
214 /// # Returns
215 ///
216 /// - `1` (POLLIN) - Socket is ready to receive
217 /// - `2` (POLLOUT) - Socket is ready to send
218 /// - `3` (POLLIN | POLLOUT) - Socket is ready for both
219 ///
220 /// # ZeroMQ Compatibility
221 ///
222 /// Corresponds to `ZMQ_EVENTS` (15) option.
223 #[inline]
224 pub fn events(&self) -> u32 {
225 self.inner.events()
226 }
227
228 /// Send a reply message.
229 ///
230 /// This must be called after `recv()` and automatically uses the stored
231 /// routing envelope from the request.
232 ///
233 /// # Arguments
234 ///
235 /// * `msg` - Vector of message frames (parts) to send as reply
236 ///
237 /// # Errors
238 ///
239 /// Returns an error if:
240 /// - Called without first calling `recv()`
241 /// - The underlying connection is closed
242 ///
243 /// # Example
244 ///
245 /// ```rust,no_run
246 /// use monocoque::zmq::RepSocket;
247 /// use bytes::Bytes;
248 ///
249 /// # async fn example(socket: &mut RepSocket) -> std::io::Result<()> {
250 /// // Send single-part reply
251 /// socket.send(vec![Bytes::from("OK")]).await?;
252 ///
253 /// // Send multi-part reply
254 /// socket.send(vec![
255 /// Bytes::from("Status: OK"),
256 /// Bytes::from("Data: ..."),
257 /// ]).await?;
258 /// # Ok(())
259 /// # }
260 /// ```
261 pub async fn send(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
262 channel_to_io_error(self.inner.send(msg).await)
263 }
264
265 /// Get a mutable reference to this socket's options.
266 #[inline]
267 pub fn options_mut(&mut self) -> &mut SocketOptions {
268 self.inner.options_mut()
269 }
270}
271
272// Unix-specific impl for IPC support
273#[cfg(unix)]
274impl RepSocket<monocoque_core::rt::UnixStream> {
275 /// Create a REP socket from an existing Unix domain socket stream (IPC).
276 pub async fn from_unix_stream(stream: monocoque_core::rt::UnixStream) -> io::Result<Self> {
277 Ok(Self {
278 inner: InternalRep::new(stream).await?,
279 monitor: None,
280 })
281 }
282
283 /// Create a REP socket from an existing Unix stream with custom options.
284 ///
285 /// This method provides full control over socket behavior through SocketOptions.
286 pub async fn from_unix_stream_with_options(
287 stream: monocoque_core::rt::UnixStream,
288 options: monocoque_core::options::SocketOptions,
289 ) -> io::Result<Self> {
290 Ok(Self {
291 inner: InternalRep::with_options(stream, options).await?,
292 monitor: None,
293 })
294 }
295}