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

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");
use std::{
sync::Arc,
time::Duration,
future::Future,
};
@ -260,32 +259,32 @@ pub const REQUEST_URI_MAX_LEN: usize = 1024;
/// The default port for the gemini protocol
pub const GEMINI_PORT: u16 = 1965;
#[derive(Clone)]
struct ServerInner {
#[cfg(feature = "gemini_srv")]
tls_acceptor: TlsAcceptor,
routes: Arc<RoutingNode<Handler>>,
routes: RoutingNode<Handler>,
timeout: Duration,
complex_timeout: Option<Duration>,
autorewrite: bool,
#[cfg(feature="ratelimiting")]
rate_limits: Arc<RoutingNode<RateLimiter<IpAddr>>>,
rate_limits: RoutingNode<RateLimiter<IpAddr>>,
#[cfg(feature="user_management")]
manager: UserManager,
}
impl ServerInner {
async fn serve_ip(self, listener: TcpListener) -> Result<()> {
let static_self: &'static Self = Box::leak(Box::new(self));
#[cfg(feature = "ratelimiting")]
tokio::spawn(prune_ratelimit_log(self.rate_limits.clone()));
tokio::spawn(prune_ratelimit_log(&static_self.rate_limits));
loop {
let (stream, _addr) = listener.accept().await
.context("Failed to accept client")?;
let this = self.clone();
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);
}
});
@ -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
// getting for now
async fn serve_unix(self, listener: UnixListener) -> Result<()> {
let static_self: &'static Self = Box::leak(Box::new(self));
#[cfg(feature = "ratelimiting")]
tokio::spawn(prune_ratelimit_log(self.rate_limits.clone()));
tokio::spawn(prune_ratelimit_log(&static_self.rate_limits));
loop {
let (stream, _addr) = listener.accept().await
.context("Failed to accept client")?;
let this = self.clone();
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);
}
});
@ -962,14 +962,14 @@ impl Server {
let data_dir = self.data_dir;
Ok(ServerInner {
routes: Arc::new(self.routes),
routes: self.routes,
timeout: self.timeout,
complex_timeout: self.complex_body_timeout_override,
autorewrite: self.autorewrite,
#[cfg(feature = "gemini_srv")]
tls_acceptor: TlsAcceptor::from(config),
#[cfg(feature="ratelimiting")]
rate_limits: Arc::new(self.rate_limits),
rate_limits: self.rate_limits,
#[cfg(feature="user_management")]
manager: UserManager::new(
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
/// "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<()> {
let server = self.build()?;
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
/// 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<()> {
let server = self.build()?;
let socket = UnixListener::bind(addr)?;