1use crate::base::SocketBase;
20use crate::{handshake::perform_handshake_with_options, session::SocketType};
21use bytes::Bytes;
22use compio_io::{AsyncRead, AsyncWrite};
23use monocoque_core::options::SocketOptions;
24use monocoque_core::rt::TcpStream;
25use std::io;
26use tracing::{debug, trace};
27
28pub struct PushSocket<S = TcpStream>
33where
34 S: AsyncRead + AsyncWrite + Unpin,
35{
36 base: SocketBase<S>,
38}
39
40impl<S> PushSocket<S>
41where
42 S: AsyncRead + AsyncWrite + Unpin,
43{
44 pub async fn new(stream: S) -> io::Result<Self> {
46 Self::with_options(stream, SocketOptions::default()).await
47 }
48
49 pub async fn with_options(mut stream: S, options: SocketOptions) -> io::Result<Self> {
51 debug!("[PUSH] Creating new PUSH socket");
52
53 debug!("[PUSH] Performing ZMTP handshake...");
55 let handshake_result = perform_handshake_with_options(
56 &mut stream,
57 SocketType::Push,
58 options.routing_id.as_deref(),
59 Some(options.handshake_timeout),
60 &options,
61 )
62 .await
63 .map_err(|e| io::Error::other(format!("Handshake failed: {}", e)))?;
64
65 debug!(
66 peer_identity = ?handshake_result.peer_identity,
67 peer_socket_type = ?handshake_result.peer_socket_type,
68 "[PUSH] Handshake complete"
69 );
70
71 debug!("[PUSH] Socket initialized");
72
73 let mut base = SocketBase::new(stream, SocketType::Push, options);
74 base.curve_cipher = handshake_result.curve_cipher;
75 Ok(Self { base })
76 }
77
78 pub async fn send(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
94 trace!("[PUSH] Sending {} frames", msg.len());
95 self.base.send_message(&msg).await?;
96
97 if self.base.check_heartbeat()? {
99 self.base.flush_send_buffer().await?;
100 }
101
102 trace!("[PUSH] Message sent successfully");
103 Ok(())
104 }
105
106 pub async fn send_one(&mut self, frame: Bytes) -> io::Result<()> {
112 trace!("[PUSH] Sending 1 frame");
113
114 if self.base.options.write_coalescing {
115 if self.base.encode_one_coalesced(&frame)? {
116 self.base.flush_send_buffer().await?;
117 }
118 } else {
119 let msg = std::slice::from_ref(&frame);
120 if self.base.should_vectored_write(msg) {
121 self.base.send_vectored(msg).await?;
122 } else {
123 self.base.encode_message_to_write_buf(msg)?;
124 self.base.write_from_buf().await?;
125 }
126 }
127
128 if self.base.check_heartbeat()? {
129 self.base.flush_send_buffer().await?;
130 }
131
132 trace!("[PUSH] Message sent successfully");
133 Ok(())
134 }
135
136 pub async fn flush(&mut self) -> io::Result<()> {
141 self.base.flush_send_buffer().await
142 }
143
144 pub async fn send_batch<I>(&mut self, msgs: I) -> io::Result<usize>
155 where
156 I: IntoIterator<Item = Vec<Bytes>>,
157 {
158 let mut count = 0;
159 for msg in msgs {
160 trace!("[PUSH] Buffering batch message {}", count);
161 self.base.encode_message_to_send_buf(&msg)?;
162 count += 1;
163 }
164 if count > 0 {
165 self.base.flush_send_buffer().await?;
166 }
167 if self.base.check_heartbeat()? {
168 self.base.flush_send_buffer().await?;
169 }
170 trace!("[PUSH] Batch of {} messages sent", count);
171 Ok(count)
172 }
173
174 pub async fn close(mut self) -> io::Result<()> {
176 trace!("[PUSH] Closing socket");
177 self.base.close().await
178 }
179
180 #[inline]
182 pub const fn options(&self) -> &SocketOptions {
183 &self.base.options
184 }
185
186 #[inline]
188 pub fn options_mut(&mut self) -> &mut SocketOptions {
189 &mut self.base.options
190 }
191
192 #[inline]
194 pub fn set_options(&mut self, options: SocketOptions) {
195 self.base.set_options(options);
196 }
197}
198
199impl PushSocket<TcpStream> {
201 pub async fn from_tcp(stream: TcpStream) -> io::Result<Self> {
203 Self::from_tcp_with_options(stream, SocketOptions::default()).await
204 }
205
206 pub async fn from_tcp_with_options(
208 stream: TcpStream,
209 options: SocketOptions,
210 ) -> io::Result<Self> {
211 crate::utils::configure_tcp_stream(&stream, &options, "PUSH")?;
213 Self::with_options(stream, options).await
214 }
215
216 pub async fn connect(addr: impl monocoque_core::rt::ToSocketAddrs) -> io::Result<Self> {
218 Self::connect_with_options(addr, SocketOptions::default()).await
219 }
220
221 pub async fn connect_with_options(
223 addr: impl monocoque_core::rt::ToSocketAddrs,
224 options: SocketOptions,
225 ) -> io::Result<Self> {
226 let stream = TcpStream::connect(addr).await?;
227 let peer_addr = stream.peer_addr()?;
228 crate::utils::configure_tcp_stream(&stream, &options, "PUSH")?;
229
230 let mut stream = stream;
231 let handshake_result = perform_handshake_with_options(
232 &mut stream,
233 SocketType::Push,
234 options.routing_id.as_deref(),
235 Some(options.handshake_timeout),
236 &options,
237 )
238 .await
239 .map_err(|e| io::Error::other(format!("Handshake failed: {}", e)))?;
240
241 debug!(
242 peer_identity = ?handshake_result.peer_identity,
243 peer_socket_type = ?handshake_result.peer_socket_type,
244 "[PUSH] Connected to {} (endpoint stored for reconnection)",
245 peer_addr
246 );
247
248 let endpoint = monocoque_core::endpoint::Endpoint::Tcp(peer_addr);
249 let mut base =
250 crate::base::SocketBase::with_endpoint(stream, SocketType::Push, endpoint, options);
251 base.curve_cipher = handshake_result.curve_cipher;
252 Ok(Self { base })
253 }
254
255 #[inline]
257 pub fn is_connected(&self) -> bool {
258 self.base.is_connected()
259 }
260
261 pub async fn try_reconnect(&mut self) -> io::Result<()> {
263 self.base.try_reconnect(SocketType::Push).await
264 }
265
266 pub async fn send_with_reconnect(&mut self, msg: Vec<Bytes>) -> io::Result<()> {
273 let max = self.base.options.max_reconnect_attempts;
274 let mut attempts = 0u32;
275
276 loop {
277 if self.base.stream.is_none() {
278 if let Some(limit) = max
279 && attempts >= limit
280 {
281 return Err(io::Error::new(
282 io::ErrorKind::NotConnected,
283 format!("Max {} reconnection attempts exceeded", limit),
284 ));
285 }
286 attempts += 1;
287 trace!(
288 "[PUSH] Stream disconnected, reconnecting (attempt {})",
289 attempts
290 );
291 self.try_reconnect().await?;
292 }
293
294 match self.base.send_message(&msg).await {
297 Ok(()) => {
298 if self.base.check_heartbeat()? {
299 self.base.flush_send_buffer().await?;
300 }
301 return Ok(());
302 }
303 Err(_) if self.base.stream.is_none() => {
304 debug!("[PUSH] Send failed (stream lost), will reconnect");
306 }
307 Err(e) => return Err(e),
308 }
309 }
310 }
311}
312
313crate::impl_socket_trait_send_only!(PushSocket<S>, SocketType::Push);
314
315#[cfg(all(test, unix))]
316mod tests {
317 use super::*;
318 use monocoque_core::options::SocketOptions;
319 use monocoque_core::rt::{LocalRuntime, TcpListener};
320 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
321 use std::sync::mpsc;
322 use std::thread;
323
324 fn fd_nodelay(fd: RawFd) -> bool {
326 let sock = unsafe { socket2::Socket::from_raw_fd(fd) };
327 let nd = sock.tcp_nodelay().expect("query TCP_NODELAY");
328 std::mem::forget(sock); nd
330 }
331
332 #[test]
337 fn tcp_nodelay_survives_reconnect() {
338 let (port_tx, port_rx) = mpsc::channel::<u16>();
339 let (done_tx, done_rx) = mpsc::channel::<()>();
340
341 let server = thread::spawn(move || {
345 let rt = LocalRuntime::new().unwrap();
346 rt.block_on(async move {
347 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
348 port_tx.send(listener.local_addr().unwrap().port()).unwrap();
349
350 let (s1, _) = listener.accept().await.unwrap();
351 let _peer1 = crate::pull::PullSocket::new(s1).await.unwrap();
352
353 let (s2, _) = listener.accept().await.unwrap();
354 let _peer2 = crate::pull::PullSocket::new(s2).await.unwrap();
355
356 done_rx.recv().unwrap();
357 });
358 });
359
360 let port = port_rx.recv().unwrap();
361 let client = thread::spawn(move || {
362 let rt = LocalRuntime::new().unwrap();
363 rt.block_on(async move {
364 let mut push =
365 PushSocket::connect_with_options(("127.0.0.1", port), SocketOptions::default())
366 .await
367 .unwrap();
368
369 let fd0 = push.base.stream.as_ref().unwrap().as_raw_fd();
371 assert!(fd_nodelay(fd0), "initial connect must set TCP_NODELAY");
372
373 push.try_reconnect().await.unwrap();
375
376 let fd1 = push.base.stream.as_ref().unwrap().as_raw_fd();
377 assert!(
378 fd_nodelay(fd1),
379 "TCP_NODELAY must be re-applied on the reconnected socket",
380 );
381
382 done_tx.send(()).unwrap();
383 });
384 });
385
386 client.join().unwrap();
387 server.join().unwrap();
388 }
389}