linera_chain/test/
http_server.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! A simple HTTP server to use for testing.
5
6use std::{future::IntoFuture, net::Ipv4Addr};
7
8use axum::Router;
9use futures::FutureExt as _;
10use tokio::{net::TcpListener, sync::oneshot};
11
12/// A handle to a running HTTP server.
13///
14/// The server is gracefully shutdown when this handle is dropped.
15pub struct HttpServer {
16    port: u16,
17    _shutdown_sender: oneshot::Sender<()>,
18}
19
20impl HttpServer {
21    /// Spawns a task with an HTTP server serving the routes defined by the [`Router`].
22    ///
23    /// Returns an [`HttpServer`] handle to keep the server running in the background.
24    pub async fn start(router: Router) -> anyhow::Result<Self> {
25        let (shutdown_sender, shutdown_receiver) = oneshot::channel();
26        let shutdown_signal = shutdown_receiver.map(|_| ());
27
28        let listener = TcpListener::bind((Ipv4Addr::from([127, 0, 0, 1]), 0)).await?;
29        let port = listener.local_addr()?.port();
30
31        tokio::spawn(
32            axum::serve(listener, router)
33                .with_graceful_shutdown(shutdown_signal)
34                .into_future(),
35        );
36
37        Ok(HttpServer {
38            port,
39            _shutdown_sender: shutdown_sender,
40        })
41    }
42
43    /// Returns the URL string this HTTP server is listening on.
44    pub fn url(&self) -> String {
45        format!("http://{}:{}", self.hostname(), self.port())
46    }
47
48    /// Returns the hostname of this HTTP server.
49    pub fn hostname(&self) -> String {
50        "localhost".to_owned()
51    }
52
53    /// Returns the port this HTTP server is listening on.
54    pub fn port(&self) -> u16 {
55        self.port
56    }
57}