1use std::{collections::HashMap, sync::Arc, time::Duration};
6
7use connection_handler::{OnConnectionClose, sleep_or_pending};
8pub use http;
9use http::{Request, Response};
10use hyper_util::service::TowerToHyperService;
11use io::ServerIo;
12use metrics::{HandshakeResult, TrackedIo};
13use tokio::task::JoinSet;
14use tokio_rustls::{TlsAcceptor, rustls};
15use tower::{Service, ServiceBuilder, ServiceExt};
16use tracing::trace;
17
18use self::{
19 body::BoxBody,
20 connection_info::{ActiveConnections, PeerConnectionCounts},
21};
22
23pub mod body;
24mod config;
25mod connection_handler;
26mod connection_info;
27mod fuse;
28mod io;
29mod listener;
30pub mod metrics;
31
32pub use config::{Config, PeerConnectionEvent};
33pub use connection_info::{ConnectInfo, ConnectionId, ConnectionInfo, PeerCertificates};
34pub use listener::{Listener, ListenerExt};
35
36pub(crate) type BoxError = Box<dyn std::error::Error + Send + Sync>;
37const ALPN_H2: &[u8] = b"h2";
39const ALPN_H1: &[u8] = b"http/1.1";
41
42#[derive(Default)]
43pub struct Builder {
44 config: Config,
45 tls_config: Option<rustls::ServerConfig>,
46}
47
48impl Builder {
49 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn config(mut self, config: Config) -> Self {
54 self.config = config;
55 self
56 }
57
58 pub fn tls_single_cert(
63 self,
64 cert_file: impl AsRef<std::path::Path>,
65 private_key_file: impl AsRef<std::path::Path>,
66 ) -> Result<Self, BoxError> {
67 let tls_config =
68 iota_tls::create_rustls_server_config_from_pem(cert_file, private_key_file)?;
69 Ok(self.tls_config(tls_config))
70 }
71
72 pub fn tls_config(mut self, tls_config: rustls::ServerConfig) -> Self {
73 self.tls_config = Some(tls_config);
74 self
75 }
76
77 pub fn serve<A, S, ResponseBody>(
78 self,
79 addr: A,
80 service: S,
81 ) -> Result<ServerHandle<std::net::SocketAddr>, BoxError>
82 where
83 A: std::net::ToSocketAddrs,
84 S: Service<
85 Request<BoxBody>,
86 Response = Response<ResponseBody>,
87 Error: Into<BoxError>,
88 Future: Send,
89 > + Clone
90 + Send
91 + 'static,
92 ResponseBody: http_body::Body<Data = bytes::Bytes, Error: Into<BoxError>> + Send + 'static,
93 {
94 let listener = listener::TcpListenerWithOptions::new(addr, self.config.tcp_keepalive)?;
95
96 Self::serve_with_listener(self, listener, service)
97 }
98
99 fn serve_with_listener<L, S, ResponseBody>(
100 self,
101 listener: L,
102 service: S,
103 ) -> Result<ServerHandle<L::Addr>, BoxError>
104 where
105 L: Listener<Addr = std::net::SocketAddr>,
106 S: Service<
107 Request<BoxBody>,
108 Response = Response<ResponseBody>,
109 Error: Into<BoxError>,
110 Future: Send,
111 > + Clone
112 + Send
113 + 'static,
114 ResponseBody: http_body::Body<Data = bytes::Bytes, Error: Into<BoxError>> + Send + 'static,
115 {
116 self.config.validate()?;
117
118 let local_addr = listener.local_addr()?;
119 let graceful_shutdown_token = tokio_util::sync::CancellationToken::new();
120 let connections = ActiveConnections::default();
121
122 let tls_config = self.tls_config.map(|mut tls| {
123 tls.alpn_protocols.push(ALPN_H2.into());
124 if self.config.accept_http1 {
125 tls.alpn_protocols.push(ALPN_H1.into());
126 }
127 Arc::new(tls)
128 });
129
130 if let (Some(metrics), Some(_)) = (&self.config.metrics, &tls_config) {
131 metrics.enable_tls();
132 }
133
134 let (watch_sender, watch_receiver) = tokio::sync::watch::channel(());
135 let peer_connection_counts =
136 PeerConnectionCounts::new(self.config.on_peer_connection_event.clone());
137 let server = Server {
138 config: self.config,
139 tls_config,
140 listener,
141 local_addr,
142 service: ServiceBuilder::new()
143 .layer(tower::util::BoxCloneService::layer())
144 .map_response(|response: Response<ResponseBody>| response.map(body::boxed))
145 .map_err(Into::into)
146 .service(service),
147 pending_connections: JoinSet::new(),
148 connection_handlers: JoinSet::new(),
149 connections: connections.clone(),
150 graceful_shutdown_token: graceful_shutdown_token.clone(),
151 _watch_receiver: watch_receiver,
152 peer_connection_counts,
153 };
154
155 let handle = ServerHandle(Arc::new(HandleInner {
156 local_addr,
157 connections,
158 graceful_shutdown_token,
159 watch_sender,
160 }));
161
162 tokio::spawn(server.serve());
163
164 Ok(handle)
165 }
166}
167
168#[derive(Debug)]
169pub struct ServerHandle<A = std::net::SocketAddr>(Arc<HandleInner<A>>);
170
171#[derive(Debug)]
172struct HandleInner<A = std::net::SocketAddr> {
173 local_addr: A,
175 connections: ActiveConnections<A>,
176 graceful_shutdown_token: tokio_util::sync::CancellationToken,
177 watch_sender: tokio::sync::watch::Sender<()>,
178}
179
180impl<A> ServerHandle<A> {
181 pub fn local_addr(&self) -> &A {
183 &self.0.local_addr
184 }
185
186 pub fn trigger_shutdown(&self) {
189 self.0.graceful_shutdown_token.cancel();
190 }
191
192 pub async fn wait_for_shutdown(&self) {
198 self.0.watch_sender.closed().await
199 }
200
201 pub async fn shutdown(&self) {
204 self.trigger_shutdown();
205 self.wait_for_shutdown().await;
206 }
207
208 pub fn is_shutdown(&self) -> bool {
210 self.0.watch_sender.is_closed()
211 }
212
213 pub fn connections(
214 &self,
215 ) -> std::sync::RwLockReadGuard<'_, HashMap<ConnectionId, ConnectionInfo<A>>> {
216 self.0.connections.read().unwrap()
217 }
218
219 pub fn number_of_connections(&self) -> usize {
221 self.connections().len()
222 }
223}
224
225impl<A> Clone for ServerHandle<A> {
226 fn clone(&self) -> Self {
227 Self(self.0.clone())
228 }
229}
230
231type ConnectingOutput<Io, Addr> = Result<(ServerIo<TrackedIo<Io>>, Addr), crate::BoxError>;
232
233struct Server<L: Listener> {
234 config: Config,
235 tls_config: Option<Arc<rustls::ServerConfig>>,
236
237 listener: L,
238 local_addr: L::Addr,
239 service: tower::util::BoxCloneService<Request<BoxBody>, Response<BoxBody>, crate::BoxError>,
240
241 pending_connections: JoinSet<ConnectingOutput<L::Io, L::Addr>>,
242 connection_handlers: JoinSet<()>,
243 connections: ActiveConnections<L::Addr>,
244 graceful_shutdown_token: tokio_util::sync::CancellationToken,
245 _watch_receiver: tokio::sync::watch::Receiver<()>,
247 peer_connection_counts: PeerConnectionCounts,
248}
249
250impl<L> Server<L>
251where
252 L: Listener<Addr = std::net::SocketAddr>,
253{
254 async fn serve(mut self) -> Result<(), BoxError> {
255 loop {
256 tokio::select! {
257 _ = self.graceful_shutdown_token.cancelled() => {
258 trace!("signal received, shutting down");
259 break;
260 },
261 (io, remote_addr) = self.listener.accept(), if self.accepts_more_connections() => {
264 self.handle_incoming(io, remote_addr);
265 },
266 Some(maybe_connection) = self.pending_connections.join_next() => {
269 let (io, remote_addr) = match maybe_connection {
270 Ok(Ok((io, remote_addr))) => {
271 (io, remote_addr)
272 }
273 Ok(Err(e)) => {
274 tracing::debug!(error = %e, "error accepting connection");
275 continue;
276 }
277 Err(e) => {
278 tracing::error!(error = %e, "connection handshake task failed");
279 continue;
280 }
281 };
282
283 trace!("connection accepted");
284 self.handle_connection(io, remote_addr);
285 },
286 Some(connection_handler_output) = self.connection_handlers.join_next() => {
287 if let Err(e) = connection_handler_output {
288 tracing::error!(error = %e, "connection task failed");
289 }
290 },
291 }
292 }
293
294 self.shutdown().await;
296
297 Ok(())
298 }
299
300 fn accepts_more_connections(&self) -> bool {
303 self.config
304 .max_pending_connections
305 .is_none_or(|max| self.pending_connections.len() < max)
306 }
307
308 fn handle_incoming(&mut self, io: L::Io, remote_addr: L::Addr) {
309 let io = TrackedIo::new(io, self.config.metrics.as_ref(), remote_addr);
310 if let Some(tls) = self.tls_config.clone() {
311 let tls_acceptor = TlsAcceptor::from(tls);
312 let handshake_timeout = self.config.handshake_timeout;
313 let handshake_guard = io.connection().and_then(|c| c.record_handshake_start());
314 self.pending_connections.spawn(async move {
315 let result = tokio::select! {
316 result = handshake(io, remote_addr, tls_acceptor) => Some(result),
317 _ = sleep_or_pending(handshake_timeout) => None,
319 };
320 if let Some(guard) = handshake_guard {
321 guard.record_result(match &result {
322 Some(Ok(_)) => HandshakeResult::Completed,
323 Some(Err(_)) => HandshakeResult::Failed,
324 None => HandshakeResult::TimedOut,
325 });
326 }
327 result.unwrap_or_else(|| {
328 Err(std::io::Error::new(
329 std::io::ErrorKind::TimedOut,
330 "TLS handshake timed out",
331 )
332 .into())
333 })
334 });
335 } else {
336 self.handle_connection(ServerIo::new_io(io), remote_addr);
337 }
338 }
339
340 fn handle_connection(&mut self, io: ServerIo<TrackedIo<L::Io>>, remote_addr: L::Addr) {
341 let mut peer_connection_guard = None;
342 if let (Some(max), Some(peer)) =
343 (self.config.max_connections_per_peer, peer_public_key(&io))
344 {
345 let Some(guard) = self.peer_connection_counts.register(&peer, max) else {
346 trace!("peer already holds {max} connections, closing the new one");
348 return;
349 };
350 peer_connection_guard = Some(guard);
351 }
352
353 let connection_shutdown_token = self.graceful_shutdown_token.child_token();
354 let connection_info = ConnectionInfo::new(
355 remote_addr,
356 io.peer_certs(),
357 connection_shutdown_token.clone(),
358 );
359 let connection_id = connection_info.id();
360 let connect_info = connection_info::ConnectInfo {
361 local_addr: self.local_addr,
362 remote_addr: *connection_info.remote_address(),
363 };
364 let peer_certificates = connection_info.peer_certificates().cloned();
365 let hyper_io = hyper_util::rt::TokioIo::new(io);
366
367 let hyper_svc = TowerToHyperService::new(self.service.clone().map_request(
368 move |mut request: Request<hyper::body::Incoming>| {
369 request.extensions_mut().insert(connect_info.clone());
370 if let Some(peer_certificates) = peer_certificates.clone() {
371 request.extensions_mut().insert(peer_certificates);
372 }
373
374 request.map(body::boxed)
375 },
376 ));
377
378 self.connections
379 .write()
380 .unwrap()
381 .insert(connection_id, connection_info);
382 let on_connection_close = OnConnectionClose::new(
383 connection_id,
384 self.connections.clone(),
385 peer_connection_guard,
386 );
387
388 self.connection_handlers
389 .spawn(connection_handler::serve_connection(
390 hyper_io,
391 hyper_svc,
392 self.config.connection_builder(),
393 connection_shutdown_token,
394 self.config.max_connection_age,
395 on_connection_close,
396 ));
397 }
398
399 async fn shutdown(mut self) {
400 const CONNECTION_SHUTDOWN_GRACE_PERIOD: Duration = Duration::from_secs(1);
403
404 self.graceful_shutdown_token.cancel();
406
407 self.pending_connections.shutdown().await;
409
410 trace!(
412 "waiting for {} connections to close",
413 self.connection_handlers.len()
414 );
415
416 let graceful_shutdown =
417 async { while self.connection_handlers.join_next().await.is_some() {} };
418
419 if tokio::time::timeout(CONNECTION_SHUTDOWN_GRACE_PERIOD, graceful_shutdown)
420 .await
421 .is_err()
422 {
423 tracing::warn!(
424 "Failed to stop all connection handlers in {:?}. Forcing shutdown.",
425 CONNECTION_SHUTDOWN_GRACE_PERIOD
426 );
427 self.connection_handlers.shutdown().await;
428 }
429 }
430}
431
432async fn handshake<Io, Addr>(
434 io: TrackedIo<Io>,
435 remote_addr: Addr,
436 tls_acceptor: TlsAcceptor,
437) -> ConnectingOutput<Io, Addr>
438where
439 Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
440{
441 tracing::trace!("accepting TLS connection");
442 let io = tls_acceptor.accept(io).await?;
443 Ok((ServerIo::new_tls_io(io), remote_addr))
444}
445
446fn peer_public_key<Io>(io: &ServerIo<Io>) -> Option<Vec<u8>> {
449 let certs = io.peer_certs()?;
450 let [certificate] = certs.as_slice() else {
451 trace!("unexpected number of peer certificates: {}", certs.len());
452 return None;
453 };
454
455 match iota_tls::public_key_from_certificate(certificate) {
456 Ok(public_key) => Some(AsRef::<[u8]>::as_ref(&public_key).to_vec()),
457 Err(e) => {
458 trace!("failed to extract the public key from the peer certificate: {e:?}");
459 None
460 }
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use axum::Router;
467
468 use super::*;
469
470 #[tokio::test]
471 async fn simple() {
472 const MESSAGE: &str = "Hello, World!";
473
474 let app = Router::new().route("/", axum::routing::get(|| async { MESSAGE }));
475
476 let handle = Builder::new().serve(("localhost", 0), app).unwrap();
477
478 let url = format!("http://{}", handle.local_addr());
479
480 let _ = rustls::crypto::ring::default_provider().install_default();
481 let response = reqwest::get(url).await.unwrap().bytes().await.unwrap();
482
483 assert_eq!(response, MESSAGE.as_bytes());
484 }
485
486 #[tokio::test]
487 async fn shutdown() {
488 const MESSAGE: &str = "Hello, World!";
489
490 let app = Router::new().route("/", axum::routing::get(|| async { MESSAGE }));
491
492 let handle = Builder::new().serve(("localhost", 0), app).unwrap();
493
494 let url = format!("http://{}", handle.local_addr());
495
496 let _ = rustls::crypto::ring::default_provider().install_default();
497 let response = reqwest::get(url).await.unwrap().bytes().await.unwrap();
498
499 assert_eq!(handle.connections().len(), 1);
501
502 assert_eq!(response, MESSAGE.as_bytes());
503
504 assert!(!handle.is_shutdown());
505
506 handle.shutdown().await;
507
508 assert!(handle.is_shutdown());
509
510 assert_eq!(handle.connections().len(), 0);
512 }
513
514 const SERVER_NAME: &str = "iota-http-test";
515
516 fn test_tls_configs() -> (rustls::ServerConfig, rustls::ClientConfig) {
519 use fastcrypto::{
520 ed25519::{Ed25519KeyPair, Ed25519PrivateKey},
521 traits::{KeyPair, ToFromBytes},
522 };
523
524 let keypair = Ed25519KeyPair::from(Ed25519PrivateKey::from_bytes(&[42; 32]).unwrap());
525 let public_key = keypair.public().to_owned();
526 (
527 iota_tls::create_rustls_server_config(keypair.private(), SERVER_NAME.to_string()),
528 iota_tls::create_rustls_client_config(public_key, SERVER_NAME.to_string(), None),
529 )
530 }
531
532 #[tokio::test]
535 async fn silent_peer_is_dropped_after_the_handshake_timeout() {
536 use tokio::io::AsyncReadExt as _;
537
538 const HANDSHAKE_TIMEOUT: Duration = Duration::from_millis(200);
539
540 let (server_tls_config, _) = test_tls_configs();
541 let handle = Builder::new()
542 .config(Config::default().handshake_timeout(Some(HANDSHAKE_TIMEOUT)))
543 .tls_config(server_tls_config)
544 .serve(("localhost", 0), Router::new())
545 .unwrap();
546
547 let mut connection = tokio::net::TcpStream::connect(handle.local_addr())
549 .await
550 .unwrap();
551
552 let mut buf = [0u8; 1];
553 let read = tokio::time::timeout(HANDSHAKE_TIMEOUT * 25, connection.read(&mut buf))
554 .await
555 .expect("the server must not wait for the handshake past the deadline");
556
557 assert!(
558 matches!(read, Ok(0) | Err(_)),
559 "the server must close the connection, got {read:?}"
560 );
561 }
562
563 #[tokio::test]
566 async fn plaintext_peer_is_refused_by_a_tls_listener() {
567 use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
568
569 let (server_tls_config, _) = test_tls_configs();
570 let served = Arc::new(std::sync::atomic::AtomicBool::new(false));
571 let app = Router::new().route(
572 "/",
573 axum::routing::get({
574 let served = served.clone();
575 move || async move {
576 served.store(true, std::sync::atomic::Ordering::SeqCst);
577 "served"
578 }
579 }),
580 );
581 let handle = Builder::new()
582 .tls_config(server_tls_config)
583 .serve(("localhost", 0), app)
584 .unwrap();
585
586 let mut connection = tokio::net::TcpStream::connect(handle.local_addr())
587 .await
588 .unwrap();
589 connection
590 .write_all(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
591 .await
592 .unwrap();
593 connection
594 .write_all(&[0, 0, 0, 0x4, 0, 0, 0, 0, 0])
595 .await
596 .unwrap();
597
598 let received = tokio::time::timeout(Duration::from_secs(5), async {
601 let mut received = Vec::new();
602 let mut buf = [0u8; 64];
603 loop {
604 match connection.read(&mut buf).await {
605 Ok(0) | Err(_) => break received,
606 Ok(n) => received.extend_from_slice(&buf[..n]),
607 }
608 }
609 })
610 .await
611 .expect("the server must close a plaintext connection promptly");
612 const TLS_ALERT_RECORD: u8 = 0x15;
613 assert!(
614 received
615 .first()
616 .is_none_or(|first| *first == TLS_ALERT_RECORD),
617 "the server must answer plaintext with a TLS alert at most, got {received:?}"
618 );
619 assert!(
620 !served.load(std::sync::atomic::Ordering::SeqCst),
621 "no request may reach the service over plaintext"
622 );
623 }
624
625 #[tokio::test]
628 async fn http1_peer_that_never_finishes_its_headers_is_closed() {
629 use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
630
631 const HEADER_DEADLINE: Duration = Duration::from_millis(200);
632
633 let handle = Builder::new()
634 .config(Config::default().http1_header_read_timeout(Some(HEADER_DEADLINE)))
635 .serve(("localhost", 0), Router::new())
636 .unwrap();
637
638 let mut connection = tokio::net::TcpStream::connect(handle.local_addr())
639 .await
640 .unwrap();
641 connection
644 .write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n")
645 .await
646 .unwrap();
647 let sent_at = tokio::time::Instant::now();
648
649 let closed = tokio::time::timeout(HEADER_DEADLINE * 25, async {
652 let mut buf = [0u8; 1024];
653 loop {
654 match connection.read(&mut buf).await {
655 Ok(0) | Err(_) => break,
656 Ok(_) => continue,
657 }
658 }
659 })
660 .await;
661 assert!(
662 closed.is_ok(),
663 "the server must close a peer that stalls its request headers"
664 );
665 assert!(
666 sent_at.elapsed() >= HEADER_DEADLINE,
667 "the peer must be given the whole deadline before it is closed"
668 );
669 }
670
671 #[tokio::test]
674 async fn silent_http2_peer_is_closed_by_keepalive() {
675 use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
676
677 const KEEPALIVE: Duration = Duration::from_millis(100);
678
679 let handle = Builder::new()
680 .config(
681 Config::default()
682 .http2_keepalive_interval(Some(KEEPALIVE))
683 .http2_keepalive_timeout(Some(KEEPALIVE)),
684 )
685 .serve(("localhost", 0), Router::new())
686 .unwrap();
687
688 let mut connection = tokio::net::TcpStream::connect(handle.local_addr())
689 .await
690 .unwrap();
691 connection
693 .write_all(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
694 .await
695 .unwrap();
696 connection
697 .write_all(&[0, 0, 0, 0x4, 0, 0, 0, 0, 0])
698 .await
699 .unwrap();
700
701 let closed = tokio::time::timeout(KEEPALIVE * 50, async {
702 let mut buf = [0u8; 1024];
703 loop {
704 match connection.read(&mut buf).await {
705 Ok(0) | Err(_) => break,
706 Ok(_) => continue,
707 }
708 }
709 })
710 .await;
711 assert!(
712 closed.is_ok(),
713 "the server must close a peer that ignores keepalive pings"
714 );
715 }
716
717 #[tokio::test]
720 async fn pending_connections_are_capped() {
721 let (server_tls_config, client_tls_config) = test_tls_configs();
722 let handle = Builder::new()
723 .config(
724 Config::default()
725 .handshake_timeout(Some(Duration::from_secs(60)))
728 .max_pending_connections(Some(1)),
729 )
730 .tls_config(server_tls_config)
731 .serve(("localhost", 0), Router::new())
732 .unwrap();
733 let addr = *handle.local_addr();
734
735 let stalled = tokio::net::TcpStream::connect(addr).await.unwrap();
737 tokio::time::sleep(Duration::from_millis(200)).await;
738
739 let connector = tokio_rustls::TlsConnector::from(Arc::new(client_tls_config));
740 let server_name = rustls::pki_types::ServerName::try_from(SERVER_NAME).unwrap();
741 let handshake = async {
742 let io = tokio::net::TcpStream::connect(addr).await.unwrap();
743 connector.connect(server_name, io).await
744 };
745 tokio::pin!(handshake);
746
747 assert!(
751 tokio::time::timeout(Duration::from_secs(2), &mut handshake)
752 .await
753 .is_err(),
754 "the server must not handshake while the pending limit is reached"
755 );
756
757 drop(stalled);
758
759 tokio::time::timeout(Duration::from_secs(10), &mut handshake)
760 .await
761 .expect("the server must resume accepting once a slot frees")
762 .expect("the handshake must succeed");
763 }
764
765 #[tokio::test]
769 async fn connections_per_peer_are_capped() {
770 use fastcrypto::{
771 ed25519::{Ed25519KeyPair, Ed25519PrivateKey},
772 traits::{KeyPair, ToFromBytes},
773 };
774 use tokio::io::AsyncReadExt as _;
775
776 const MAX_PER_PEER: usize = 2;
777
778 let client_key =
779 |seed: u8| Ed25519KeyPair::from(Ed25519PrivateKey::from_bytes(&[seed; 32]).unwrap());
780 let server_keypair = client_key(1);
781 let server_public_key = server_keypair.public().to_owned();
782 let server_config = iota_tls::create_rustls_server_config_with_client_verifier(
783 server_keypair.private(),
784 SERVER_NAME.to_string(),
785 iota_tls::AllowPublicKeys::new(
786 [
787 client_key(2).public().to_owned(),
788 client_key(3).public().to_owned(),
789 ]
790 .into(),
791 ),
792 );
793
794 let events = Arc::new(std::sync::Mutex::new(Vec::new()));
795 let handle = Builder::new()
796 .config(
797 Config::default()
798 .max_connections_per_peer(Some(MAX_PER_PEER))
799 .on_peer_connection_event({
800 let events = events.clone();
801 move |peer, event| events.lock().unwrap().push((peer.to_vec(), event))
802 }),
803 )
804 .tls_config(server_config)
805 .serve(("localhost", 0), Router::new())
806 .unwrap();
807 let addr = *handle.local_addr();
808
809 let server_name = rustls::pki_types::ServerName::try_from(SERVER_NAME).unwrap();
810 let connect = |seed: u8| {
811 let connector =
812 tokio_rustls::TlsConnector::from(Arc::new(iota_tls::create_rustls_client_config(
813 server_public_key.clone(),
814 SERVER_NAME.to_string(),
815 Some(client_key(seed).private()),
816 )));
817 let server_name = server_name.clone();
818 async move {
819 let io = tokio::net::TcpStream::connect(addr).await.unwrap();
820 connector.connect(server_name, io).await.unwrap()
821 }
822 };
823
824 let events_of = |seed: u8| -> Vec<PeerConnectionEvent> {
825 let peer = client_key(seed).public().as_ref().to_vec();
826 events
827 .lock()
828 .unwrap()
829 .iter()
830 .filter(|(reported, _)| *reported == peer)
831 .map(|(_, event)| *event)
832 .collect()
833 };
834
835 let established = async |expected: usize| {
839 for _ in 0..100 {
840 if handle.number_of_connections() == expected {
841 return;
842 }
843 tokio::time::sleep(Duration::from_millis(20)).await;
844 }
845 panic!(
846 "expected {expected} connections, the server holds {}",
847 handle.number_of_connections()
848 );
849 };
850
851 let mut peer = Vec::new();
852 for _ in 0..MAX_PER_PEER {
853 peer.push(connect(2).await);
854 }
855 established(MAX_PER_PEER).await;
856
857 let mut refused = connect(2).await;
858 let mut buf = [0u8; 1];
859 let read = tokio::time::timeout(Duration::from_secs(10), refused.read(&mut buf))
860 .await
861 .expect("the server must close a connection past the limit");
862 assert!(
863 matches!(read, Ok(0) | Err(_)),
864 "the server must close the connection, got {read:?}"
865 );
866 established(MAX_PER_PEER).await;
867 assert_eq!(
868 events_of(2),
869 [
870 PeerConnectionEvent::Established { held: 1 },
871 PeerConnectionEvent::Established { held: 2 },
872 PeerConnectionEvent::RefusedAtLimit { held: 2 },
873 ],
874 "the refusal must be reported against the peer that caused it"
875 );
876
877 let other_peer = connect(3).await;
879 established(MAX_PER_PEER + 1).await;
880
881 peer.pop();
882 established(MAX_PER_PEER).await;
883 let reconnected = connect(2).await;
884 established(MAX_PER_PEER + 1).await;
885 assert_eq!(
886 events_of(2),
887 [
888 PeerConnectionEvent::Established { held: 1 },
889 PeerConnectionEvent::Established { held: 2 },
890 PeerConnectionEvent::RefusedAtLimit { held: 2 },
891 PeerConnectionEvent::Closed { held: 1 },
892 PeerConnectionEvent::Established { held: 2 },
893 ],
894 "closing and reconnecting must be reported with the count held afterwards"
895 );
896 assert_eq!(
897 events_of(3),
898 [PeerConnectionEvent::Established { held: 1 }],
899 "another peer's events must not leak into this one's"
900 );
901
902 drop((peer, other_peer, reconnected));
903 }
904
905 #[tokio::test]
909 async fn peer_with_extra_certificates_is_refused_at_the_handshake() {
910 use fastcrypto::{
911 ed25519::{Ed25519KeyPair, Ed25519PrivateKey},
912 traits::{KeyPair, ToFromBytes},
913 };
914 use tokio::io::AsyncReadExt as _;
915
916 let key =
917 |seed: u8| Ed25519KeyPair::from(Ed25519PrivateKey::from_bytes(&[seed; 32]).unwrap());
918 let server_public_key = key(1).public().to_owned();
919 let client_public_key = key(2).public().to_owned();
920 let server_config = iota_tls::create_rustls_server_config_with_client_verifier(
921 key(1).private(),
922 SERVER_NAME.to_string(),
923 iota_tls::AllowPublicKeys::new([client_public_key].into()),
924 );
925 let handle = Builder::new()
926 .config(Config::default().max_connections_per_peer(Some(1)))
927 .tls_config(server_config)
928 .serve(("localhost", 0), Router::new())
929 .unwrap();
930
931 let client_certificate =
933 iota_tls::SelfSignedCertificate::new(key(2).private(), SERVER_NAME);
934 let extra_certificate = iota_tls::SelfSignedCertificate::new(key(3).private(), SERVER_NAME);
935 let client_config =
936 iota_tls::ServerCertVerifier::new(server_public_key, SERVER_NAME.to_string())
937 .rustls_client_config_with_client_auth(
938 vec![
939 client_certificate.rustls_certificate(),
940 extra_certificate.rustls_certificate(),
941 ],
942 client_certificate.rustls_private_key(),
943 )
944 .unwrap();
945
946 let connector = tokio_rustls::TlsConnector::from(Arc::new(client_config));
950 let io = tokio::net::TcpStream::connect(handle.local_addr())
951 .await
952 .unwrap();
953 let server_name = rustls::pki_types::ServerName::try_from(SERVER_NAME).unwrap();
954 if let Ok(mut connection) = connector.connect(server_name, io).await {
955 let mut buf = [0u8; 1];
956 let read = tokio::time::timeout(Duration::from_secs(10), connection.read(&mut buf))
957 .await
958 .expect("the server must close a connection with more than one certificate");
959 assert!(
960 matches!(read, Ok(0) | Err(_)),
961 "the server must close the connection, got {read:?}"
962 );
963 }
964 assert_eq!(handle.number_of_connections(), 0);
965 }
966
967 #[tokio::test]
970 async fn unrecoverable_limits_are_rejected() {
971 let served = |config| {
972 Builder::new()
973 .config(config)
974 .tls_config(test_tls_configs().0)
975 .serve(("localhost", 0), Router::new())
976 };
977
978 assert!(
979 served(Config::default().max_pending_connections(Some(0))).is_err(),
980 "a zero limit must be rejected"
981 );
982 assert!(
983 served(
984 Config::default()
985 .handshake_timeout(None)
986 .max_pending_connections(Some(8))
987 )
988 .is_err(),
989 "a limit without a handshake deadline must be rejected"
990 );
991 assert!(
992 served(
993 Config::default()
994 .handshake_timeout(None)
995 .max_pending_connections(None)
996 )
997 .is_ok(),
998 "removing both bounds stays allowed"
999 );
1000 }
1001}