cloudiful_server/actix/
runtime.rs1use 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}