mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-17 15:51:23 +01:00
allow listening on multiple ports in config
This is a config compatibility break. The ability to listen on multiple ports, including both TLS and non-TLS, is necessary for running complement against grapevine.
This commit is contained in:
parent
b7ad00ef6e
commit
f7d7952f9b
4 changed files with 99 additions and 45 deletions
|
|
@ -29,13 +29,6 @@ in
|
||||||
type = types.submodule {
|
type = types.submodule {
|
||||||
freeformType = format.type;
|
freeformType = format.type;
|
||||||
options = {
|
options = {
|
||||||
address = lib.mkOption {
|
|
||||||
type = types.nonEmptyStr;
|
|
||||||
description = ''
|
|
||||||
The local IP address to bind to.
|
|
||||||
'';
|
|
||||||
default = "::1";
|
|
||||||
};
|
|
||||||
conduit_compat = lib.mkOption {
|
conduit_compat = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
description = ''
|
description = ''
|
||||||
|
|
@ -56,12 +49,18 @@ in
|
||||||
then "/var/lib/matrix-conduit"
|
then "/var/lib/matrix-conduit"
|
||||||
else "/var/lib/grapevine";
|
else "/var/lib/grapevine";
|
||||||
};
|
};
|
||||||
port = lib.mkOption {
|
listen = lib.mkOption {
|
||||||
type = types.port;
|
type = types.listOf format.type;
|
||||||
description = ''
|
description = ''
|
||||||
The local port to bind to.
|
List of places to listen for incoming connections.
|
||||||
'';
|
'';
|
||||||
default = 6167;
|
default = [
|
||||||
|
{
|
||||||
|
type = "tcp";
|
||||||
|
address = "::1";
|
||||||
|
port = 6167;
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,8 @@ pub(crate) static DEFAULT_PATH: Lazy<PathBuf> =
|
||||||
pub(crate) struct Config {
|
pub(crate) struct Config {
|
||||||
#[serde(default = "false_fn")]
|
#[serde(default = "false_fn")]
|
||||||
pub(crate) conduit_compat: bool,
|
pub(crate) conduit_compat: bool,
|
||||||
#[serde(default = "default_address")]
|
#[serde(default = "default_listen")]
|
||||||
pub(crate) address: IpAddr,
|
pub(crate) listen: Vec<ListenConfig>,
|
||||||
#[serde(default = "default_port")]
|
|
||||||
pub(crate) port: u16,
|
|
||||||
pub(crate) tls: Option<TlsConfig>,
|
pub(crate) tls: Option<TlsConfig>,
|
||||||
|
|
||||||
pub(crate) server_name: OwnedServerName,
|
pub(crate) server_name: OwnedServerName,
|
||||||
|
|
@ -98,6 +96,19 @@ pub(crate) struct TlsConfig {
|
||||||
pub(crate) key: String,
|
pub(crate) key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
pub(crate) enum ListenConfig {
|
||||||
|
Tcp {
|
||||||
|
#[serde(default = "default_address")]
|
||||||
|
address: IpAddr,
|
||||||
|
#[serde(default = "default_port")]
|
||||||
|
port: u16,
|
||||||
|
#[serde(default = "false_fn")]
|
||||||
|
tls: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
fn false_fn() -> bool {
|
fn false_fn() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +117,14 @@ fn true_fn() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_listen() -> Vec<ListenConfig> {
|
||||||
|
vec![ListenConfig::Tcp {
|
||||||
|
address: default_address(),
|
||||||
|
port: default_port(),
|
||||||
|
tls: false,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
fn default_address() -> IpAddr {
|
fn default_address() -> IpAddr {
|
||||||
Ipv4Addr::LOCALHOST.into()
|
Ipv4Addr::LOCALHOST.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,15 @@ pub(crate) enum ConfigSearch {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub(crate) enum Serve {
|
pub(crate) enum Serve {
|
||||||
|
#[error("no listeners were specified in the configuration file")]
|
||||||
|
NoListeners,
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"listener requested TLS, but no TLS cert was specified in the \
|
||||||
|
configuration file. Please set 'tls.certs' and 'tls.key'"
|
||||||
|
)]
|
||||||
|
NoTlsCerts,
|
||||||
|
|
||||||
#[error("failed to read TLS cert and key files at {certs:?} and {key:?}")]
|
#[error("failed to read TLS cert and key files at {certs:?} and {key:?}")]
|
||||||
LoadCerts {
|
LoadCerts {
|
||||||
certs: String,
|
certs: String,
|
||||||
|
|
|
||||||
79
src/main.rs
79
src/main.rs
|
|
@ -15,6 +15,7 @@ use axum::{
|
||||||
use axum_server::{
|
use axum_server::{
|
||||||
bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle,
|
bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle,
|
||||||
};
|
};
|
||||||
|
use futures_util::FutureExt;
|
||||||
use http::{
|
use http::{
|
||||||
header::{self, HeaderName},
|
header::{self, HeaderName},
|
||||||
Method, StatusCode, Uri,
|
Method, StatusCode, Uri,
|
||||||
|
|
@ -26,14 +27,14 @@ use ruma::api::{
|
||||||
},
|
},
|
||||||
IncomingRequest,
|
IncomingRequest,
|
||||||
};
|
};
|
||||||
use tokio::signal;
|
use tokio::{signal, task::JoinSet};
|
||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
use tower_http::{
|
use tower_http::{
|
||||||
cors::{self, CorsLayer},
|
cors::{self, CorsLayer},
|
||||||
trace::TraceLayer,
|
trace::TraceLayer,
|
||||||
ServiceBuilderExt as _,
|
ServiceBuilderExt as _,
|
||||||
};
|
};
|
||||||
use tracing::{debug, info, info_span, warn, Instrument};
|
use tracing::{debug, error, info, info_span, warn, Instrument};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod args;
|
mod args;
|
||||||
|
|
@ -46,7 +47,7 @@ mod utils;
|
||||||
|
|
||||||
pub(crate) use api::ruma_wrapper::{Ar, Ra};
|
pub(crate) use api::ruma_wrapper::{Ar, Ra};
|
||||||
use api::{client_server, server_server};
|
use api::{client_server, server_server};
|
||||||
pub(crate) use config::Config;
|
pub(crate) use config::{Config, ListenConfig};
|
||||||
pub(crate) use database::KeyValueDatabase;
|
pub(crate) use database::KeyValueDatabase;
|
||||||
pub(crate) use service::{pdu::PduEvent, Services};
|
pub(crate) use service::{pdu::PduEvent, Services};
|
||||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||||
|
|
@ -135,7 +136,6 @@ async fn run_server() -> Result<(), error::Serve> {
|
||||||
use error::Serve as Error;
|
use error::Serve as Error;
|
||||||
|
|
||||||
let config = &services().globals.config;
|
let config = &services().globals.config;
|
||||||
let addr = SocketAddr::from((config.address, config.port));
|
|
||||||
|
|
||||||
let x_requested_with = HeaderName::from_static("x-requested-with");
|
let x_requested_with = HeaderName::from_static("x-requested-with");
|
||||||
|
|
||||||
|
|
@ -184,36 +184,60 @@ async fn run_server() -> Result<(), error::Serve> {
|
||||||
.layer(axum::middleware::from_fn(observability::http_metrics_layer));
|
.layer(axum::middleware::from_fn(observability::http_metrics_layer));
|
||||||
|
|
||||||
let app = routes(config).layer(middlewares).into_make_service();
|
let app = routes(config).layer(middlewares).into_make_service();
|
||||||
let handle = ServerHandle::new();
|
let mut handles = Vec::new();
|
||||||
|
let mut servers = JoinSet::new();
|
||||||
|
|
||||||
tokio::spawn(shutdown_signal(handle.clone()));
|
let tls_config = if let Some(tls) = &config.tls {
|
||||||
|
Some(RustlsConfig::from_pem_file(&tls.certs, &tls.key).await.map_err(
|
||||||
match &config.tls {
|
|err| Error::LoadCerts {
|
||||||
Some(tls) => {
|
|
||||||
let conf = RustlsConfig::from_pem_file(&tls.certs, &tls.key)
|
|
||||||
.await
|
|
||||||
.map_err(|err| Error::LoadCerts {
|
|
||||||
certs: tls.certs.clone(),
|
certs: tls.certs.clone(),
|
||||||
key: tls.key.clone(),
|
key: tls.key.clone(),
|
||||||
err,
|
err,
|
||||||
})?;
|
},
|
||||||
let server = bind_rustls(addr, conf).handle(handle).serve(app);
|
)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if config.listen.is_empty() {
|
||||||
|
return Err(Error::NoListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
for listen in &config.listen {
|
||||||
|
match listen {
|
||||||
|
ListenConfig::Tcp {
|
||||||
|
address,
|
||||||
|
port,
|
||||||
|
tls,
|
||||||
|
} => {
|
||||||
|
let addr = SocketAddr::from((*address, *port));
|
||||||
|
let handle = ServerHandle::new();
|
||||||
|
handles.push(handle.clone());
|
||||||
|
let server = if *tls {
|
||||||
|
let tls_config =
|
||||||
|
tls_config.clone().ok_or(Error::NoTlsCerts)?;
|
||||||
|
bind_rustls(addr, tls_config)
|
||||||
|
.handle(handle)
|
||||||
|
.serve(app.clone())
|
||||||
|
.left_future()
|
||||||
|
} else {
|
||||||
|
bind(addr).handle(handle).serve(app.clone()).right_future()
|
||||||
|
};
|
||||||
|
servers.spawn(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "systemd")]
|
#[cfg(feature = "systemd")]
|
||||||
sd_notify::notify(true, &[sd_notify::NotifyState::Ready])
|
sd_notify::notify(true, &[sd_notify::NotifyState::Ready])
|
||||||
.expect("should be able to notify systemd");
|
.expect("should be able to notify systemd");
|
||||||
|
|
||||||
server.await.map_err(Error::Listen)?;
|
tokio::spawn(shutdown_signal(handles));
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let server = bind(addr).handle(handle).serve(app);
|
|
||||||
|
|
||||||
#[cfg(feature = "systemd")]
|
while let Some(result) = servers.join_next().await {
|
||||||
sd_notify::notify(true, &[sd_notify::NotifyState::Ready])
|
result
|
||||||
.expect("should be able to notify systemd");
|
.expect("should be able to join server task")
|
||||||
|
.map_err(Error::Listen)?;
|
||||||
server.await.map_err(Error::Listen)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -456,7 +480,7 @@ fn routes(config: &Config) -> Router {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn shutdown_signal(handle: ServerHandle) {
|
async fn shutdown_signal(handles: Vec<ServerHandle>) {
|
||||||
let ctrl_c = async {
|
let ctrl_c = async {
|
||||||
signal::ctrl_c().await.expect("failed to install Ctrl+C handler");
|
signal::ctrl_c().await.expect("failed to install Ctrl+C handler");
|
||||||
};
|
};
|
||||||
|
|
@ -480,10 +504,13 @@ async fn shutdown_signal(handle: ServerHandle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("Received {}, shutting down...", sig);
|
warn!("Received {}, shutting down...", sig);
|
||||||
handle.graceful_shutdown(Some(Duration::from_secs(30)));
|
|
||||||
|
|
||||||
services().globals.shutdown();
|
services().globals.shutdown();
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
handle.graceful_shutdown(Some(Duration::from_secs(30)));
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "systemd")]
|
#[cfg(feature = "systemd")]
|
||||||
sd_notify::notify(true, &[sd_notify::NotifyState::Stopping])
|
sd_notify::notify(true, &[sd_notify::NotifyState::Stopping])
|
||||||
.expect("should be able to notify systemd");
|
.expect("should be able to notify systemd");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue