product_os_server/lib.rs
1//! Product OS Server
2//!
3//! A comprehensive, feature-rich server library supporting HTTP/HTTPS, TLS, WebSockets,
4//! Server-Sent Events (SSE), and command-and-control distributed networking.
5//!
6//! # Features
7//!
8//! - **HTTP/HTTPS Servers**: Full-featured web servers with TLS support
9//! - **Dual Protocol Support**: Serve both HTTP and HTTPS simultaneously
10//! - **Security**: Built-in CSP, CSRF protection, security headers
11//! - **Compression**: Gzip, Deflate, and Brotli compression support
12//! - **WebSockets**: Native WebSocket handler support
13//! - **SSE**: Server-Sent Events for real-time updates
14//! - **CORS**: Configurable Cross-Origin Resource Sharing
15//! - **Command & Control**: Distributed network capabilities with authentication
16//! - **Flexible Executors**: Support for Tokio and custom executors
17//! - **no_std Support**: Can be used in embedded environments with appropriate features
18//!
19//! # Example
20//!
21//! ```no_run
22//! use product_os_server::{ProductOSServer, StatusCode, Response, Body};
23//! use product_os_server::ServerConfig;
24//! use product_os_async_executor::TokioExecutor;
25//!
26//! # #[cfg(feature = "executor_tokio")]
27//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
28//! // Create server with default configuration
29//! let config = ServerConfig::new();
30//! let mut server: ProductOSServer<(), TokioExecutor, _> =
31//! ProductOSServer::new_with_config(config);
32//!
33//! // Add a simple GET handler
34//! async fn hello_handler() -> Result<Response<Body>, StatusCode> {
35//! Ok(Response::new(Body::empty()))
36//! }
37//!
38//! server.add_get("/hello", hello_handler);
39//!
40//! // Start the server (non-blocking)
41//! // server.start(false).await?;
42//! # Ok(())
43//! # }
44//! ```
45//!
46//! # Feature Flags
47//!
48//! - `core`: Core server functionality (hyper, axum, tracing)
49//! - `executor_tokio`: Tokio executor support
50//! - `tls`: HTTPS/TLS support via rustls
51//! - `dual_server`: Dual HTTP/HTTPS protocol support
52//! - `cors`: CORS middleware
53//! - `ws`: WebSocket support
54//! - `sse`: Server-Sent Events support
55//! - `compression`: Response compression (gzip, deflate, brotli)
56//! - `cspolicy`: Content Security Policy headers
57//! - `csrf`: CSRF protection
58//! - `controller`: Command and control distributed networking
59//! - `middleware`: Extended middleware support
60//! - `extract_headers`: Typed header extraction
61//!
62//! # Safety and Security
63//!
64//! This crate provides security features including:
65//! - TLS/HTTPS with rustls
66//! - Content Security Policy configuration
67//! - CSRF token validation
68//! - Security headers (HSTS, X-Frame-Options, etc.)
69//! - Authentication for command-and-control operations
70//!
71//! Always review your configuration for your specific security requirements.
72
73#![no_std]
74#![warn(missing_docs)]
75#![warn(clippy::unwrap_used)]
76#![warn(clippy::expect_used)]
77#![warn(clippy::panic)]
78
79extern crate no_std_compat as std;
80extern crate alloc;
81
82use core::marker::PhantomData;
83use core::str::FromStr;
84use std::prelude::v1::*;
85
86/// Error types for server operations.
87pub mod error;
88pub use error::{ProductOSServerError, Result as ServerResult};
89
90/// Server configuration types (Network, Certificate, Compression, ServerConfig)
91pub mod config;
92pub use config::{ServerConfig, Network, Certificate, CertificateFiles, CertificateFilesKind, Compression};
93
94#[cfg(feature = "tls")]
95mod https_server;
96
97mod certificates;
98
99mod logging;
100mod http_server;
101
102#[cfg(feature = "cspolicy")]
103mod csp;
104
105#[cfg(feature = "controller")]
106mod command_handler;
107
108#[cfg(feature = "controller")]
109mod feature_handler;
110#[cfg(feature = "dual_server")]
111mod dual_server;
112
113mod server_executor;
114
115#[cfg(any(feature = "oidc", feature = "authentication", feature = "content", feature = "service_handler"))]
116mod features_services;
117
118use std::collections::BTreeMap;
119
120use std::sync::Arc;
121
122#[cfg(feature = "controller")]
123use parking_lot::Mutex;
124
125
126#[cfg(feature = "core")]
127pub use axum::{
128 BoxError,
129 http::uri::Scheme,
130 response::IntoResponse
131};
132
133
134#[cfg(feature = "extract_headers")]
135pub use axum_extra::typed_header::TypedHeader;
136
137#[cfg(feature = "core")]
138pub use hyper::{
139 upgrade,
140 client::conn
141};
142
143pub use product_os_router::{
144 Layer, Service, ServiceExt, service_fn,
145 Router, Method, ProductOSRouter, ServiceBuilder,
146 Json,
147 Form,
148 Body, BodyBytes, Bytes, HttpBody,
149 Request, StatusCode, Response, IntoResponse as RouterIntoResponse,
150 Handler, Uri
151};
152
153#[cfg(feature = "core")]
154pub use axum::extract::Extension;
155
156
157#[cfg(feature = "middleware")]
158pub use product_os_router::*;
159
160#[cfg(feature = "controller")]
161use product_os_command_control::ProductOSController;
162
163#[cfg(feature = "controller")]
164use product_os_store::{ ProductOSKeyValueStore, ProductOSRelationalStore };
165
166
167#[cfg(feature = "core")]
168pub use axum::{
169 extract::*,
170};
171
172#[cfg(feature = "sse")]
173pub use axum::response::sse::{Event, Sse};
174
175#[cfg(feature = "core")]
176use axum::routing::Route;
177
178
179#[cfg(feature = "compression")]
180use tower_http::{
181 compression::CompressionLayer,
182 decompression::DecompressionLayer
183};
184
185#[cfg(feature = "csrf")]
186use axum_csrf::{CsrfConfig, CsrfLayer};
187
188
189
190#[cfg(feature = "executor_tokio")]
191use product_os_async_executor::TokioExecutor;
192
193use product_os_async_executor::Executor;
194use crate::certificates::ProductOSServerCertificates;
195
196/// Default HSTS max-age in seconds (24 hours).
197const DEFAULT_HSTS_MAX_AGE: u32 = 86400;
198
199/// Default timeout for controller lock acquisition (10 seconds).
200#[cfg(feature = "controller")]
201pub(crate) const CONTROLLER_LOCK_TIMEOUT: core::time::Duration = core::time::Duration::from_secs(10);
202
203/// Applies security headers and optional CSRF/CSP middleware to a router.
204///
205/// This helper is used by both the constructor and `set_security` to avoid duplication.
206fn apply_security_headers<S: Clone + Send + Sync + 'static>(
207 router: &mut ProductOSRouter<S>,
208 host: &str,
209 security: &product_os_security::Security,
210) {
211 if !security.enable {
212 return;
213 }
214
215 #[cfg(feature = "cspolicy")]
216 {
217 let csp_config = security
218 .csp
219 .as_ref()
220 .map_or_else(product_os_security::CSPConfig::new, Clone::clone);
221 let csp_value = csp::ContentSecurityPolicy::from_csp_config(&csp_config).get_csp();
222 router.add_default_header("content-security-policy", &csp_value);
223 }
224
225 router.add_default_header("cross-origin-embedder-policy", "require-corp");
226 router.add_default_header("cross-origin-opener-policy", "same-origin");
227 router.add_default_header("referrer-policy", "strict-origin-when-cross-origin");
228 let hsts_value = alloc::format!("max-age={}; includeSubDomains; preload", DEFAULT_HSTS_MAX_AGE);
229 router.add_default_header("strict-transport-security", &hsts_value);
230 router.add_default_header("x-Content-type-options", "nosniff");
231 router.add_default_header("x-powered-by", host);
232
233 #[cfg(feature = "csrf")]
234 {
235 if security.csrf {
236 router.add_middleware(CsrfLayer::new(CsrfConfig::default()));
237 #[cfg(feature = "core")]
238 tracing::info!("CSRF added as extension middleware");
239 }
240 }
241}
242
243/// Applies compression and decompression middleware layers to a router.
244///
245/// This helper is used by both the constructor and `set_compression` to avoid duplication.
246#[cfg(feature = "compression")]
247fn apply_compression<S: Clone + Send + Sync + 'static>(
248 router: &mut ProductOSRouter<S>,
249 compression_config: &crate::config::Compression,
250) {
251 if !compression_config.enable {
252 return;
253 }
254 let mut compression = CompressionLayer::new();
255 if compression_config.gzip { compression = compression.gzip(true); }
256 if compression_config.deflate { compression = compression.deflate(true); }
257 if compression_config.brotli { compression = compression.br(true); }
258 router.add_middleware(compression);
259
260 let mut decompression = DecompressionLayer::new();
261 if compression_config.gzip { decompression = decompression.gzip(true); }
262 if compression_config.deflate { decompression = decompression.deflate(true); }
263 if compression_config.brotli { decompression = decompression.br(true); }
264 router.add_middleware(decompression);
265}
266
267/// Specifies which async executor implementation to use for the server.
268///
269/// Currently, only Tokio is fully supported. Embassy is reserved for future
270/// embedded system support.
271pub enum ExecutorType {
272 /// Use the Tokio async runtime. This is the recommended and fully-supported executor.
273 Tokio,
274 /// Use the Embassy async runtime (for embedded / no_std environments).
275 /// **Note:** Embassy support is experimental and not yet fully implemented.
276 Embassy,
277}
278
279
280
281
282/// The main server struct for Product OS Server
283///
284/// `ProductOSServer` provides a complete web server implementation with support for:
285/// - HTTP and HTTPS protocols
286/// - Routing and middleware
287/// - TLS/SSL via rustls
288/// - WebSockets and Server-Sent Events
289/// - Command-and-control distributed networking (with `controller` feature)
290/// - Compression, CORS, CSRF, and security headers
291///
292/// # Type Parameters
293///
294/// - `S`: The shared state type (must be `Clone + Send + Sync + 'static`)
295/// - `E`: The executor type implementing `Executor` traits
296/// - `X`: The underlying executor context type
297///
298/// # Examples
299///
300/// ## Basic HTTP Server
301///
302/// ```no_run
303/// use product_os_server::{ProductOSServer, StatusCode, Response, Body};
304/// use product_os_server::ServerConfig;
305/// use product_os_async_executor::TokioExecutor;
306///
307/// # #[cfg(feature = "executor_tokio")]
308/// # async fn example() {
309/// let config = ServerConfig::new();
310/// let mut server: ProductOSServer<(), TokioExecutor, _> =
311/// ProductOSServer::new_with_config(config);
312///
313/// async fn handler() -> Result<Response<Body>, StatusCode> {
314/// Ok(Response::new(Body::empty()))
315/// }
316///
317/// server.add_get("/", handler);
318/// # }
319/// ```
320///
321/// ## Server with Shared State
322///
323/// ```no_run
324/// use product_os_server::{ProductOSServer, StatusCode, Response};
325/// use product_os_server::ServerConfig;
326/// use product_os_async_executor::TokioExecutor;
327///
328/// # #[cfg(feature = "executor_tokio")]
329/// # async fn example() {
330/// #[derive(Clone)]
331/// struct AppState {
332/// counter: std::sync::Arc<parking_lot::Mutex<i32>>,
333/// }
334///
335/// let state = AppState {
336/// counter: std::sync::Arc::new(parking_lot::Mutex::new(0)),
337/// };
338///
339/// let config = ServerConfig::new();
340/// let mut server: ProductOSServer<AppState, TokioExecutor, _> =
341/// ProductOSServer::new_with_state_with_config(config, state);
342/// # }
343/// ```
344pub struct ProductOSServer<S, E, X>
345where
346 E: product_os_async_executor::Executor<X> + product_os_async_executor::ExecutorPerform<X> + product_os_async_executor::Timer
347{
348 router: ProductOSRouter<S>,
349
350 config: crate::config::ServerConfig,
351 certificates: product_os_security::certificates::Certificates,
352
353 #[cfg(feature = "controller")]
354 controller: Option<Arc<Mutex<ProductOSController>>>,
355
356 executor: Arc<E>,
357 executor_underlying: PhantomData<X>
358}
359
360#[cfg(feature = "executor_tokio")]
361impl<X> ProductOSServer<(), TokioExecutor, X>
362where
363 TokioExecutor: product_os_async_executor::Executor<X>, TokioExecutor: product_os_async_executor::ExecutorPerform<X>
364{
365 /// Creates a new server with the given configuration and no shared state.
366 ///
367 /// This is a convenience constructor for Tokio-based servers that automatically
368 /// creates and initializes the Tokio executor.
369 ///
370 /// # Arguments
371 ///
372 /// * `config` - Server configuration including network settings, security options, etc.
373 ///
374 /// # Examples
375 ///
376 /// ```no_run
377 /// use product_os_server::ProductOSServer;
378 /// use product_os_server::ServerConfig;
379 /// use product_os_async_executor::TokioExecutor;
380 ///
381 /// # #[cfg(feature = "executor_tokio")]
382 /// # fn example() {
383 /// let config = ServerConfig::new();
384 /// let server: ProductOSServer<(), TokioExecutor, _> =
385 /// ProductOSServer::new_with_config(config);
386 /// # }
387 /// ```
388 pub fn new_with_config(config: crate::config::ServerConfig) -> Self {
389 let executor = match TokioExecutor::context_sync() {
390 Ok(exec) => Arc::new(exec),
391 Err(e) => panic!("Failed to create Tokio executor context: {:?}. Ensure a Tokio runtime is active.", e),
392 };
393 ProductOSServer::new_with_executor_with_state_with_config(Some(executor), config, ())
394 }
395
396 /// Creates a new server with default configuration and no shared state.
397 ///
398 /// This constructor uses default configuration settings and creates a Tokio executor.
399 ///
400 /// # Arguments
401 ///
402 /// * `executor` - Optional executor Arc. If None, a new executor will be created.
403 ///
404 /// # Examples
405 ///
406 /// ```no_run
407 /// use product_os_server::ProductOSServer;
408 /// use product_os_async_executor::TokioExecutor;
409 ///
410 /// # #[cfg(feature = "executor_tokio")]
411 /// # fn example() {
412 /// let server: ProductOSServer<(), TokioExecutor, _> =
413 /// ProductOSServer::new(None);
414 /// # }
415 /// ```
416 pub fn new(_executor: Option<Arc<TokioExecutor>>) -> Self {
417 let executor = match TokioExecutor::context_sync() {
418 Ok(exec) => Arc::new(exec),
419 Err(e) => panic!("Failed to create Tokio executor context: {:?}. Ensure a Tokio runtime is active.", e),
420 };
421 ProductOSServer::new_with_executor_with_state(Some(executor), ())
422 }
423}
424
425
426
427impl<E, X> ProductOSServer<(), E, X>
428where
429 E: product_os_async_executor::Executor<X> + product_os_async_executor::ExecutorPerform<X> + product_os_async_executor::Timer + 'static
430{
431 /// Creates a new server with a custom executor and configuration.
432 ///
433 /// This constructor allows specifying a custom executor for handling async operations.
434 ///
435 /// # Arguments
436 ///
437 /// * `executor` - Optional executor Arc. If None, will panic.
438 /// * `config` - Server configuration
439 pub fn new_with_executor_with_config(executor: Option<Arc<E>>, config: crate::config::ServerConfig) -> Self {
440 ProductOSServer::new_with_executor_with_state_with_config(executor, config, ())
441 }
442
443 /// Creates a new server with a custom executor and default configuration.
444 ///
445 /// # Arguments
446 ///
447 /// * `executor` - Optional executor Arc. If None, will panic.
448 pub fn new_with_executor(executor: Option<Arc<E>>) -> Self {
449 ProductOSServer::new_with_executor_with_state(executor, ())
450 }
451
452 #[cfg(feature = "controller")]
453 /// Adds a feature to the server.
454 ///
455 /// # Panics
456 ///
457 /// Panics if no controller is configured. Use `try_add_feature` for a non-panicking alternative.
458 #[deprecated(since = "0.0.53", note = "Use try_add_feature which returns Result instead of panicking")]
459 pub async fn add_feature(&mut self, feature_arc: Arc<dyn product_os_capabilities::Feature>, base_path: Option<String>) {
460 self.try_add_feature(feature_arc, base_path).await
461 .unwrap_or_else(|e| panic!("{}", e));
462 }
463
464 #[cfg(feature = "controller")]
465 /// Adds a feature to the server, returning an error if no controller is configured.
466 ///
467 /// Features provide modular functionality that can be dynamically added to the server.
468 /// This method requires the `controller` feature to be enabled.
469 ///
470 /// # Arguments
471 ///
472 /// * `feature_arc` - Arc-wrapped feature implementation
473 /// * `base_path` - Optional base path for mounting the feature's routes
474 ///
475 /// # Errors
476 ///
477 /// Returns `ControllerError` if no controller is configured on the server.
478 pub async fn try_add_feature(&mut self, feature_arc: Arc<dyn product_os_capabilities::Feature>, base_path: Option<String>) -> crate::error::Result<()> {
479 match &self.controller {
480 None => {
481 Err(ProductOSServerError::ControllerError {
482 operation: "add_feature".to_string(),
483 message: alloc::format!("Feature {} failed to add to server: no controller", feature_arc.identifier()),
484 })
485 }
486 Some(c) => {
487 let controller_unlocked = c.clone();
488 let mut controller = controller_unlocked.lock();
489
490 let feature_base_path = base_path.unwrap_or_default();
491
492 controller.add_feature(feature_arc.clone(), feature_base_path, &mut self.router).await;
493 #[cfg(feature = "core")]
494 tracing::info!("Feature {} successfully added to server", feature_arc.identifier());
495 Ok(())
496 }
497 }
498 }
499
500 #[cfg(feature = "controller")]
501 /// Adds a mutable feature to the server.
502 ///
503 /// # Panics
504 ///
505 /// Panics if no controller is configured. Use `try_add_feature_mut` for a non-panicking alternative.
506 #[deprecated(since = "0.0.53", note = "Use try_add_feature_mut which returns Result instead of panicking")]
507 pub async fn add_feature_mut(&mut self, feature_arc_mut: Arc<Mutex<dyn product_os_capabilities::Feature>>, base_path: Option<String>) {
508 self.try_add_feature_mut(feature_arc_mut, base_path).await
509 .unwrap_or_else(|e| panic!("{}", e));
510 }
511
512 #[cfg(feature = "controller")]
513 /// Adds a mutable feature to the server, returning an error if no controller is configured.
514 ///
515 /// Similar to `try_add_feature`, but accepts a mutable feature wrapped in an `Arc<Mutex>`.
516 ///
517 /// # Arguments
518 ///
519 /// * `feature_arc_mut` - Arc-wrapped mutex containing the feature implementation
520 /// * `base_path` - Optional base path for mounting the feature's routes
521 ///
522 /// # Errors
523 ///
524 /// Returns `ControllerError` if no controller is configured on the server.
525 pub async fn try_add_feature_mut(&mut self, feature_arc_mut: Arc<Mutex<dyn product_os_capabilities::Feature>>, base_path: Option<String>) -> crate::error::Result<()> {
526 match &self.controller {
527 None => {
528 let feature_locked = feature_arc_mut.lock();
529 Err(ProductOSServerError::ControllerError {
530 operation: "add_feature_mut".to_string(),
531 message: alloc::format!("Feature {} failed to add to server: no controller", feature_locked.identifier()),
532 })
533 }
534 Some(c) => {
535 let controller_unlocked = c.clone();
536 let mut controller = controller_unlocked.lock();
537
538 let feature_base_path = base_path.unwrap_or_default();
539
540 controller.add_feature_mut(feature_arc_mut.clone(), feature_base_path, &mut self.router).await;
541 let feature_locked = feature_arc_mut.lock();
542 #[cfg(feature = "core")]
543 tracing::info!("Feature {} successfully added to server", feature_locked.identifier());
544 Ok(())
545 }
546 }
547 }
548}
549
550
551#[cfg(feature = "executor_tokio")]
552impl<S, X> ProductOSServer<S, TokioExecutor, X>
553where
554 S: Clone + Send + Sync + 'static,
555 TokioExecutor: product_os_async_executor::Executor<X>, TokioExecutor: product_os_async_executor::ExecutorPerform<X>
556{
557 /// Creates a new server with state and configuration (Tokio executor).
558 ///
559 /// # Arguments
560 ///
561 /// * `config` - Server configuration
562 /// * `state` - Shared state available to all handlers
563 pub fn new_with_state_with_config(config: crate::config::ServerConfig, state: S) -> Self {
564 let executor = match TokioExecutor::context_sync() {
565 Ok(exec) => Arc::new(exec),
566 Err(e) => panic!("Failed to create Tokio executor context: {:?}. Ensure a Tokio runtime is active.", e),
567 };
568 ProductOSServer::new_with_executor_with_state_with_config(Some(executor), config, state)
569 }
570
571 /// Creates a new server with state and default configuration (Tokio executor).
572 ///
573 /// # Arguments
574 ///
575 /// * `state` - Shared state available to all handlers
576 pub fn new_with_state(state: S) -> Self {
577 let executor = match TokioExecutor::context_sync() {
578 Ok(exec) => Arc::new(exec),
579 Err(e) => panic!("Failed to create Tokio executor context: {:?}. Ensure a Tokio runtime is active.", e),
580 };
581 ProductOSServer::new_with_executor_with_state(Some(executor), state)
582 }
583}
584
585
586impl<S, E, X> ProductOSServer<S, E, X>
587where
588 S: Clone + Send + Sync + 'static,
589 E: product_os_async_executor::Executor<X> + product_os_async_executor::ExecutorPerform<X> + product_os_async_executor::Timer + 'static
590{
591 /// Creates a new server with custom executor, state, and configuration.
592 ///
593 /// This is the most flexible constructor, allowing full customization of executor,
594 /// state, and configuration.
595 ///
596 /// # Arguments
597 ///
598 /// * `executor` - Optional executor Arc. If None, will panic.
599 /// * `config` - Server configuration including network, security, compression settings
600 /// * `state` - Shared state that will be available to all handlers
601 pub fn new_with_executor_with_state_with_config(executor: Option<Arc<E>>, config: crate::config::ServerConfig, state: S) -> Self {
602 #[cfg(feature = "core")]
603 let _ = logging::set_global_logger(logging::define_logging(config.log_level()));
604 #[cfg(feature = "core")]
605 tracing::info!("Log Level: {}", config.log_level());
606
607 let mut router = ProductOSRouter::new_with_state(state);
608
609 // Setup certificates
610 let certificates = ProductOSServerCertificates::setup_certificates(&config.certificate);
611
612 // Parse security config from raw JSON
613 let security_config: Option<product_os_security::Security> = config.security.as_ref()
614 .and_then(|v| {
615 serde_json::from_value(v.clone()).map_err(|e| {
616 #[cfg(feature = "core")]
617 tracing::warn!("Failed to parse security config: {}", e);
618 e
619 }).ok()
620 });
621 if let Some(ref security) = security_config {
622 apply_security_headers(&mut router, &config.network.host, security);
623 }
624
625 #[cfg(feature = "compression")]
626 {
627 if let Some(ref compress) = config.compression {
628 apply_compression(&mut router, compress);
629 }
630 }
631
632 #[cfg(feature = "controller")]
633 let mut option_controller_mutex = None;
634
635 #[cfg(feature = "controller")]
636 {
637 // Parse command_control and store configs from raw JSON
638 let cc_config: Option<product_os_command_control::CommandControl> = config.command_control.as_ref()
639 .and_then(|v| serde_json::from_value(v.clone()).map_err(|e| {
640 #[cfg(feature = "core")]
641 tracing::warn!("Failed to parse command_control config: {}", e);
642 e
643 }).ok());
644 let store_config: Option<product_os_store::Stores> = config.store.as_ref()
645 .and_then(|v| serde_json::from_value(v.clone()).map_err(|e| {
646 #[cfg(feature = "core")]
647 tracing::warn!("Failed to parse store config: {}", e);
648 e
649 }).ok());
650
651 match &cc_config {
652 None => {}
653 Some(command_control) => {
654 if command_control.enable {
655 // Setup stores
656 match &store_config {
657 None => {}
658 Some(store) => {
659 match &store.key_value {
660 None => {}
661 Some(key_value_config) => {
662 let mut key_value = ProductOSKeyValueStore::new(key_value_config);
663 match key_value.connect() {
664 Ok(_) => {
665 #[cfg(feature = "core")]
666 tracing::info!("Successfully connected to key value database: {:?}", key_value_config.kind);
667 }
668 Err(e) => {
669 #[cfg(feature = "core")]
670 tracing::error!("Connect to key value database error: {:?}", e);
671 }
672 } // Explicit connection required
673 key_value.set_group("product-os-node");
674
675 let key_value_store = Arc::new(key_value);
676
677 match &store.relational {
678 None => {}
679 Some(relational_config) => {
680 let mut relational = ProductOSRelationalStore::new(relational_config);
681 match relational.connect_sync() {
682 Ok(_) => {
683 #[cfg(feature = "core")]
684 tracing::info!("Successfully connected to relational database: {:?}", relational_config.kind);
685 }
686 Err(e) => {
687 #[cfg(feature = "core")]
688 tracing::error!("Connect to relational database error: {:?}", e);
689 }
690 };
691
692 // ----- Add Route Handlers
693
694 command_handler::command_responder(&mut router);
695 feature_handler::feature_responder(&mut router, None);
696
697
698 // ----- Add Middleware
699
700 #[cfg(any(feature = "postgres_store", feature = "sqlite_store"))]
701 let controller = ProductOSController::new(command_control.clone(), certificates.clone(), Some(key_value_store.clone()), Some(Arc::new(relational)));
702 #[cfg(not(any(feature = "postgres_store", feature = "sqlite_store")))]
703 let controller = {
704 // Silence unused variable warning when postgres/sqlite stores are not enabled
705 let _ = relational;
706 ProductOSController::new(command_control.clone(), certificates.clone(), Some(key_value_store.clone()), None)
707 };
708 let controller_mutex = Arc::new(Mutex::new(controller));
709
710 router.add_middleware(Extension(controller_mutex.clone()));
711 #[cfg(feature = "core")]
712 tracing::info!("Controller added as extension middleware");
713 option_controller_mutex = Some(controller_mutex);
714 }
715 }
716 }
717 }
718 }
719 }
720 }
721 }
722 }
723 }
724
725 let executor = match executor {
726 Some(exec) => exec,
727 None => {
728 #[cfg(feature = "core")]
729 tracing::error!("Unable to create executor context - no executor available");
730 match E::context_sync() {
731 Ok(exec) => Arc::new(exec),
732 Err(e) => panic!("Failed to create default executor context: {:?}. An executor must be available.", e),
733 }
734 }
735 };
736
737 // Return Server
738 Self {
739 router,
740
741 config,
742 certificates,
743
744 #[cfg(feature = "controller")]
745 controller: option_controller_mutex,
746
747 executor,
748 executor_underlying: PhantomData
749 }
750 }
751
752 /// Creates a new server with custom executor and state.
753 ///
754 /// Uses default configuration.
755 ///
756 /// # Arguments
757 ///
758 /// * `executor` - Optional executor Arc. If None, will panic.
759 /// * `state` - Shared state available to all handlers
760 pub fn new_with_executor_with_state(executor: Option<Arc<E>>, state: S) -> Self {
761 let config = crate::config::ServerConfig::new();
762
763 let router = ProductOSRouter::new_with_state(state);
764
765 #[cfg(feature = "core")]
766 {
767 let log_level = config.log_level();
768 let _ = logging::set_global_logger(logging::define_logging(log_level));
769 tracing::info!("Log Level: {}", config.log_level());
770 }
771
772 // Setup certificates
773 let certificates = ProductOSServerCertificates::setup_certificates(&config.certificate);
774
775 let executor = match executor {
776 Some(exec) => exec,
777 None => {
778 #[cfg(feature = "core")]
779 tracing::error!("Unable to create executor context - no executor available");
780 match E::context_sync() {
781 Ok(exec) => Arc::new(exec),
782 Err(e) => panic!("Failed to create default executor context: {:?}. An executor must be available.", e),
783 }
784 }
785 };
786
787 // Return Server
788 Self {
789 router,
790
791 config,
792 certificates,
793
794 #[cfg(feature = "controller")]
795 controller: None,
796
797 executor,
798 executor_underlying: PhantomData
799 }
800 }
801
802 /// Sets the logging level.
803 ///
804 /// # Arguments
805 ///
806 /// * `log_level` - The tracing level (TRACE, DEBUG, INFO, WARN, ERROR)
807 ///
808 /// # Examples
809 ///
810 /// ```no_run
811 /// # use product_os_server::ProductOSServer;
812 /// # use product_os_server::ServerConfig;
813 /// # use product_os_async_executor::TokioExecutor;
814 /// # #[cfg(feature = "executor_tokio")]
815 /// # fn example() {
816 /// # let config = ServerConfig::new();
817 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
818 /// # ProductOSServer::new_with_config(config);
819 /// server.set_logging(tracing::Level::DEBUG);
820 /// # }
821 /// ```
822 #[cfg(feature = "core")]
823 pub fn set_logging(&mut self, log_level: tracing::Level) {
824 let _ = logging::set_global_logger(logging::define_logging(log_level));
825 tracing::info!("Log Level: {}", self.config.log_level());
826 }
827
828 /// Sets or updates the TLS certificate configuration.
829 ///
830 /// # Arguments
831 ///
832 /// * `certificate_config` - Optional certificate configuration. If None, will generate self-signed.
833 pub fn set_certificate(&mut self, certificate_config: &Option<crate::config::Certificate>) {
834 self.certificates = ProductOSServerCertificates::setup_certificates(certificate_config);
835 }
836
837 /// Sets or updates the security configuration.
838 ///
839 /// This includes settings for security headers, CSRF protection, etc.
840 ///
841 /// # Arguments
842 ///
843 /// * `security` - Optional security configuration. If None, security features are disabled.
844 pub fn set_security(&mut self, security: Option<product_os_security::Security>) {
845 if let Some(security) = security {
846 apply_security_headers(&mut self.router, &self.config.network.host, &security);
847 }
848 }
849
850 /// Sets or updates the compression configuration.
851 ///
852 /// This configures the compression middleware for HTTP responses.
853 /// Supports gzip, deflate, and brotli compression.
854 ///
855 /// # Arguments
856 ///
857 /// * `compression_config` - Optional compression configuration. If None, compression is disabled.
858 #[cfg(feature = "compression")]
859 pub fn set_compression(&mut self, compression_config: Option<crate::config::Compression>) {
860 if let Some(ref compress) = compression_config {
861 apply_compression(&mut self.router, compress);
862 }
863 }
864
865 /// Sets or replaces the controller.
866 ///
867 /// # Arguments
868 ///
869 /// * `controller` - Optional controller mutex. If None, removes the controller.
870 #[cfg(feature = "controller")]
871 pub fn set_controller(&mut self, controller: Option<Arc<Mutex<ProductOSController>>>) {
872 self.controller = controller;
873 }
874
875 /// Adds a service to the server.
876 ///
877 /// Services are background tasks that run alongside the server.
878 ///
879 /// # Arguments
880 ///
881 /// * `service_arc` - Arc-wrapped service implementation
882 #[cfg(feature = "controller")]
883 pub async fn add_service(&mut self, service_arc: Arc<dyn product_os_capabilities::Service>) {
884 match &self.controller {
885 None => {
886 panic!("Service {} failed to add to server: no controller", service_arc.identifier());
887 }
888 Some(c) => {
889 let controller_unlocked = c.clone();
890 let mut controller = controller_unlocked.lock();
891
892 controller.add_service(service_arc.clone()).await;
893 #[cfg(feature = "core")]
894 tracing::info!("Service {} successfully added to server", service_arc.identifier());
895 }
896 }
897 }
898
899 /// Adds a mutable service to the server.
900 ///
901 /// Similar to `add_service`, but accepts a mutable service wrapped in an Arc<Mutex>.
902 ///
903 /// # Arguments
904 ///
905 /// * `service_arc_mut` - Arc-wrapped mutex containing the service implementation
906 #[cfg(feature = "controller")]
907 pub async fn add_service_mut(&mut self, service_arc_mut: Arc<Mutex<dyn product_os_capabilities::Service>>) {
908 match &self.controller {
909 None => {
910 let service_locked = service_arc_mut.lock();
911 panic!("Service {} failed to add to server: no controller", service_locked.identifier());
912 }
913 Some(c) => {
914 let controller_unlocked = c.clone();
915 let mut controller = controller_unlocked.lock();
916
917 controller.add_service_mut(service_arc_mut.clone()).await;
918 let service_locked = service_arc_mut.lock();
919 #[cfg(feature = "core")]
920 tracing::info!("Service {} successfully added to server", service_locked.identifier());
921 }
922 }
923 }
924
925 /*
926 #[cfg(all(feature = "controller", feature = "support_feature_service"))]
927 pub async fn add_feature_service(&mut self, feature_service_arc: Arc<dyn product_os_capabilities::FeatureService>, base_path: Option<String>) {
928 self.add_feature(feature_service_arc.clone(), base_path);
929 self.add_service(feature_service_arc);
930 }
931
932 #[cfg(all(feature = "controller", feature = "support_feature_service"))]
933 pub async fn add_feature_service_mut(&mut self, feature_service_arc_mut: Arc<Mutex<dyn product_os_capabilities::FeatureService>>, base_path: Option<String>) {
934 self.add_feature_mut(feature_service_arc_mut.clone(), base_path);
935 self.add_service_mut(feature_service_arc_mut);
936 }
937 */
938
939 /// Gets the controller.
940 ///
941 /// # Panics
942 ///
943 /// Panics if no controller is configured. Use `try_get_controller` for a non-panicking alternative.
944 #[cfg(feature = "controller")]
945 #[deprecated(since = "0.0.53", note = "Use try_get_controller which returns Result instead of panicking")]
946 pub fn get_controller(&self) -> Arc<Mutex<ProductOSController>> {
947 self.try_get_controller()
948 .unwrap_or_else(|e| panic!("{}", e))
949 }
950
951 /// Gets the controller, returning an error if none is configured.
952 ///
953 /// # Returns
954 ///
955 /// An `Arc<Mutex<ProductOSController>>` if a controller is configured.
956 ///
957 /// # Errors
958 ///
959 /// Returns `ControllerError` if no controller is configured.
960 #[cfg(feature = "controller")]
961 pub fn try_get_controller(&self) -> crate::error::Result<Arc<Mutex<ProductOSController>>> {
962 match &self.controller {
963 None => Err(ProductOSServerError::ControllerError {
964 operation: "get_controller".to_string(),
965 message: "No controller configured on this server".to_string(),
966 }),
967 Some(c) => Ok(c.clone()),
968 }
969 }
970
971 /// Retrieves a mutable reference to the router.
972 ///
973 /// This allows direct manipulation of the underlying router for advanced use cases.
974 ///
975 /// # Returns
976 ///
977 /// A mutable reference to the `ProductOSRouter`.
978 ///
979 /// # Examples
980 ///
981 /// ```no_run
982 /// # use product_os_server::ProductOSServer;
983 /// # use product_os_server::ServerConfig;
984 /// # use product_os_async_executor::TokioExecutor;
985 /// # #[cfg(feature = "executor_tokio")]
986 /// # fn example() {
987 /// # let config = ServerConfig::new();
988 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
989 /// # ProductOSServer::new_with_config(config);
990 /// let router = server.get_router();
991 /// // Perform advanced router operations
992 /// # }
993 /// ```
994 pub fn get_router(&mut self) -> &mut ProductOSRouter<S> {
995 &mut self.router
996 }
997
998 /// Adds a route with a method router.
999 ///
1000 /// # Arguments
1001 ///
1002 /// * `path` - The URL path for the route (e.g., "/api/users")
1003 /// * `service_handler` - The method router handling the route
1004 ///
1005 /// # Examples
1006 ///
1007 /// ```no_run
1008 /// # use product_os_server::{ProductOSServer, Response, StatusCode, Body};
1009 /// # use product_os_server::ServerConfig;
1010 /// # use product_os_async_executor::TokioExecutor;
1011 /// # #[cfg(feature = "executor_tokio")]
1012 /// # fn example() {
1013 /// # let config = ServerConfig::new();
1014 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1015 /// # ProductOSServer::new_with_config(config);
1016 /// use product_os_router::MethodRouter;
1017 ///
1018 /// async fn handler() -> Result<Response<Body>, StatusCode> {
1019 /// Ok(Response::new(Body::empty()))
1020 /// }
1021 ///
1022 /// let method_router = MethodRouter::new().get(handler);
1023 /// server.add_route("/test", method_router);
1024 /// # }
1025 /// ```
1026 pub fn add_route(&mut self, path: &str, service_handler: product_os_router::MethodRouter<S>) {
1027 self.router.add_route(path, service_handler);
1028 }
1029
1030 /// Sets a fallback handler for unmatched routes.
1031 ///
1032 /// The fallback handler is called when no route matches the incoming request.
1033 /// This is typically used to return a 404 Not Found response.
1034 ///
1035 /// # Arguments
1036 ///
1037 /// * `service_handler` - The method router for handling unmatched routes
1038 ///
1039 /// # Examples
1040 ///
1041 /// ```no_run
1042 /// # use product_os_server::{ProductOSServer, Response, StatusCode, Body};
1043 /// # use product_os_server::ServerConfig;
1044 /// # use product_os_async_executor::TokioExecutor;
1045 /// # #[cfg(feature = "executor_tokio")]
1046 /// # fn example() {
1047 /// # let config = ServerConfig::new();
1048 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1049 /// # ProductOSServer::new_with_config(config);
1050 /// use product_os_router::MethodRouter;
1051 ///
1052 /// async fn not_found() -> Result<Response<Body>, StatusCode> {
1053 /// Err(StatusCode::NOT_FOUND)
1054 /// }
1055 ///
1056 /// let fallback = MethodRouter::new().fallback(not_found);
1057 /// server.set_fallback(fallback);
1058 /// # }
1059 /// ```
1060 pub fn set_fallback(&mut self, service_handler: product_os_router::MethodRouter<S>) {
1061 self.router.set_fallback(service_handler);
1062 }
1063
1064 /// Adds a GET request handler.
1065 ///
1066 /// Convenience method for adding a handler that responds to GET requests.
1067 ///
1068 /// # Arguments
1069 ///
1070 /// * `path` - The URL path (e.g., "/api/users")
1071 /// * `handler` - The async function handling the request
1072 ///
1073 /// # Examples
1074 ///
1075 /// ```no_run
1076 /// # use product_os_server::{ProductOSServer, Response, StatusCode, Body};
1077 /// # use product_os_server::ServerConfig;
1078 /// # use product_os_async_executor::TokioExecutor;
1079 /// # #[cfg(feature = "executor_tokio")]
1080 /// # fn example() {
1081 /// # let config = ServerConfig::new();
1082 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1083 /// # ProductOSServer::new_with_config(config);
1084 /// async fn get_users() -> Result<Response<Body>, StatusCode> {
1085 /// Ok(Response::new(Body::empty()))
1086 /// }
1087 ///
1088 /// server.add_get("/api/users", get_users);
1089 /// # }
1090 /// ```
1091 pub fn add_get<H, T>(&mut self, path: &str, handler: H)
1092 where
1093 H: Handler<T, S>,
1094 T: 'static
1095 {
1096 self.router.add_get(path, handler);
1097 }
1098
1099 /// Adds a POST request handler.
1100 ///
1101 /// Convenience method for adding a handler that responds to POST requests.
1102 ///
1103 /// # Arguments
1104 ///
1105 /// * `path` - The URL path (e.g., "/api/users")
1106 /// * `handler` - The async function handling the request
1107 ///
1108 /// # Examples
1109 ///
1110 /// ```no_run
1111 /// # use product_os_server::{ProductOSServer, Response, StatusCode, Json, Body};
1112 /// # use product_os_server::ServerConfig;
1113 /// # use product_os_async_executor::TokioExecutor;
1114 /// # use serde::{Deserialize, Serialize};
1115 /// # #[cfg(feature = "executor_tokio")]
1116 /// # fn example() {
1117 /// # let config = ServerConfig::new();
1118 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1119 /// # ProductOSServer::new_with_config(config);
1120 /// # #[derive(Deserialize, Serialize)]
1121 /// # struct User { name: String }
1122 /// async fn create_user(Json(user): Json<User>) -> Result<Response<Body>, StatusCode> {
1123 /// Ok(Response::new(Body::empty()))
1124 /// }
1125 ///
1126 /// server.add_post("/api/users", create_user);
1127 /// # }
1128 /// ```
1129 pub fn add_post<H, T>(&mut self, path: &str, handler: H)
1130 where
1131 H: Handler<T, S>,
1132 T: 'static
1133 {
1134 self.router.add_post(path, handler);
1135 }
1136
1137 /// Adds a handler for a specific HTTP method and path.
1138 ///
1139 /// # Arguments
1140 ///
1141 /// * `path` - The URL path
1142 /// * `method` - The HTTP method (GET, POST, PUT, DELETE, etc.)
1143 /// * `handler` - The async function to handle the request
1144 ///
1145 /// # Examples
1146 ///
1147 /// ```no_run
1148 /// # use product_os_server::{ProductOSServer, Response, StatusCode, Method};
1149 /// # use product_os_server::ServerConfig;
1150 /// # use product_os_async_executor::TokioExecutor;
1151 /// # #[cfg(feature = "executor_tokio")]
1152 /// # fn example() {
1153 /// # let config = ServerConfig::new();
1154 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1155 /// # ProductOSServer::new_with_config(config);
1156 /// async fn patch_handler() -> Result<Response<product_os_server::Body>, StatusCode> {
1157 /// Ok(Response::new(product_os_server::Body::empty()))
1158 /// }
1159 /// server.add_handler("/resource", Method::PATCH, patch_handler);
1160 /// # }
1161 /// ```
1162 pub fn add_handler<H, T>(&mut self, path: &str, method: Method, handler: H)
1163 where
1164 H: Handler<T, S>,
1165 T: 'static
1166 {
1167 self.router.add_handler(path, method, handler);
1168 }
1169
1170 /// Sets a fallback handler that will be called for unmatched routes.
1171 ///
1172 /// # Arguments
1173 ///
1174 /// * `handler` - The async function to handle unmatched requests
1175 ///
1176 /// # Examples
1177 ///
1178 /// ```no_run
1179 /// # use product_os_server::{ProductOSServer, Response, StatusCode};
1180 /// # use product_os_server::ServerConfig;
1181 /// # use product_os_async_executor::TokioExecutor;
1182 /// # #[cfg(feature = "executor_tokio")]
1183 /// # fn example() {
1184 /// # let config = ServerConfig::new();
1185 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1186 /// # ProductOSServer::new_with_config(config);
1187 /// async fn not_found() -> Result<Response<product_os_server::Body>, StatusCode> {
1188 /// Err(StatusCode::NOT_FOUND)
1189 /// }
1190 /// server.set_fallback_handler(not_found);
1191 /// # }
1192 /// ```
1193 pub fn set_fallback_handler<H, T>(&mut self, handler: H)
1194 where
1195 H: Handler<T, S>,
1196 T: 'static
1197 {
1198 self.router.set_fallback_handler(handler);
1199 }
1200
1201 /// Adds a CORS-enabled handler for a specific HTTP method and path.
1202 ///
1203 /// This method wraps the handler with CORS middleware.
1204 ///
1205 /// # Arguments
1206 ///
1207 /// * `path` - The URL path
1208 /// * `method` - The HTTP method
1209 /// * `handler` - The async function handling the request
1210 #[cfg(feature = "cors")]
1211 pub fn add_cors_handler<H, T>(&mut self, path: &str, method: Method, handler: H)
1212 where
1213 H: Handler<T, S>,
1214 T: 'static
1215 {
1216 self.router.add_cors_handler(path, method, handler);
1217 }
1218
1219 /// Adds a WebSocket handler for the specified path.
1220 ///
1221 /// # Arguments
1222 ///
1223 /// * `path` - The URL path for WebSocket connections
1224 /// * `ws_handler` - The handler for WebSocket upgrades
1225 #[cfg(feature = "ws")]
1226 pub fn add_ws_handler<H, T>(&mut self, path: &str, ws_handler: H)
1227 where
1228 H: Handler<T, S>,
1229 T: 'static
1230 {
1231 self.router.add_ws_handler(path, ws_handler);
1232 }
1233
1234 /// Adds a Server-Sent Events (SSE) handler for the specified path.
1235 ///
1236 /// SSE handlers are added as GET routes since SSE uses HTTP GET requests.
1237 ///
1238 /// # Arguments
1239 ///
1240 /// * `path` - The URL path for SSE connections
1241 /// * `sse_handler` - The handler producing SSE events
1242 #[cfg(feature = "sse")]
1243 pub fn add_sse_handler<H, T>(&mut self, path: &str, sse_handler: H)
1244 where
1245 H: Handler<T, S>,
1246 T: 'static
1247 {
1248 self.add_get(path, sse_handler);
1249 }
1250
1251 /// Adds multiple handlers for different HTTP methods on the same path.
1252 ///
1253 /// # Arguments
1254 ///
1255 /// * `path` - The URL path
1256 /// * `handlers` - A map of HTTP methods to their handlers
1257 ///
1258 /// # Examples
1259 ///
1260 /// ```ignore
1261 /// // Note: This API requires handlers of the same type. In practice,
1262 /// // use add_get, add_post, etc. for different handler implementations.
1263 /// # use product_os_server::{ProductOSServer, Response, StatusCode, Method, Body};
1264 /// # use product_os_server::ServerConfig;
1265 /// # use product_os_async_executor::TokioExecutor;
1266 /// # use std::collections::BTreeMap;
1267 /// # #[cfg(feature = "executor_tokio")]
1268 /// # fn example() {
1269 /// # let config = ServerConfig::new();
1270 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1271 /// # ProductOSServer::new_with_config(config);
1272 /// async fn handler() -> Result<Response<Body>, StatusCode> {
1273 /// Ok(Response::new(Body::empty()))
1274 /// }
1275 ///
1276 /// // Use add_handler for each method instead:
1277 /// server.add_handler("/resource", Method::GET, handler);
1278 /// server.add_handler("/resource", Method::POST, handler);
1279 /// # }
1280 /// ```
1281 pub fn add_handlers<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
1282 where
1283 H: Handler<T, S>,
1284 T: 'static
1285 {
1286 self.router.add_handlers(path, handlers);
1287 }
1288
1289 /// Adds multiple CORS-enabled handlers for different HTTP methods on the same path.
1290 ///
1291 /// # Arguments
1292 ///
1293 /// * `path` - The URL path
1294 /// * `handlers` - A map of HTTP methods to their handlers
1295 #[cfg(feature = "cors")]
1296 pub fn add_cors_handlers<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
1297 where
1298 H: Handler<T, S>,
1299 T: 'static
1300 {
1301 self.router.add_cors_handlers(path, handlers);
1302 }
1303
1304 /// Adds a global CORS middleware layer that applies to all routes.
1305 ///
1306 /// This permits any origin, any HTTP method, and any request header,
1307 /// making it suitable for development servers and APIs consumed by
1308 /// third-party front-ends. For production you may want to use
1309 /// [`add_middleware`] with a more restrictive `CorsLayer` instead.
1310 ///
1311 /// # Examples
1312 ///
1313 /// ```ignore
1314 /// # use product_os_server::ProductOSServer;
1315 /// # use product_os_async_executor::TokioExecutor;
1316 /// // Enable permissive CORS on all routes
1317 /// server.add_cors_middleware();
1318 /// ```
1319 #[cfg(feature = "cors")]
1320 pub fn add_cors_middleware(&mut self) {
1321 self.router.add_cors_middleware();
1322 }
1323
1324 /// Adds middleware to the server.
1325 ///
1326 /// Middleware layers are applied to all routes and can handle cross-cutting concerns
1327 /// like logging, authentication, rate limiting, etc.
1328 ///
1329 /// # Type Parameters
1330 ///
1331 /// * `L` - The middleware layer type
1332 ///
1333 /// # Examples
1334 ///
1335 /// ```no_run
1336 /// # use product_os_server::ProductOSServer;
1337 /// # use product_os_server::ServerConfig;
1338 /// # use product_os_async_executor::TokioExecutor;
1339 /// # #[cfg(feature = "executor_tokio")]
1340 /// # fn example() {
1341 /// # let config = ServerConfig::new();
1342 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1343 /// # ProductOSServer::new_with_config(config);
1344 /// // Add middleware (example with tower-http)
1345 /// // use tower_http::trace::TraceLayer;
1346 /// // server.add_middleware(TraceLayer::new_for_http());
1347 /// # }
1348 /// ```
1349 #[cfg(feature = "core")]
1350 pub fn add_middleware<L, ResBody>(&mut self, middleware: L)
1351 where
1352 L: Layer<Route> + Clone + Send + Sync + 'static,
1353 L::Service: Service<Request<Body>, Response = Response<ResBody>> + Clone + Send + Sync + 'static,
1354 <L::Service as Service<Request<Body>>>::Future: Send + 'static,
1355 <L::Service as Service<Request<Body>>>::Error: Into<BoxError> + 'static,
1356 ResBody: HttpBody<Data = Bytes> + Send + 'static,
1357 ResBody::Error: Into<BoxError>,
1358 {
1359 self.router.add_middleware(middleware);
1360 }
1361
1362 /// Sets the entire router, replacing the existing one.
1363 ///
1364 /// # Arguments
1365 ///
1366 /// * `router` - The new router to use
1367 ///
1368 /// # Examples
1369 ///
1370 /// ```no_run
1371 /// # use product_os_server::{ProductOSServer, ProductOSRouter};
1372 /// # use product_os_server::ServerConfig;
1373 /// # use product_os_async_executor::TokioExecutor;
1374 /// # #[cfg(feature = "executor_tokio")]
1375 /// # fn example() {
1376 /// # let config = ServerConfig::new();
1377 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1378 /// # ProductOSServer::new_with_config(config);
1379 /// let new_router = ProductOSRouter::new();
1380 /// server.set_router(new_router);
1381 /// # }
1382 /// ```
1383 pub fn set_router(&mut self, router: ProductOSRouter<S>)
1384 where
1385 S: Clone + Send + Sync + 'static
1386 {
1387 self.router = router;
1388 }
1389
1390
1391 /// Creates and starts a dual protocol (HTTP/HTTPS) server.
1392 ///
1393 /// This server can handle both HTTP and HTTPS connections on the same port,
1394 /// automatically detecting the protocol from the incoming connection.
1395 ///
1396 /// # Arguments
1397 ///
1398 /// * `serve_on_main_thread` - Whether to block the main thread
1399 /// * `listen_all_interfaces` - Whether to listen on all network interfaces
1400 /// * `custom_port` - Optional custom port number
1401 /// * `custom_router` - Optional custom router to use
1402 /// * `with_connect_info` - Whether to extract connection info
1403 /// * `_force_secure` - Reserved for future use (force redirect HTTP to HTTPS)
1404 #[cfg(feature = "dual_server")]
1405 pub async fn create_dual_service_server(&mut self, serve_on_main_thread: bool, listen_all_interfaces: bool, custom_port: Option<u16>, custom_router: Option<Router>, with_connect_info: bool, _force_secure: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
1406 #[cfg(feature = "tls")]
1407 {
1408 let certificates = Some(self.certificates.to_owned());
1409
1410 let router: Router = match custom_router {
1411 None => self.router.get_router(),
1412 Some(r) => r
1413 };
1414
1415 let address = self.config.socket_address(custom_port, listen_all_interfaces);
1416
1417 if serve_on_main_thread {
1418 dual_server::create_dual_tokio_service(address, certificates, router, with_connect_info).await?;
1419 }
1420 else {
1421 if let Err(e) = server_executor::ServerExecutor::spawn(self.executor.clone(), async move {
1422 match dual_server::create_dual_tokio_service(address, certificates, router, with_connect_info).await {
1423 Ok(_) => {}
1424 Err(e) => tracing::error!("Error starting HTTPS server: {}", e)
1425 }
1426 }) {
1427 tracing::error!("Failed to spawn dual server task: {}", e);
1428 }
1429 }
1430 }
1431
1432 #[cfg(not(feature = "tls"))]
1433 {
1434 tracing::info!("TLS feature is not enabled - please include the \"tls\" feature in your toml config file");
1435 }
1436
1437 Ok(())
1438 }
1439
1440
1441 /// Creates and starts an HTTPS server.
1442 ///
1443 /// Internal method for creating HTTPS servers with TLS support.
1444 /// Use `start()` instead for normal server initialization.
1445 ///
1446 /// # Arguments
1447 ///
1448 /// * `_serve_on_main_thread` - Whether to block the main thread
1449 /// * `_listen_all_interfaces` - Whether to listen on all network interfaces
1450 /// * `_custom_port` - Optional custom port number
1451 /// * `_custom_router` - Optional custom router to use
1452 /// * `_with_connect_info` - Whether to extract connection info
1453 pub async fn create_https_server(&mut self, serve_on_main_thread: bool, listen_all_interfaces: bool, custom_port: Option<u16>, custom_router: Option<Router>, with_connect_info: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
1454 #[cfg(all(feature = "tls", feature = "executor_tokio"))]
1455 {
1456 let certificates = Some(self.certificates.to_owned());
1457
1458 let router: Router = match custom_router {
1459 None => self.router.get_router(),
1460 Some(r) => r
1461 };
1462
1463 let address = self.config.socket_address(custom_port, listen_all_interfaces);
1464
1465 if serve_on_main_thread {
1466 https_server::create_https_tokio_service(address, certificates, router, with_connect_info).await?;
1467 }
1468 else {
1469 if let Err(e) = server_executor::ServerExecutor::spawn(self.executor.clone(), async move {
1470 match https_server::create_https_tokio_service(address, certificates, router, with_connect_info).await {
1471 Ok(_) => {}
1472 Err(e) => tracing::error!("Error starting HTTPS server: {}", e)
1473 }
1474 }) {
1475 tracing::error!("Failed to spawn HTTPS server task: {}", e);
1476 }
1477 }
1478 }
1479 #[cfg(not(feature = "tls"))]
1480 {
1481 // Mark parameters as used to avoid warnings when tls feature is disabled
1482 let _ = (serve_on_main_thread, listen_all_interfaces, custom_port, custom_router, with_connect_info);
1483 #[cfg(feature = "core")]
1484 tracing::info!("TLS feature is not enabled - please include the \"tls\" feature in your toml config file");
1485 }
1486
1487 Ok(())
1488 }
1489
1490 /// Creates and starts an HTTP server.
1491 ///
1492 /// Internal method for creating HTTP servers.
1493 /// Use `start()` instead for normal server initialization.
1494 ///
1495 /// # Arguments
1496 ///
1497 /// * `serve_on_main_thread` - Whether to block the main thread
1498 /// * `listen_all_interfaces` - Whether to listen on all network interfaces
1499 /// * `custom_port` - Optional custom port number
1500 /// * `custom_router` - Optional custom router to use
1501 /// * `with_connect_info` - Whether to extract connection info
1502 pub async fn create_http_server(&mut self, serve_on_main_thread: bool, listen_all_interfaces: bool, custom_port: Option<u16>, custom_router: Option<Router>, with_connect_info: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
1503 #[cfg(feature = "executor_tokio")]
1504 {
1505 let router: Router = match custom_router {
1506 None => self.router.get_router(),
1507 Some(r) => r
1508 };
1509
1510 let address = self.config.socket_address(custom_port, listen_all_interfaces);
1511
1512 if serve_on_main_thread {
1513 http_server::create_http_tokio_service(address, router, with_connect_info).await?;
1514 }
1515 else {
1516 if let Err(e) = server_executor::ServerExecutor::spawn(self.executor.clone(), async move {
1517 match http_server::create_http_tokio_service(address, router, with_connect_info).await {
1518 Ok(_) => {}
1519 Err(e) => tracing::error!("Error starting HTTP server: {}", e)
1520 }
1521 }) {
1522 tracing::error!("Failed to spawn HTTP server task: {}", e);
1523 }
1524 }
1525 }
1526 #[cfg(not(feature = "executor_tokio"))]
1527 {
1528 let _ = (serve_on_main_thread, listen_all_interfaces, custom_port, custom_router, with_connect_info);
1529 #[cfg(feature = "core")]
1530 tracing::info!("executor_tokio feature is not enabled for HTTP server");
1531 }
1532
1533 Ok(())
1534 }
1535
1536
1537 /// Initializes data stores.
1538 ///
1539 /// Internal method for store initialization. Currently a no-op but reserved
1540 /// for future store initialization logic.
1541 pub async fn init_stores(&mut self) {
1542 // Do nothing for now
1543 }
1544
1545 /// Gets the relational store.
1546 ///
1547 /// # Returns
1548 ///
1549 /// The relational store wrapped in an Arc, or an error if the controller couldn't be locked.
1550 ///
1551 /// # Panics
1552 ///
1553 /// Panics if no controller is configured.
1554 #[cfg(feature = "controller")]
1555 pub fn get_relational_store(&mut self) -> Result<Arc<ProductOSRelationalStore>, ()> {
1556 match &self.controller {
1557 None => {
1558 panic!("No controller");
1559 }
1560 Some(c) => {
1561 let controller_unlocked = c.clone();
1562 let controller_locked = controller_unlocked.try_lock();
1563 match controller_locked {
1564 Some(mut controller) => Ok(controller.get_relational_store()),
1565 None => Err(())
1566 }
1567 }
1568 }
1569 }
1570
1571 /// Gets the key-value store.
1572 ///
1573 /// # Returns
1574 ///
1575 /// The key-value store wrapped in an Arc, or an error if the controller couldn't be locked.
1576 ///
1577 /// # Panics
1578 ///
1579 /// Panics if no controller is configured.
1580 #[cfg(feature = "controller")]
1581 pub fn get_key_value_store(&mut self) -> Result<Arc<ProductOSKeyValueStore>, ()> {
1582 match &self.controller {
1583 None => {
1584 panic!("No controller");
1585 }
1586 Some(c) => {
1587 let controller_unlocked = c.clone();
1588 let controller_locked = controller_unlocked.try_lock();
1589 match controller_locked {
1590 Some(mut controller) => Ok(controller.get_key_value_store()),
1591 None => Err(())
1592 }
1593 }
1594 }
1595 }
1596
1597 /// Gets the server configuration.
1598 ///
1599 /// # Returns
1600 ///
1601 /// A clone of the current server configuration.
1602 ///
1603 /// # Examples
1604 ///
1605 /// ```no_run
1606 /// # use product_os_server::ProductOSServer;
1607 /// # use product_os_server::ServerConfig;
1608 /// # use product_os_async_executor::TokioExecutor;
1609 /// # #[cfg(feature = "executor_tokio")]
1610 /// # fn example() {
1611 /// # let config = ServerConfig::new();
1612 /// # let server: ProductOSServer<(), TokioExecutor, _> =
1613 /// # ProductOSServer::new_with_config(config);
1614 /// let current_config = server.get_config();
1615 /// println!("Server port: {}", current_config.network.port);
1616 /// # }
1617 /// ```
1618 pub fn get_config(&self) -> crate::config::ServerConfig {
1619 self.config.clone()
1620 }
1621
1622 /// Updates the server configuration.
1623 ///
1624 /// # Arguments
1625 ///
1626 /// * `config` - The new configuration to apply
1627 ///
1628 /// # Examples
1629 ///
1630 /// ```no_run
1631 /// # use product_os_server::ProductOSServer;
1632 /// # use product_os_server::ServerConfig;
1633 /// # use product_os_async_executor::TokioExecutor;
1634 /// # #[cfg(feature = "executor_tokio")]
1635 /// # fn example() {
1636 /// # let config = ServerConfig::new();
1637 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1638 /// # ProductOSServer::new_with_config(config);
1639 /// let mut new_config = ServerConfig::new();
1640 /// new_config.network.port = 8080;
1641 /// server.update_config(new_config);
1642 /// # }
1643 /// ```
1644 pub fn update_config(&mut self, config: crate::config::ServerConfig) {
1645 self.config = config;
1646 }
1647
1648 /// Gets the base URL of the server.
1649 ///
1650 /// Returns the URL where the server is accessible, constructed from the
1651 /// configuration's host and port settings.
1652 ///
1653 /// # Returns
1654 ///
1655 /// A `Uri` representing the server's base URL.
1656 ///
1657 /// # Examples
1658 ///
1659 /// ```no_run
1660 /// # use product_os_server::ProductOSServer;
1661 /// # use product_os_server::ServerConfig;
1662 /// # use product_os_async_executor::TokioExecutor;
1663 /// # #[cfg(feature = "executor_tokio")]
1664 /// # fn example() {
1665 /// # let config = ServerConfig::new();
1666 /// # let server: ProductOSServer<(), TokioExecutor, _> =
1667 /// # ProductOSServer::new_with_config(config);
1668 /// let base_url = server.get_base_url();
1669 /// println!("Server available at: {}", base_url);
1670 /// # }
1671 /// ```
1672 #[deprecated(since = "0.0.53", note = "Use try_get_base_url() which returns Result instead of panicking")]
1673 pub fn get_base_url(&self) -> Uri {
1674 self.try_get_base_url()
1675 .unwrap_or_else(|e| panic!("Failed to parse base URL: {:?}", e))
1676 }
1677
1678 /// Returns the base URL as a `Uri`, or an error if URL parsing fails.
1679 ///
1680 /// This is the non-panicking version of `get_base_url`.
1681 pub fn try_get_base_url(&self) -> crate::error::Result<Uri> {
1682 let url = self.config.try_url_address()?;
1683 Uri::from_str(url.as_str())
1684 .map_err(|e| ProductOSServerError::ConfigurationError {
1685 component: "base_url".to_string(),
1686 message: alloc::format!("Failed to parse URL as URI: {}", e),
1687 })
1688 }
1689
1690
1691 /// Starts the server.
1692 ///
1693 /// This method initializes and starts the HTTP/HTTPS server(s) based on the configuration.
1694 /// It handles:
1695 /// - Starting HTTPS server if TLS is configured
1696 /// - Starting HTTP server for insecure connections (if allowed)
1697 /// - Setting up dual protocol servers
1698 /// - Initializing the command-and-control system (if controller feature is enabled)
1699 ///
1700 /// # Arguments
1701 ///
1702 /// * `serve_on_main_thread` - If true, blocks the main thread; if false, spawns background tasks
1703 ///
1704 /// # Returns
1705 ///
1706 /// Returns `Ok(())` if the server starts successfully, or an error if initialization fails.
1707 ///
1708 /// # Examples
1709 ///
1710 /// ```no_run
1711 /// # use product_os_server::ProductOSServer;
1712 /// # use product_os_server::ServerConfig;
1713 /// # use product_os_async_executor::TokioExecutor;
1714 /// # #[cfg(feature = "executor_tokio")]
1715 /// # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
1716 /// # let config = ServerConfig::new();
1717 /// # let mut server: ProductOSServer<(), TokioExecutor, _> =
1718 /// # ProductOSServer::new_with_config(config);
1719 /// // Start server in background
1720 /// server.start(false).await?;
1721 /// # Ok(())
1722 /// # }
1723 /// ```
1724 ///
1725 /// # Note
1726 ///
1727 /// When `serve_on_main_thread` is true and the controller feature is enabled,
1728 /// this method will block indefinitely after starting the controller.
1729 #[cfg(feature = "core")]
1730 pub async fn start(&mut self, serve_on_main_thread: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
1731 // ----- Start Server
1732 if self.config.is_secure() {
1733 #[cfg(feature = "controller")]
1734 {
1735 if self.config.network.allow_insecure {
1736 if !&self.config.network.insecure_use_different_port {
1737 #[cfg(feature = "dual_server")]
1738 self.create_dual_service_server(false, self.config.network.listen_all_interfaces.to_owned(), Some(self.config.insecure_port()), None, false, self.config.network.insecure_force_secure.to_owned()).await?;
1739 }
1740 else {
1741 if self.config.network.insecure_force_secure {
1742 let secure_cfg = ForceSecureConfig {
1743 host: self.config.network.host.clone(),
1744 port: self.config.network.port,
1745 };
1746 let mut router = Router::new();
1747 router = router
1748 .fallback(product_os_router::MethodRouter::new()
1749 .get(force_secure_handler)
1750 .post(force_secure_handler)
1751 .put(force_secure_handler)
1752 .patch(force_secure_handler)
1753 .delete(force_secure_handler)
1754 .trace(force_secure_handler)
1755 .head(force_secure_handler)
1756 .options(force_secure_handler))
1757 .layer(axum::extract::Extension(secure_cfg));
1758
1759 self.create_http_server(false, self.config.network.listen_all_interfaces.to_owned(), Some(self.config.insecure_port()), Some(router), false).await?;
1760 }
1761 else {
1762 self.create_http_server(false, self.config.network.listen_all_interfaces.to_owned(), Some(self.config.insecure_port()), None, false).await?;
1763 }
1764 }
1765 }
1766
1767 self.create_https_server(false, self.config.network.listen_all_interfaces.to_owned(), None, None, false).await?;
1768 }
1769 #[cfg(not(feature = "controller"))]
1770 {
1771 if self.config.network.allow_insecure {
1772 if self.config.network.insecure_force_secure {
1773 let secure_cfg = ForceSecureConfig {
1774 host: self.config.network.host.clone(),
1775 port: self.config.network.port,
1776 };
1777 let mut router = Router::new();
1778 router = router
1779 .fallback(product_os_router::MethodRouter::new()
1780 .get(force_secure_handler)
1781 .post(force_secure_handler)
1782 .put(force_secure_handler)
1783 .patch(force_secure_handler)
1784 .delete(force_secure_handler)
1785 .trace(force_secure_handler)
1786 .head(force_secure_handler)
1787 .options(force_secure_handler))
1788 .layer(axum::extract::Extension(secure_cfg));
1789
1790 self.create_http_server(serve_on_main_thread, self.config.network.listen_all_interfaces.to_owned(), Some(self.config.network.insecure_port), Some(router), false).await?;
1791 }
1792 else {
1793 self.create_http_server(serve_on_main_thread, self.config.network.listen_all_interfaces.to_owned(), Some(self.config.network.insecure_port), None, false).await?;
1794 }
1795 }
1796
1797 self.create_https_server(serve_on_main_thread, self.config.network.listen_all_interfaces.to_owned(), None, None, false).await?;
1798 }
1799 }
1800 else {
1801 #[cfg(feature = "controller")]
1802 {
1803 self.create_http_server(false, self.config.network.listen_all_interfaces.to_owned(), None, None, false).await?;
1804 }
1805 #[cfg(not(feature = "controller"))]
1806 {
1807 self.create_http_server(serve_on_main_thread, self.config.network.listen_all_interfaces.to_owned(), None, None, false).await?;
1808 }
1809 };
1810
1811 #[cfg(feature = "controller")]
1812 {
1813 self.init_stores().await;
1814
1815 let cc_cfg: Option<product_os_command_control::CommandControl> = self.config.command_control.as_ref()
1816 .and_then(|v| serde_json::from_value(v.clone()).ok());
1817 match &cc_cfg {
1818 None => {
1819 panic!("No controller config");
1820 }
1821 Some(command_control_config) => {
1822 if command_control_config.enable {
1823 match &self.controller {
1824 None => {
1825 panic!("No controller");
1826 }
1827 Some(c) => {
1828 let controller_unlocked = c.clone();
1829 let executor = self.executor.clone();
1830
1831 if serve_on_main_thread {
1832 product_os_command_control::run_controller(controller_unlocked, executor).await;
1833 // Wait for shutdown signal (Ctrl+C / SIGINT)
1834 match tokio::signal::ctrl_c().await {
1835 Ok(()) => {
1836 #[cfg(feature = "core")]
1837 tracing::info!("Shutdown signal received, stopping server...");
1838 }
1839 Err(e) => {
1840 #[cfg(feature = "core")]
1841 tracing::error!("Failed to listen for shutdown signal: {:?}", e);
1842 }
1843 }
1844 }
1845 else {
1846 if let Err(e) = server_executor::ServerExecutor::spawn(self.executor.clone(), async move {
1847 product_os_command_control::run_controller(controller_unlocked, executor).await;
1848 }) {
1849 #[cfg(feature = "core")]
1850 tracing::error!("Failed to spawn controller task: {}", e);
1851 }
1852 }
1853 }
1854 }
1855 }
1856 }
1857 }
1858 }
1859
1860 Ok(())
1861 }
1862
1863 /// Starts all registered services.
1864 ///
1865 /// # Errors
1866 ///
1867 /// Returns `ControllerError` if the controller is not configured, not enabled,
1868 /// cannot be locked, or if starting services fails.
1869 #[cfg(feature = "controller")]
1870 pub async fn start_services(&mut self) -> Result<(), ProductOSServerError> {
1871 let cc_cfg: Option<product_os_command_control::CommandControl> = self.config.command_control.as_ref()
1872 .and_then(|v| serde_json::from_value(v.clone()).map_err(|e| {
1873 #[cfg(feature = "core")]
1874 tracing::warn!("Failed to parse command_control config: {}", e);
1875 e
1876 }).ok());
1877 let command_control_config = cc_cfg.ok_or_else(|| ProductOSServerError::ControllerError {
1878 operation: "start_services".to_string(),
1879 message: "No controller config".to_string(),
1880 })?;
1881 if !command_control_config.enable {
1882 return Err(ProductOSServerError::ControllerError {
1883 operation: "start_services".to_string(),
1884 message: "Controller not enabled".to_string(),
1885 });
1886 }
1887 let controller_arc = self.try_get_controller()?;
1888 let mut controller = controller_arc.try_lock_for(CONTROLLER_LOCK_TIMEOUT)
1889 .ok_or_else(|| ProductOSServerError::LockError { resource: "controller".to_string() })?;
1890 controller.start_services().await
1891 .map_err(|e| ProductOSServerError::ControllerError {
1892 operation: "start_services".to_string(),
1893 message: alloc::format!("Failed to start services: {:?}", e),
1894 })
1895 }
1896
1897 /// Starts a specific service by its identifier.
1898 ///
1899 /// # Arguments
1900 ///
1901 /// * `identifier` - The unique identifier of the service to start
1902 ///
1903 /// # Errors
1904 ///
1905 /// Returns `ControllerError` if the controller is not configured, not enabled,
1906 /// cannot be locked, or if starting the service fails.
1907 #[cfg(feature = "controller")]
1908 pub async fn start_service(&mut self, identifier: &str) -> Result<(), ProductOSServerError> {
1909 let cc_cfg: Option<product_os_command_control::CommandControl> = self.config.command_control.as_ref()
1910 .and_then(|v| serde_json::from_value(v.clone()).map_err(|e| {
1911 #[cfg(feature = "core")]
1912 tracing::warn!("Failed to parse command_control config: {}", e);
1913 e
1914 }).ok());
1915 let command_control_config = cc_cfg.ok_or_else(|| ProductOSServerError::ControllerError {
1916 operation: "start_service".to_string(),
1917 message: "No controller config".to_string(),
1918 })?;
1919 if !command_control_config.enable {
1920 return Err(ProductOSServerError::ControllerError {
1921 operation: "start_service".to_string(),
1922 message: "Controller not enabled".to_string(),
1923 });
1924 }
1925 let controller_arc = self.try_get_controller()?;
1926 let mut controller = controller_arc.try_lock_for(CONTROLLER_LOCK_TIMEOUT)
1927 .ok_or_else(|| ProductOSServerError::LockError { resource: "controller".to_string() })?;
1928 controller.start_service(identifier).await
1929 .map_err(|e| ProductOSServerError::ControllerError {
1930 operation: "start_service".to_string(),
1931 message: alloc::format!("Failed to start service '{}': {:?}", identifier, e),
1932 })
1933 }
1934
1935 /// Stops a specific service by its identifier.
1936 ///
1937 /// # Arguments
1938 ///
1939 /// * `identifier` - The unique identifier of the service to stop
1940 ///
1941 /// # Errors
1942 ///
1943 /// Returns `ControllerError` if the controller is not configured, not enabled,
1944 /// cannot be locked, or if stopping the service fails.
1945 #[cfg(feature = "controller")]
1946 pub async fn stop_service(&mut self, identifier: &str) -> Result<(), ProductOSServerError> {
1947 let cc_cfg: Option<product_os_command_control::CommandControl> = self.config.command_control.as_ref()
1948 .and_then(|v| serde_json::from_value(v.clone()).map_err(|e| {
1949 #[cfg(feature = "core")]
1950 tracing::warn!("Failed to parse command_control config: {}", e);
1951 e
1952 }).ok());
1953 let command_control_config = cc_cfg.ok_or_else(|| ProductOSServerError::ControllerError {
1954 operation: "stop_service".to_string(),
1955 message: "No controller config".to_string(),
1956 })?;
1957 if !command_control_config.enable {
1958 return Err(ProductOSServerError::ControllerError {
1959 operation: "stop_service".to_string(),
1960 message: "Controller not enabled".to_string(),
1961 });
1962 }
1963 let controller_arc = self.try_get_controller()?;
1964 let mut controller = controller_arc.try_lock_for(CONTROLLER_LOCK_TIMEOUT)
1965 .ok_or_else(|| ProductOSServerError::LockError { resource: "controller".to_string() })?;
1966 controller.stop_service(identifier).await
1967 .map_err(|e| ProductOSServerError::ControllerError {
1968 operation: "stop_service".to_string(),
1969 message: alloc::format!("Failed to stop service '{}': {:?}", identifier, e),
1970 })
1971 }
1972}
1973
1974
1975
1976
1977/// Configuration passed to the force-secure redirect handler via an axum `Extension`.
1978#[cfg(feature = "core")]
1979#[derive(Clone, Debug)]
1980struct ForceSecureConfig {
1981 host: String,
1982 port: u16,
1983}
1984
1985/// Redirects HTTP requests to their HTTPS equivalent using the configured host and port.
1986///
1987/// Builds a full `https://host:port/path?query` URL and issues a 301 Permanent Redirect.
1988#[cfg(feature = "core")]
1989async fn force_secure_handler(
1990 Extension(secure_cfg): Extension<ForceSecureConfig>,
1991 request: Request<Body>,
1992) -> Response<BodyBytes> {
1993 let uri = request.uri();
1994 let path = uri.path();
1995 let query = uri.query().map(|q| alloc::format!("?{}", q)).unwrap_or_default();
1996
1997 let https_url = if secure_cfg.port == 443 {
1998 alloc::format!("https://{}{}{}", secure_cfg.host, path, query)
1999 } else {
2000 alloc::format!("https://{}:{}{}{}", secure_cfg.host, secure_cfg.port, path, query)
2001 };
2002
2003 let (parts, _) = axum::response::Redirect::permanent(https_url.as_str()).into_response().into_parts();
2004
2005 let mut response_builder = Response::builder().status(parts.status);
2006 for (key, value) in parts.headers.iter() {
2007 response_builder = response_builder.header(key.to_owned(), value.to_owned());
2008 }
2009
2010 response_builder.body(BodyBytes::empty()).unwrap_or_else(|_e| Response::new(BodyBytes::empty()))
2011}
2012
2013/// Deprecated: Old force-secure handler that redirects to the same path without building a proper HTTPS URL.
2014///
2015/// This handler is broken -- it does not include the scheme, host, or port in the redirect Location header.
2016/// Use `force_secure_handler` instead, which builds a correct `https://host:port/path` redirect.
2017#[cfg(feature = "core")]
2018#[deprecated(since = "0.0.53", note = "Use force_secure_handler which builds correct HTTPS redirect URLs")]
2019#[allow(dead_code)]
2020async fn force_secure_handler_deprecated(request: Request<Body>) -> Response<BodyBytes> {
2021 let uri_path = request.uri().path();
2022
2023 let mut url: String = String::new();
2024 url.push_str(uri_path);
2025
2026 let (parts, _) = axum::response::Redirect::permanent(url.as_str()).into_response().into_parts();
2027
2028 let mut response_builder = Response::builder().status(parts.status);
2029 for (key, value) in parts.headers.iter() {
2030 response_builder = response_builder.header(key.to_owned(), value.to_owned());
2031 }
2032
2033 response_builder.body(BodyBytes::empty()).unwrap_or_else(|_e| Response::new(BodyBytes::empty()))
2034}
2035