Skip to main content

monocoque/zmq/
router.rs

1//! ROUTER 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::router::RouterSocket as InternalRouter;
10use std::io;
11
12/// A ROUTER socket for identity-based routing.
13///
14/// ROUTER sockets prefix incoming messages with the sender's identity,
15/// and route outgoing messages based on the first frame (identity).
16/// They're used for:
17///
18/// - Async request-reply servers
19/// - Brokers and proxies
20/// - Stateful connection tracking
21///
22/// ## ZeroMQ Compatibility
23///
24/// Compatible with `zmq::ROUTER` and `zmq::DEALER` sockets from libzmq.
25///
26/// ## Message Format
27///
28/// **Incoming**: `[identity, delimiter, ...user_frames]`\
29/// **Outgoing**: `[identity, delimiter, ...user_frames]` (routes to peer with that identity)
30///
31/// ## Example
32///
33/// ```rust,no_run
34/// use monocoque::zmq::RouterSocket;
35///
36/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
37/// // Bind and accept first connection
38/// let (listener, mut socket) = RouterSocket::bind("127.0.0.1:5555").await?;
39///
40/// // Echo server
41/// while let Ok(Some(msg)) = socket.recv().await {
42///     // msg[0] = identity, msg[1] = delimiter, msg[2+] = payload
43///     socket.send(msg).await?; // Echo back to sender
44/// }
45/// # Ok(())
46/// # }
47/// ```
48pub struct RouterSocket<S = TcpStream>
49where
50    S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
51{
52    inner: InternalRouter<S>,
53    monitor: Option<SocketEventSender>,
54}
55
56impl RouterSocket {
57    /// Bind to an address and accept the first connection.
58    ///
59    /// This is the recommended way to create a server-side ROUTER socket.
60    /// It handles TCP binding, accepting the first connection, and ZMTP handshake.
61    ///
62    /// # Returns
63    ///
64    /// A tuple of `(listener, socket)` where:
65    /// - `listener` can be used to accept additional connections
66    /// - `socket` is ready to send/receive with the first peer
67    ///
68    /// # Errors
69    ///
70    /// Returns an error if:
71    /// - The address is already in use
72    /// - Permission denied (e.g., binding to privileged port without root)
73    /// - Invalid address format
74    ///
75    /// # Example
76    ///
77    /// ```rust,no_run
78    /// use monocoque::zmq::RouterSocket;
79    ///
80    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
81    /// let (listener, socket) = RouterSocket::bind("127.0.0.1:5555").await?;
82    ///
83    /// // Use socket for first connection
84    /// // Accept more connections from listener if needed:
85    /// // let (stream, _) = listener.accept().await?;
86    /// // let socket2 = RouterSocket::from_stream(stream).await;
87    /// # Ok(())
88    /// # }
89    /// ```
90    pub async fn bind(
91        addr: impl monocoque_core::rt::ToSocketAddrs,
92    ) -> io::Result<(TcpListener, Self)> {
93        let listener = TcpListener::bind(addr).await?;
94        let (stream, _) = listener.accept().await?;
95        let socket = Self::from_tcp(stream).await?;
96        Ok((listener, socket))
97    }
98
99    /// Create a ROUTER socket from an existing TCP stream.
100    ///
101    /// **Deprecated**: Use [`RouterSocket::from_tcp()`] instead to enable TCP_NODELAY for optimal latency.
102    ///
103    /// Use this for advanced scenarios or when accepting multiple connections
104    /// from a listener.
105    ///
106    /// # Example
107    ///
108    /// ```rust,no_run
109    /// use monocoque::zmq::RouterSocket;
110    /// use monocoque_core::rt::TcpListener;
111    ///
112    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
113    /// let listener = TcpListener::bind("127.0.0.1:5555").await?;
114    ///
115    /// loop {
116    ///     let (stream, addr) = listener.accept().await?;
117    ///     println!("New connection from {}", addr);
118    ///     // Prefer this:
119    ///     let socket = RouterSocket::from_tcp(stream).await?;
120    ///     // Over this:
121    ///     // let socket = RouterSocket::from_stream(stream).await;
122    ///     // Handle socket (e.g., spawn task)
123    /// }
124    /// # Ok(())
125    /// # }
126    /// ```
127    #[deprecated(
128        since = "0.1.0",
129        note = "Use `from_tcp()` instead to enable TCP_NODELAY"
130    )]
131    pub async fn from_stream(stream: TcpStream) -> io::Result<Self> {
132        Ok(Self {
133            inner: InternalRouter::new(stream).await?,
134            monitor: None,
135        })
136    }
137
138    /// Create a ROUTER socket from an existing TCP stream with TCP_NODELAY enabled.
139    pub async fn from_tcp(stream: TcpStream) -> io::Result<Self> {
140        Ok(Self {
141            inner: InternalRouter::from_tcp(stream).await?,
142            monitor: None,
143        })
144    }
145
146    /// Create a ROUTER socket from a TCP stream with custom options.
147    pub async fn from_tcp_with_options(
148        stream: TcpStream,
149        options: monocoque_core::options::SocketOptions,
150    ) -> io::Result<Self> {
151        Ok(Self {
152            inner: InternalRouter::from_tcp_with_options(stream, options).await?,
153            monitor: None,
154        })
155    }
156
157    /// Create a ROUTER socket from any stream with custom options.
158    pub async fn with_options<Stream>(
159        stream: Stream,
160        options: monocoque_core::options::SocketOptions,
161    ) -> io::Result<RouterSocket<Stream>>
162    where
163        Stream: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
164    {
165        Ok(RouterSocket {
166            inner: InternalRouter::with_options(stream, options).await?,
167            monitor: None,
168        })
169    }
170}
171
172// Generic impl - works with any stream type
173impl<S> RouterSocket<S>
174where
175    S: compio_io::AsyncRead + compio_io::AsyncWrite + Unpin,
176{
177    /// Enable monitoring for this socket.
178    ///
179    /// Returns a receiver for socket lifecycle events. Once enabled, the socket
180    /// will emit events like Accepted, Disconnected, etc.
181    ///
182    /// # Example
183    ///
184    /// ```rust,no_run
185    /// use monocoque::zmq::RouterSocket;
186    ///
187    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
188    /// let (_listener, mut socket) = RouterSocket::bind("127.0.0.1:5555").await?;
189    /// let monitor = socket.monitor();
190    ///
191    /// // Spawn task to handle events
192    /// monocoque::rt::spawn_detached(async move {
193    ///     while let Ok(event) = monitor.recv_async().await {
194    ///         println!("Socket event: {}", event);
195    ///     }
196    /// });
197    /// # Ok(())
198    /// # }
199    /// ```
200    pub fn monitor(&mut self) -> SocketMonitor {
201        let (sender, receiver) = create_monitor();
202        self.monitor = Some(sender);
203        receiver
204    }
205
206    /// Get a mutable reference to this socket's options.
207    #[inline]
208    pub fn options_mut(&mut self) -> &mut SocketOptions {
209        self.inner.options_mut()
210    }
211
212    /// Send a multipart message.
213    ///
214    /// The first frame must be the peer identity to route to.
215    /// Messages are sent asynchronously.
216    ///
217    /// # Errors
218    ///
219    /// Returns an error if the underlying connection is closed or broken.
220    ///
221    /// # Example
222    ///
223    /// ```rust,no_run
224    /// # use monocoque::zmq::RouterSocket;
225    /// # use bytes::Bytes;
226    /// # async fn example(mut socket: RouterSocket, identity: Bytes) -> Result<(), Box<dyn std::error::Error>> {
227    /// socket.send(vec![
228    ///     identity,              // Route to this peer
229    ///     Bytes::new(),          // Delimiter
230    ///     Bytes::from("reply"),  // Payload
231    /// ]).await?;
232    /// # Ok(())
233    /// # }
234    /// ```
235    pub async fn send(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
236        channel_to_io_error(self.inner.send(msg).await)
237    }
238
239    /// Send a message to the internal buffer without flushing.
240    ///
241    /// Use this for batching multiple messages before a single flush.
242    pub fn send_buffered(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
243        channel_to_io_error(self.inner.send_buffered(msg))
244    }
245
246    /// Flush all buffered messages to the network.
247    pub async fn flush(&mut self) -> io::Result<()> {
248        channel_to_io_error(self.inner.flush().await)
249    }
250
251    /// Send multiple messages in a single batch.
252    pub async fn send_batch(&mut self, messages: &[Vec<Bytes>]) -> io::Result<()> {
253        channel_to_io_error(self.inner.send_batch(messages).await)
254    }
255
256    /// Get the number of bytes currently buffered.
257    #[inline]
258    pub fn buffered_bytes(&self) -> usize {
259        self.inner.buffered_bytes()
260    }
261
262    /// Get the socket type.
263    ///
264    /// # ZeroMQ Compatibility
265    ///
266    /// Corresponds to `ZMQ_TYPE` (16) option.
267    #[inline]
268    pub const fn socket_type() -> SocketType {
269        SocketType::Router
270    }
271
272    /// Get the endpoint this socket is connected/bound to, if available.
273    ///
274    /// Returns `None` if the socket was created from a raw stream.
275    ///
276    /// # ZeroMQ Compatibility
277    ///
278    /// Corresponds to `ZMQ_LAST_ENDPOINT` (32) option.
279    #[inline]
280    pub fn last_endpoint(&self) -> Option<&monocoque_core::endpoint::Endpoint> {
281        self.inner.last_endpoint()
282    }
283
284    /// Check if the last received message has more frames coming.
285    ///
286    /// Returns `true` if there are more frames in the current multipart message.
287    ///
288    /// # ZeroMQ Compatibility
289    ///
290    /// Corresponds to `ZMQ_RCVMORE` (13) option.
291    #[inline]
292    pub fn has_more(&self) -> bool {
293        self.inner.has_more()
294    }
295
296    /// Get the event state of the socket.
297    ///
298    /// Returns a bitmask indicating ready-to-receive and ready-to-send states.
299    ///
300    /// # Returns
301    ///
302    /// - `1` (POLLIN) - Socket is ready to receive
303    /// - `2` (POLLOUT) - Socket is ready to send
304    /// - `3` (POLLIN | POLLOUT) - Socket is ready for both
305    ///
306    /// # ZeroMQ Compatibility
307    ///
308    /// Corresponds to `ZMQ_EVENTS` (15) option.
309    #[inline]
310    pub fn events(&self) -> u32 {
311        self.inner.events()
312    }
313
314    /// Set the routing identity for the next accepted connection.
315    ///
316    /// This identity will be used for the next peer that connects to this ROUTER.
317    /// The option is consumed after the connection and must be set again for
318    /// subsequent connections.
319    ///
320    /// # Arguments
321    ///
322    /// * `id` - The identity to assign (1-255 bytes, cannot start with null byte)
323    ///
324    /// # Errors
325    ///
326    /// Returns an error if the identity is invalid (empty, too long, or starts
327    /// with null byte).
328    ///
329    /// # ZeroMQ Compatibility
330    ///
331    /// Corresponds to `ZMQ_CONNECT_ROUTING_ID` (62).
332    ///
333    /// # Example
334    ///
335    /// ```rust,no_run
336    /// use monocoque::zmq::RouterSocket;
337    /// use bytes::Bytes;
338    ///
339    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
340    /// let (_listener, mut router) = RouterSocket::bind("tcp://0.0.0.0:5555").await?;
341    ///
342    /// // Assign explicit identity to next connection
343    /// router.set_connect_routing_id(b"worker-001".to_vec())?;
344    ///
345    /// // When a peer connects, it will be identified as "worker-001"
346    /// # Ok(())
347    /// # }
348    /// ```
349    pub fn set_connect_routing_id(&mut self, id: Vec<u8>) -> io::Result<()> {
350        // Validate identity for ROUTER socket
351        monocoque_core::options::SocketOptions::validate_router_identity(&id)?;
352        self.inner.options_mut().connect_routing_id = Some(Bytes::from(id));
353        Ok(())
354    }
355
356    /// Enable or disable ROUTER_MANDATORY mode.
357    ///
358    /// When enabled, sending to an unknown identity returns an error.
359    /// When disabled (default), messages to unknown identities are silently dropped.
360    ///
361    /// **Note**: The current single-peer ROUTER implementation doesn't have a
362    /// routing table yet, so this option affects future multi-peer support.
363    ///
364    /// # ZeroMQ Compatibility
365    ///
366    /// Corresponds to `ZMQ_ROUTER_MANDATORY` (33).
367    ///
368    /// # Example
369    ///
370    /// ```rust,no_run
371    /// # use monocoque::zmq::RouterSocket;
372    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
373    /// let (_listener, mut router) = RouterSocket::bind("tcp://0.0.0.0:5555").await?;
374    ///
375    /// // Fail fast if routing to unknown peer
376    /// router.set_router_mandatory(true);
377    /// # Ok(())
378    /// # }
379    /// ```
380    pub fn set_router_mandatory(&mut self, enabled: bool) {
381        self.inner.options_mut().router_mandatory = enabled;
382    }
383
384    /// Enable or disable ROUTER_HANDOVER mode.
385    ///
386    /// When enabled, a new connection with an existing identity will take over
387    /// that identity, closing the old connection.
388    /// When disabled (default), duplicate identities are rejected.
389    ///
390    /// **Note**: The current single-peer ROUTER implementation doesn't have a
391    /// routing table yet, so this option affects future multi-peer support.
392    ///
393    /// # ZeroMQ Compatibility
394    ///
395    /// Corresponds to `ZMQ_ROUTER_HANDOVER` (56).
396    ///
397    /// # Example
398    ///
399    /// ```rust,no_run
400    /// # use monocoque::zmq::RouterSocket;
401    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
402    /// let (_listener, mut router) = RouterSocket::bind("tcp://0.0.0.0:5555").await?;
403    ///
404    /// // Allow identity takeover for reconnecting clients
405    /// router.set_router_handover(true);
406    /// # Ok(())
407    /// # }
408    /// ```
409    pub fn set_router_handover(&mut self, enabled: bool) {
410        self.inner.options_mut().router_handover = enabled;
411    }
412
413    /// Get the peer identity for this connection.
414    ///
415    /// Returns the identity of the connected peer. This is either:
416    /// - The identity set via `set_connect_routing_id()`
417    /// - The peer's self-reported identity from the handshake
418    /// - An auto-generated identity
419    ///
420    /// # Example
421    ///
422    /// ```rust,no_run
423    /// # use monocoque::zmq::RouterSocket;
424    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
425    /// let (_listener, router) = RouterSocket::bind("tcp://0.0.0.0:5555").await?;
426    ///
427    /// let identity = router.peer_identity();
428    /// println!("Peer identity: {:?}", identity);
429    /// # Ok(())
430    /// # }
431    /// ```
432    pub const fn peer_identity(&self) -> &Bytes {
433        self.inner.peer_identity()
434    }
435
436    /// Receive a multipart message.
437    ///
438    /// The returned message will have the sender's identity as the first frame,
439    /// followed by a delimiter, then the payload frames.
440    ///
441    /// Returns `None` if the connection is closed.
442    ///
443    /// # Example
444    ///
445    /// ```rust,no_run
446    /// # use monocoque::zmq::RouterSocket;
447    /// # async fn example(mut socket: RouterSocket) -> Result<(), Box<dyn std::error::Error>> {
448    /// while let Ok(Some(msg)) = socket.recv().await {
449    ///     let identity = &msg[0];
450    ///     let payload = &msg[2..]; // Skip identity and delimiter
451    ///     println!("From {:?}: {:?}", identity, payload);
452    /// }
453    /// # Ok(())
454    /// # }
455    /// ```
456    pub async fn recv(&mut self) -> io::Result<Option<Vec<Bytes>>> {
457        self.inner.recv().await
458    }
459}
460
461// Unix-specific impl for IPC support
462#[cfg(unix)]
463impl RouterSocket<monocoque_core::rt::UnixStream> {
464    /// Create a ROUTER socket from an existing Unix domain socket stream (IPC).
465    pub async fn from_unix_stream(stream: monocoque_core::rt::UnixStream) -> io::Result<Self> {
466        Ok(Self {
467            inner: InternalRouter::new(stream).await?,
468            monitor: None,
469        })
470    }
471
472    /// Create a ROUTER socket from an existing Unix stream with custom options.
473    ///
474    /// This method provides full control over socket behavior through SocketOptions.
475    pub async fn from_unix_stream_with_options(
476        stream: monocoque_core::rt::UnixStream,
477        options: monocoque_core::options::SocketOptions,
478    ) -> io::Result<Self> {
479        Ok(Self {
480            inner: InternalRouter::with_options(stream, options).await?,
481            monitor: None,
482        })
483    }
484}
485
486// Implement ProxySocket for the high-level RouterSocket wrapper
487impl monocoque_zmtp::proxy::ProxySocket for RouterSocket<TcpStream> {
488    fn recv_multipart<'life0, 'async_trait>(
489        &'life0 mut self,
490    ) -> ::core::pin::Pin<
491        Box<dyn ::core::future::Future<Output = io::Result<Option<Vec<Bytes>>>> + 'async_trait>,
492    >
493    where
494        'life0: 'async_trait,
495        Self: 'async_trait,
496    {
497        Box::pin(async move { self.recv().await })
498    }
499
500    fn send_multipart<'life0, 'async_trait>(
501        &'life0 mut self,
502        msg: Vec<Bytes>,
503    ) -> ::core::pin::Pin<Box<dyn ::core::future::Future<Output = io::Result<()>> + 'async_trait>>
504    where
505        'life0: 'async_trait,
506        Self: 'async_trait,
507    {
508        Box::pin(async move { self.send(msg).await })
509    }
510
511    fn socket_desc(&self) -> &'static str {
512        "ROUTER"
513    }
514}