use std::{
fmt, io,
net::{SocketAddr, TcpListener},
time::Duration,
};
use actix_http::{body::MessageBody, HttpService, Request, Response};
use actix_server::{Server, ServerBuilder};
use actix_service::{map_config, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt};
use actix_web::{dev::AppConfig, Error};
use rustls::ServerConfig;
const HTTP11_ALPN: &[u8] = b"http/1.1";
fn app_config(secure: bool, host: String, addr: SocketAddr) -> AppConfig {
AppConfig::__priv_test_new(secure, host, addr)
}
pub(super) fn build_h1_server<F, I, S, B>(
factory: F,
listeners: Vec<TcpListener>,
workers: usize,
tls: Option<ServerConfig>,
) -> io::Result<Server>
where
F: Fn() -> I + Send + Clone + 'static,
I: IntoServiceFactory<S, Request>,
S: ServiceFactory<Request, Config = AppConfig> + 'static,
S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
S::Service: 'static,
<S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static,
{
if let Some(config) = &tls {
if config.alpn_protocols.len() != 1 || config.alpn_protocols[0].as_slice() != HTTP11_ALPN {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Bamboo's inbound TLS config must advertise only ALPN http/1.1",
));
}
}
if listeners.is_empty() {
return Err(io::Error::new(
io::ErrorKind::AddrNotAvailable,
"at least one HTTP listener is required",
));
}
let mut builder = ServerBuilder::default().workers(workers);
let graceful_shutdown_signal = builder.graceful_shutdown_signal();
for (index, listener) in listeners.into_iter().enumerate() {
let addr = listener.local_addr()?;
let name = format!("bamboo-http1-{index}-{addr}");
let host = addr.to_string();
let app_factory = factory.clone();
builder = match &tls {
Some(tls_config) => {
let tls_config = tls_config.clone();
let shutdown_signal = graceful_shutdown_signal.clone();
builder.listen(name, listener, move || {
let shutdown_signal = shutdown_signal.clone();
let app_config = app_config(true, host.clone(), addr);
let app = app_factory()
.into_factory()
.map_err(|err| err.into().error_response());
HttpService::build()
.graceful_shutdown_signal(move || {
let shutdown_signal = shutdown_signal.clone();
async move { shutdown_signal.notified().await }
})
.secure()
.local_addr(addr)
.client_disconnect_timeout(Duration::from_secs(1))
.h1(map_config(app, move |_| app_config.clone()))
.rustls_0_23(tls_config.clone())
})?
}
None => {
let shutdown_signal = graceful_shutdown_signal.clone();
builder.listen(name, listener, move || {
let shutdown_signal = shutdown_signal.clone();
let app_config = app_config(false, host.clone(), addr);
let app = app_factory()
.into_factory()
.map_err(|err| err.into().error_response());
HttpService::build()
.graceful_shutdown_signal(move || {
let shutdown_signal = shutdown_signal.clone();
async move { shutdown_signal.notified().await }
})
.local_addr(addr)
.client_disconnect_timeout(Duration::from_secs(1))
.h1(map_config(app, move |_| app_config.clone()))
.tcp()
})?
}
};
}
Ok(builder.run())
}