Skip to main content

iota_network_stack/
server.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    convert::Infallible,
7    num::NonZeroUsize,
8    task::{Context, Poll},
9    time::Duration,
10};
11
12use eyre::{Result, eyre};
13use iota_http::metrics::ListenerMetrics;
14use tokio_rustls::rustls::ServerConfig;
15use tonic::{
16    body::Body,
17    codegen::http::{HeaderValue, Request, Response},
18    server::NamedService,
19};
20use tower::{Layer, Service, ServiceBuilder};
21use tower_http::{
22    propagate_header::PropagateHeaderLayer, set_header::SetRequestHeaderLayer, trace::TraceLayer,
23};
24
25use crate::{
26    concurrency::ServiceConcurrencyLimit,
27    config::Config,
28    metrics::{
29        DefaultMetricsCallbackProvider, GRPC_ENDPOINT_PATH_HEADER, MetricsCallbackProvider,
30        MetricsHandler,
31    },
32    multiaddr::{Multiaddr, Protocol},
33};
34
35/// Server-side deadline for a request whose config sets no `request_timeout`.
36///
37/// A request holds its admission slot from the moment its headers arrive
38/// until the handler responds, including the time spent receiving the body,
39/// so without a deadline a peer that never finishes sending pins the slot
40/// indefinitely.
41pub const DEFAULT_GRPC_REQUEST_TIMEOUT: Duration = Duration::from_secs(300);
42
43pub struct ServerBuilder<M: MetricsCallbackProvider = DefaultMetricsCallbackProvider> {
44    config: Config,
45    metrics_provider: M,
46    router: tonic::service::Routes,
47    health_reporter: tonic_health::server::HealthReporter,
48    listener_metrics: Option<ListenerMetrics>,
49}
50
51impl<M: MetricsCallbackProvider> ServerBuilder<M> {
52    pub fn from_config(config: &Config, metrics_provider: M) -> Self {
53        let (health_reporter, health_service) = tonic_health::server::health_reporter();
54        let router = tonic::service::Routes::new(health_service);
55
56        Self {
57            config: config.to_owned(),
58            metrics_provider,
59            router,
60            health_reporter,
61            listener_metrics: None,
62        }
63    }
64
65    /// Measures the connections and the requests of the listener. See
66    /// [`iota_http::Config::metrics`].
67    pub fn listener_metrics(mut self, metrics: ListenerMetrics) -> Self {
68        self.listener_metrics = Some(metrics);
69        self
70    }
71
72    pub fn health_reporter(&self) -> tonic_health::server::HealthReporter {
73        self.health_reporter.clone()
74    }
75
76    /// The server-side request deadline this server enforces, unless a
77    /// request carries a shorter `grpc-timeout` header.
78    pub fn request_timeout(&self) -> Duration {
79        self.config
80            .request_timeout
81            .unwrap_or(DEFAULT_GRPC_REQUEST_TIMEOUT)
82    }
83
84    /// Add a new service to this Server.
85    pub fn add_service<S>(mut self, svc: S) -> Self
86    where
87        S: Service<Request<Body>, Response = Response<Body>, Error = Infallible>
88            + NamedService
89            + Clone
90            + Send
91            + Sync
92            + 'static,
93        S::Future: Send + 'static,
94    {
95        self.router = self.router.add_service(svc);
96        self
97    }
98
99    /// Add a new service to this Server with its own concurrency limit,
100    /// enforced independently of every other service on this server.
101    ///
102    /// With `load_shed` enabled, requests over the limit are rejected
103    /// immediately with gRPC `RESOURCE_EXHAUSTED`; otherwise they wait for a
104    /// slot to free up.
105    pub fn add_service_with_concurrency_limit<S>(
106        mut self,
107        svc: S,
108        limit: NonZeroUsize,
109        load_shed: bool,
110    ) -> Self
111    where
112        S: Service<Request<Body>, Response = Response<Body>, Error = Infallible>
113            + NamedService
114            + Clone
115            + Send
116            + Sync
117            + 'static,
118        S::Future: Send + 'static,
119    {
120        self.router = self
121            .router
122            .add_service(ServiceConcurrencyLimit::new(svc, limit, load_shed));
123        self
124    }
125
126    pub async fn bind(self, addr: &Multiaddr, tls_config: Option<ServerConfig>) -> Result<Server> {
127        let request_timeout = self.request_timeout();
128        let http_config = self.config.http_config().metrics(self.listener_metrics);
129
130        let metrics_provider = self.metrics_provider;
131        let metrics = MetricsHandler::new(metrics_provider.clone());
132        let request_metrics = TraceLayer::new_for_grpc()
133            .on_request(metrics.clone())
134            .on_response(metrics.clone())
135            .on_failure(metrics);
136
137        fn add_path_to_request_header<T>(request: &Request<T>) -> Option<HeaderValue> {
138            let path = request.uri().path();
139            Some(HeaderValue::from_str(path).unwrap())
140        }
141
142        let limiting_layers = ServiceBuilder::new()
143            .option_layer(
144                self.config
145                    .load_shed
146                    .unwrap_or_default()
147                    .then_some(tower::load_shed::LoadShedLayer::new()),
148            )
149            .option_layer(
150                self.config
151                    .global_concurrency_limit
152                    .map(tower::limit::GlobalConcurrencyLimitLayer::new),
153            );
154
155        let route_layers = ServiceBuilder::new()
156            .map_request(insert_tonic_connect_info)
157            .layer(RequestLifetimeLayer { metrics_provider })
158            .layer(SetRequestHeaderLayer::overriding(
159                GRPC_ENDPOINT_PATH_HEADER.clone(),
160                add_path_to_request_header,
161            ))
162            .layer(request_metrics)
163            .layer(PropagateHeaderLayer::new(GRPC_ENDPOINT_PATH_HEADER.clone()))
164            .layer_fn(move |service| {
165                crate::grpc_timeout::GrpcTimeout::new(service, Some(request_timeout))
166            });
167
168        let mut builder = iota_http::Builder::new().config(http_config);
169
170        let has_tls = tls_config.is_some();
171        if let Some(tls_config) = tls_config {
172            builder = builder.tls_config(tls_config);
173        }
174
175        let server_handle = builder
176            .serve(
177                addr,
178                limiting_layers.service(self.router.into_axum_router().layer(route_layers)),
179            )
180            .map_err(|e| eyre!(e))?;
181
182        let mut local_addr = update_tcp_port_in_multiaddr(addr, server_handle.local_addr().port());
183        if has_tls {
184            local_addr = local_addr.rewrite_http_to_https();
185        }
186        Ok(Server {
187            server_handle,
188            local_addr,
189            health_reporter: self.health_reporter,
190        })
191    }
192}
193
194/// TLS server name to use for the public IOTA validator interface.
195pub const IOTA_TLS_SERVER_NAME: &str = "iota";
196
197pub struct Server {
198    server_handle: iota_http::ServerHandle,
199    local_addr: Multiaddr,
200    health_reporter: tonic_health::server::HealthReporter,
201}
202
203impl Server {
204    pub async fn serve(self) -> Result<(), tonic::transport::Error> {
205        self.server_handle.wait_for_shutdown().await;
206        Ok(())
207    }
208
209    pub fn trigger_shutdown(&self) {
210        self.server_handle.trigger_shutdown();
211    }
212
213    pub fn local_addr(&self) -> &Multiaddr {
214        &self.local_addr
215    }
216
217    pub fn health_reporter(&self) -> tonic_health::server::HealthReporter {
218        self.health_reporter.clone()
219    }
220
221    pub fn handle(&self) -> &iota_http::ServerHandle {
222        &self.server_handle
223    }
224}
225
226/// Copies the client address and the listener address of a connection served
227/// by iota-http into tonic's `TcpConnectInfo`, which
228/// `tonic::Request::remote_addr` reads.
229pub fn insert_tonic_connect_info<B>(mut request: Request<B>) -> Request<B> {
230    if let Some(connect_info) = request.extensions().get::<iota_http::ConnectInfo>() {
231        let tonic_connect_info = tonic::transport::server::TcpConnectInfo {
232            local_addr: Some(connect_info.local_addr),
233            remote_addr: Some(connect_info.remote_addr),
234        };
235        request.extensions_mut().insert(tonic_connect_info);
236    }
237    request
238}
239
240fn update_tcp_port_in_multiaddr(addr: &Multiaddr, port: u16) -> Multiaddr {
241    addr.replace(1, |protocol| {
242        if let Protocol::Tcp(_) = protocol {
243            Some(Protocol::Tcp(port))
244        } else {
245            panic!("expected tcp protocol at index 1");
246        }
247    })
248    .expect("tcp protocol at index 1")
249}
250
251#[cfg(test)]
252mod test {
253    use std::{
254        ops::Deref,
255        sync::{Arc, Mutex},
256        time::Duration,
257    };
258
259    use fastcrypto::{ed25519::Ed25519KeyPair, traits::KeyPair};
260    use iota_metrics::test_utils::MetricsReader;
261    use tonic::Code;
262    use tonic_health::pb::{HealthCheckRequest, health_client::HealthClient};
263
264    use crate::{Multiaddr, config::Config, metrics::MetricsCallbackProvider};
265
266    #[test]
267    fn insert_tonic_connect_info_copies_both_addresses() {
268        let local_addr = "127.0.0.1:1".parse().unwrap();
269        let remote_addr = "127.0.0.2:2".parse().unwrap();
270        let mut request = http::Request::new(());
271        request.extensions_mut().insert(iota_http::ConnectInfo {
272            local_addr,
273            remote_addr,
274        });
275
276        let request = super::insert_tonic_connect_info(request);
277
278        let connect_info = request
279            .extensions()
280            .get::<tonic::transport::server::TcpConnectInfo>()
281            .unwrap();
282        assert_eq!(connect_info.local_addr(), Some(local_addr));
283        assert_eq!(connect_info.remote_addr(), Some(remote_addr));
284    }
285
286    #[tokio::test]
287    async fn test_metrics_layer_successful() {
288        #[derive(Clone)]
289        struct Metrics {
290            /// a flag to figure out whether the
291            /// on_request method has been called.
292            metrics_called: Arc<Mutex<bool>>,
293        }
294
295        impl MetricsCallbackProvider for Metrics {
296            fn on_request(&self, path: String) {
297                assert_eq!(path, "/grpc.health.v1.Health/Check");
298            }
299
300            fn on_response(
301                &self,
302                path: String,
303                _latency: Duration,
304                status: u16,
305                grpc_status_code: Code,
306            ) {
307                assert_eq!(path, "/grpc.health.v1.Health/Check");
308                assert_eq!(status, 200);
309                assert_eq!(grpc_status_code, Code::Ok);
310                let mut m = self.metrics_called.lock().unwrap();
311                *m = true
312            }
313        }
314
315        let metrics = Metrics {
316            metrics_called: Arc::new(Mutex::new(false)),
317        };
318
319        let address: Multiaddr = "/ip4/127.0.0.1/tcp/0/http".parse().unwrap();
320        let config = Config::new();
321        let keypair = Ed25519KeyPair::generate(&mut rand08::thread_rng());
322
323        let server = config
324            .server_builder_with_metrics(metrics.clone())
325            .bind(
326                &address,
327                Some(iota_tls::create_rustls_server_config(
328                    keypair.copy().private(),
329                    "test".to_string(),
330                )),
331            )
332            .await
333            .unwrap();
334
335        let address = server.local_addr().to_owned();
336        let channel = config
337            .connect(
338                &address,
339                iota_tls::create_rustls_client_config(
340                    keypair.public().to_owned(),
341                    "test".to_string(),
342                    None,
343                ),
344            )
345            .await
346            .unwrap();
347        let mut client = HealthClient::new(channel);
348
349        client
350            .check(HealthCheckRequest {
351                service: "".to_owned(),
352            })
353            .await
354            .unwrap();
355
356        server.server_handle.shutdown().await;
357
358        assert!(metrics.metrics_called.lock().unwrap().deref());
359    }
360
361    #[tokio::test]
362    async fn listener_metrics_count_the_connections_of_the_server() {
363        let registry = prometheus_filtered::Registry::new();
364        let address: Multiaddr = "/ip4/127.0.0.1/tcp/0/http".parse().unwrap();
365        let config = Config::new();
366        let server = config
367            .server_builder()
368            .listener_metrics(iota_http::metrics::ListenerMetrics::new(
369                "test",
370                &registry,
371                prometheus_filtered::MetricLevel::Info,
372            ))
373            .bind(&address, None)
374            .await
375            .unwrap();
376
377        let channel = tonic::transport::Endpoint::from_shared(format!(
378            "http://{}",
379            server.local_addr().to_socket_addr().unwrap()
380        ))
381        .unwrap()
382        .connect()
383        .await
384        .unwrap();
385        HealthClient::new(channel)
386            .check(HealthCheckRequest {
387                service: "".to_owned(),
388            })
389            .await
390            .unwrap();
391
392        let reader = MetricsReader::new(&registry).with_prefix("test");
393        assert_eq!(reader.value("inbound_connections_accepted", &[]), 1.0);
394        assert!(
395            !reader.has_family("pending_handshakes_peak"),
396            "a plain listener has no TLS metrics"
397        );
398        server.trigger_shutdown();
399    }
400
401    #[tokio::test]
402    async fn listener_metrics_time_the_tls_handshakes_of_the_server() {
403        let registry = prometheus_filtered::Registry::new();
404        let address: Multiaddr = "/ip4/127.0.0.1/tcp/0/http".parse().unwrap();
405        let config = Config::new();
406        let keypair = Ed25519KeyPair::generate(&mut rand08::thread_rng());
407        let server = config
408            .server_builder()
409            .listener_metrics(iota_http::metrics::ListenerMetrics::new(
410                "test",
411                &registry,
412                prometheus_filtered::MetricLevel::Info,
413            ))
414            .bind(
415                &address,
416                Some(iota_tls::create_rustls_server_config(
417                    keypair.copy().private(),
418                    "test".to_string(),
419                )),
420            )
421            .await
422            .unwrap();
423
424        let channel = config
425            .connect(
426                server.local_addr(),
427                iota_tls::create_rustls_client_config(
428                    keypair.public().to_owned(),
429                    "test".to_string(),
430                    None,
431                ),
432            )
433            .await
434            .unwrap();
435        HealthClient::new(channel)
436            .check(HealthCheckRequest {
437                service: "".to_owned(),
438            })
439            .await
440            .unwrap();
441
442        let reader = MetricsReader::new(&registry).with_prefix("test");
443        assert_eq!(
444            reader
445                .histogram_totals("handshake_latency", &[("result", "completed")])
446                .count,
447            1
448        );
449        assert_eq!(reader.value("pending_handshakes_peak", &[]), 1.0);
450        server.trigger_shutdown();
451    }
452
453    #[tokio::test]
454    async fn test_metrics_layer_error() {
455        #[derive(Clone)]
456        struct Metrics {
457            /// a flag to figure out whether the
458            /// on_request method has been called.
459            metrics_called: Arc<Mutex<bool>>,
460        }
461
462        impl MetricsCallbackProvider for Metrics {
463            fn on_request(&self, path: String) {
464                assert_eq!(path, "/grpc.health.v1.Health/Check");
465            }
466
467            fn on_response(
468                &self,
469                path: String,
470                _latency: Duration,
471                status: u16,
472                grpc_status_code: Code,
473            ) {
474                assert_eq!(path, "/grpc.health.v1.Health/Check");
475                assert_eq!(status, 200);
476                // According to https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc
477                // code 5 is not_found , which is what we expect to get in this case
478                assert_eq!(grpc_status_code, Code::NotFound);
479                let mut m = self.metrics_called.lock().unwrap();
480                *m = true
481            }
482        }
483
484        let metrics = Metrics {
485            metrics_called: Arc::new(Mutex::new(false)),
486        };
487
488        let address: Multiaddr = "/ip4/127.0.0.1/tcp/0/http".parse().unwrap();
489        let config = Config::new();
490        let keypair = Ed25519KeyPair::generate(&mut rand08::thread_rng());
491
492        let server = config
493            .server_builder_with_metrics(metrics.clone())
494            .bind(
495                &address,
496                Some(iota_tls::create_rustls_server_config(
497                    keypair.copy().private(),
498                    "test".to_string(),
499                )),
500            )
501            .await
502            .unwrap();
503        let address = server.local_addr().to_owned();
504        let channel = config
505            .connect(
506                &address,
507                iota_tls::create_rustls_client_config(
508                    keypair.public().to_owned(),
509                    "test".to_string(),
510                    None,
511                ),
512            )
513            .await
514            .unwrap();
515        let mut client = HealthClient::new(channel);
516
517        // Call the healthcheck for a service that doesn't exist
518        // that should give us back an error with code 5 (not_found)
519        // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc
520        let _ = client
521            .check(HealthCheckRequest {
522                service: "non-existing-service".to_owned(),
523            })
524            .await;
525
526        server.server_handle.shutdown().await;
527
528        assert!(metrics.metrics_called.lock().unwrap().deref());
529    }
530
531    async fn test_multiaddr(address: Multiaddr) {
532        let config = Config::new();
533        let keypair = Ed25519KeyPair::generate(&mut rand08::thread_rng());
534
535        let server_handle = config
536            .server_builder()
537            .bind(
538                &address,
539                Some(iota_tls::create_rustls_server_config(
540                    keypair.copy().private(),
541                    "test".to_string(),
542                )),
543            )
544            .await
545            .unwrap();
546        let address = server_handle.local_addr().to_owned();
547        let channel = config
548            .connect(
549                &address,
550                iota_tls::create_rustls_client_config(
551                    keypair.public().to_owned(),
552                    "test".to_string(),
553                    None,
554                ),
555            )
556            .await
557            .unwrap();
558        let mut client = HealthClient::new(channel);
559
560        client
561            .check(HealthCheckRequest {
562                service: "".to_owned(),
563            })
564            .await
565            .unwrap();
566
567        server_handle.server_handle.shutdown().await;
568    }
569
570    #[tokio::test]
571    async fn dns() {
572        let address: Multiaddr = "/dns/localhost/tcp/0/http".parse().unwrap();
573        test_multiaddr(address).await;
574    }
575
576    #[tokio::test]
577    async fn ip4() {
578        let address: Multiaddr = "/ip4/127.0.0.1/tcp/0/http".parse().unwrap();
579        test_multiaddr(address).await;
580    }
581
582    #[tokio::test]
583    async fn ip6() {
584        let address: Multiaddr = "/ip6/::1/tcp/0/http".parse().unwrap();
585        test_multiaddr(address).await;
586    }
587
588    #[test]
589    fn request_timeout_falls_back_to_the_default() {
590        let unset = Config::new();
591        assert_eq!(
592            unset.server_builder().request_timeout(),
593            super::DEFAULT_GRPC_REQUEST_TIMEOUT
594        );
595
596        let mut configured = Config::new();
597        configured.request_timeout = Some(Duration::from_secs(7));
598        assert_eq!(
599            configured.server_builder().request_timeout(),
600            Duration::from_secs(7)
601        );
602    }
603
604    /// Answers only once the whole request body has arrived, so a body that
605    /// never completes keeps the request in flight for as long as the peer
606    /// likes.
607    #[derive(Clone)]
608    struct DrainBody;
609
610    impl tower::Service<http::Request<tonic::body::Body>> for DrainBody {
611        type Response = http::Response<tonic::body::Body>;
612        type Error = std::convert::Infallible;
613        type Future = futures::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;
614
615        fn poll_ready(
616            &mut self,
617            _cx: &mut std::task::Context<'_>,
618        ) -> std::task::Poll<Result<(), Self::Error>> {
619            std::task::Poll::Ready(Ok(()))
620        }
621
622        fn call(&mut self, request: http::Request<tonic::body::Body>) -> Self::Future {
623            use http_body_util::BodyExt as _;
624            Box::pin(async move {
625                let _ = request.into_body().collect().await;
626                Ok(tonic::Status::new(Code::Ok, "").into_http())
627            })
628        }
629    }
630
631    impl tonic::server::NamedService for DrainBody {
632        const NAME: &'static str = "test.DrainBody";
633    }
634
635    /// A request body that either is already complete or never produces its
636    /// data, mirroring a peer that sends HEADERS and then goes silent.
637    enum TestBody {
638        Empty,
639        Stalled,
640    }
641
642    impl http_body::Body for TestBody {
643        type Data = bytes::Bytes;
644        type Error = std::convert::Infallible;
645
646        fn poll_frame(
647            self: std::pin::Pin<&mut Self>,
648            _cx: &mut std::task::Context<'_>,
649        ) -> std::task::Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
650            match *self {
651                TestBody::Empty => std::task::Poll::Ready(None),
652                TestBody::Stalled => std::task::Poll::Pending,
653            }
654        }
655
656        fn is_end_stream(&self) -> bool {
657            matches!(self, TestBody::Empty)
658        }
659    }
660
661    /// A request that never finishes sending its body must be answered by the
662    /// server-side deadline, releasing the admission slot it was holding.
663    #[tokio::test]
664    async fn server_timeout_releases_a_stalled_request_and_its_admission_slot() {
665        const REQUEST_TIMEOUT: Duration = Duration::from_millis(200);
666
667        let mut config = Config::new();
668        config.request_timeout = Some(REQUEST_TIMEOUT);
669        let address: Multiaddr = "/ip4/127.0.0.1/tcp/0/http".parse().unwrap();
670        let server = config
671            .server_builder()
672            .add_service_with_concurrency_limit(DrainBody, std::num::NonZeroUsize::MIN, false)
673            .bind(&address, None)
674            .await
675            .unwrap();
676
677        let stream = tokio::net::TcpStream::connect(server.local_addr().to_socket_addr().unwrap())
678            .await
679            .unwrap();
680        let (mut sender, connection) = hyper::client::conn::http2::handshake(
681            hyper_util::rt::TokioExecutor::new(),
682            hyper_util::rt::TokioIo::new(stream),
683        )
684        .await
685        .unwrap();
686        tokio::spawn(connection);
687
688        let request = |body| {
689            http::Request::post("/test.DrainBody/Drain")
690                .header("content-type", "application/grpc")
691                .body(body)
692                .unwrap()
693        };
694        let grpc_status = |response: &http::Response<hyper::body::Incoming>| {
695            response.headers()["grpc-status"]
696                .to_str()
697                .unwrap()
698                .to_owned()
699        };
700
701        // Occupies the only admission slot without ever sending its body.
702        let stalled = tokio::time::timeout(
703            REQUEST_TIMEOUT * 25,
704            sender.send_request(request(TestBody::Stalled)),
705        )
706        .await
707        .expect("the server must answer a stalled request once its deadline passes")
708        .unwrap();
709        assert_eq!(
710            grpc_status(&stalled),
711            (Code::DeadlineExceeded as i32).to_string()
712        );
713
714        // The slot is free again, so a complete request is served.
715        let served = sender.send_request(request(TestBody::Empty)).await.unwrap();
716        assert_eq!(grpc_status(&served), (Code::Ok as i32).to_string());
717
718        server.server_handle.shutdown().await;
719    }
720
721    /// The configured stream cap is advertised to the peer, so requests beyond
722    /// it on one connection never reach the server until an earlier one ends.
723    #[tokio::test]
724    async fn stream_cap_bounds_the_requests_one_connection_can_open() {
725        #[derive(Clone, Default)]
726        struct Started(Arc<std::sync::atomic::AtomicUsize>);
727
728        impl MetricsCallbackProvider for Started {
729            fn on_request(&self, _path: String) {}
730
731            fn on_response(
732                &self,
733                _path: String,
734                _latency: Duration,
735                _status: u16,
736                _grpc_status_code: Code,
737            ) {
738            }
739
740            fn on_start(&self, _path: &str) {
741                self.0.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
742            }
743        }
744
745        const STREAM_CAP: u32 = 2;
746
747        let started = Started::default();
748        let mut config = Config::new();
749        config.http2_max_concurrent_streams = Some(STREAM_CAP);
750        let address: Multiaddr = "/ip4/127.0.0.1/tcp/0/http".parse().unwrap();
751        let server = config
752            .server_builder_with_metrics(started.clone())
753            .add_service(DrainBody)
754            .bind(&address, None)
755            .await
756            .unwrap();
757
758        let stream = tokio::net::TcpStream::connect(server.local_addr().to_socket_addr().unwrap())
759            .await
760            .unwrap();
761        let (sender, connection) = hyper::client::conn::http2::handshake(
762            hyper_util::rt::TokioExecutor::new(),
763            hyper_util::rt::TokioIo::new(stream),
764        )
765        .await
766        .unwrap();
767        tokio::spawn(connection);
768
769        // One more stalled request than the cap allows; the surplus waits on
770        // the client side for a stream to free up.
771        let in_flight: Vec<_> = (0..STREAM_CAP + 1)
772            .map(|_| {
773                let mut sender = sender.clone();
774                tokio::spawn(async move {
775                    sender
776                        .send_request(
777                            http::Request::post("/test.DrainBody/Drain")
778                                .body(TestBody::Stalled)
779                                .unwrap(),
780                        )
781                        .await
782                })
783            })
784            .collect();
785        tokio::time::sleep(Duration::from_millis(300)).await;
786
787        assert_eq!(
788            started.0.load(std::sync::atomic::Ordering::SeqCst),
789            STREAM_CAP as usize,
790            "the server must only see as many requests as the stream cap allows"
791        );
792
793        for request in in_flight {
794            request.abort();
795        }
796        server.server_handle.shutdown().await;
797    }
798}
799
800#[derive(Clone)]
801struct RequestLifetimeLayer<M: MetricsCallbackProvider> {
802    metrics_provider: M,
803}
804
805impl<M: MetricsCallbackProvider, S> Layer<S> for RequestLifetimeLayer<M> {
806    type Service = RequestLifetime<M, S>;
807
808    fn layer(&self, inner: S) -> Self::Service {
809        RequestLifetime {
810            inner,
811            metrics_provider: self.metrics_provider.clone(),
812            path: None,
813        }
814    }
815}
816
817#[derive(Clone)]
818struct RequestLifetime<M: MetricsCallbackProvider, S> {
819    inner: S,
820    metrics_provider: M,
821    path: Option<String>,
822}
823
824impl<M: MetricsCallbackProvider, S, RequestBody> Service<Request<RequestBody>>
825    for RequestLifetime<M, S>
826where
827    S: Service<Request<RequestBody>>,
828{
829    type Response = S::Response;
830    type Error = S::Error;
831    type Future = S::Future;
832
833    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
834        self.inner.poll_ready(cx)
835    }
836
837    fn call(&mut self, request: Request<RequestBody>) -> Self::Future {
838        if self.path.is_none() {
839            let path = request.uri().path().to_string();
840            self.metrics_provider.on_start(&path);
841            self.path = Some(path);
842        }
843        self.inner.call(request)
844    }
845}
846
847impl<M: MetricsCallbackProvider, S> Drop for RequestLifetime<M, S> {
848    fn drop(&mut self) {
849        if let Some(path) = &self.path {
850            self.metrics_provider.on_drop(path)
851        }
852    }
853}