Skip to main content

reifydb_sub_server_http/
factory.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{sync::Arc, time::Duration};
5
6use reifydb_auth::{
7	registry::AuthenticationRegistry,
8	service::{AuthService, AuthServiceConfig},
9};
10use reifydb_core::util::ioc::IocContainer;
11use reifydb_engine::engine::StandardEngine;
12use reifydb_runtime::SharedRuntime;
13use reifydb_sub_api::subsystem::{Subsystem, SubsystemFactory};
14use reifydb_sub_server::{
15	interceptor::RequestInterceptorChain,
16	state::{AppState, StateConfig},
17};
18use reifydb_type::Result;
19
20use crate::subsystem::HttpSubsystem;
21
22/// Configurator for the HTTP server subsystem (builder pattern).
23pub struct HttpConfigurator {
24	bind_addr: Option<String>,
25	admin_bind_addr: Option<String>,
26	max_connections: usize,
27	query_timeout: Duration,
28	request_timeout: Duration,
29	runtime: Option<SharedRuntime>,
30}
31
32impl Default for HttpConfigurator {
33	fn default() -> Self {
34		Self::new()
35	}
36}
37
38impl HttpConfigurator {
39	/// Create a new HTTP configurator with default values.
40	pub fn new() -> Self {
41		Self {
42			bind_addr: None,
43			admin_bind_addr: None,
44			max_connections: 10_000,
45			query_timeout: Duration::from_secs(30),
46			request_timeout: Duration::from_secs(60),
47			runtime: None,
48		}
49	}
50
51	/// Set the bind address.
52	pub fn bind_addr(mut self, addr: impl Into<String>) -> Self {
53		self.bind_addr = Some(addr.into());
54		self
55	}
56
57	/// Set the admin bind address.
58	/// When set, admin operations are served on this separate port.
59	pub fn admin_bind_addr(mut self, addr: impl Into<String>) -> Self {
60		self.admin_bind_addr = Some(addr.into());
61		self
62	}
63
64	/// Set the maximum number of connections.
65	pub fn max_connections(mut self, max: usize) -> Self {
66		self.max_connections = max;
67		self
68	}
69
70	/// Set the query timeout.
71	pub fn query_timeout(mut self, timeout: Duration) -> Self {
72		self.query_timeout = timeout;
73		self
74	}
75
76	/// Set the request timeout.
77	pub fn request_timeout(mut self, timeout: Duration) -> Self {
78		self.request_timeout = timeout;
79		self
80	}
81
82	/// Set the shared runtime.
83	pub fn runtime(mut self, runtime: SharedRuntime) -> Self {
84		self.runtime = Some(runtime);
85		self
86	}
87
88	/// Finalize the configurator into an immutable config.
89	pub(crate) fn configure(self) -> HttpConfig {
90		HttpConfig {
91			bind_addr: self.bind_addr,
92			admin_bind_addr: self.admin_bind_addr,
93			max_connections: self.max_connections,
94			query_timeout: self.query_timeout,
95			request_timeout: self.request_timeout,
96			runtime: self.runtime,
97		}
98	}
99}
100
101/// Immutable configuration for the HTTP server subsystem.
102#[derive(Clone, Debug)]
103pub struct HttpConfig {
104	/// Address to bind the HTTP server to (e.g., "0.0.0.0:8091").
105	pub bind_addr: Option<String>,
106	/// Address to bind the admin HTTP server to (e.g., "127.0.0.1:9091").
107	/// When set, admin operations are only available on this port.
108	/// When not set, admin operations are not available.
109	pub admin_bind_addr: Option<String>,
110	/// Maximum number of concurrent connections.
111	pub max_connections: usize,
112	/// Timeout for query execution.
113	pub query_timeout: Duration,
114	/// Timeout for entire request lifecycle.
115	pub request_timeout: Duration,
116	/// Optional shared runtime.
117	pub runtime: Option<SharedRuntime>,
118}
119
120impl Default for HttpConfig {
121	fn default() -> Self {
122		HttpConfigurator::new().configure()
123	}
124}
125
126/// Factory for creating HTTP subsystem instances.
127pub struct HttpSubsystemFactory {
128	config_fn: Box<dyn FnOnce() -> HttpConfig + Send>,
129}
130
131impl HttpSubsystemFactory {
132	/// Create a new HTTP subsystem factory with the given configurator closure.
133	pub fn new<F>(configurator: F) -> Self
134	where
135		F: FnOnce(HttpConfigurator) -> HttpConfigurator + Send + 'static,
136	{
137		Self {
138			config_fn: Box::new(move || configurator(HttpConfigurator::new()).configure()),
139		}
140	}
141}
142
143impl SubsystemFactory for HttpSubsystemFactory {
144	fn create(self: Box<Self>, ioc: &IocContainer) -> Result<Box<dyn Subsystem>> {
145		let config = (self.config_fn)();
146
147		let engine = ioc.resolve::<StandardEngine>()?;
148		let ioc_runtime = ioc.resolve::<SharedRuntime>()?;
149		let interceptors = ioc.resolve::<RequestInterceptorChain>().unwrap_or_default();
150
151		let query_config = StateConfig::new()
152			.query_timeout(config.query_timeout)
153			.request_timeout(config.request_timeout)
154			.max_connections(config.max_connections);
155
156		let runtime = config.runtime.unwrap_or(ioc_runtime);
157
158		let auth_service = AuthService::new(
159			Arc::new(engine.clone()),
160			Arc::new(AuthenticationRegistry::new(runtime.clock().clone())),
161			runtime.rng().clone(),
162			runtime.clock().clone(),
163			AuthServiceConfig::default(),
164		);
165
166		let state = AppState::new(
167			runtime.actor_system(),
168			engine,
169			auth_service,
170			query_config,
171			interceptors,
172			runtime.clock().clone(),
173			runtime.rng().clone(),
174		);
175		let subsystem =
176			HttpSubsystem::new(config.bind_addr.clone(), config.admin_bind_addr.clone(), state, runtime);
177
178		Ok(Box::new(subsystem))
179	}
180}