Clone fewer Arcs by using statics to distribute references to the server

This commit is contained in:
Emi Tatsuo 2020-12-15 10:13:18 -05:00
parent 285768f509
commit 597bc6aa6b
Signed by: Emi
GPG Key ID: 68FAB2E2E6DFC98B
1 changed files with 19 additions and 12 deletions

View File

@ -192,7 +192,6 @@ compile_error!("Please enable only one of either the `gemini_srv` or `scgi_srv`
compile_error!("Please enable at least one of either the `gemini_srv` or `scgi_srv` features on the kochab crate"); compile_error!("Please enable at least one of either the `gemini_srv` or `scgi_srv` features on the kochab crate");
use std::{ use std::{
sync::Arc,
time::Duration, time::Duration,
future::Future, future::Future,
}; };
@ -260,32 +259,32 @@ pub const REQUEST_URI_MAX_LEN: usize = 1024;
/// The default port for the gemini protocol /// The default port for the gemini protocol
pub const GEMINI_PORT: u16 = 1965; pub const GEMINI_PORT: u16 = 1965;
#[derive(Clone)]
struct ServerInner { struct ServerInner {
#[cfg(feature = "gemini_srv")] #[cfg(feature = "gemini_srv")]
tls_acceptor: TlsAcceptor, tls_acceptor: TlsAcceptor,
routes: Arc<RoutingNode<Handler>>, routes: RoutingNode<Handler>,
timeout: Duration, timeout: Duration,
complex_timeout: Option<Duration>, complex_timeout: Option<Duration>,
autorewrite: bool, autorewrite: bool,
#[cfg(feature="ratelimiting")] #[cfg(feature="ratelimiting")]
rate_limits: Arc<RoutingNode<RateLimiter<IpAddr>>>, rate_limits: RoutingNode<RateLimiter<IpAddr>>,
#[cfg(feature="user_management")] #[cfg(feature="user_management")]
manager: UserManager, manager: UserManager,
} }
impl ServerInner { impl ServerInner {
async fn serve_ip(self, listener: TcpListener) -> Result<()> { async fn serve_ip(self, listener: TcpListener) -> Result<()> {
let static_self: &'static Self = Box::leak(Box::new(self));
#[cfg(feature = "ratelimiting")] #[cfg(feature = "ratelimiting")]
tokio::spawn(prune_ratelimit_log(self.rate_limits.clone())); tokio::spawn(prune_ratelimit_log(&static_self.rate_limits));
loop { loop {
let (stream, _addr) = listener.accept().await let (stream, _addr) = listener.accept().await
.context("Failed to accept client")?; .context("Failed to accept client")?;
let this = self.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(err) = this.serve_client(stream).await { if let Err(err) = static_self.serve_client(stream).await {
error!("{:?}", err); error!("{:?}", err);
} }
}); });
@ -296,16 +295,17 @@ impl ServerInner {
// Yeah it's code duplication, but I can't find a way around it, so this is what we're // Yeah it's code duplication, but I can't find a way around it, so this is what we're
// getting for now // getting for now
async fn serve_unix(self, listener: UnixListener) -> Result<()> { async fn serve_unix(self, listener: UnixListener) -> Result<()> {
let static_self: &'static Self = Box::leak(Box::new(self));
#[cfg(feature = "ratelimiting")] #[cfg(feature = "ratelimiting")]
tokio::spawn(prune_ratelimit_log(self.rate_limits.clone())); tokio::spawn(prune_ratelimit_log(&static_self.rate_limits));
loop { loop {
let (stream, _addr) = listener.accept().await let (stream, _addr) = listener.accept().await
.context("Failed to accept client")?; .context("Failed to accept client")?;
let this = self.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(err) = this.serve_client(stream).await { if let Err(err) = static_self.serve_client(stream).await {
error!("{:?}", err); error!("{:?}", err);
} }
}); });
@ -962,14 +962,14 @@ impl Server {
let data_dir = self.data_dir; let data_dir = self.data_dir;
Ok(ServerInner { Ok(ServerInner {
routes: Arc::new(self.routes), routes: self.routes,
timeout: self.timeout, timeout: self.timeout,
complex_timeout: self.complex_body_timeout_override, complex_timeout: self.complex_body_timeout_override,
autorewrite: self.autorewrite, autorewrite: self.autorewrite,
#[cfg(feature = "gemini_srv")] #[cfg(feature = "gemini_srv")]
tls_acceptor: TlsAcceptor::from(config), tls_acceptor: TlsAcceptor::from(config),
#[cfg(feature="ratelimiting")] #[cfg(feature="ratelimiting")]
rate_limits: Arc::new(self.rate_limits), rate_limits: self.rate_limits,
#[cfg(feature="user_management")] #[cfg(feature="user_management")]
manager: UserManager::new( manager: UserManager::new(
self.database.unwrap_or_else(move|| sled::open(data_dir).unwrap()) self.database.unwrap_or_else(move|| sled::open(data_dir).unwrap())
@ -981,6 +981,11 @@ impl Server {
/// ///
/// `addr` can be anything `tokio` can parse, including just a string like /// `addr` can be anything `tokio` can parse, including just a string like
/// "localhost:1965" /// "localhost:1965"
///
/// This will only ever exit with an error. It's important to note that even if the
/// function exits, the server will NOT be deallocated, since references to it in
/// concurrently running futures may still exist. As such, a loop that handles an
/// error by re-serving a new server may trigger a memory leak.
pub async fn serve_ip(self, addr: impl ToSocketAddrs + Send) -> Result<()> { pub async fn serve_ip(self, addr: impl ToSocketAddrs + Send) -> Result<()> {
let server = self.build()?; let server = self.build()?;
let socket = TcpListener::bind(addr).await?; let socket = TcpListener::bind(addr).await?;
@ -992,6 +997,8 @@ impl Server {
/// ///
/// Requires an address in the form of a path to bind to. This is only available when /// Requires an address in the form of a path to bind to. This is only available when
/// in `scgi_srv` mode. /// in `scgi_srv` mode.
///
/// Please read the details and warnings of [`serve_ip()`] for more information
pub async fn serve_unix(self, addr: impl AsRef<std::path::Path>) -> Result<()> { pub async fn serve_unix(self, addr: impl AsRef<std::path::Path>) -> Result<()> {
let server = self.build()?; let server = self.build()?;
let socket = UnixListener::bind(addr)?; let socket = UnixListener::bind(addr)?;