vintage/
server_handle.rs

1use std::io;
2use std::net::SocketAddr;
3use std::sync::mpsc::Receiver;
4use std::thread::JoinHandle;
5
6/// The reason the server exited
7#[derive(Debug, Default)]
8pub enum ServerExitReason {
9    /// It was gracefully shutdown
10    #[default]
11    Normal,
12    /// Polling the server socket for new connections failed somehow.
13    Err(io::Error),
14    /// The server panicked. The payload will contain the panic message.
15    Panic(String),
16}
17
18/// Handle to a running FastCGI server
19pub struct ServerHandle {
20    pub(crate) address: SocketAddr,
21    pub(crate) server_loop: JoinHandle<ServerExitReason>,
22    pub(crate) server_waker: mio::Waker,
23    pub(crate) observe_shutdown: Receiver<()>,
24}
25
26impl ServerHandle {
27    /// Blocks until the server terminates and returns the reason.
28    ///
29    /// This function does not attempt to stop the server.
30    /// It waits (potentially indefinitely) until the server exits.
31    /// If you want to stop sthe server, use [`stop()`](crate::ServerHandle::stop).
32    pub fn join(self) -> ServerExitReason {
33        match self.server_loop.join() {
34            Ok(r) => r,
35            Err(any) => match any.as_ref().downcast_ref::<String>() {
36                Some(s) => ServerExitReason::Panic(s.clone()),
37                None => match any.as_ref().downcast_ref::<&str>() {
38                    Some(s) => ServerExitReason::Panic(s.to_string()),
39                    None => ServerExitReason::Panic(String::new()),
40                },
41            },
42        }
43    }
44
45    /// Stops the FastCGI server
46    ///
47    /// The server waits for all in-flight requests to complete before it is shutdown
48    pub fn stop(self) {
49        // Wake up the server thread.
50        // It will be able to tell that it was woken up by the waker instead of by a new readable Tcp connection.
51        // If this call fails, just return.
52        // We don't want to attempt to block on the `recv()` call in the next line if its possible
53        // we didn't wake the server.
54        // This means our graceful shutdown is "best effort".
55        // Nothing we can do if some OS-level error happened.
56        let Ok(()) = self.server_waker.wake() else {
57            return;
58        };
59
60        // Normally, after the server thread is woken up by the waker, it will eventually
61        // rendezvous here.
62        // Except if it exited due to an error or panicked, in which case this call would return
63        // with an error. But we ignore it because we only care that the server loop is stopped.
64        let _ = self.observe_shutdown.recv();
65    }
66
67    /// Returns the address at which the server is currently listening
68    pub fn address(&self) -> SocketAddr {
69        self.address
70    }
71}