alux_http_rocket/
server.rs1use crate::RocketRoute;
2use alux_http::{HttpServerAlg, HttpServerSetup};
3use rocket::config::{LogLevel, Shutdown as ShutdownConfig};
4use rocket::fairing::AdHoc;
5#[cfg(unix)]
6use std::collections::HashSet;
7use std::io;
8use tokio::sync::oneshot;
9use tokio::task::JoinHandle;
10
11type RocketServerError = Box<dyn std::error::Error + Send + Sync>;
12
13const GRACE: u32 = 5;
19
20const MERCY: u32 = 0;
26
27#[derive(Debug, Default)]
29pub struct RocketServer;
30
31pub struct RocketOpen {
33 shutdown: rocket::Shutdown,
34 task: JoinHandle<Result<(), RocketServerError>>,
35}
36
37impl Drop for RocketOpen {
38 fn drop(&mut self) {
39 self.shutdown.clone().notify();
40 self.task.abort();
41 }
42}
43
44struct Launching(Option<rocket::Shutdown>);
49
50impl Launching {
51 fn launched(&mut self) {
53 self.0 = None;
54 }
55}
56
57impl Drop for Launching {
58 fn drop(&mut self) {
59 if let Some(shutdown) = self.0.take() {
60 shutdown.notify();
61 }
62 }
63}
64
65fn rocket_error(error: &rocket::Error) -> RocketServerError {
67 io::Error::other(error.to_string()).into()
68}
69
70impl HttpServerAlg for RocketServer {
71 type Program = RocketRoute;
72 type Open = RocketOpen;
73 type Error = RocketServerError;
74
75 async fn open(&mut self, setup: HttpServerSetup<Self::Program>) -> Result<Self::Open, Self::Error> {
76 let (bind, route) = setup.into_parts();
77 let (listening, mut bound) = oneshot::channel();
78 let rocket = route
79 .mount(rocket::custom(rocket::Config {
80 address: bind.address().ip(),
81 port: bind.address().port(),
82 log_level: LogLevel::Off,
85 shutdown: ShutdownConfig {
86 grace: GRACE,
87 mercy: MERCY,
88 ctrlc: false,
92 #[cfg(unix)]
93 signals: HashSet::new(),
94 ..ShutdownConfig::default()
95 },
96 ..rocket::Config::default()
97 }))
98 .attach(AdHoc::on_liftoff("alux-http-rocket bound", move |_| {
101 Box::pin(async move {
102 let _ = listening.send(());
103 })
104 }))
105 .ignite()
106 .await
107 .map_err(|error| rocket_error(&error))?;
108 let shutdown = rocket.shutdown();
109 let mut launching = Launching(Some(shutdown.clone()));
110 let mut task =
111 tokio::task::spawn_local(async move { rocket.launch().await.map(|_| ()).map_err(|e| rocket_error(&e)) });
112
113 let open = tokio::select! {
115 result = &mut task => match result? {
116 Ok(()) => Err(io::Error::other("Rocket stopped before binding").into()),
117 Err(error) => Err(error),
118 },
119 Ok(()) = &mut bound => Ok(RocketOpen { shutdown, task }),
120 };
121 launching.launched();
122
123 open
124 }
125
126 async fn close(&mut self, open: &mut Self::Open) -> Result<(), Self::Error> {
127 open.shutdown.clone().notify();
128 if !open.task.is_finished() {
129 let _ = (&mut open.task).await;
130 }
131
132 Ok(())
133 }
134
135 async fn end(&mut self, open: &mut Self::Open) -> Result<(), Self::Error> {
138 self.close(open).await
139 }
140}