#![no_std]
extern crate no_std_compat as std;
use std::prelude::v1::*;
#[cfg(feature = "tls")]
mod https_server;
mod logging;
mod http_server;
#[cfg(feature = "cspolicy")]
mod csp;
#[cfg(feature = "controller")]
mod command_handler;
#[cfg(feature = "controller")]
mod feature_handler;
#[cfg(feature = "dual_server")]
mod dual_server;
mod server_executor;
use std::collections::BTreeMap;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::ops::Deref;
use std::sync::Arc;
use parking_lot::Mutex;
pub use axum::{
BoxError,
handler::Handler,
http::uri::Scheme,
};
pub use hyper::{
upgrade,
client::conn
};
#[cfg(feature = "tls")]
pub use axum_server::{
service::*
};
pub use product_os_router::{
Layer, Route, Service, ServiceExt, service_fn,
Router, Method, ProductOSRouter, ServiceBuilder,
Json,
Form,
Body, BodyBytes, Bytes, HttpBody,
Request, StatusCode, Response, IntoResponse,
Extension
};
#[cfg(feature = "middleware")]
pub use product_os_router::*;
#[cfg(feature = "controller")]
use product_os_command_control::ProductOSController;
#[cfg(feature = "controller")]
use product_os_store::{ ProductOSKeyValueStore, ProductOSRelationalStore };
pub use axum::{
extract::*,
};
#[cfg(feature = "sse")]
pub use axum::response::sse::{Event, Sse};
#[cfg(feature="dual_server")]
use axum_server_dual_protocol::ServerExt;
#[cfg(feature = "compression")]
use tower_http::{
compression::CompressionLayer,
decompression::DecompressionLayer
};
#[cfg(feature = "csrf")]
use axum_csrf::{CsrfConfig, CsrfLayer};
use tracing::Level;
#[cfg(feature = "cors")]
use tower_http::cors::CorsLayer;
#[cfg(feature = "custom")]
use std::future::Future;
use product_os_async_executor::{Executor, Task};
use std::fs::File;
pub enum ExecutorType {
Tokio,
Embassy
}
pub struct ProductOSServer<S> {
router: ProductOSRouter<S>,
config: product_os_configuration::Configuration,
certificates: product_os_security::certificates::Certificates,
#[cfg(feature = "controller")]
controller: Option<Arc<Mutex<ProductOSController>>>,
}
impl ProductOSServer<()> {
pub fn new_with_config(config: product_os_configuration::Configuration) -> Self {
ProductOSServer::new_with_state_and_config(config, ())
}
pub fn new() -> Self {
ProductOSServer::new_with_state(())
}
#[cfg(feature = "controller")]
pub async fn add_feature(&mut self, feature_arc: Arc<dyn product_os_capabilities::Feature>, base_path: Option<String>) {
match &self.controller {
None => {
panic!("Feature {} failed to add to server: no controller", feature_arc.identifier());
}
Some(c) => {
let controller_unlocked = c.clone();
let mut controller = controller_unlocked.lock();
let mut feature_base_path = String::new();
match base_path {
None => {}
Some(path) => { feature_base_path.push_str(path.as_str()); }
}
controller.add_feature(feature_arc.clone(), feature_base_path, &mut self.router).await;
tracing::info!("Feature {} successfully added to server", feature_arc.identifier());
}
}
}
#[cfg(feature = "controller")]
pub async fn add_feature_mut(&mut self, feature_arc_mut: Arc<Mutex<dyn product_os_capabilities::Feature>>, base_path: Option<String>) {
match &self.controller {
None => {
let feature_locked = feature_arc_mut.lock();
panic!("Feature {} failed to add to server: no controller", feature_locked.identifier());
}
Some(c) => {
let controller_unlocked = c.clone();
let mut controller = controller_unlocked.lock();
let mut feature_base_path = String::new();
match base_path {
None => {}
Some(path) => { feature_base_path.push_str(path.as_str()); }
}
controller.add_feature_mut(feature_arc_mut.clone(), feature_base_path, &mut self.router).await;
let feature_locked = feature_arc_mut.lock();
tracing::info!("Feature {} successfully added to server", feature_locked.identifier());
}
}
}
}
impl<S> ProductOSServer<S>
where
S: Clone + Send + Sync + 'static
{
pub fn new_with_state_and_config(config: product_os_configuration::Configuration, state: S) -> Self {
logging::set_global_logger(logging::define_logging(config.log_level()));
tracing::info!("Log Level: {}", config.log_level());
let mut router = ProductOSRouter::new_with_state(state);
let certificates = match config.certificate.clone() {
None => {
tracing::info!("Generating self-signed certificate");
product_os_security::certificates::Certificates::new(Some(vec![String::from("CN=ProductOS")]), None)
},
Some(cert_config) => {
match cert_config.files {
Some(files) => {
tracing::info!("Using configuration file for certificate");
product_os_security::certificates::Certificates::new_from_file(files.cert_file, files.key_file)
}
None => {
match cert_config.attributes {
Some(attributes) => {
tracing::info!("Generating self-signed certificate with attributes");
product_os_security::certificates::Certificates::new(Some(vec![attributes]), None)
}
None => {
tracing::error!("Unable to get certificate config attributes - using default");
product_os_security::certificates::Certificates::new(Some(vec![String::from("CN=ProductOS")]), None)
}
}
}
}
}
};
match &config.security {
None => {}
Some(security) => {
if security.enable {
#[cfg(feature = "cspolicy")]
router.add_default_header("content-security-policy".to_string(), csp::ContentSecurityPolicy::new(&config).get_csp());
router.add_default_header("cross-origin-embedder-policy".to_string(), "require-corp".to_string());
router.add_default_header("cross-origin-opener-policy".to_string(), "same-origin".to_string());
router.add_default_header("referrer-policy".to_string(), "strict-origin-when-cross-origin".to_string());
router.add_default_header("strict-transport-security".to_string(), "max-age=86400; includeSubDomains; preload".to_string());
router.add_default_header("x-Content-type-options".to_string(), "nosniff".to_string());
router.add_default_header("x-powered-by".to_string(), config.network.host.to_owned());
#[cfg(feature = "csrf")]
{
if security.csrf {
router.add_middleware(CsrfLayer::new(CsrfConfig::default()));
tracing::info!("CSRF added as extension middleware");
}
}
}
}
}
#[cfg(feature = "compression")]
{
match &config.compression {
None => {}
Some(compress) => {
if compress.enable {
let mut compression = CompressionLayer::new();
if config.is_compression_gzip() { compression = compression.gzip(true); }
if config.is_compression_deflate() { compression = compression.deflate(true); }
if config.is_compression_brotli() { compression = compression.br(true); }
router.add_middleware(compression);
let mut decompression = DecompressionLayer::new();
if config.is_compression_gzip() { decompression = decompression.gzip(true); }
if config.is_compression_deflate() { decompression = decompression.deflate(true); }
if config.is_compression_brotli() { decompression = decompression.br(true); }
router.add_middleware(decompression);
}
}
}
}
#[cfg(feature = "controller")]
let mut option_controller_mutex = None;
#[cfg(feature = "controller")]
{
match &config.command_control {
None => {}
Some(command_control) => {
if command_control.enable {
match &config.store {
None => {}
Some(store) => {
match &store.key_value {
None => {}
Some(key_value) => {
let mut key_value = ProductOSKeyValueStore::new(&key_value);
key_value.connect().unwrap_or_default(); key_value.set_group("product-os-node");
let key_value_store = Arc::new(key_value);
match &store.relational {
None => {}
Some(relational) => {
let mut relational = ProductOSRelationalStore::new(&relational);
match relational.connect_sync() {
Ok(_) => {}
Err(_) => {}
};
let relational_store = Arc::new(relational);
command_handler::command_responder(&mut router);
feature_handler::feature_responder(&mut router, None);
let controller = ProductOSController::new(config.clone(), certificates.clone(), Some(key_value_store.clone()), Some(relational_store.clone()));
let controller_mutex = Arc::new(Mutex::new(controller));
router.add_middleware(Extension(controller_mutex.clone()));
tracing::info!("Controller added as extension middleware");
option_controller_mutex = Some(controller_mutex);
}
}
}
}
}
}
}
}
}
}
Self {
router,
config,
certificates,
#[cfg(feature = "controller")]
controller: option_controller_mutex,
}
}
pub fn new_with_state(state: S) -> Self {
let config = product_os_configuration::Configuration::new();
let mut router = ProductOSRouter::new_with_state(state);
let log_level = config.log_level();
logging::set_global_logger(logging::define_logging(log_level));
tracing::info!("Log Level: {}", config.log_level());
let certificates = {
match config.certificate.clone() {
None => {
tracing::info!("Generating self-signed certificate");
product_os_security::certificates::Certificates::new(Some(vec![String::from("CN=ProductOS")]), None)
},
Some(cert_config) => {
match cert_config.files {
Some(files) => {
tracing::info!("Using configuration file for certificate");
product_os_security::certificates::Certificates::new_from_file(files.cert_file, files.key_file)
}
None => {
match cert_config.attributes {
Some(attributes) => {
tracing::info!("Generating self-signed certificate with attributes");
product_os_security::certificates::Certificates::new(Some(vec![attributes]), None)
}
None => {
tracing::error!("Unable to get certificate config attributes - using default");
product_os_security::certificates::Certificates::new(Some(vec![String::from("CN=ProductOS")]), None)
}
}
}
}
}
}
};
Self {
router,
config,
certificates,
#[cfg(feature = "controller")]
controller: None,
}
}
pub fn set_logging(&mut self, log_level: Level) {
logging::set_global_logger(logging::define_logging(log_level));
tracing::info!("Log Level: {}", self.config.log_level());
}
pub fn set_certificate(&mut self, certificate: Option<product_os_configuration::Certificate>) {
self.certificates = match certificate.clone() {
None => {
tracing::info!("Generating self-signed certificate");
product_os_security::certificates::Certificates::new(Some(vec![String::from("CN=ProductOS")]), None)
},
Some(cert_config) => {
match cert_config.files {
Some(files) => {
tracing::info!("Using configuration file for certificate");
product_os_security::certificates::Certificates::new_from_file(files.cert_file, files.key_file)
}
None => {
match cert_config.attributes {
Some(attributes) => {
tracing::info!("Generating self-signed certificate with attributes");
product_os_security::certificates::Certificates::new(Some(vec![attributes]), None)
}
None => {
tracing::error!("Unable to get certificate config attributes - using default");
product_os_security::certificates::Certificates::new(Some(vec![String::from("CN=ProductOS")]), None)
}
}
}
}
}
};
}
pub fn set_security(&mut self, security: Option<product_os_configuration::Security>) {
match security {
None => {}
Some(security) => {
if security.enable {
#[cfg(feature = "cspolicy")]
self.router.add_default_header("content-security-policy".to_string(), csp::ContentSecurityPolicy::new(&self.config).get_csp());
self.router.add_default_header("cross-origin-embedder-policy".to_string(), "require-corp".to_string());
self.router.add_default_header("cross-origin-opener-policy".to_string(), "same-origin".to_string());
self.router.add_default_header("referrer-policy".to_string(), "strict-origin-when-cross-origin".to_string());
self.router.add_default_header("strict-transport-security".to_string(), "max-age=86400; includeSubDomains; preload".to_string());
self.router.add_default_header("x-Content-type-options".to_string(), "nosniff".to_string());
self.router.add_default_header("x-powered-by".to_string(), self.config.network.host.to_owned());
#[cfg(feature = "csrf")]
{
if security.csrf {
self.router.add_middleware(CsrfLayer::new(CsrfConfig::default()));
tracing::info!("CSRF added as extension middleware");
}
}
}
}
}
}
#[cfg(feature = "compression")]
pub fn set_compression(&mut self, compression_config: Option<product_os_configuration::Compression>) {
match compression_config {
None => {}
Some(compression_config) => {
if compression_config.enable {
let mut compression = CompressionLayer::new();
if compression_config.gzip { compression = compression.gzip(true); }
if compression_config.deflate { compression = compression.deflate(true); }
if compression_config.brotli { compression = compression.br(true); }
self.router.add_middleware(compression);
let mut decompression = DecompressionLayer::new();
if compression_config.gzip { decompression = decompression.gzip(true); }
if compression_config.deflate { decompression = decompression.deflate(true); }
if compression_config.brotli { decompression = decompression.br(true); }
self.router.add_middleware(decompression);
}
}
}
}
#[cfg(feature = "controller")]
pub fn set_controller(&mut self, controller: Option<Arc<Mutex<ProductOSController>>>) {
self.controller = controller;
}
#[cfg(feature = "controller")]
pub async fn add_service(&mut self, service_arc: Arc<dyn product_os_capabilities::Service>) {
match &self.controller {
None => {
panic!("Service {} failed to add to server: no controller", service_arc.identifier());
}
Some(c) => {
let controller_unlocked = c.clone();
let mut controller = controller_unlocked.lock();
controller.add_service(service_arc.clone()).await;
tracing::info!("Service {} successfully added to server", service_arc.identifier());
}
}
}
#[cfg(feature = "controller")]
pub async fn add_service_mut(&mut self, service_arc_mut: Arc<Mutex<dyn product_os_capabilities::Service>>) {
match &self.controller {
None => {
let service_locked = service_arc_mut.lock();
panic!("Service {} failed to add to server: no controller", service_locked.identifier());
}
Some(c) => {
let controller_unlocked = c.clone();
let mut controller = controller_unlocked.lock();
controller.add_service_mut(service_arc_mut.clone()).await;
let service_locked = service_arc_mut.lock();
tracing::info!("Service {} successfully added to server", service_locked.identifier());
}
}
}
#[cfg(feature = "controller")]
pub fn get_controller(&self) -> Arc<Mutex<ProductOSController>> {
match &self.controller {
None => {
panic!("No controller");
}
Some(c) => {
c.clone()
}
}
}
pub fn get_router(&mut self) -> &mut ProductOSRouter<S> {
&mut self.router
}
pub fn add_route(&mut self, path: &str, service_handler: product_os_router::MethodRouter<S>) {
self.router.add_route(path, service_handler);
}
pub fn set_fallback(&mut self, service_handler: product_os_router::MethodRouter<S>) {
self.router.set_fallback(service_handler);
}
pub fn add_get<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.router.add_get(path, handler);
}
pub fn add_post<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.router.add_post(path, handler);
}
pub fn add_handler<H, T>(&mut self, path: &str, method: Method, handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.router.add_handler(path, method, handler);
}
pub fn set_fallback_handler<H, T>(&mut self, handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.router.set_fallback_handler(handler);
}
#[cfg(feature = "cors")]
pub fn add_cors_handler<H, T>(&mut self, path: &str, method: Method, handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.router.add_cors_handler(path, method, handler);
}
#[cfg(feature = "ws")]
pub fn add_ws_handler<H, T>(&mut self, path: &str, ws_handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.add_get(path, ws_handler);
}
#[cfg(feature = "sse")]
pub fn add_sse_handler<H, T>(&mut self, path: &str, sse_handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.add_get(path, sse_handler);
}
pub fn add_handlers<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
where
H: Handler<T, S>,
T: 'static
{
self.router.add_handlers(path, handlers);
}
#[cfg(feature = "cors")]
pub fn add_cors_handlers<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
where
H: Handler<T, S>,
T: 'static
{
self.router.add_cors_handlers(path, handlers);
}
pub fn add_middleware<L, NewResBody>(&mut self, middleware: L)
where
L: Layer<Route> + Clone + Send + 'static,
L::Service: Service<Request<Body>, Response = Response<NewResBody>, Error = Infallible> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
NewResBody: HttpBody<Data = Bytes> + Send + 'static,
NewResBody::Error: Into<BoxError>,
{
self.router.add_middleware(middleware);
}
pub fn set_router(&mut self, router: ProductOSRouter<S>)
where
S: Clone + Send + Sync + 'static
{
self.router = router;
}
#[cfg(feature = "dual_server")]
pub async fn create_dual_service_server<E, X>(&mut self, executor: E, serve_on_main_thread: bool, custom_port: Option<u16>, custom_router: Option<Router>, with_connect_info: bool, force_secure: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
E: product_os_async_executor::Executor<X>
{
#[cfg(feature = "tls")]
{
let router = match custom_router {
None => self.router.get_router(),
Some(r) => r
};
let address = self.config.socket_address(custom_port);
match dual_server::create_dual_service_with_axum(address, Some(self.certificates.to_owned())) {
Ok(server) => {
tracing::info!("HTTPS and HTTP server listening on {}", address);
if serve_on_main_thread {
if with_connect_info {
server
.set_upgrade(force_secure)
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
}
}
else {
#[cfg(feature = "executor_tokio")]
{
if with_connect_info {
server_executor::ServerExecutor::spawn(&executor, async move {
server
.set_upgrade(force_secure)
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
server_executor::ServerExecutor::spawn(&executor, async move {
server
.set_upgrade(force_secure)
.serve(router.into_make_service())
.await.unwrap();
});
}
}
}
},
Err(e) => tracing::error!("Error starting HTTPS server: {}", e)
}
}
#[cfg(not(feature = "tls"))]
{
tracing::info!("TLS feature is not enabled - please include the \"tls\" feature in your toml config file");
}
Ok(())
}
pub async fn create_https_server(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, custom_router: Option<Router>, with_connect_info: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(feature = "tls")]
{
let router: Router = match custom_router {
None => self.router.get_router(),
Some(r) => r
};
let address = self.config.socket_address(custom_port);
#[cfg(feature = "executor_tokio")]
match https_server::create_https_service_with_axum(address, Some(self.certificates.to_owned())) {
Ok(server) => {
tracing::info!("HTTPS server listening on {}", address);
if serve_on_main_thread {
if with_connect_info {
server
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
}
}
else {
if with_connect_info {
product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.serve(router.into_make_service())
.await.unwrap();
});
}
}
},
Err(e) => tracing::error!("Error starting HTTPS server: {}", e)
}
}
#[cfg(not(feature = "tls"))]
{
tracing::info!("TLS feature is not enabled - please include the \"tls\" feature in your toml config file");
}
Ok(())
}
pub async fn create_http_server(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, custom_router: Option<Router>, with_connect_info: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let router: Router = match custom_router {
None => self.router.get_router(),
Some(r) => r
};
let address = self.config.socket_address(custom_port);
match http_server::create_http_service_with_axum(address) {
Ok(server) => {
tracing::info!("HTTP server listening on {}", address);
if serve_on_main_thread {
#[cfg(feature = "executor_tokio")]
{
if with_connect_info {
server
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
}
else {
server
.serve(router.into_make_service())
.await.unwrap();
}
}
}
else {
#[cfg(feature = "executor_tokio")]
{
if with_connect_info {
let _ = product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
let _ = product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.serve(router.into_make_service())
.await.unwrap();
});
}
}
}
},
Err(e) => tracing::error!("Error starting HTTP server: {}", e)
}
Ok(())
}
#[cfg(all(feature = "dual_server", feature = "custom"))]
pub async fn create_dual_server_custom<F>(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, with_connect_info: bool, force_secure: bool, service_function: impl FnMut(Request<Body>) -> F) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
F: Future<Output = Result<Response, Infallible>> + Send + Clone + 'static
{
#[cfg(feature = "tls")]
{
let service = product_os_router::service_fn(service_function);
let make_service = product_os_router::Shared::new(service);
let address = self.config.socket_address(custom_port);
#[cfg(feature = "executor_tokio")]
match dual_server::create_dual_service_with_axum(address, Some(self.certificates.to_owned())) {
Ok(server) => {
tracing::info!("HTTPS and HTTP server listening on {}", address);
if serve_on_main_thread {
if with_connect_info {
server
.set_upgrade(force_secure)
.serve(make_service)
.await.unwrap();
}
}
else {
if with_connect_info {
let _ = product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.set_upgrade(force_secure)
.serve(make_service)
.await.unwrap();
});
}
else {
let _ = product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.set_upgrade(force_secure)
.serve(make_service)
.await.unwrap();
});
}
}
},
Err(e) => tracing::error!("Error starting HTTPS server: {}", e)
}
}
#[cfg(not(feature = "tls"))]
{
tracing::info!("TLS feature is not enabled - please include the \"tls\" feature in your toml config file");
}
Ok(())
}
#[cfg(feature = "custom")]
pub async fn create_https_server_custom<F>(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, with_connect_info: bool, service_function: impl FnMut(Request<Body>) -> F) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
F: Future<Output = Result<Response, Infallible>> + Send + Clone + 'static
{
#[cfg(feature = "tls")]
{
let service = product_os_router::service_fn(service_function);
let make_service = product_os_router::Shared::new(service);
let address = self.config.socket_address(custom_port);
#[cfg(feature = "executor_tokio")]
match https_server::create_https_service_with_axum(address, Some(self.certificates.to_owned())) {
Ok(server) => {
tracing::info!("HTTPS server listening on {}", address);
if serve_on_main_thread {
if with_connect_info {
server
.serve(make_service)
.await.unwrap();
}
}
else {
if with_connect_info {
product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.serve(make_service)
.await.unwrap();
});
}
else {
product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.serve(make_service)
.await.unwrap();
});
}
}
},
Err(e) => tracing::error!("Error starting HTTPS server: {}", e)
}
}
#[cfg(not(feature = "tls"))]
{
tracing::info!("TLS feature is not enabled - please include the \"tls\" feature in your toml config file");
}
Ok(())
}
#[cfg(feature = "custom")]
pub async fn create_http_server_custom<F>(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, with_connect_info: bool, service_function: impl FnMut(Request<Body>) -> F) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
F: Future<Output = Result<Response, Infallible>> + Send + Clone + 'static
{
let service = product_os_router::service_fn(service_function);
let make_service = Shared::new(service);
let address = self.config.socket_address(custom_port);
match http_server::create_http_service_with_axum(address) {
Ok(server) => {
tracing::info!("HTTP server listening on {}", address);
if serve_on_main_thread {
#[cfg(feature = "executor_tokio")]
{
let executor = server_executor::ServerExecutor::new();
if with_connect_info {
server
.executor(executor)
.serve(make_service)
.await.unwrap();
}
else {
server
.executor(executor)
.serve(make_service)
.await.unwrap();
}
}
}
else {
#[cfg(feature = "executor_tokio")]
{
if with_connect_info {
product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.executor(executor)
.serve(make_service)
.await.unwrap();
});
}
else {
product_os_async_executor::TokioExecutor::spawn_in_context(async move {
server
.executor(executor)
.serve(make_service)
.await.unwrap();
});
}
}
}
},
Err(e) => tracing::error!("Error starting HTTP server: {}", e)
}
Ok(())
}
pub async fn init_stores(&mut self) {
}
#[cfg(feature = "controller")]
pub fn get_relational_store(&mut self) -> Result<Arc<ProductOSRelationalStore>, ()> {
match &self.controller {
None => {
panic!("No controller");
}
Some(c) => {
let controller_unlocked = c.clone();
let controller_locked = controller_unlocked.try_lock();
match controller_locked {
Some(mut controller) => Ok(controller.get_relational_store()),
None => Err(())
}
}
}
}
#[cfg(feature = "controller")]
pub fn get_key_value_store(&mut self) -> Result<Arc<ProductOSKeyValueStore>, ()> {
match &self.controller {
None => {
panic!("No controller");
}
Some(c) => {
let controller_unlocked = c.clone();
let controller_locked = controller_unlocked.try_lock();
match controller_locked {
Some(mut controller) => Ok(controller.get_key_value_store()),
None => Err(())
}
}
}
}
pub fn get_config(&self) -> product_os_configuration::Configuration {
self.config.clone()
}
pub fn update_config(&mut self, config: product_os_configuration::Configuration) {
self.config = config;
}
pub fn get_base_url(&self) -> url::Url {
self.config.url_address().to_owned()
}
#[cfg(feature = "custom")]
pub async fn start_custom<F>(&mut self, service_function: impl FnMut(Request<Body>) -> F + Clone) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
F: Future<Output = Result<Response, Infallible>> + Send + Clone + 'static
{
if self.config.is_secure() {
#[cfg(feature = "controller")]
{
if self.config.network.allow_insecure {
if self.config.network.insecure_force_secure {
self.create_http_server_custom(false, Some(self.config.network.insecure_port.to_owned()), false, service_function.clone()).await?;
}
else {
self.create_http_server_custom(false, Some(self.config.network.insecure_port.to_owned()), false, service_function.clone()).await?;
}
}
self.create_https_server_custom(false, None, false, service_function).await?;
}
#[cfg(not(feature = "controller"))]
{
if self.config.network.allow_insecure {
if self.config.network.insecure_force_secure {
self.create_http_server(false, Some(self.config.network.insecure_port), service).await?;
}
else {
self.create_http_server(false, Some(self.config.network.insecure_port)).await?;
}
}
self.create_https_server(true, None, None, false).await?;
}
}
else {
#[cfg(feature = "controller")]
{
self.create_http_server_custom(false, None, false, service_function).await?;
}
#[cfg(not(feature = "controller"))]
{
self.create_http_server(true, None, None).await?;
}
};
#[cfg(feature = "controller")]
{
self.init_stores().await;
match &self.config.command_control {
None => {}
Some(command_control) => {
if command_control.enable {
match &self.controller {
None => {
panic!("No controller");
}
Some(c) => {
#[cfg(feature = "executor_tokio")]
{
let executor = server_executor::ServerExecutor::new();
let controller_unlocked = c.clone();
product_os_command_control::run_controller(controller_unlocked, &executor).await;
}
}
}
}
}
}
}
Ok(())
}
pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
if self.config.is_secure() {
#[cfg(feature = "controller")]
{
if self.config.network.allow_insecure {
if !&self.config.network.insecure_use_different_port {
#[cfg(all(feature = "dual_server", feature = "executor_tokio"))]
{
let executor = server_executor::ServerExecutor::new();
self.create_dual_service_server(executor, false, Some(self.config.insecure_port()), None, false, self.config.network.insecure_force_secure.to_owned()).await?;
}
}
else {
if self.config.network.insecure_force_secure {
let mut router = Router::new();
router = router.fallback(MethodRouter::new()
.get(force_secure_handler)
.post(force_secure_handler)
.put(force_secure_handler)
.patch(force_secure_handler)
.delete(force_secure_handler)
.trace(force_secure_handler)
.head(force_secure_handler)
.options(force_secure_handler));
self.create_http_server(false, Some(self.config.insecure_port()), Some(router), false).await?;
}
else {
self.create_http_server(false, Some(self.config.insecure_port()), None, false).await?;
}
}
}
self.create_https_server(false, None, None, false).await?;
}
#[cfg(not(feature = "controller"))]
{
if self.config.network.allow_insecure {
if self.config.network.insecure_force_secure {
let mut router = Router::new();
router = router.fallback(product_os_router::MethodRouter::new()
.get(force_secure_handler)
.post(force_secure_handler)
.put(force_secure_handler)
.patch(force_secure_handler)
.delete(force_secure_handler)
.trace(force_secure_handler)
.head(force_secure_handler)
.options(force_secure_handler));
self.create_http_server(false, Some(self.config.network.insecure_port), Some(router), false).await?;
}
else {
self.create_http_server(false, Some(self.config.network.insecure_port), None, false).await?;
}
}
self.create_https_server(true, None, None, false).await?;
}
}
else {
#[cfg(feature = "controller")]
{
self.create_http_server(false, None, None, false).await?;
}
#[cfg(not(feature = "controller"))]
{
self.create_http_server(true, None, None, false).await?;
}
};
#[cfg(feature = "controller")]
{
self.init_stores().await;
match &self.config.command_control {
None => {}
Some(command_control) => {
if command_control.enable {
match &self.controller {
None => {
panic!("No controller");
}
Some(c) => {
#[cfg(feature = "executor_tokio")]
{
let executor = server_executor::ServerExecutor::new();
let controller_unlocked = c.clone();
product_os_command_control::run_controller(controller_unlocked, &executor).await;
}
}
}
}
}
}
}
Ok(())
}
}
async fn force_secure_handler(request: Request<Body>) -> Response {
let uri_path = request.uri().path();
let mut url: String = String::new();
url.push_str(uri_path);
axum::response::Redirect::permanent(url.as_str()).into_response()
}