resymo_agent/common/
http.rs1use actix_http::Request;
2use actix_service::IntoServiceFactory;
3use actix_web::body::MessageBody;
4use actix_web::dev::{AppConfig, Response, Service, ServiceFactory};
5use actix_web::*;
6use anyhow::bail;
7use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
8use std::fmt;
9use std::net::{IpAddr, SocketAddr};
10use std::path::PathBuf;
11use std::str::FromStr;
12
13#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, clap::Args, schemars::JsonSchema)]
14pub struct Options {
15 #[serde(default, skip_serializing_if = "Option::is_none")]
17 #[arg(long, env)]
18 bind_host: Option<String>,
19
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 #[arg(long, env)]
23 bind_port: Option<u16>,
24
25 #[cfg(feature = "openssl")]
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 #[arg(long, env)]
29 tls_certificate: Option<PathBuf>,
30
31 #[cfg(feature = "openssl")]
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 #[arg(long, env)]
35 tls_key: Option<PathBuf>,
36}
37
38pub struct Defaults {
39 pub port: u16,
40 pub host: IpAddr,
41}
42
43pub async fn run_server<F, I, S, B>(
44 options: Options,
45 defaults: Defaults,
46 factory: F,
47) -> anyhow::Result<()>
48where
49 F: Fn() -> I + Send + Clone + 'static,
50 I: IntoServiceFactory<S, Request>,
51
52 S: ServiceFactory<Request, Config = AppConfig> + 'static,
53 S::Error: Into<Error> + 'static,
54 S::InitError: fmt::Debug,
55 S::Response: Into<Response<B>> + 'static,
56 <S::Service as Service<Request>>::Future: 'static,
57 S::Service: 'static,
58
59 B: MessageBody + 'static,
60{
61 let bind_addr = SocketAddr::new(
62 options
63 .bind_host
64 .as_deref()
65 .map(IpAddr::from_str)
66 .transpose()?
67 .unwrap_or(defaults.host),
68 options.bind_port.unwrap_or(defaults.port),
69 );
70
71 log::info!(" Binding on: {}", bind_addr);
72 log::info!(
73 " TLS - key: {}",
74 options
75 .tls_key
76 .as_ref()
77 .map(|p| p.display().to_string())
78 .unwrap_or_else(|| "<none>".to_string())
79 );
80 log::info!(
81 " TLS - certificate: {}",
82 options
83 .tls_certificate
84 .as_ref()
85 .map(|p| p.display().to_string())
86 .unwrap_or_else(|| "<none>".to_string())
87 );
88
89 let server = HttpServer::new(factory);
90
91 let server = match (options.tls_key, options.tls_certificate) {
92 (Some(key), Some(cert)) => {
93 #[cfg(feature = "openssl")]
94 {
95 let mut acceptor = SslAcceptor::mozilla_modern_v5(SslMethod::tls_server())?;
96 acceptor.set_certificate_chain_file(cert)?;
97 acceptor.set_private_key_file(key, SslFiletype::PEM)?;
98 server.bind_openssl(bind_addr, acceptor)?.run()
99 }
100 }
101 (None, None) => server.bind(bind_addr)?.run(),
102 _ => {
103 bail!("Enabling TLS requires both --tls-key and --tls-certificate");
104 }
105 };
106
107 server.await?;
108
109 Ok(())
110}