1mod task;
2
3use std::{
4 collections::HashMap,
5 convert::Infallible,
6 io,
7 task::{Context, Poll, Waker},
8 time::{Duration, Instant},
9};
10
11use fnv::{FnvHashMap, FnvHashSet};
12use futures::{
13 StreamExt,
14 channel::{mpsc, oneshot},
15 stream::{FuturesUnordered, SelectAll},
16};
17use tracing::Instrument;
18use volans_core::{
19 ConnectedPoint, Multiaddr, PeerId,
20 muxing::{StreamMuxerBox, StreamMuxerExt},
21};
22
23use crate::{
24 ConnectionHandler, ConnectionId, ExecSwitch, Executor, InboundStreamHandler,
25 OutboundStreamHandler,
26 connection::{InboundConnection, OutboundConnection},
27 error::{ConnectionError, PendingConnectionError},
28};
29
30pub struct Pool<THandler>
38where
39 THandler: ConnectionHandler,
40{
41 local_id: PeerId,
42
43 pending: HashMap<ConnectionId, PendingConnection>,
45 pending_peer_connections: FnvHashMap<PeerId, FnvHashSet<ConnectionId>>,
46
47 established: FnvHashMap<ConnectionId, EstablishedConnection<THandler::Action>>,
48
49 established_peer_connections: FnvHashMap<PeerId, FnvHashSet<ConnectionId>>,
51
52 executor: ExecSwitch,
53
54 pending_connection_events_tx: mpsc::Sender<task::PendingConnectionEvent>,
56
57 pending_connection_events_rx: mpsc::Receiver<task::PendingConnectionEvent>,
59
60 no_established_connections_waker: Option<Waker>,
62
63 established_connection_events:
64 SelectAll<mpsc::Receiver<task::EstablishedConnectionEvent<THandler::Event>>>,
65
66 new_connection_dropped_listeners: FuturesUnordered<oneshot::Receiver<StreamMuxerBox>>,
68
69 task_command_buffer_size: usize,
71 max_negotiating_inbound_streams: usize,
73 per_connection_event_buffer_size: usize,
75 idle_connection_timeout: Duration,
77}
78
79impl<THandler> Pool<THandler>
80where
81 THandler: ConnectionHandler,
82{
83 pub fn new(local_id: PeerId, config: PoolConfig) -> Self {
84 let (pending_connection_events_tx, pending_connection_events_rx) = mpsc::channel(0);
85
86 Pool {
87 local_id,
88 pending: HashMap::new(),
89 pending_peer_connections: FnvHashMap::default(),
90 established: FnvHashMap::default(),
91 established_peer_connections: FnvHashMap::default(),
92 executor: ExecSwitch::new(config.executor),
93 pending_connection_events_tx,
94 pending_connection_events_rx,
95 no_established_connections_waker: None,
96 established_connection_events: SelectAll::new(),
97 new_connection_dropped_listeners: FuturesUnordered::new(),
98 task_command_buffer_size: config.task_command_buffer_size,
99 max_negotiating_inbound_streams: config.max_negotiating_inbound_streams,
100 per_connection_event_buffer_size: config.per_connection_event_buffer_size,
101 idle_connection_timeout: config.idle_connection_timeout,
102 }
103 }
104
105 pub fn disconnect(&mut self, id: &PeerId) {
106 for connection in self
108 .pending_peer_connections
109 .remove(id)
110 .into_iter()
111 .flatten()
112 {
113 if let Some(mut pending) = self.pending.remove(&connection) {
114 pending.abort();
115 }
116 }
117 if let Some(connections) = self.established_peer_connections.get(id) {
119 for connection in connections.iter() {
120 if let Some(established) = self.established.get_mut(&connection) {
121 established.start_close();
122 }
123 }
124 }
125 }
126
127 pub(crate) fn get_established(
128 &mut self,
129 id: ConnectionId,
130 ) -> Option<&mut EstablishedConnection<THandler::Action>> {
131 self.established.get_mut(&id)
132 }
133
134 pub(crate) fn is_peer_connected(&self, id: &PeerId) -> bool {
135 self.established_peer_connections.contains_key(id)
136 }
137
138 pub(crate) fn is_peer_dialing(&self, id: &PeerId) -> bool {
139 if let Some(connections) = self.pending_peer_connections.get(id) {
140 for connection in connections.iter() {
141 if let Some(pending) = self.pending.get(connection) {
142 if matches!(pending.endpoint, ConnectedPoint::Dialer { .. }) {
143 return true;
144 }
145 }
146 }
147 }
148 return false;
149 }
150
151 pub fn iter_established_connections_of_peer(
152 &mut self,
153 peer_id: &PeerId,
154 ) -> impl Iterator<Item = ConnectionId> + '_ {
155 match self.established_peer_connections.get(peer_id) {
156 Some(conns) => either::Either::Left(conns.iter().copied()),
157 None => either::Either::Right(std::iter::empty()),
158 }
159 }
160
161 pub fn num_peer_established(&self, peer_id: &PeerId) -> usize {
162 self.established_peer_connections
163 .get(peer_id)
164 .map_or(0, |conns| conns.len())
165 }
166
167 pub(crate) fn iter_peer_connected(&self) -> impl Iterator<Item = &PeerId> {
168 self.established_peer_connections.keys()
169 }
170
171 pub(crate) fn iter_connected(&self) -> impl Iterator<Item = &ConnectionId> {
172 self.established.keys()
173 }
174
175 pub fn add_outgoing<TFut>(
176 &mut self,
177 id: ConnectionId,
178 future: TFut,
179 addr: Multiaddr,
180 peer_id: Option<PeerId>,
181 ) where
182 TFut: Future<Output = Result<(PeerId, StreamMuxerBox), io::Error>> + Send + 'static,
183 {
184 let (abort_notifier, abort_receiver) = oneshot::channel();
185 let span = tracing::debug_span!(parent: tracing::Span::none(), "new_outgoing_connection", id = %id, peer_id = ?peer_id, remote_addr = %addr);
186 span.follows_from(tracing::Span::current());
187 self.executor.spawn(
188 task::new_for_pending_connection(
189 id,
190 addr.clone(),
191 future,
192 abort_receiver,
193 self.pending_connection_events_tx.clone(),
194 )
195 .instrument(span),
196 );
197 if let Some(peer_id) = peer_id {
198 self.pending_peer_connections
199 .entry(peer_id)
200 .or_default()
201 .insert(id);
202 }
203 self.pending.insert(
204 id,
205 PendingConnection {
206 peer_id,
207 endpoint: ConnectedPoint::Dialer { addr },
208 abort_notifier: Some(abort_notifier),
209 accepted_at: Instant::now(),
210 },
211 );
212 }
213
214 pub fn add_incoming<TFut>(
215 &mut self,
216 id: ConnectionId,
217 future: TFut,
218 local_addr: Multiaddr,
219 remote_addr: Multiaddr,
220 ) where
221 TFut: Future<Output = Result<(PeerId, StreamMuxerBox), io::Error>> + Send + 'static,
222 {
223 let (abort_notifier, abort_receiver) = oneshot::channel();
224 let span = tracing::debug_span!(parent: tracing::Span::none(), "new_incoming_connection", id = %id, local_addr = %local_addr, remote_addr = %remote_addr);
225 span.follows_from(tracing::Span::current());
226 self.executor.spawn(
227 task::new_for_pending_connection(
228 id,
229 remote_addr.clone(),
230 future,
231 abort_receiver,
232 self.pending_connection_events_tx.clone(),
233 )
234 .instrument(span),
235 );
236 self.pending.insert(
237 id,
238 PendingConnection {
239 peer_id: None,
240 endpoint: ConnectedPoint::Listener {
241 local_addr,
242 remote_addr,
243 },
244 abort_notifier: Some(abort_notifier),
245 accepted_at: Instant::now(),
246 },
247 );
248 }
249
250 pub fn spawn_inbound_connection(
251 &mut self,
252 id: ConnectionId,
253 obtained_peer_id: PeerId,
254 endpoint: ConnectedPoint,
255 connection: NewConnection,
256 handler: THandler,
257 ) where
258 THandler: InboundStreamHandler,
259 {
260 let muxer = connection.extract();
261 let established_peer_connections = self
262 .established_peer_connections
263 .entry(obtained_peer_id)
264 .or_default();
265
266 let (command_tx, command_rx) = mpsc::channel(self.task_command_buffer_size);
267 let (event_tx, event_rx) = mpsc::channel(self.per_connection_event_buffer_size);
268 self.established.insert(
270 id,
271 EstablishedConnection {
272 endpoint,
273 sender: command_tx,
274 },
275 );
276 established_peer_connections.insert(id);
278 self.established_connection_events.push(event_rx);
279 if let Some(waker) = Option::take(&mut self.no_established_connections_waker) {
280 waker.wake();
281 }
282 let span = tracing::debug_span!(parent: tracing::Span::none(), "new_inbound_established", %id, peer = %obtained_peer_id);
283 span.follows_from(tracing::Span::current());
284 let connection = InboundConnection::new(
285 muxer,
286 handler,
287 self.max_negotiating_inbound_streams,
288 self.idle_connection_timeout,
289 );
290 self.executor.spawn(
291 task::new_for_established_connection(
292 id,
293 obtained_peer_id,
294 connection,
295 command_rx,
296 event_tx,
297 )
298 .instrument(span),
299 );
300 }
301
302 pub fn spawn_outbound_connection(
303 &mut self,
304 id: ConnectionId,
305 obtained_peer_id: PeerId,
306 endpoint: ConnectedPoint,
307 connection: NewConnection,
308 handler: THandler,
309 ) where
310 THandler: OutboundStreamHandler,
311 {
312 let muxer = connection.extract();
313 let established_peer_connections = self
314 .established_peer_connections
315 .entry(obtained_peer_id)
316 .or_default();
317
318 let (command_tx, command_rx) = mpsc::channel(self.task_command_buffer_size);
319 let (event_tx, event_rx) = mpsc::channel(self.per_connection_event_buffer_size);
320 self.established.insert(
322 id,
323 EstablishedConnection {
324 endpoint,
325 sender: command_tx,
326 },
327 );
328 established_peer_connections.insert(id);
330 self.established_connection_events.push(event_rx);
331 if let Some(waker) = Option::take(&mut self.no_established_connections_waker) {
332 waker.wake();
333 }
334 let span = tracing::debug_span!(parent: tracing::Span::none(), "new_outbound_established", %id, peer = %obtained_peer_id);
335 span.follows_from(tracing::Span::current());
336 let connection = OutboundConnection::new(muxer, handler, self.idle_connection_timeout);
337 self.executor.spawn(
338 task::new_for_established_connection(
339 id,
340 obtained_peer_id,
341 connection,
342 command_rx,
343 event_tx,
344 )
345 .instrument(span),
346 );
347 }
348
349 #[tracing::instrument(level = "debug", name = "Pool::poll", skip(self, cx))]
350 pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll<PoolEvent<THandler::Event>> {
351 match self.established_connection_events.poll_next_unpin(cx) {
352 Poll::Pending => {}
353 Poll::Ready(None) => {
354 self.no_established_connections_waker = Some(cx.waker().clone());
356 }
357 Poll::Ready(Some(task::EstablishedConnectionEvent::Notify { id, peer_id, event })) => {
358 return Poll::Ready(PoolEvent::ConnectionEvent { id, peer_id, event });
359 }
360 Poll::Ready(Some(task::EstablishedConnectionEvent::Closed { id, peer_id, error })) => {
361 if let Some(connections) = self.established_peer_connections.get_mut(&peer_id) {
362 connections.remove(&id);
363 if connections.is_empty() {
364 self.established_peer_connections.remove(&peer_id);
365 }
366 }
367 let EstablishedConnection { endpoint, .. } = self
368 .established
369 .remove(&id)
370 .expect("Connection should be established before being closed");
371
372 let num_remaining_established = self
373 .established_peer_connections
374 .get(&peer_id)
375 .map_or(0, |conns| conns.len());
376
377 return Poll::Ready(PoolEvent::ConnectionClosed {
378 id,
379 peer_id,
380 endpoint,
381 num_remaining_established,
382 error,
383 });
384 }
385 }
386 loop {
387 if let Poll::Ready(Some(result)) =
388 self.new_connection_dropped_listeners.poll_next_unpin(cx)
389 {
390 if let Ok(dropped_connection) = result {
391 self.executor.spawn(async move {
392 let _ = dropped_connection.close().await;
393 });
394 }
395 continue;
396 }
397
398 let event = match self.pending_connection_events_rx.poll_next_unpin(cx) {
399 Poll::Ready(Some(event)) => event,
400 Poll::Pending => return Poll::Pending,
401 Poll::Ready(None) => unreachable!("Pool holds both sender and receiver."),
402 };
403
404 let id = event.id();
405 let PendingConnection {
406 peer_id: expected_peer_id,
407 endpoint,
408 abort_notifier: _,
409 accepted_at,
410 } = self
411 .pending
412 .remove(&id)
413 .expect("Pending connection should exist before being established");
414
415 match event {
416 task::PendingConnectionEvent::ConnectionEstablished {
418 id,
419 peer_id: obtained_peer_id,
420 muxer,
421 } => {
422 if let Some(peer_id) = expected_peer_id {
424 if peer_id != peer_id {
425 let err_event = match &endpoint {
426 ConnectedPoint::Dialer { .. } => {
427 PoolEvent::PendingConnectionError {
428 id,
429 peer_id: Some(peer_id),
430 endpoint,
431 error: PendingConnectionError::WrongPeerId {
432 obtained: peer_id,
433 },
434 }
435 }
436 ConnectedPoint::Listener { .. } => unreachable!(
437 "Listener connections should not have peer ID mismatch"
438 ),
439 };
440 return Poll::Ready(err_event);
441 }
442 }
443 if self.local_id == obtained_peer_id {
445 let err_event = match &endpoint {
446 ConnectedPoint::Dialer { .. } => PoolEvent::PendingConnectionError {
447 id,
448 peer_id: expected_peer_id,
449 endpoint,
450 error: PendingConnectionError::LocalPeerId,
451 },
452 ConnectedPoint::Listener { .. } => PoolEvent::PendingConnectionError {
453 id,
454 peer_id: expected_peer_id,
455 endpoint,
456 error: PendingConnectionError::LocalPeerId,
457 },
458 };
459 return Poll::Ready(err_event);
460 }
461 let established_in = accepted_at.elapsed();
462
463 let (connection, drop_listener) = NewConnection::new(muxer);
464 self.new_connection_dropped_listeners.push(drop_listener);
465
466 return Poll::Ready(PoolEvent::ConnectionEstablished {
467 id,
468 peer_id: obtained_peer_id,
469 endpoint,
470 connection,
471 established_in,
472 });
473 }
474 task::PendingConnectionEvent::PendingFailed { id, error } => {
476 return Poll::Ready(PoolEvent::PendingConnectionError {
477 id,
478 peer_id: expected_peer_id,
479 endpoint,
480 error,
481 });
482 }
483 }
484 }
485 }
486}
487
488pub(crate) struct PendingConnection {
489 peer_id: Option<PeerId>,
490 endpoint: ConnectedPoint,
491 abort_notifier: Option<oneshot::Sender<Infallible>>,
492 accepted_at: Instant,
493}
494
495impl PendingConnection {
496 fn abort(&mut self) {
497 if let Some(notifier) = self.abort_notifier.take() {
498 drop(notifier);
499 }
500 }
501}
502
503#[derive(Debug)]
504pub struct EstablishedConnection<TAction> {
505 endpoint: ConnectedPoint,
506 sender: mpsc::Sender<task::Command<TAction>>,
507}
508
509impl<TAction> EstablishedConnection<TAction> {
510 pub(crate) fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), ()>> {
511 self.sender.poll_ready(cx).map_err(|_| ())
512 }
513
514 pub(crate) fn start_send(&mut self, action: TAction) -> Result<(), ()> {
515 self.sender
516 .start_send(task::Command::Action(action))
517 .map_err(|_| ())
518 }
519
520 pub(crate) fn start_close(&mut self) {
521 match self.sender.clone().try_send(task::Command::Close) {
522 Ok(()) => {}
523 Err(e) => assert!(e.is_disconnected(), "No capacity for close command."),
524 };
525 }
526}
527
528#[derive(Debug)]
529pub enum PoolEvent<TEvent> {
530 ConnectionEstablished {
531 id: ConnectionId,
532 peer_id: PeerId,
533 endpoint: ConnectedPoint,
534 connection: NewConnection,
535 established_in: Duration,
536 },
537
538 PendingConnectionError {
539 id: ConnectionId,
540 peer_id: Option<PeerId>,
541 endpoint: ConnectedPoint,
542 error: PendingConnectionError,
543 },
544
545 ConnectionClosed {
546 id: ConnectionId,
547 peer_id: PeerId,
548 endpoint: ConnectedPoint,
549 num_remaining_established: usize,
550 error: Option<ConnectionError>,
551 },
552 ConnectionEvent {
553 id: ConnectionId,
554 peer_id: PeerId,
555 event: TEvent,
556 },
557}
558
559#[derive(Debug)]
560pub struct NewConnection {
561 connection: Option<StreamMuxerBox>,
562 drop_sender: Option<oneshot::Sender<StreamMuxerBox>>,
563}
564
565impl NewConnection {
566 fn new(conn: StreamMuxerBox) -> (Self, oneshot::Receiver<StreamMuxerBox>) {
567 let (sender, receiver) = oneshot::channel();
568
569 (
570 Self {
571 connection: Some(conn),
572 drop_sender: Some(sender),
573 },
574 receiver,
575 )
576 }
577
578 fn extract(mut self) -> StreamMuxerBox {
579 self.connection
580 .take()
581 .expect("Connection should be available when extracted")
582 }
583}
584
585impl Drop for NewConnection {
586 fn drop(&mut self) {
587 if let Some(connection) = self.connection.take() {
588 let _ = self
589 .drop_sender
590 .take()
591 .expect("`drop_sender` to always be `Some`")
592 .send(connection);
593 }
594 }
595}
596
597pub struct PoolConfig {
598 executor: Box<dyn Executor + Send>,
599 task_command_buffer_size: usize,
600 per_connection_event_buffer_size: usize,
601 idle_connection_timeout: Duration,
602 max_negotiating_inbound_streams: usize,
603}
604
605impl PoolConfig {
606 pub fn new(executor: Box<dyn Executor + Send>) -> Self {
607 Self {
608 executor,
609 task_command_buffer_size: 32,
610 per_connection_event_buffer_size: 10,
611 idle_connection_timeout: Duration::from_secs(60),
612 max_negotiating_inbound_streams: 128,
613 }
614 }
615
616 pub fn with_task_command_buffer_size(mut self, size: usize) -> Self {
617 self.task_command_buffer_size = size;
618 self
619 }
620
621 pub fn with_per_connection_event_buffer_size(mut self, size: usize) -> Self {
622 self.per_connection_event_buffer_size = size;
623 self
624 }
625
626 pub fn with_idle_connection_timeout(mut self, timeout: Duration) -> Self {
627 self.idle_connection_timeout = timeout;
628 self
629 }
630
631 pub fn with_max_negotiating_inbound_streams(mut self, count: usize) -> Self {
632 self.max_negotiating_inbound_streams = count;
633 self
634 }
635}