snarkos_node_tcp/protocols/
writing.rs1use std::{any::Any, collections::HashMap, io, net::SocketAddr, sync::Arc, time::Duration};
17
18use async_trait::async_trait;
19use futures_util::sink::SinkExt;
20#[cfg(feature = "locktick")]
21use locktick::parking_lot::RwLock;
22#[cfg(not(feature = "locktick"))]
23use parking_lot::RwLock;
24use tokio::{
25 io::AsyncWrite,
26 sync::{mpsc, oneshot},
27 time::timeout,
28};
29use tokio_util::codec::{Encoder, FramedWrite};
30use tracing::*;
31
32#[cfg(doc)]
33use crate::{Config, Tcp, protocols::Handshake};
34use crate::{
35 Connection,
36 ConnectionSide,
37 P2P,
38 connections::{DisconnectOrigin, create_connection_span},
39 protocols::{DisconnectOnDrop, Protocol, ProtocolHandler, ReturnableConnection},
40};
41
42type WritingSenders = Arc<RwLock<HashMap<SocketAddr, mpsc::Sender<WrappedMessage>>>>;
43
44#[async_trait]
47pub trait Writing: P2P
48where
49 Self: Clone + Send + Sync + 'static,
50{
51 fn message_queue_depth(&self) -> usize {
57 1024
58 }
59
60 const TIMEOUT: Duration = Duration::from_secs(10);
66
67 type Message: Send;
71
72 type Codec: Encoder<Self::Message, Error = io::Error> + Send;
74
75 async fn enable_writing(&self) {
77 let (conn_sender, mut conn_receiver) = mpsc::channel(self.tcp().config().max_connections as usize);
78
79 let conn_senders: WritingSenders = Default::default();
81 let senders = conn_senders.clone();
83
84 let (tx_writing, rx_writing) = oneshot::channel();
86
87 let self_clone = self.clone();
89 let writing_task = tokio::spawn(async move {
90 trace!(parent: self_clone.tcp().span(), "spawned the Writing handler task");
91 tx_writing.send(()).unwrap(); while let Some(returnable_conn) = conn_receiver.recv().await {
95 self_clone.handle_new_connection(returnable_conn, &conn_senders).await;
96 }
97 });
98 let _ = rx_writing.await;
99 self.tcp().tasks.lock().push(writing_task);
100
101 let hdl = Box::new(WritingHandler { handler: ProtocolHandler(conn_sender), senders });
103 assert!(self.tcp().protocols.writing.set(hdl).is_ok(), "the Writing protocol was enabled more than once!");
104 }
105
106 fn codec(&self, addr: SocketAddr, side: ConnectionSide) -> Self::Codec;
109
110 fn unicast(&self, addr: SocketAddr, message: Self::Message) -> io::Result<oneshot::Receiver<io::Result<()>>> {
121 if let Some(handler) = self.tcp().protocols.writing.get() {
123 if let Some(sender) = handler.senders.read().get(&addr).cloned() {
125 let (msg, delivery) = WrappedMessage::new(Box::new(message));
126 sender
127 .try_send(msg)
128 .map_err(|e| {
129 let conn_span = create_connection_span(addr, self.tcp().span());
130 error!(parent: conn_span, "can't send a message: {e}");
131 io::ErrorKind::Other.into()
132 })
133 .map(|_| delivery)
134 } else {
135 Err(io::ErrorKind::NotConnected.into())
136 }
137 } else {
138 Err(io::ErrorKind::Unsupported.into())
139 }
140 }
141
142 fn broadcast(&self, message: Self::Message) -> io::Result<()>
151 where
152 Self::Message: Clone,
153 {
154 if let Some(handler) = self.tcp().protocols.writing.get() {
156 let senders = handler.senders.read().clone();
157 for (addr, message_sender) in senders {
158 let (msg, _delivery) = WrappedMessage::new(Box::new(message.clone()));
159 let _ = message_sender.try_send(msg).map_err(|e| {
160 let conn_span = create_connection_span(addr, self.tcp().span());
161 error!(parent: conn_span, "can't send a message: {e}");
162 });
163 }
164
165 Ok(())
166 } else {
167 Err(io::ErrorKind::Unsupported.into())
168 }
169 }
170}
171
172#[async_trait]
174trait WritingInternal: Writing {
175 async fn write_to_stream<W: AsyncWrite + Unpin + Send>(
177 &self,
178 message: Self::Message,
179 writer: &mut FramedWrite<W, Self::Codec>,
180 ) -> Result<usize, <Self::Codec as Encoder<Self::Message>>::Error>;
181
182 async fn handle_new_connection(&self, (conn, conn_returner): ReturnableConnection, conn_senders: &WritingSenders);
184}
185
186#[async_trait]
187impl<W: Writing> WritingInternal for W {
188 async fn write_to_stream<A: AsyncWrite + Unpin + Send>(
189 &self,
190 message: Self::Message,
191 writer: &mut FramedWrite<A, Self::Codec>,
192 ) -> Result<usize, <Self::Codec as Encoder<Self::Message>>::Error> {
193 let write = async {
197 writer.feed(message).await?;
198 let len = writer.write_buffer().len();
199 writer.flush().await?;
200 Ok(len)
201 };
202 match timeout(W::TIMEOUT, write).await {
203 Ok(result) => result,
204 Err(_) => Err(io::Error::new(io::ErrorKind::TimedOut, "write timed out")),
205 }
206 }
207
208 async fn handle_new_connection(
209 &self,
210 (mut conn, conn_returner): ReturnableConnection,
211 conn_senders: &WritingSenders,
212 ) {
213 let addr = conn.addr();
214 let codec = self.codec(addr, !conn.side());
215 let writer = conn.writer.take().expect("missing connection writer!");
216 let mut framed = FramedWrite::new(writer, codec);
217
218 let (outbound_message_sender, mut outbound_message_receiver) = mpsc::channel(self.message_queue_depth());
219
220 conn_senders.write().insert(addr, outbound_message_sender);
222
223 let sender_cleanup = SenderCleanup { addr, senders: Arc::clone(conn_senders) };
225
226 let (tx_writer, rx_writer) = oneshot::channel();
228
229 let self_clone = self.clone();
231 let conn_stats = Arc::clone(conn.stats());
232 let conn_span = conn.span().clone();
233 let writer_task = tokio::spawn(Box::pin(async move {
234 let node = self_clone.tcp();
235 trace!(parent: &conn_span, "spawned a task for writing messages");
236 tx_writer.send(()).unwrap(); let _sender_cleanup = sender_cleanup;
240
241 let _conn_cleanup = DisconnectOnDrop::new(node.clone(), addr, DisconnectOrigin::Writing);
243
244 while let Some(wrapped_msg) = outbound_message_receiver.recv().await {
245 let msg = wrapped_msg.msg.downcast().unwrap();
246
247 match self_clone.write_to_stream(*msg, &mut framed).await {
248 Ok(len) => {
249 let _ = wrapped_msg.delivery_notification.send(Ok(()));
250 conn_stats.register_sent_message(len);
251 node.stats().register_sent_message(len);
252 trace!(parent: &conn_span, "sent {len}B");
253 }
254 Err(e) => {
255 error!(parent: &conn_span, "couldn't send a message: {e}");
256 let _ = wrapped_msg.delivery_notification.send(Err(e));
257 break;
258 }
259 }
260 }
261 }));
262 let _ = rx_writer.await;
263 conn.tasks.push(writer_task);
264
265 if conn_returner.send(Ok(conn)).is_err() {
267 unreachable!("couldn't return a Connection to the Tcp");
268 }
269 }
270}
271
272struct WrappedMessage {
274 msg: Box<dyn Any + Send>,
275 delivery_notification: oneshot::Sender<io::Result<()>>,
276}
277
278impl WrappedMessage {
279 fn new(msg: Box<dyn Any + Send>) -> (Self, oneshot::Receiver<io::Result<()>>) {
280 let (tx, rx) = oneshot::channel();
281 let wrapped_msg = Self { msg, delivery_notification: tx };
282
283 (wrapped_msg, rx)
284 }
285}
286
287pub(crate) struct WritingHandler {
289 handler: ProtocolHandler<Connection, io::Result<Connection>>,
290 senders: WritingSenders,
291}
292
293impl Protocol<Connection, io::Result<Connection>> for WritingHandler {
294 async fn trigger(&self, item: ReturnableConnection) {
295 self.handler.trigger(item).await;
296 }
297}
298
299struct SenderCleanup {
300 addr: SocketAddr,
301 senders: WritingSenders,
302}
303
304impl Drop for SenderCleanup {
305 fn drop(&mut self) {
306 self.senders.write().remove(&self.addr);
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use crate::{Config, ConnectionSide, Tcp, protocols::Reading};
314 use bytes::Bytes;
315 use std::net::{IpAddr, Ipv4Addr};
316 use tokio::time::Instant;
317 use tokio_util::codec::BytesCodec;
318
319 fn test_config() -> Config {
320 Config { listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)), ..Default::default() }
321 }
322
323 #[derive(Clone)]
324 struct TestNode(Tcp);
325 impl P2P for TestNode {
326 fn tcp(&self) -> &Tcp {
327 &self.0
328 }
329 }
330 #[async_trait]
331 impl Writing for TestNode {
332 type Codec = BytesCodec;
333 type Message = Bytes;
334
335 const TIMEOUT: Duration = Duration::from_millis(200);
336
337 fn codec(&self, _a: SocketAddr, _s: ConnectionSide) -> Self::Codec {
338 Default::default()
339 }
340 }
341 #[async_trait]
342 impl Reading for TestNode {
343 type Codec = BytesCodec;
344 type Message = bytes::BytesMut;
345
346 fn codec(&self, _a: SocketAddr, _s: ConnectionSide) -> Self::Codec {
347 Default::default()
348 }
349
350 async fn process_message(&self, _s: SocketAddr, _m: Self::Message) -> io::Result<()> {
351 Ok(())
352 }
353 }
354
355 fn flood(sender: &TestNode, peer: SocketAddr) {
356 let msg = Bytes::from(vec![0u8; 1024 * 1024]);
357 for _ in 0..64 {
358 if sender.unicast(peer, msg.clone()).is_err() {
359 break;
360 }
361 }
362 }
363 async fn disconnected(sender: &TestNode, peer: SocketAddr, within: Duration) -> bool {
364 let deadline = Instant::now() + within;
365 while Instant::now() < deadline {
366 if !sender.tcp().is_connected(peer) {
367 return true;
368 }
369 tokio::time::sleep(Duration::from_millis(50)).await;
370 }
371 false
372 }
373
374 #[tokio::test]
375 async fn stalled_peer_is_disconnected() {
376 let sender = TestNode(Tcp::new(test_config()));
377 sender.tcp().enable_listener().await.unwrap();
378 sender.enable_writing().await;
379 let receiver = Tcp::new(test_config());
380 let ip = receiver.enable_listener().await.unwrap();
381 sender.tcp().connect(ip).await.unwrap();
382 let peer = *sender.tcp().connected_addrs().first().unwrap();
383 flood(&sender, peer);
384 assert!(disconnected(&sender, peer, Duration::from_secs(10)).await, "stalled peer not disconnected");
385 }
386
387 #[tokio::test]
388 async fn reading_peer_is_kept() {
389 let sender = TestNode(Tcp::new(test_config()));
390 sender.tcp().enable_listener().await.unwrap();
391 sender.enable_writing().await;
392 let receiver = TestNode(Tcp::new(test_config()));
393 let ip = receiver.tcp().enable_listener().await.unwrap();
394 receiver.enable_reading().await;
395 sender.tcp().connect(ip).await.unwrap();
396 let peer = *sender.tcp().connected_addrs().first().unwrap();
397 flood(&sender, peer);
398 assert!(!disconnected(&sender, peer, Duration::from_secs(3)).await, "reading peer was disconnected");
399 }
400}