The switch now reaches the running server
The join: the policy decides, features owns the listener objects, ListenerControl owns the running sockets, and only Server has both. Server::set_protocol_policy is what a click performs. It applies the locks to what was asked before storing anything (LP-21), so what is recorded is what the server allows. Closing removes each listener object and then stops its socket; opening puts the object back and then spawns it. The order is the point in both directions -- a socket stopped while its object remains returns on the next restart, and a socket spawned before its object exists has nothing to come back to. saved_listeners is carried over from the stored policy rather than taken from the request. A client never sets it, and a /set that omitted it would otherwise lose the listeners still waiting to come back. Putting a listener back has to bind a fresh socket, so it re-parses from the registry -- the objects are already back by then -- rather than trying to revive the saved one. Only main knows which session manager a protocol wants, so it leaves a spawner behind at startup and spawn_listener is now shared between that and the initial spawn. Without a spawner a restored listener is reported as pending a restart rather than promised, which is what the test servers will see. A listener that cannot be put back does not stop the others and stays saved for another try (LP-5). Still nothing an operator can reach: no JMAP method calls this yet, and no sign-in is refused. What it does do is close and reopen a port on a running server, which is the part that did not exist this morning.
This commit is contained in:
+65
-33
@@ -9,14 +9,21 @@
|
||||
#![warn(clippy::cast_possible_wrap)]
|
||||
#![warn(clippy::cast_sign_loss)]
|
||||
|
||||
use common::{BuildServer, config::server::ServerProtocol, manager::boot::BootManager};
|
||||
use common::{
|
||||
BuildServer, Inner,
|
||||
config::server::{Listener, ServerProtocol},
|
||||
manager::boot::BootManager,
|
||||
network::TcpAcceptor,
|
||||
};
|
||||
use http::HttpSessionManager;
|
||||
use imap::core::ImapSessionManager;
|
||||
use managesieve::core::ManageSieveSessionManager;
|
||||
use pop3::Pop3SessionManager;
|
||||
use services::{StartServices, broadcast::subscriber::spawn_broadcast_subscriber};
|
||||
use smtp::{StartQueueManager, core::SmtpSessionManager};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::watch;
|
||||
use trc::Collector;
|
||||
use utils::wait_for_shutdown;
|
||||
|
||||
@@ -75,43 +82,24 @@ async fn main() -> std::io::Result<()> {
|
||||
// rest accepting (legacy-protocols LP-2). The registry lives in `Data` and
|
||||
// so outlives the listeners, which it must: it owns the sending ends.
|
||||
let listener_control = &init.inner.data.listener_control;
|
||||
let spawn_inner = init.inner.clone();
|
||||
let (shutdown_tx, shutdown_rx) =
|
||||
init.servers
|
||||
.spawn_with_control(listener_control, |server, acceptor, shutdown_rx| {
|
||||
match &server.protocol {
|
||||
ServerProtocol::Smtp | ServerProtocol::Lmtp => server.spawn(
|
||||
SmtpSessionManager::new(init.inner.clone()),
|
||||
init.inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Http => server.spawn(
|
||||
HttpSessionManager::new(init.inner.clone()),
|
||||
init.inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Imap => server.spawn(
|
||||
ImapSessionManager::new(init.inner.clone()),
|
||||
init.inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Pop3 => server.spawn(
|
||||
Pop3SessionManager::new(init.inner.clone()),
|
||||
init.inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::ManageSieve => server.spawn(
|
||||
ManageSieveSessionManager::new(init.inner.clone()),
|
||||
init.inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
};
|
||||
spawn_listener(&spawn_inner, server, acceptor, shutdown_rx);
|
||||
});
|
||||
|
||||
// Leave behind how to spawn a listener, so putting one back opens its port
|
||||
// without a restart (LP-5). Only this file knows the session manager for a
|
||||
// protocol, so only this file can say.
|
||||
let spawn_inner = init.inner.clone();
|
||||
init.inner
|
||||
.data
|
||||
.listener_control
|
||||
.set_spawner(Box::new(move |server, acceptor, shutdown_rx| {
|
||||
spawn_listener(&spawn_inner, server, acceptor, shutdown_rx);
|
||||
}));
|
||||
|
||||
// Start broadcast subscriber
|
||||
let inner = init.inner.clone();
|
||||
spawn_broadcast_subscriber(init.inner, shutdown_rx);
|
||||
@@ -132,3 +120,47 @@ async fn main() -> std::io::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Starts one listener under the session manager its protocol calls for.
|
||||
///
|
||||
/// Used twice: once for every listener at startup, and again whenever the
|
||||
/// legacy-protocols switch puts a listener back (LP-5).
|
||||
fn spawn_listener(
|
||||
inner: &Arc<Inner>,
|
||||
server: Listener,
|
||||
acceptor: TcpAcceptor,
|
||||
shutdown_rx: watch::Receiver<bool>,
|
||||
) {
|
||||
match &server.protocol {
|
||||
ServerProtocol::Smtp | ServerProtocol::Lmtp => server.spawn(
|
||||
SmtpSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Http => server.spawn(
|
||||
HttpSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Imap => server.spawn(
|
||||
ImapSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Pop3 => server.spawn(
|
||||
Pop3SessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::ManageSieve => server.spawn(
|
||||
ManageSieveSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user