Skip to main content

iota_http/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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>;
37/// h2 alpn in plain format for rustls.
38const ALPN_H2: &[u8] = b"h2";
39/// h1 alpn in plain format for rustls.
40const 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    // Convenience method for configuring TLS with a single server cert
59    //
60    // Attempts to load PEM formatted files for the certificate chain and private
61    // key material from the provided file system paths.
62    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    /// The local address of the server.
174    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    /// Returns the local address of the server
182    pub fn local_addr(&self) -> &A {
183        &self.0.local_addr
184    }
185
186    /// Trigger a graceful shutdown of the server, but don't wait till the
187    /// server has completed shutting down
188    pub fn trigger_shutdown(&self) {
189        self.0.graceful_shutdown_token.cancel();
190    }
191
192    /// Completes once the network has been shutdown.
193    ///
194    /// This explicitly *does not* trigger the network to shutdown, see
195    /// `trigger_shutdown` or `shutdown` if you want to trigger shutting
196    /// down the server.
197    pub async fn wait_for_shutdown(&self) {
198        self.0.watch_sender.closed().await
199    }
200
201    /// Triggers a shutdown of the server and waits for it to complete shutting
202    /// down.
203    pub async fn shutdown(&self) {
204        self.trigger_shutdown();
205        self.wait_for_shutdown().await;
206    }
207
208    /// Checks if the Server has been shutdown.
209    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    /// Returns the number of active connections the server is handling
220    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    // Used to signal to a ServerHandle when the server has completed shutting down
246    _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                // While the handshake limit is reached, leave new connections in the kernel
262                // backlog rather than accepting them into an unbounded set of pending tasks.
263                (io, remote_addr) = self.listener.accept(), if self.accepts_more_connections() => {
264                    self.handle_incoming(io, remote_addr);
265                },
266                // A failed task affects only its own connection, so the loop keeps serving
267                // the others.
268                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        // Shutting down, wait for all connection handlers to finish
295        self.shutdown().await;
296
297        Ok(())
298    }
299
300    /// Whether another connection may be accepted, or the limit on concurrent
301    /// TLS handshakes is currently reached.
302    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                    // Dropping the handshake closes the connection, releasing its file descriptor.
318                    _ = 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                // Dropping the connection closes it, releasing its file descriptor.
347                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        // The time we are willing to wait for a connection to get gracefully shutdown
401        // before we attempt to forcefully shutdown all active connections
402        const CONNECTION_SHUTDOWN_GRACE_PERIOD: Duration = Duration::from_secs(1);
403
404        // Just to be careful make sure the token is canceled
405        self.graceful_shutdown_token.cancel();
406
407        // Terminate any in-progress pending connections
408        self.pending_connections.shutdown().await;
409
410        // Wait for all connection handlers to terminate
411        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
432/// Runs the TLS handshake to completion.
433async 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
446/// Identifies the peer by the public key of the single certificate it
447/// authenticated with, or `None` if it presented no certificate.
448fn 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        // a request was just made so we should have 1 active connection
500        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        // Now that the network has been shutdown there should be zero connections
511        assert_eq!(handle.connections().len(), 0);
512    }
513
514    const SERVER_NAME: &str = "iota-http-test";
515
516    /// A server config and a client config that trusts it, without client
517    /// authentication.
518    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    /// The peer is unauthenticated until its handshake completes, so a peer
533    /// that never starts one must not hold the connection open indefinitely.
534    #[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        // Connect, then never send a ClientHello.
548        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    /// A TLS-configured listener only speaks TLS: a peer that starts with a
564    /// plaintext HTTP/2 preface is closed before any request is dispatched.
565    #[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        // The TLS acceptor answers garbage with an alert record and closes;
599        // anything else would mean the plaintext bytes were served.
600        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    /// An HTTP/1 peer that never finishes sending its request headers is
626    /// closed once the header deadline passes.
627    #[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        // A request line and one header, but never the blank line that ends
642        // the header block.
643        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        // Without the deadline the peer is held forever, so a wide window here
650        // only guards against a slow machine, it does not weaken the assertion.
651        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    /// A peer that completes the HTTP/2 preface and then falls silent is
672    /// closed once it fails to answer the keepalive ping.
673    #[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 preface and an empty SETTINGS frame, then nothing more.
692        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    /// While the pending limit is reached the server stops accepting, and
718    /// resumes once a slot frees.
719    #[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                    // Long enough that the stalled peer below is released by the test
726                    // rather than by the deadline.
727                    .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        // Occupy the only handshake slot with a peer that never speaks.
736        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        // The connection reaches the kernel backlog but is not accepted. Without the
748        // limit the handshake completes in milliseconds, so a wide window here only
749        // guards against a slow machine, it does not weaken the assertion.
750        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    /// A peer at its connection limit gets no further connections, while every
766    /// other peer is served as usual and the peer itself recovers a slot as
767    /// soon as one of its connections closes.
768    #[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        // The connection is established before the server has necessarily
836        // registered or dropped it, so settle on the count rather than assert
837        // on it immediately.
838        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        // A peer sitting at its limit must not consume anyone else's budget.
878        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    /// The cap identifies a peer by its single certificate, so a peer sending
906    /// a longer chain is refused at the handshake and never holds a
907    /// connection.
908    #[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        // The allowed certificate followed by an unrelated one.
932        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        // In TLS 1.3 the client may consider the handshake done before the
947        // server's rejection arrives, so the refusal can surface on the first
948        // read instead.
949        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    /// A limit the accept loop can never fall below, and a limit whose slots
968    /// nothing releases, both leave the server unable to accept.
969    #[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}