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 tokio::task::JoinSet;
13use tokio_rustls::{TlsAcceptor, rustls};
14use tower::{Service, ServiceBuilder, ServiceExt};
15use tracing::trace;
16
17use self::{body::BoxBody, connection_info::ActiveConnections};
18
19pub mod body;
20mod config;
21mod connection_handler;
22mod connection_info;
23mod fuse;
24mod io;
25mod listener;
26
27pub use config::Config;
28pub use connection_info::{ConnectInfo, ConnectionId, ConnectionInfo, PeerCertificates};
29pub use listener::{Listener, ListenerExt};
30
31pub(crate) type BoxError = Box<dyn std::error::Error + Send + Sync>;
32const ALPN_H2: &[u8] = b"h2";
34const ALPN_H1: &[u8] = b"http/1.1";
36
37#[derive(Default)]
38pub struct Builder {
39 config: Config,
40 tls_config: Option<rustls::ServerConfig>,
41}
42
43impl Builder {
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn config(mut self, config: Config) -> Self {
49 self.config = config;
50 self
51 }
52
53 pub fn tls_single_cert(
58 self,
59 cert_file: impl AsRef<std::path::Path>,
60 private_key_file: impl AsRef<std::path::Path>,
61 ) -> Result<Self, BoxError> {
62 let tls_config =
63 iota_tls::create_rustls_server_config_from_pem(cert_file, private_key_file)?;
64 Ok(self.tls_config(tls_config))
65 }
66
67 pub fn tls_config(mut self, tls_config: rustls::ServerConfig) -> Self {
68 self.tls_config = Some(tls_config);
69 self
70 }
71
72 pub fn serve<A, S, ResponseBody>(
73 self,
74 addr: A,
75 service: S,
76 ) -> Result<ServerHandle<std::net::SocketAddr>, BoxError>
77 where
78 A: std::net::ToSocketAddrs,
79 S: Service<
80 Request<BoxBody>,
81 Response = Response<ResponseBody>,
82 Error: Into<BoxError>,
83 Future: Send,
84 > + Clone
85 + Send
86 + 'static,
87 ResponseBody: http_body::Body<Data = bytes::Bytes, Error: Into<BoxError>> + Send + 'static,
88 {
89 let listener = listener::TcpListenerWithOptions::new(
90 addr,
91 self.config.tcp_nodelay,
92 self.config.tcp_keepalive,
93 )?;
94
95 Self::serve_with_listener(self, listener, service)
96 }
97
98 fn serve_with_listener<L, S, ResponseBody>(
99 self,
100 listener: L,
101 service: S,
102 ) -> Result<ServerHandle<L::Addr>, BoxError>
103 where
104 L: Listener,
105 S: Service<
106 Request<BoxBody>,
107 Response = Response<ResponseBody>,
108 Error: Into<BoxError>,
109 Future: Send,
110 > + Clone
111 + Send
112 + 'static,
113 ResponseBody: http_body::Body<Data = bytes::Bytes, Error: Into<BoxError>> + Send + 'static,
114 {
115 self.config.validate()?;
116
117 let local_addr = listener.local_addr()?;
118 let graceful_shutdown_token = tokio_util::sync::CancellationToken::new();
119 let connections = ActiveConnections::default();
120
121 let tls_config = self.tls_config.map(|mut tls| {
122 tls.alpn_protocols.push(ALPN_H2.into());
123 if self.config.accept_http1 {
124 tls.alpn_protocols.push(ALPN_H1.into());
125 }
126 Arc::new(tls)
127 });
128
129 let (watch_sender, watch_receiver) = tokio::sync::watch::channel(());
130 let server = Server {
131 config: self.config,
132 tls_config,
133 listener,
134 local_addr: local_addr.clone(),
135 service: ServiceBuilder::new()
136 .layer(tower::util::BoxCloneService::layer())
137 .map_response(|response: Response<ResponseBody>| response.map(body::boxed))
138 .map_err(Into::into)
139 .service(service),
140 pending_connections: JoinSet::new(),
141 connection_handlers: JoinSet::new(),
142 connections: connections.clone(),
143 graceful_shutdown_token: graceful_shutdown_token.clone(),
144 _watch_receiver: watch_receiver,
145 };
146
147 let handle = ServerHandle(Arc::new(HandleInner {
148 local_addr,
149 connections,
150 graceful_shutdown_token,
151 watch_sender,
152 }));
153
154 tokio::spawn(server.serve());
155
156 Ok(handle)
157 }
158}
159
160#[derive(Debug)]
161pub struct ServerHandle<A = std::net::SocketAddr>(Arc<HandleInner<A>>);
162
163#[derive(Debug)]
164struct HandleInner<A = std::net::SocketAddr> {
165 local_addr: A,
167 connections: ActiveConnections<A>,
168 graceful_shutdown_token: tokio_util::sync::CancellationToken,
169 watch_sender: tokio::sync::watch::Sender<()>,
170}
171
172impl<A> ServerHandle<A> {
173 pub fn local_addr(&self) -> &A {
175 &self.0.local_addr
176 }
177
178 pub fn trigger_shutdown(&self) {
181 self.0.graceful_shutdown_token.cancel();
182 }
183
184 pub async fn wait_for_shutdown(&self) {
190 self.0.watch_sender.closed().await
191 }
192
193 pub async fn shutdown(&self) {
196 self.trigger_shutdown();
197 self.wait_for_shutdown().await;
198 }
199
200 pub fn is_shutdown(&self) -> bool {
202 self.0.watch_sender.is_closed()
203 }
204
205 pub fn connections(
206 &self,
207 ) -> std::sync::RwLockReadGuard<'_, HashMap<ConnectionId, ConnectionInfo<A>>> {
208 self.0.connections.read().unwrap()
209 }
210
211 pub fn number_of_connections(&self) -> usize {
213 self.connections().len()
214 }
215}
216
217impl<A> Clone for ServerHandle<A> {
218 fn clone(&self) -> Self {
219 Self(self.0.clone())
220 }
221}
222
223type ConnectingOutput<Io, Addr> = Result<(ServerIo<Io>, Addr), crate::BoxError>;
224
225struct Server<L: Listener> {
226 config: Config,
227 tls_config: Option<Arc<rustls::ServerConfig>>,
228
229 listener: L,
230 local_addr: L::Addr,
231 service: tower::util::BoxCloneService<Request<BoxBody>, Response<BoxBody>, crate::BoxError>,
232
233 pending_connections: JoinSet<ConnectingOutput<L::Io, L::Addr>>,
234 connection_handlers: JoinSet<()>,
235 connections: ActiveConnections<L::Addr>,
236 graceful_shutdown_token: tokio_util::sync::CancellationToken,
237 _watch_receiver: tokio::sync::watch::Receiver<()>,
239}
240
241impl<L> Server<L>
242where
243 L: Listener,
244{
245 async fn serve(mut self) -> Result<(), BoxError> {
246 loop {
247 tokio::select! {
248 _ = self.graceful_shutdown_token.cancelled() => {
249 trace!("signal received, shutting down");
250 break;
251 },
252 (io, remote_addr) = self.listener.accept(), if self.accepts_more_connections() => {
255 self.handle_incoming(io, remote_addr);
256 },
257 Some(maybe_connection) = self.pending_connections.join_next() => {
260 let (io, remote_addr) = match maybe_connection {
261 Ok(Ok((io, remote_addr))) => {
262 (io, remote_addr)
263 }
264 Ok(Err(e)) => {
265 tracing::debug!(error = %e, "error accepting connection");
266 continue;
267 }
268 Err(e) => {
269 tracing::error!(error = %e, "connection handshake task failed");
270 continue;
271 }
272 };
273
274 trace!("connection accepted");
275 self.handle_connection(io, remote_addr);
276 },
277 Some(connection_handler_output) = self.connection_handlers.join_next() => {
278 if let Err(e) = connection_handler_output {
279 tracing::error!(error = %e, "connection task failed");
280 }
281 },
282 }
283 }
284
285 self.shutdown().await;
287
288 Ok(())
289 }
290
291 fn accepts_more_connections(&self) -> bool {
294 self.config
295 .max_pending_connections
296 .is_none_or(|max| self.pending_connections.len() < max)
297 }
298
299 fn handle_incoming(&mut self, io: L::Io, remote_addr: L::Addr) {
300 if let Some(tls) = self.tls_config.clone() {
301 let tls_acceptor = TlsAcceptor::from(tls);
302 let allow_insecure = self.config.allow_insecure;
303 let handshake_timeout = self.config.handshake_timeout;
304 self.pending_connections.spawn(async move {
305 tokio::select! {
306 result = handshake(io, remote_addr, tls_acceptor, allow_insecure) => result,
307 _ = sleep_or_pending(handshake_timeout) => Err(std::io::Error::new(
309 std::io::ErrorKind::TimedOut,
310 "TLS handshake timed out",
311 )
312 .into()),
313 }
314 });
315 } else {
316 self.handle_connection(ServerIo::new_io(io), remote_addr);
317 }
318 }
319
320 fn handle_connection(&mut self, io: ServerIo<L::Io>, remote_addr: L::Addr) {
321 let connection_shutdown_token = self.graceful_shutdown_token.child_token();
322 let connection_info = ConnectionInfo::new(
323 remote_addr,
324 io.peer_certs(),
325 connection_shutdown_token.clone(),
326 );
327 let connection_id = connection_info.id();
328 let connect_info = connection_info::ConnectInfo {
329 local_addr: self.local_addr.clone(),
330 remote_addr: connection_info.remote_address().clone(),
331 };
332 let peer_certificates = connection_info.peer_certificates().cloned();
333 let hyper_io = hyper_util::rt::TokioIo::new(io);
334
335 let hyper_svc = TowerToHyperService::new(self.service.clone().map_request(
336 move |mut request: Request<hyper::body::Incoming>| {
337 request.extensions_mut().insert(connect_info.clone());
338 if let Some(peer_certificates) = peer_certificates.clone() {
339 request.extensions_mut().insert(peer_certificates);
340 }
341
342 request.map(body::boxed)
343 },
344 ));
345
346 self.connections
347 .write()
348 .unwrap()
349 .insert(connection_id, connection_info);
350 let on_connection_close = OnConnectionClose::new(connection_id, self.connections.clone());
351
352 self.connection_handlers
353 .spawn(connection_handler::serve_connection(
354 hyper_io,
355 hyper_svc,
356 self.config.connection_builder(),
357 connection_shutdown_token,
358 self.config.max_connection_age,
359 on_connection_close,
360 ));
361 }
362
363 async fn shutdown(mut self) {
364 const CONNECTION_SHUTDOWN_GRACE_PERIOD: Duration = Duration::from_secs(1);
367
368 self.graceful_shutdown_token.cancel();
370
371 self.pending_connections.shutdown().await;
373
374 trace!(
376 "waiting for {} connections to close",
377 self.connection_handlers.len()
378 );
379
380 let graceful_shutdown =
381 async { while self.connection_handlers.join_next().await.is_some() {} };
382
383 if tokio::time::timeout(CONNECTION_SHUTDOWN_GRACE_PERIOD, graceful_shutdown)
384 .await
385 .is_err()
386 {
387 tracing::warn!(
388 "Failed to stop all connection handlers in {:?}. Forcing shutdown.",
389 CONNECTION_SHUTDOWN_GRACE_PERIOD
390 );
391 self.connection_handlers.shutdown().await;
392 }
393 }
394}
395
396async fn handshake<Io, Addr>(
399 io: Io,
400 remote_addr: Addr,
401 tls_acceptor: TlsAcceptor,
402 allow_insecure: bool,
403) -> ConnectingOutput<Io, Addr>
404where
405 Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
406{
407 if allow_insecure {
408 if let Some(tcp) = <dyn std::any::Any>::downcast_ref::<tokio::net::TcpStream>(&io) {
411 let mut buf = [0; 1];
413 tcp.peek(&mut buf).await?;
416 if buf != [0x16] {
419 tracing::trace!("accepting insecure connection");
420 return Ok((ServerIo::new_io(io), remote_addr));
421 }
422 } else {
423 tracing::warn!(
424 "'allow_insecure' is configured but io type is not 'tokio::net::TcpStream'"
425 );
426 }
427 }
428
429 tracing::trace!("accepting TLS connection");
430 let io = tls_acceptor.accept(io).await?;
431 Ok((ServerIo::new_tls_io(io), remote_addr))
432}
433
434#[cfg(test)]
435mod tests {
436 use axum::Router;
437
438 use super::*;
439
440 #[tokio::test]
441 async fn simple() {
442 const MESSAGE: &str = "Hello, World!";
443
444 let app = Router::new().route("/", axum::routing::get(|| async { MESSAGE }));
445
446 let handle = Builder::new().serve(("localhost", 0), app).unwrap();
447
448 let url = format!("http://{}", handle.local_addr());
449
450 let response = reqwest::get(url).await.unwrap().bytes().await.unwrap();
451
452 assert_eq!(response, MESSAGE.as_bytes());
453 }
454
455 #[tokio::test]
456 async fn shutdown() {
457 const MESSAGE: &str = "Hello, World!";
458
459 let app = Router::new().route("/", axum::routing::get(|| async { MESSAGE }));
460
461 let handle = Builder::new().serve(("localhost", 0), app).unwrap();
462
463 let url = format!("http://{}", handle.local_addr());
464
465 let response = reqwest::get(url).await.unwrap().bytes().await.unwrap();
466
467 assert_eq!(handle.connections().len(), 1);
469
470 assert_eq!(response, MESSAGE.as_bytes());
471
472 assert!(!handle.is_shutdown());
473
474 handle.shutdown().await;
475
476 assert!(handle.is_shutdown());
477
478 assert_eq!(handle.connections().len(), 0);
480 }
481
482 const SERVER_NAME: &str = "iota-http-test";
483
484 fn test_tls_configs() -> (rustls::ServerConfig, rustls::ClientConfig) {
487 use fastcrypto::{
488 ed25519::{Ed25519KeyPair, Ed25519PrivateKey},
489 traits::{KeyPair, ToFromBytes},
490 };
491
492 let keypair = Ed25519KeyPair::from(Ed25519PrivateKey::from_bytes(&[42; 32]).unwrap());
493 let public_key = keypair.public().to_owned();
494 (
495 iota_tls::create_rustls_server_config(keypair.private(), SERVER_NAME.to_string()),
496 iota_tls::create_rustls_client_config(public_key, SERVER_NAME.to_string(), None),
497 )
498 }
499
500 #[tokio::test]
503 async fn silent_peer_is_dropped_after_the_handshake_timeout() {
504 use tokio::io::AsyncReadExt as _;
505
506 const HANDSHAKE_TIMEOUT: Duration = Duration::from_millis(200);
507
508 let (server_tls_config, _) = test_tls_configs();
509 let handle = Builder::new()
510 .config(Config::default().handshake_timeout(Some(HANDSHAKE_TIMEOUT)))
511 .tls_config(server_tls_config)
512 .serve(("localhost", 0), Router::new())
513 .unwrap();
514
515 let mut connection = tokio::net::TcpStream::connect(handle.local_addr())
517 .await
518 .unwrap();
519
520 let mut buf = [0u8; 1];
521 let read = tokio::time::timeout(HANDSHAKE_TIMEOUT * 25, connection.read(&mut buf))
522 .await
523 .expect("the server must not wait for the handshake past the deadline");
524
525 assert!(
526 matches!(read, Ok(0) | Err(_)),
527 "the server must close the connection, got {read:?}"
528 );
529 }
530
531 #[tokio::test]
534 async fn pending_connections_are_capped() {
535 let (server_tls_config, client_tls_config) = test_tls_configs();
536 let handle = Builder::new()
537 .config(
538 Config::default()
539 .handshake_timeout(Some(Duration::from_secs(60)))
542 .max_pending_connections(Some(1)),
543 )
544 .tls_config(server_tls_config)
545 .serve(("localhost", 0), Router::new())
546 .unwrap();
547 let addr = *handle.local_addr();
548
549 let stalled = tokio::net::TcpStream::connect(addr).await.unwrap();
551 tokio::time::sleep(Duration::from_millis(200)).await;
552
553 let connector = tokio_rustls::TlsConnector::from(Arc::new(client_tls_config));
554 let server_name = rustls::pki_types::ServerName::try_from(SERVER_NAME).unwrap();
555 let handshake = async {
556 let io = tokio::net::TcpStream::connect(addr).await.unwrap();
557 connector.connect(server_name, io).await
558 };
559 tokio::pin!(handshake);
560
561 assert!(
565 tokio::time::timeout(Duration::from_secs(2), &mut handshake)
566 .await
567 .is_err(),
568 "the server must not handshake while the pending limit is reached"
569 );
570
571 drop(stalled);
572
573 tokio::time::timeout(Duration::from_secs(10), &mut handshake)
574 .await
575 .expect("the server must resume accepting once a slot frees")
576 .expect("the handshake must succeed");
577 }
578
579 #[tokio::test]
582 async fn unrecoverable_limits_are_rejected() {
583 let served = |config| {
584 Builder::new()
585 .config(config)
586 .tls_config(test_tls_configs().0)
587 .serve(("localhost", 0), Router::new())
588 };
589
590 assert!(
591 served(Config::default().max_pending_connections(Some(0))).is_err(),
592 "a zero limit must be rejected"
593 );
594 assert!(
595 served(
596 Config::default()
597 .handshake_timeout(None)
598 .max_pending_connections(Some(8))
599 )
600 .is_err(),
601 "a limit without a handshake deadline must be rejected"
602 );
603 assert!(
604 served(
605 Config::default()
606 .handshake_timeout(None)
607 .max_pending_connections(None)
608 )
609 .is_ok(),
610 "removing both bounds stays allowed"
611 );
612 }
613}