1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//!
//! An HTTP frontend for eiffelvis_core
//!

mod handlers;

use handlers::*;

use axum::{routing::get, Router};
pub use axum_server::Handle;
use axum_server::{self, tls_rustls::RustlsConfig};
use tracing::info;

use std::{
    io,
    net::{IpAddr, SocketAddr},
    sync::Arc,
};

use tower_http::cors::{Any, CorsLayer};

use uuid::Uuid;

use eiffelvis_core::domain::{app::EiffelVisApp, types::BaseEvent};

pub trait EiffelVisHttpApp: EiffelVisApp + Send + Sync + 'static {}
impl<T> EiffelVisHttpApp for T where T: EiffelVisApp + Send + Sync + 'static {}

type App<T> = Arc<tokio::sync::RwLock<T>>;

/// Takes an eiffelvis app and binds the http server on the given address.
/// This is likely the only function you'll ever need to call.
/// `shutdown` is used to trigger graceful shutdown, tokio::signal is useful for this.
pub async fn app<T: EiffelVisHttpApp>(
    app: App<T>,
    address: IpAddr,
    port: u16,
    handle: Handle,
    tls: Option<(String, String)>,
) -> io::Result<()> {
    let tls = match tls {
        Some((cert, key)) => Some(RustlsConfig::from_pem_file(cert, key).await?),
        _ => None,
    };

    let service = make_service(app);

    serve_service(SocketAddr::new(address, port), service, handle, tls).await
}

/// Serves an axum router on the given address, with optional TLS
async fn serve_service(
    addr: SocketAddr,
    service: Router,
    handle: Handle,
    tls: Option<RustlsConfig>,
) -> io::Result<()> {
    match tls {
        Some(config) => {
            info!("Binding to {:?} using tls", addr);
            axum_server::bind_rustls(addr, config)
                .handle(handle)
                .serve(service.into_make_service())
                .await
        }
        None => {
            info!("Binding to {:?} without tls", addr);
            axum_server::bind(addr)
                .handle(handle)
                .serve(service.into_make_service())
                .await
        }
    }
}