Skip to main content

cloudiful_server/actix/
runtime.rs

1use actix_web::{
2    App, Error, HttpServer,
3    dev::{Server as ActixServer, ServerHandle, ServiceFactory, ServiceRequest},
4    web,
5};
6use log::info;
7use std::net::SocketAddr;
8
9use crate::{ServerError, ValidatedServerConfig, load_tls_config};
10
11use super::cors::build_cors;
12
13pub struct BoundServer {
14    addrs: Vec<SocketAddr>,
15    server: ActixServer,
16}
17
18impl BoundServer {
19    pub fn addrs(&self) -> &[SocketAddr] {
20        self.addrs.as_slice()
21    }
22
23    pub fn handle(&self) -> ServerHandle {
24        self.server.handle()
25    }
26
27    pub async fn run(self) -> Result<(), ServerError> {
28        self.server.await.map_err(ServerError::from)
29    }
30}
31
32pub struct Server<F, U = ()>
33where
34    F: Fn(&mut web::ServiceConfig) + Clone + Send + 'static,
35    U: Clone + Send + 'static,
36{
37    services: F,
38    config: ValidatedServerConfig<U>,
39}
40
41impl<F, U> Server<F, U>
42where
43    F: Fn(&mut web::ServiceConfig) + Clone + Send + 'static,
44    U: Clone + Send + 'static,
45{
46    pub fn new(config: ValidatedServerConfig<U>, services: F) -> Self {
47        Self { services, config }
48    }
49
50    pub fn bind(self) -> Result<BoundServer, ServerError> {
51        let Server { services, config } = self;
52        let listen_addr = config.listen_addr().to_string();
53        let uses_tls = config.tls_enabled();
54        let cors_config = config.cors().clone();
55        let app_data = config.app_data().cloned();
56
57        let http_server = HttpServer::new(move || {
58            let app = App::new().wrap(build_cors(&cors_config));
59            build_app(app, &services, &app_data)
60        });
61
62        let http_server = match load_tls_config(&config)? {
63            Some(tls_config) => http_server.bind_rustls_0_23(listen_addr.as_str(), tls_config)?,
64            None => http_server.bind(listen_addr.as_str())?,
65        };
66
67        let addrs = http_server.addrs();
68        let scheme = if uses_tls { "https" } else { "http" };
69        info!("starting {scheme} server on {:?}", addrs);
70
71        Ok(BoundServer {
72            addrs,
73            server: http_server.run(),
74        })
75    }
76
77    pub async fn start(self) -> Result<(), ServerError> {
78        self.bind()?.run().await
79    }
80}
81
82fn build_app<T, F, U>(app: App<T>, services: &F, app_data: &Option<U>) -> App<T>
83where
84    T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
85    F: Fn(&mut web::ServiceConfig) + Clone + Send + 'static,
86    U: Clone + Send + 'static,
87{
88    let app = match app_data.clone() {
89        Some(app_data) => app.app_data(app_data),
90        None => app,
91    };
92
93    app.configure(services.clone())
94}