1use std::{
5 any::Any,
6 io,
7 net::SocketAddr,
8 sync::{
9 Arc,
10 atomic::{AtomicBool, Ordering},
11 },
12};
13
14use axum::{serve, serve::Listener};
15use reifydb_core::{
16 error::CoreError,
17 interface::version::{ComponentType, HasVersion, SystemVersion},
18};
19use reifydb_runtime::{
20 shutdown::{Shutdown, ShutdownErrors},
21 sync::{mutex::Mutex, rwlock::RwLock},
22};
23use reifydb_sub_api::subsystem::{HealthStatus, Subsystem};
24use reifydb_sub_server::{
25 accept::{PermittedStream, accept_admitted},
26 state::AppState,
27};
28use reifydb_value::Result;
29use tokio::{
30 net::TcpListener,
31 runtime::Handle,
32 sync::{Semaphore, oneshot},
33};
34use tracing::info;
35
36use crate::{routes::router, state::HttpServerState};
37
38pub struct HttpSubsystem {
39 bind_addr: Option<String>,
40
41 admin_bind_addr: Option<String>,
42
43 actual_addr: RwLock<Option<SocketAddr>>,
44
45 admin_actual_addr: RwLock<Option<SocketAddr>>,
46
47 state: AppState,
48
49 running: Arc<AtomicBool>,
50
51 shutdown_tx: Mutex<Option<oneshot::Sender<()>>>,
52
53 shutdown_complete_rx: Mutex<Option<oneshot::Receiver<io::Result<()>>>>,
54
55 admin_shutdown_tx: Mutex<Option<oneshot::Sender<()>>>,
56
57 admin_shutdown_complete_rx: Mutex<Option<oneshot::Receiver<io::Result<()>>>>,
58
59 connection_semaphore: Arc<Semaphore>,
60
61 handle: Handle,
62}
63
64type ShutdownHandles = (oneshot::Sender<()>, oneshot::Receiver<io::Result<()>>);
65
66impl HttpSubsystem {
67 pub fn new(
68 bind_addr: Option<String>,
69 admin_bind_addr: Option<String>,
70 state: AppState,
71 handle: Handle,
72 ) -> Result<Self> {
73 let connection_semaphore = Arc::new(Semaphore::new(state.max_connections()));
74 let subsystem = Self {
75 bind_addr,
76 admin_bind_addr,
77 actual_addr: RwLock::new(None),
78 admin_actual_addr: RwLock::new(None),
79 state,
80 running: Arc::new(AtomicBool::new(false)),
81 shutdown_tx: Mutex::new(None),
82 shutdown_complete_rx: Mutex::new(None),
83 admin_shutdown_tx: Mutex::new(None),
84 admin_shutdown_complete_rx: Mutex::new(None),
85 connection_semaphore,
86 handle,
87 };
88 subsystem.spawn_main_server()?;
89 subsystem.spawn_admin_server()?;
90 Ok(subsystem)
91 }
92
93 pub fn bind_addr(&self) -> Option<&str> {
94 self.bind_addr.as_deref()
95 }
96
97 pub fn local_addr(&self) -> Option<SocketAddr> {
98 *self.actual_addr.read()
99 }
100
101 pub fn port(&self) -> Option<u16> {
102 self.local_addr().map(|a| a.port())
103 }
104
105 pub fn admin_local_addr(&self) -> Option<SocketAddr> {
106 *self.admin_actual_addr.read()
107 }
108
109 pub fn admin_port(&self) -> Option<u16> {
110 self.admin_local_addr().map(|a| a.port())
111 }
112
113 fn spawn_main_server(&self) -> Result<()> {
114 let Some(listener) = self.bind_main_listener()? else {
115 return Ok(());
116 };
117 let (shutdown_tx, complete_rx) = self.spawn_main_serve_task(listener);
118 self.store_main_shutdown_handles(shutdown_tx, complete_rx);
119 Ok(())
120 }
121
122 #[inline]
123 fn bind_main_listener(&self) -> Result<Option<TcpListener>> {
124 let Some(addr) = self.bind_addr.clone() else {
125 self.running.store(true, Ordering::SeqCst);
126 return Ok(None);
127 };
128 let listener = self.bind_listener(&addr)?;
129 let actual_addr = local_addr_or_err(&listener)?;
130 *self.actual_addr.write() = Some(actual_addr);
131 info!("HTTP server bound to {}", actual_addr);
132 Ok(Some(listener))
133 }
134
135 #[inline]
136 fn spawn_main_serve_task(&self, listener: TcpListener) -> ShutdownHandles {
137 let (shutdown_tx, shutdown_rx) = oneshot::channel();
138 let (complete_tx, complete_rx) = oneshot::channel();
139 let server_state = HttpServerState::new(self.state.clone());
140 let semaphore = self.connection_semaphore.clone();
141 let running = self.running.clone();
142 self.handle.spawn(async move {
143 running.store(true, Ordering::SeqCst);
144 let served = serve_http(listener, server_state, semaphore, shutdown_rx, "HTTP server").await;
145 running.store(false, Ordering::SeqCst);
146 let _ = complete_tx.send(served);
147 info!("HTTP server stopped");
148 });
149 (shutdown_tx, complete_rx)
150 }
151
152 #[inline]
153 fn store_main_shutdown_handles(
154 &self,
155 shutdown_tx: oneshot::Sender<()>,
156 complete_rx: oneshot::Receiver<io::Result<()>>,
157 ) {
158 *self.shutdown_tx.lock() = Some(shutdown_tx);
159 *self.shutdown_complete_rx.lock() = Some(complete_rx);
160 }
161
162 fn spawn_admin_server(&self) -> Result<()> {
163 let Some(listener) = self.bind_admin_listener()? else {
164 return Ok(());
165 };
166 let (shutdown_tx, complete_rx) = self.spawn_admin_serve_task(listener);
167 self.store_admin_shutdown_handles(shutdown_tx, complete_rx);
168 Ok(())
169 }
170
171 #[inline]
172 fn bind_admin_listener(&self) -> Result<Option<TcpListener>> {
173 let Some(admin_addr) = self.admin_bind_addr.clone() else {
174 return Ok(None);
175 };
176 let listener = self.bind_listener(&admin_addr)?;
177 let actual_addr = local_addr_or_err(&listener)?;
178 *self.admin_actual_addr.write() = Some(actual_addr);
179 info!("HTTP admin server bound to {}", actual_addr);
180 Ok(Some(listener))
181 }
182
183 #[inline]
184 fn spawn_admin_serve_task(&self, listener: TcpListener) -> ShutdownHandles {
185 let (admin_shutdown_tx, admin_shutdown_rx) = oneshot::channel();
186 let (admin_complete_tx, admin_complete_rx) = oneshot::channel();
187 let admin_config = self.state.config().clone().admin_enabled(true);
188 let admin_app_state = self.state.clone_with_config(admin_config);
189 let admin_server_state = HttpServerState::new(admin_app_state);
190 let semaphore = self.connection_semaphore.clone();
191 self.handle.spawn(async move {
192 let served = serve_http(
193 listener,
194 admin_server_state,
195 semaphore,
196 admin_shutdown_rx,
197 "HTTP admin server",
198 )
199 .await;
200 let _ = admin_complete_tx.send(served);
201 info!("HTTP admin server stopped");
202 });
203 (admin_shutdown_tx, admin_complete_rx)
204 }
205
206 #[inline]
207 fn store_admin_shutdown_handles(
208 &self,
209 shutdown_tx: oneshot::Sender<()>,
210 complete_rx: oneshot::Receiver<io::Result<()>>,
211 ) {
212 *self.admin_shutdown_tx.lock() = Some(shutdown_tx);
213 *self.admin_shutdown_complete_rx.lock() = Some(complete_rx);
214 }
215
216 #[inline]
217 fn stop_admin_server(&self, errors: &mut ShutdownErrors) {
218 let admin_tx = self.admin_shutdown_tx.lock().take();
219 if let Some(tx) = admin_tx {
220 let _ = tx.send(());
221 }
222 let admin_rx = self.admin_shutdown_complete_rx.lock().take();
223 if let Some(rx) = admin_rx {
224 match self.handle.block_on(rx) {
225 Ok(Ok(())) => {}
226 Ok(Err(err)) => errors.fail("admin server", err),
227 Err(err) => errors.fail("admin server", err),
228 }
229 }
230 }
231
232 #[inline]
233 fn stop_main_server(&self, errors: &mut ShutdownErrors) {
234 let main_tx = self.shutdown_tx.lock().take();
235 if let Some(tx) = main_tx {
236 let _ = tx.send(());
237 }
238 let main_rx = self.shutdown_complete_rx.lock().take();
239 if let Some(rx) = main_rx {
240 match self.handle.block_on(rx) {
241 Ok(Ok(())) => {}
242 Ok(Err(err)) => errors.fail("main server", err),
243 Err(err) => errors.fail("main server", err),
244 }
245 }
246 }
247
248 #[inline]
249 fn bind_listener(&self, addr: &str) -> Result<TcpListener> {
250 self.handle.block_on(TcpListener::bind(addr)).map_err(|e| {
251 CoreError::SubsystemBindFailed {
252 addr: addr.to_string(),
253 reason: e.to_string(),
254 }
255 .into()
256 })
257 }
258}
259
260impl HasVersion for HttpSubsystem {
261 fn version(&self) -> SystemVersion {
262 SystemVersion {
263 name: env!("CARGO_PKG_NAME")
264 .strip_prefix("reifydb-")
265 .unwrap_or(env!("CARGO_PKG_NAME"))
266 .to_string(),
267 version: env!("CARGO_PKG_VERSION").to_string(),
268 description: "HTTP server subsystem for query and command handling".to_string(),
269 r#type: ComponentType::Subsystem,
270 }
271 }
272}
273
274impl Shutdown for HttpSubsystem {
275 fn shutdown(&self) -> Result<()> {
276 let mut errors = ShutdownErrors::new("http");
277 self.stop_admin_server(&mut errors);
278 self.stop_main_server(&mut errors);
279 self.running.store(false, Ordering::SeqCst);
280 errors.finish()
281 }
282}
283
284impl Subsystem for HttpSubsystem {
285 fn name(&self) -> &'static str {
286 "http"
287 }
288
289 fn is_running(&self) -> bool {
290 self.running.load(Ordering::SeqCst)
291 }
292
293 fn health_status(&self) -> HealthStatus {
294 if self.running.load(Ordering::SeqCst) {
295 HealthStatus::Healthy
296 } else if self.shutdown_tx.lock().is_some() {
297 HealthStatus::Warning {
298 description: "Starting up".to_string(),
299 }
300 } else {
301 HealthStatus::Failed {
302 description: "Not running".to_string(),
303 }
304 }
305 }
306
307 fn as_any(&self) -> &dyn Any {
308 self
309 }
310}
311
312#[inline]
313fn local_addr_or_err(listener: &TcpListener) -> Result<SocketAddr> {
314 listener.local_addr().map_err(|e| {
315 CoreError::SubsystemAddressUnavailable {
316 reason: e.to_string(),
317 }
318 .into()
319 })
320}
321
322async fn serve_http(
323 listener: TcpListener,
324 server_state: HttpServerState,
325 semaphore: Arc<Semaphore>,
326 shutdown_rx: oneshot::Receiver<()>,
327 name: &'static str,
328) -> io::Result<()> {
329 let app = router(server_state);
330 let listener = LimitedListener::new(listener, semaphore, name);
331 serve(listener, app)
332 .with_graceful_shutdown(async move {
333 shutdown_rx.await.ok();
334 info!("{} received shutdown signal", name);
335 })
336 .await
337}
338
339pub struct LimitedListener {
340 listener: TcpListener,
341 semaphore: Arc<Semaphore>,
342 name: &'static str,
343}
344
345impl LimitedListener {
346 pub fn new(listener: TcpListener, semaphore: Arc<Semaphore>, name: &'static str) -> Self {
347 Self {
348 listener,
349 semaphore,
350 name,
351 }
352 }
353}
354
355impl Listener for LimitedListener {
356 type Io = PermittedStream;
357 type Addr = SocketAddr;
358
359 async fn accept(&mut self) -> (Self::Io, Self::Addr) {
360 let (stream, permit, peer) = accept_admitted(&self.listener, &self.semaphore, self.name).await;
361 (PermittedStream::new(stream, permit), peer)
362 }
363
364 fn local_addr(&self) -> io::Result<Self::Addr> {
365 self.listener.local_addr()
366 }
367}