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,
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::{error, 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<()>>>,
54
55 admin_shutdown_tx: Mutex<Option<oneshot::Sender<()>>>,
56
57 admin_shutdown_complete_rx: Mutex<Option<oneshot::Receiver<()>>>,
58
59 connection_semaphore: Arc<Semaphore>,
60
61 handle: Handle,
62}
63
64type ShutdownHandles = (oneshot::Sender<()>, oneshot::Receiver<()>);
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 result = serve_http(listener, server_state, semaphore, shutdown_rx, "HTTP server").await;
145 if let Err(e) = result {
146 error!("HTTP server error: {}", e);
147 }
148 running.store(false, Ordering::SeqCst);
149 let _ = complete_tx.send(());
150 info!("HTTP server stopped");
151 });
152 (shutdown_tx, complete_rx)
153 }
154
155 #[inline]
156 fn store_main_shutdown_handles(&self, shutdown_tx: oneshot::Sender<()>, complete_rx: oneshot::Receiver<()>) {
157 *self.shutdown_tx.lock() = Some(shutdown_tx);
158 *self.shutdown_complete_rx.lock() = Some(complete_rx);
159 }
160
161 fn spawn_admin_server(&self) -> Result<()> {
162 let Some(listener) = self.bind_admin_listener()? else {
163 return Ok(());
164 };
165 let (shutdown_tx, complete_rx) = self.spawn_admin_serve_task(listener);
166 self.store_admin_shutdown_handles(shutdown_tx, complete_rx);
167 Ok(())
168 }
169
170 #[inline]
171 fn bind_admin_listener(&self) -> Result<Option<TcpListener>> {
172 let Some(admin_addr) = self.admin_bind_addr.clone() else {
173 return Ok(None);
174 };
175 let listener = self.bind_listener(&admin_addr)?;
176 let actual_addr = local_addr_or_err(&listener)?;
177 *self.admin_actual_addr.write() = Some(actual_addr);
178 info!("HTTP admin server bound to {}", actual_addr);
179 Ok(Some(listener))
180 }
181
182 #[inline]
183 fn spawn_admin_serve_task(&self, listener: TcpListener) -> ShutdownHandles {
184 let (admin_shutdown_tx, admin_shutdown_rx) = oneshot::channel();
185 let (admin_complete_tx, admin_complete_rx) = oneshot::channel();
186 let admin_config = self.state.config().clone().admin_enabled(true);
187 let admin_app_state = self.state.clone_with_config(admin_config);
188 let admin_server_state = HttpServerState::new(admin_app_state);
189 let semaphore = self.connection_semaphore.clone();
190 self.handle.spawn(async move {
191 let result = serve_http(
192 listener,
193 admin_server_state,
194 semaphore,
195 admin_shutdown_rx,
196 "HTTP admin server",
197 )
198 .await;
199 if let Err(e) = result {
200 error!("HTTP admin server error: {}", e);
201 }
202 let _ = admin_complete_tx.send(());
203 info!("HTTP admin server stopped");
204 });
205 (admin_shutdown_tx, admin_complete_rx)
206 }
207
208 #[inline]
209 fn store_admin_shutdown_handles(&self, shutdown_tx: oneshot::Sender<()>, complete_rx: oneshot::Receiver<()>) {
210 *self.admin_shutdown_tx.lock() = Some(shutdown_tx);
211 *self.admin_shutdown_complete_rx.lock() = Some(complete_rx);
212 }
213
214 #[inline]
215 fn stop_admin_server(&self) {
216 let admin_tx = self.admin_shutdown_tx.lock().take();
217 if let Some(tx) = admin_tx {
218 let _ = tx.send(());
219 }
220 let admin_rx = self.admin_shutdown_complete_rx.lock().take();
221 if let Some(rx) = admin_rx {
222 let _ = self.handle.block_on(rx);
223 }
224 }
225
226 #[inline]
227 fn stop_main_server(&self) {
228 let main_tx = self.shutdown_tx.lock().take();
229 if let Some(tx) = main_tx {
230 let _ = tx.send(());
231 }
232 let main_rx = self.shutdown_complete_rx.lock().take();
233 if let Some(rx) = main_rx {
234 let _ = self.handle.block_on(rx);
235 }
236 }
237
238 #[inline]
239 fn bind_listener(&self, addr: &str) -> Result<TcpListener> {
240 self.handle.block_on(TcpListener::bind(addr)).map_err(|e| {
241 CoreError::SubsystemBindFailed {
242 addr: addr.to_string(),
243 reason: e.to_string(),
244 }
245 .into()
246 })
247 }
248}
249
250impl HasVersion for HttpSubsystem {
251 fn version(&self) -> SystemVersion {
252 SystemVersion {
253 name: env!("CARGO_PKG_NAME")
254 .strip_prefix("reifydb-")
255 .unwrap_or(env!("CARGO_PKG_NAME"))
256 .to_string(),
257 version: env!("CARGO_PKG_VERSION").to_string(),
258 description: "HTTP server subsystem for query and command handling".to_string(),
259 r#type: ComponentType::Subsystem,
260 }
261 }
262}
263
264impl Shutdown for HttpSubsystem {
265 fn shutdown(&self) {
266 self.stop_admin_server();
267 self.stop_main_server();
268 self.running.store(false, Ordering::SeqCst);
269 }
270}
271
272impl Subsystem for HttpSubsystem {
273 fn name(&self) -> &'static str {
274 "http"
275 }
276
277 fn is_running(&self) -> bool {
278 self.running.load(Ordering::SeqCst)
279 }
280
281 fn health_status(&self) -> HealthStatus {
282 if self.running.load(Ordering::SeqCst) {
283 HealthStatus::Healthy
284 } else if self.shutdown_tx.lock().is_some() {
285 HealthStatus::Warning {
286 description: "Starting up".to_string(),
287 }
288 } else {
289 HealthStatus::Failed {
290 description: "Not running".to_string(),
291 }
292 }
293 }
294
295 fn as_any(&self) -> &dyn Any {
296 self
297 }
298}
299
300#[inline]
301fn local_addr_or_err(listener: &TcpListener) -> Result<SocketAddr> {
302 listener.local_addr().map_err(|e| {
303 CoreError::SubsystemAddressUnavailable {
304 reason: e.to_string(),
305 }
306 .into()
307 })
308}
309
310async fn serve_http(
311 listener: TcpListener,
312 server_state: HttpServerState,
313 semaphore: Arc<Semaphore>,
314 shutdown_rx: oneshot::Receiver<()>,
315 name: &'static str,
316) -> io::Result<()> {
317 let app = router(server_state);
318 let listener = LimitedListener::new(listener, semaphore, name);
319 serve(listener, app)
320 .with_graceful_shutdown(async move {
321 shutdown_rx.await.ok();
322 info!("{} received shutdown signal", name);
323 })
324 .await
325}
326
327pub struct LimitedListener {
328 listener: TcpListener,
329 semaphore: Arc<Semaphore>,
330 name: &'static str,
331}
332
333impl LimitedListener {
334 pub fn new(listener: TcpListener, semaphore: Arc<Semaphore>, name: &'static str) -> Self {
335 Self {
336 listener,
337 semaphore,
338 name,
339 }
340 }
341}
342
343impl Listener for LimitedListener {
344 type Io = PermittedStream;
345 type Addr = SocketAddr;
346
347 async fn accept(&mut self) -> (Self::Io, Self::Addr) {
348 let (stream, permit, peer) = accept_admitted(&self.listener, &self.semaphore, self.name).await;
349 (PermittedStream::new(stream, permit), peer)
350 }
351
352 fn local_addr(&self) -> io::Result<Self::Addr> {
353 self.listener.local_addr()
354 }
355}