foxtive_ntex/http/server/config.rs
1use crate::http::Method;
2use crate::http::kernel::Route;
3use foxtive::setup::FoxtiveSetup;
4use foxtive::setup::trace::Tracing;
5use ntex::time::Seconds;
6use std::pin::Pin;
7use std::sync::Arc;
8
9pub type ShutdownSignalHandler = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
10
11/// Configuration for serving static files.
12///
13/// This struct defines the mapping between URL paths and filesystem directories
14/// for static file serving. Only available when the `static` feature is enabled.
15///
16/// # Example
17/// ```rust
18/// use foxtive_ntex::http::server::StaticFileConfig;
19///
20/// let config = StaticFileConfig {
21/// path: "/assets".to_string(),
22/// dir: "./public".to_string(),
23/// };
24///
25/// // This would serve files from "./public" directory at "/assets/*" URL path
26/// // e.g., "./public/style.css" would be accessible at "/assets/style.css"
27/// ```
28///
29/// # Security Notes
30/// - The `dir` path should be carefully validated to prevent directory traversal attacks
31/// - Consider using absolute paths or canonicalized paths for the `dir` field
32/// - Ensure proper file permissions are set on the served directory
33#[cfg(feature = "static")]
34pub struct StaticFileConfig {
35 /// The URL path prefix where static files will be served.
36 ///
37 /// This defines the base route under which static files are accessible.
38 /// Should start with "/" (e.g., "/static", "/assets", "/public").
39 pub path: String,
40
41 /// The filesystem directory path containing the static files to serve.
42 ///
43 /// This can be either a relative path (relative to the application's working directory)
44 /// or an absolute path. All files within this directory and its subdirectories
45 /// will be served under the configured URL path.
46 pub dir: String,
47}
48
49/// Configuration for JSON request body parsing.
50///
51/// This struct controls how JSON payloads are processed, including size limits
52/// and content-type validation.
53///
54/// # Default Settings
55/// - Maximum body size: 512,000 bytes (500 KB)
56/// - Content-type validation: None (accepts any content-type)
57///
58/// # Example
59/// ```rust
60/// use foxtive_ntex::http::server::JsonConfig;
61///
62/// // Use default configuration (50 KB limit)
63/// let config = JsonConfig::default();
64///
65/// // Custom size limit
66/// let config = JsonConfig::default().limit(1024 * 1024); // 1 MB
67/// ```
68#[derive(Clone)]
69pub struct JsonConfig {
70 /// Maximum allowed size for JSON request bodies in bytes.
71 ///
72 /// Requests exceeding this limit will be rejected with a payload too large error.
73 /// Default: 51,200 bytes (50 KB)
74 pub(crate) limit: usize,
75}
76
77pub struct ServerConfig {
78 pub(crate) host: String,
79 pub(crate) port: u16,
80 pub(crate) workers: usize,
81
82 pub(crate) max_connections: usize,
83
84 pub(crate) max_connections_rate: usize,
85
86 pub(crate) client_timeout: Seconds,
87
88 pub(crate) client_disconnect: Seconds,
89
90 pub(crate) keep_alive: Seconds,
91
92 pub(crate) backlog: i32,
93
94 pub(crate) json_config: Option<JsonConfig>,
95
96 pub(crate) app: String,
97 pub(crate) foxtive_setup: FoxtiveSetup,
98
99 pub(crate) tracing: Option<Tracing>,
100
101 #[cfg(feature = "static")]
102 pub(crate) static_config: StaticFileConfig,
103
104 /// whether the app bootstrap has started
105 pub(crate) has_started_bootstrap: bool,
106
107 /// list of allowed CORS origins
108 pub(crate) allowed_origins: Vec<String>,
109
110 /// list of allowed CORS origins
111 pub(crate) allowed_methods: Vec<Method>,
112
113 pub(crate) boot_thread: Arc<dyn Fn() -> Vec<Route> + Send + Sync>,
114
115 pub(crate) on_shutdown: Option<ShutdownSignalHandler>,
116
117 pub(crate) shutdown_signal: Option<Pin<Box<dyn Future<Output = ()> + Send>>>,
118}
119
120impl ServerConfig {
121 pub fn create(host: &str, port: u16, setup: FoxtiveSetup) -> ServerConfig {
122 ServerConfig {
123 host: host.to_string(),
124 port,
125 workers: 2,
126 max_connections: 25_000,
127 max_connections_rate: 256,
128 client_timeout: Seconds(3),
129 client_disconnect: Seconds(5),
130 keep_alive: Seconds(5),
131 backlog: 2048,
132 app: "foxtive".to_string(),
133 foxtive_setup: setup,
134 #[cfg(feature = "static")]
135 static_config: StaticFileConfig::default(),
136 has_started_bootstrap: false,
137 allowed_origins: vec![],
138 allowed_methods: vec![],
139 boot_thread: Arc::new(Vec::new),
140 tracing: None,
141 json_config: None,
142 on_shutdown: None,
143 shutdown_signal: None,
144 }
145 }
146
147 #[cfg(feature = "static")]
148 pub fn create_with_static(
149 host: &str,
150 port: u16,
151 setup: FoxtiveSetup,
152 config: StaticFileConfig,
153 ) -> ServerConfig {
154 Self::create(host, port, setup).static_config(config)
155 }
156
157 pub fn app(mut self, app: &str) -> Self {
158 self.app = app.to_string();
159 self
160 }
161
162 pub fn tracing(mut self, config: Tracing) -> Self {
163 self.tracing = Some(config);
164 self
165 }
166
167 /// Set number of workers to start.
168 ///
169 /// By default http server uses 2
170 pub fn workers(mut self, workers: usize) -> Self {
171 self.workers = workers;
172 self
173 }
174
175 /// Set the maximum number of pending connections.
176 ///
177 /// This refers to the number of clients that can be waiting to be served.
178 /// Exceeding this number results in the client getting an error when
179 /// attempting to connect. It should only affect servers under significant
180 /// load.
181 ///
182 /// Generally set in the 64-2048 range. Default value is 2048.
183 ///
184 /// This method should be called before `bind()` method call.
185 pub fn backlog(mut self, backlog: i32) -> Self {
186 self.backlog = backlog;
187 self
188 }
189
190 /// Set server keep-alive setting.
191 ///
192 /// By default keep alive is set to a 5 seconds.
193 pub fn keep_alive(mut self, keep_alive: Seconds) -> Self {
194 self.keep_alive = keep_alive;
195 self
196 }
197
198 /// Set request read timeout in seconds.
199 ///
200 /// Defines a timeout for reading client request headers. If a client does not transmit
201 /// the entire set headers within this time, the request is terminated with
202 /// the 408 (Request Time-out) error.
203 ///
204 /// To disable timeout set value to 0.
205 ///
206 /// By default client timeout is set to 3 seconds.
207 pub fn client_timeout(mut self, timeout: u16) -> Self {
208 self.client_timeout = Seconds(timeout);
209 self
210 }
211
212 /// Set server connection disconnect timeout in seconds.
213 ///
214 /// Defines a timeout for shutdown connection. If a shutdown procedure does not complete
215 /// within this time, the request is dropped.
216 ///
217 /// To disable timeout set value to 0.
218 ///
219 /// By default client timeout is set to 5 seconds.
220 pub fn client_disconnect(mut self, timeout: u16) -> Self {
221 self.client_disconnect = Seconds(timeout);
222 self
223 }
224
225 /// Sets the maximum per-worker number of concurrent connections.
226 ///
227 /// All socket listeners will stop accepting connections when this limit is reached
228 /// for each worker.
229 ///
230 /// By default max connections is set to a 25k.
231 pub fn max_conn(mut self, max: usize) -> Self {
232 self.max_connections = max;
233 self
234 }
235
236 /// Sets the maximum per-worker concurrent connection establish process.
237 ///
238 /// All listeners will stop accepting connections when this limit is reached. It
239 /// can be used to limit the global SSL CPU usage.
240 ///
241 /// By default max connections is set to a 256.
242 pub fn max_conn_rate(mut self, max: usize) -> Self {
243 self.max_connections_rate = max;
244 self
245 }
246
247 pub fn allowed_origins(mut self, allowed_origins: Vec<String>) -> Self {
248 self.allowed_origins = allowed_origins;
249 self
250 }
251
252 pub fn allowed_methods(mut self, allowed_methods: Vec<Method>) -> Self {
253 self.allowed_methods = allowed_methods;
254 self
255 }
256
257 #[cfg(feature = "static")]
258 pub fn static_config(mut self, static_config: StaticFileConfig) -> Self {
259 self.static_config = static_config;
260 self
261 }
262
263 pub fn boot_thread(mut self, boot_thread: Arc<dyn Fn() -> Vec<Route> + Send + Sync>) -> Self {
264 self.boot_thread = boot_thread;
265 self
266 }
267
268 pub fn has_started_bootstrap(mut self, has_started_bootstrap: bool) -> Self {
269 self.has_started_bootstrap = has_started_bootstrap;
270 self
271 }
272
273 pub fn json_config(mut self, json_config: JsonConfig) -> Self {
274 self.json_config = Some(json_config);
275 self
276 }
277
278 /// Sets a custom shutdown handler to be called when the application is shutting down.
279 ///
280 /// This method allows you to provide a future that will be awaited during shutdown.
281 /// It is typically used to perform cleanup tasks like closing database connections,
282 /// flushing logs, or other async teardown operations.
283 ///
284 /// **Note:** If a custom `shutdown_signal` is also provided using [`shutdown_signal`],
285 /// that will take precedence over this handler, and this `on_shutdown` handler will
286 /// **not** be executed.
287 ///
288 pub fn on_shutdown<F>(mut self, func: F) -> Self
289 where
290 F: Future<Output = ()> + Send + 'static,
291 {
292 self.on_shutdown = Some(Box::pin(func));
293 self
294 }
295
296 /// Sets a custom shutdown signal handler that determines when the application should begin shutting down.
297 ///
298 /// This method allows you to provide a future that, when resolved, triggers the application shutdown.
299 /// It is typically used to listen for signals like `Ctrl+C` or system termination requests (`SIGTERM`).
300 ///
301 /// If this shutdown signal is provided, it will override any handler set using [`on_shutdown`].
302 pub fn shutdown_signal<F>(mut self, func: F) -> Self
303 where
304 F: Future<Output = ()> + Send + 'static,
305 {
306 self.shutdown_signal = Some(Box::pin(func));
307 self
308 }
309}
310
311impl JsonConfig {
312 /// Change max size of payload. By default max size is 50Kb
313 pub fn limit(mut self, limit: usize) -> Self {
314 self.limit = limit;
315 self
316 }
317}
318
319#[cfg(feature = "static")]
320impl Default for StaticFileConfig {
321 fn default() -> Self {
322 Self {
323 path: "static".to_string(),
324 dir: "./static".to_string(),
325 }
326 }
327}
328
329impl Default for JsonConfig {
330 fn default() -> Self {
331 JsonConfig {
332 limit: 51_000, // 50 KB
333 }
334 }
335}