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}