Skip to main content

reifydb_sub_server_http/
factory.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::sync::Arc;
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::{
13	actor::system::ActorSpawner,
14	context::{clock::Clock, rng::Rng},
15};
16use reifydb_sub_api::subsystem::{Subsystem, SubsystemFactory};
17use reifydb_sub_server::{
18	interceptor::RequestInterceptorChain,
19	state::{AppState, StateConfig},
20};
21use reifydb_value::{Result, value::duration::Duration};
22use tokio::runtime::Handle;
23
24use crate::subsystem::HttpSubsystem;
25
26pub struct HttpConfigurator {
27	bind_addr: Option<String>,
28	admin_bind_addr: Option<String>,
29	max_connections: usize,
30	query_timeout: Duration,
31	request_timeout: Duration,
32	spawner: Option<ActorSpawner>,
33	clock: Option<Clock>,
34	rng: Option<Rng>,
35	handle: Option<Handle>,
36}
37
38impl Default for HttpConfigurator {
39	fn default() -> Self {
40		Self::new()
41	}
42}
43
44impl HttpConfigurator {
45	pub fn new() -> Self {
46		Self {
47			bind_addr: None,
48			admin_bind_addr: None,
49			max_connections: 10_000,
50			query_timeout: Duration::from_seconds(30).unwrap(),
51			request_timeout: Duration::from_seconds(60).unwrap(),
52			spawner: None,
53			clock: None,
54			rng: None,
55			handle: None,
56		}
57	}
58
59	pub fn bind_addr(mut self, addr: impl Into<String>) -> Self {
60		self.bind_addr = Some(addr.into());
61		self
62	}
63
64	pub fn admin_bind_addr(mut self, addr: impl Into<String>) -> Self {
65		self.admin_bind_addr = Some(addr.into());
66		self
67	}
68
69	pub fn max_connections(mut self, max: usize) -> Self {
70		self.max_connections = max;
71		self
72	}
73
74	pub fn query_timeout(mut self, timeout: Duration) -> Self {
75		self.query_timeout = timeout;
76		self
77	}
78
79	pub fn request_timeout(mut self, timeout: Duration) -> Self {
80		self.request_timeout = timeout;
81		self
82	}
83
84	pub fn spawner(mut self, spawner: ActorSpawner) -> Self {
85		self.spawner = Some(spawner);
86		self
87	}
88
89	pub fn clock(mut self, clock: Clock) -> Self {
90		self.clock = Some(clock);
91		self
92	}
93
94	pub fn rng(mut self, rng: Rng) -> Self {
95		self.rng = Some(rng);
96		self
97	}
98
99	pub fn handle(mut self, handle: Handle) -> Self {
100		self.handle = Some(handle);
101		self
102	}
103
104	pub(crate) fn configure(self) -> HttpConfig {
105		HttpConfig {
106			bind_addr: self.bind_addr,
107			admin_bind_addr: self.admin_bind_addr,
108			max_connections: self.max_connections,
109			query_timeout: self.query_timeout,
110			request_timeout: self.request_timeout,
111			spawner: self.spawner,
112			clock: self.clock,
113			rng: self.rng,
114			handle: self.handle,
115		}
116	}
117}
118
119#[derive(Clone)]
120pub struct HttpConfig {
121	pub bind_addr: Option<String>,
122
123	pub admin_bind_addr: Option<String>,
124
125	pub max_connections: usize,
126
127	pub query_timeout: Duration,
128
129	pub request_timeout: Duration,
130
131	pub spawner: Option<ActorSpawner>,
132
133	pub clock: Option<Clock>,
134
135	pub rng: Option<Rng>,
136
137	pub handle: Option<Handle>,
138}
139
140impl Default for HttpConfig {
141	fn default() -> Self {
142		HttpConfigurator::new().configure()
143	}
144}
145
146pub struct HttpSubsystemFactory {
147	config_fn: Box<dyn FnOnce() -> HttpConfig + Send>,
148}
149
150impl HttpSubsystemFactory {
151	pub fn new<F>(configurator: F) -> Self
152	where
153		F: FnOnce(HttpConfigurator) -> HttpConfigurator + Send + 'static,
154	{
155		Self {
156			config_fn: Box::new(move || configurator(HttpConfigurator::new()).configure()),
157		}
158	}
159}
160
161type ResolvedDeps = (StandardEngine, RequestInterceptorChain, ActorSpawner, Clock, Rng, Handle);
162
163impl SubsystemFactory for HttpSubsystemFactory {
164	fn create(self: Box<Self>, ioc: &IocContainer) -> Result<Box<dyn Subsystem>> {
165		let config = (self.config_fn)();
166
167		let (engine, interceptors, spawner, clock, rng, handle) =
168			Self::resolve_deps(ioc, config.spawner, config.clock, config.rng, config.handle)?;
169
170		let state = Self::build_app_state(
171			config.query_timeout,
172			config.request_timeout,
173			config.max_connections,
174			spawner,
175			engine,
176			clock,
177			rng,
178			interceptors,
179		);
180
181		let subsystem =
182			Self::build_subsystem(config.bind_addr.clone(), config.admin_bind_addr.clone(), state, handle)?;
183
184		Ok(Box::new(subsystem))
185	}
186}
187
188impl HttpSubsystemFactory {
189	#[inline]
190	#[allow(clippy::too_many_arguments)]
191	fn resolve_deps(
192		ioc: &IocContainer,
193		spawner: Option<ActorSpawner>,
194		clock: Option<Clock>,
195		rng: Option<Rng>,
196		handle: Option<Handle>,
197	) -> Result<ResolvedDeps> {
198		let engine = ioc.resolve::<StandardEngine>()?;
199		let interceptors = ioc.resolve::<RequestInterceptorChain>().unwrap_or_default();
200
201		let spawner = match spawner {
202			Some(spawner) => spawner,
203			None => ioc.resolve::<ActorSpawner>()?,
204		};
205		let clock = match clock {
206			Some(clock) => clock,
207			None => ioc.resolve::<Clock>()?,
208		};
209		let rng = match rng {
210			Some(rng) => rng,
211			None => ioc.resolve::<Rng>()?,
212		};
213		let handle = match handle {
214			Some(handle) => handle,
215			None => ioc.resolve::<Handle>()?,
216		};
217
218		Ok((engine, interceptors, spawner, clock, rng, handle))
219	}
220
221	#[inline]
222	#[allow(clippy::too_many_arguments)]
223	fn build_app_state(
224		query_timeout: Duration,
225		request_timeout: Duration,
226		max_connections: usize,
227		spawner: ActorSpawner,
228		engine: StandardEngine,
229		clock: Clock,
230		rng: Rng,
231		interceptors: RequestInterceptorChain,
232	) -> AppState {
233		let query_config = StateConfig::new()
234			.query_timeout(query_timeout)
235			.request_timeout(request_timeout)
236			.max_connections(max_connections);
237
238		let auth_service = AuthService::new(
239			Arc::new(engine.clone()),
240			Arc::new(AuthenticationRegistry::new(clock.clone())),
241			rng.clone(),
242			clock.clone(),
243			AuthServiceConfig::default(),
244		);
245
246		AppState::new(spawner, engine, auth_service, query_config, interceptors, clock, rng)
247	}
248
249	#[inline]
250	fn build_subsystem(
251		bind_addr: Option<String>,
252		admin_bind_addr: Option<String>,
253		state: AppState,
254		handle: Handle,
255	) -> Result<HttpSubsystem> {
256		HttpSubsystem::new(bind_addr, admin_bind_addr, state, handle)
257	}
258}