Skip to main content

reifydb_sub_server_http/
factory.rs

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