#[cfg(feature = "tls")]
mod https_server;
mod logging;
mod http_server;
#[cfg(not(feature = "no-security-headers"))]
mod csp;
#[cfg(feature = "controller")]
mod command_handler;
#[cfg(feature = "controller")]
mod feature_handler;
mod simple;
mod dual_server;
use std::collections::HashMap;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::sync::Arc;
use parking_lot::Mutex;
pub use axum::{
body::{Body, BoxBody, Bytes, HttpBody},
BoxError,
handler::Handler,
http::Request,
http::StatusCode,
http::uri::Scheme,
Json,
response::Response,
extract::Extension
};
pub use hyper::{
upgrade,
client::conn
};
#[cfg(feature = "tls")]
pub use axum_server::{
service::*
};
pub use product_os_router::{ Layer, Service, ServiceExt, service_fn };
pub use product_os_router::Router;
pub use product_os_router::Method;
pub use product_os_router::ProductOSRouter;
pub use product_os_router::ServiceBuilder;
#[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 };
#[cfg(feature = "ws")]
pub use axum::{
extract::*,
};
use axum::response::{IntoResponse, Redirect};
#[cfg(feature = "sse")]
pub use axum::response::sse::{Event, Sse};
#[cfg(feature="tls")]
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 product_os_configuration::Configuration;
use product_os_security::certificates::Certificates;
use tokio::runtime::Handle;
use tracing::Level;
#[cfg(feature = "cors")]
use tower_http::cors::CorsLayer;
#[cfg(feature = "oidc")]
use product_os_oauth_oidc::ProductOSOIDCServer;
#[cfg(feature = "authentication")]
use product_os_authentication::ProductOSAuthentication;
#[cfg(feature = "content")]
use product_os_content::ProductOSContentPlatform;
pub use crate::simple::ProductOSServerSimple;
pub struct ProductOSServer {
router: ProductOSRouter<()>,
config: product_os_configuration::Configuration,
certificates: Certificates,
#[cfg(feature = "controller")]
controller: Option<Arc<Mutex<ProductOSController>>>
}
impl ProductOSServer {
#[tokio::main]
pub async fn new_with_config_sync(config: product_os_configuration::Configuration) -> Self {
ProductOSServer::new_with_config(config).await
}
pub async fn new_with_config(config: product_os_configuration::Configuration) -> Self {
logging::set_global_logger(logging::define_logging(config.log_level()));
tracing::info!("Log Level: {}", config.log_level());
let mut router = ProductOSRouter::new(());
let certificates = match config.certificate.clone() {
None => {
tracing::info!("Generating self-signed certificate");
product_os_security::certificates::Certificates::new(Some(vec!(config.get_host())))
},
Some(cert_config) => {
tracing::info!("Using configuration file for certificate");
product_os_security::certificates::Certificates::new_from_file(cert_config.cert_file, cert_config.key_file)
}
};
match &config.security {
None => {}
Some(security) => {
if security.enable {
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".to_string());
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(), key_value_store.clone(), 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() -> Self {
let config = Configuration::new();
let mut router = ProductOSRouter::new(());
let log_level = config.log_level();
logging::set_global_logger(logging::define_logging(log_level));
tracing::info!("Log Level: {}", config.log_level());
let certificates = {
tracing::info!("Generating self-signed certificate on localhost");
Certificates::new(Some(vec!(config.get_host())))
};
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");
Certificates::new(Some(vec!(self.config.get_host())))
},
Some(cert_config) => {
tracing::info!("Using configuration file for certificate");
Certificates::new_from_file(cert_config.cert_file, cert_config.key_file)
}
};
}
pub fn set_security(&mut self, security: Option<product_os_configuration::Security>) {
match security {
None => {}
Some(security) => {
if security.enable {
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(all(feature = "oidc", feature = "controller"))]
#[tokio::main]
pub async fn add_oidc(&mut self, identifier: String, base: Option<String>) {
let base_path = match base {
None => "/auth".to_string(),
Some(p) => p
};
let relational_store = self.get_relational_store();
match relational_store {
Ok(rs) => {
match &self.controller {
None => {
panic!("OIDC failed to add to server: no controller");
}
Some(c) => {
let controller_unlocked = c.clone();
let mut controller = controller_unlocked.lock();
let oidc_server = ProductOSOIDCServer::new(identifier, controller.get_configuration().get_oidc_configuration(), rs.clone());
let oidc_server_arc = Arc::new(oidc_server);
match product_os_oauth_oidc::setup_oidc_store(rs.clone()).await {
Ok(_) => tracing::info!("Database setup for OIDC complete"),
Err(e) => {
tracing::error!("Error setting up database for OIDC: {:?}", e);
panic!("Failed to complete database setup for OIDC");
}
}
controller.add_feature(oidc_server_arc.clone(), base_path, &mut self.router).await;
tracing::info!("OIDC successfully added to server");
}
}
}
Err(e) => panic!("Failed to lock store for adding oidc: {:?}", e)
}
}
#[cfg(all(feature = "authentication", feature = "controller"))]
#[tokio::main]
pub async fn add_authentication(&mut self, base: Option<String>, oidc_base: Option<String>) {
let base_path = match base {
None => "/authentication".to_string(),
Some(p) => p
};
let oidc_base_path = match oidc_base {
None => "/oidc".to_string(),
Some(p) => p
};
let relational_store = self.get_relational_store();
match relational_store {
Ok(rs) => {
match &self.controller {
None => {
panic!("Failed to complete database setup for user authentication: no controller");
}
Some(c) => {
let controller_unlocked = c.clone();
let mut controller = controller_unlocked.lock();
let authentication = ProductOSAuthentication::new(&controller.get_configuration(), rs.clone(), Some(controller_unlocked.clone()), oidc_base_path);
let auth_arc = Arc::new(authentication);
match product_os_authentication::setup_auth_store(rs.clone(), controller.get_configuration().get_authentication_scopes(), auth_arc.clone()).await {
Ok(_) => tracing::info!("Database setup for user authentication complete"),
Err(e) => {
tracing::error!("Error setting up database for user authentication: {:?}", e);
panic!("Failed to complete database setup for user authentication");
}
}
controller.add_feature(auth_arc.clone(), base_path, &mut self.router).await;
tracing::info!("Authentication successfully added to server");
}
}
}
Err(e) => panic!("Failed to lock store for adding user authentication: {:?}", e)
}
}
#[cfg(all(feature = "content", feature = "controller"))]
#[tokio::main]
pub async fn add_content_platform(&mut self, base: Option<String>, auth_base: Option<String>) {
let base_path = match base {
None => "".to_string(),
Some(p) => p
};
let auth_base_path = match auth_base {
None => "/authentication".to_string(),
Some(p) => p
};
let relational_store = self.get_relational_store();
match relational_store {
Ok(rs) => {
let key_value_store = self.get_key_value_store();
match key_value_store {
Ok(kvs) => {
match &self.controller {
None => {
panic!("Failed to complete database setup for content platform: no controller");
}
Some(c) => {
let controller_unlocked = c.clone();
let mut controller = controller_unlocked.lock();
let content_platform = ProductOSContentPlatform::new(
rs.clone(),
Some(kvs.clone()),
Some(controller_unlocked.clone()),
auth_base_path);
let content_platform_arc = Arc::new(content_platform);
match product_os_content::setup_content_store(rs.clone()).await {
Ok(_) => tracing::info!("Database setup for content platform complete"),
Err(e) => {
tracing::error!("Error setting up database for content platform: {:?}", e);
panic!("Failed to complete database setup for content platform");
}
}
controller.add_feature(content_platform_arc.clone(), base_path, &mut self.router).await;
tracing::info!("Content Platform successfully added to server");
}
}
}
Err(e) => panic!("Failed to lock store for adding content platform: {:?}", e)
}
}
Err(e) => panic!("Failed to lock store for adding content platform: {:?}", e)
}
}
#[cfg(feature = "controller")]
#[tokio::main]
pub async fn add_feature_sync(&mut self, feature_arc: Arc<dyn product_os_capabilities::Feature>, base_path: Option<String>) {
self.add_feature(feature_arc, base_path).await
}
#[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")]
#[tokio::main]
pub async fn add_feature_mut_sync(&mut self, feature_arc_mut: Arc<Mutex<dyn product_os_capabilities::Feature>>, base_path: Option<String>) {
self.add_feature_mut(feature_arc_mut, base_path).await
}
#[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());
}
}
}
#[cfg(feature = "controller")]
#[tokio::main]
pub async fn add_service_sync(&mut self, service_arc: Arc<dyn product_os_capabilities::Service>) {
self.add_service(service_arc).await
}
#[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")]
#[tokio::main]
pub async fn add_service_mut_sync(&mut self, service_arc_mut: Arc<Mutex<dyn product_os_capabilities::Service>>) {
self.add_service_mut(service_arc_mut).await
}
#[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(all(feature = "controller", feature = "support_feature_service"))]
#[tokio::main]
pub async fn add_feature_service_sync(&mut self, feature_service_arc: Arc<dyn product_os_capabilities::FeatureService>, base_path: Option<String>) {
self.add_feature_service(feature_service_arc.clone(), base_path);
}
#[cfg(all(feature = "controller", feature = "support_feature_service"))]
pub async fn add_feature_service(&mut self, feature_service_arc: Arc<dyn product_os_capabilities::FeatureService>, base_path: Option<String>) {
self.add_feature(feature_service_arc.clone(), base_path);
self.add_service(feature_service_arc);
}
#[cfg(all(feature = "controller", feature = "support_feature_service"))]
#[tokio::main]
pub async fn add_feature_service_mut_sync(&mut self, feature_service_arc_mut: Arc<Mutex<dyn product_os_capabilities::FeatureService>>, base_path: Option<String>) {
self.add_feature_service_mut(feature_service_arc_mut.clone(), base_path);
}
#[cfg(all(feature = "controller", feature = "support_feature_service"))]
pub async fn add_feature_service_mut(&mut self, feature_service_arc_mut: Arc<Mutex<dyn product_os_capabilities::FeatureService>>, base_path: Option<String>) {
self.add_feature_mut(feature_service_arc_mut.clone(), base_path);
self.add_service_mut(feature_service_arc_mut);
}
#[cfg(feature = "controller")]
#[tokio::main]
pub async fn add_handler_service_sync(&mut self, name: String, path: String, table_name: String, identifier_name: String) {
self.add_handler_service(name, path, table_name, identifier_name).await
}
#[cfg(feature = "controller")]
pub async fn add_handler_service(&mut self, name: String, path: String, table_name: String, identifier_name: String) {
match &self.controller {
None => {
panic!("Handler Service {} failed to add to server: no controller", name);
}
Some(c) => {
let controller_unlocked = c.clone();
let handler = product_os_service_handler::ProductOSServiceHandler::new(name, path, table_name, identifier_name, controller_unlocked);
let handler_name = handler.get_identifier_name();
handler.load_service(&mut self.router).await;
tracing::info!("Handler Service {} successfully added to server", handler_name);
}
}
}
#[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 {
&mut self.router
}
pub fn add_route(&mut self, path: &str, service_handler: product_os_router::MethodRouter) {
self.router.add_route(path, service_handler);
}
pub fn set_fallback(&mut self, service_handler: product_os_router::MethodRouter) {
self.router.set_fallback(service_handler);
}
pub fn add_get<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, (), Body>,
T: 'static
{
self.router.add_get(path, handler);
}
pub fn add_post<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, (), Body>,
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, (), Body>,
T: 'static
{
self.router.add_handler(path, method, handler);
}
pub fn set_fallback_handler<H, T>(&mut self, handler: H)
where
H: Handler<T, (), Body>,
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, (), Body>,
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, (), Body>,
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, (), Body>,
T: 'static
{
self.add_get(path, sse_handler);
}
pub fn add_handlers<H, T>(&mut self, path: &str, handlers: HashMap<Method, H>)
where
H: Handler<T, (), Body>,
T: 'static
{
self.router.add_handlers(path, handlers);
}
#[cfg(feature = "cors")]
pub fn add_cors_handlers<H, T>(&mut self, path: &str, handlers: HashMap<Method, H>)
where
H: Handler<T, (), Body>,
T: 'static
{
self.router.add_cors_handlers(path, handlers);
}
pub fn add_middleware<L, NewResBody>(&mut self, middleware: L)
where
L: Layer<product_os_router::Route<Body>> + 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) {
self.router = router;
}
pub async fn create_dual_service_server(&mut self, 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>> {
#[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);
match dual_server::create_dual_service(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 {
server
.set_upgrade(force_secure)
.serve(router.into_make_service())
.await.unwrap();
}
}
else {
if with_connect_info {
tokio::spawn(async move {
server
.set_upgrade(force_secure)
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
tokio::spawn(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);
match https_server::create_https_service(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 {
server.serve(router.into_make_service())
.await.unwrap();
}
}
else {
if with_connect_info {
tokio::spawn(async move {
server.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
tokio::spawn(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(address) {
Ok(server) => {
tracing::info!("HTTP 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 {
server.serve(router.into_make_service())
.await.unwrap();
}
}
else {
if with_connect_info {
tokio::spawn(async move {
server.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
tokio::spawn(async move {
server.serve(router.into_make_service())
.await.unwrap();
});
}
}
},
Err(e) => tracing::error!("Error starting HTTP server: {}", e)
}
Ok(())
}
#[cfg(feature = "custom")]
pub async fn create_dual_server_custom(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, force_secure: bool, service_function: impl FnMut<(Request<Body>,), Output = impl Future<Output = Result<Response, Infallible>> + Send + 'static> + Send + Clone + 'static) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(feature = "tls")]
{
let address = self.config.socket_address(custom_port);
match dual_server::create_dual_service(address, Some(self.certificates.to_owned())) {
Ok(server) => {
tracing::info!("HTTPS custom server listening on {}", address);
let service = product_os_router::service_fn(service_function);
let make_service = product_os_router::Shared::new(service);
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 {
server
.set_upgrade(force_secure)
.serve(router.into_make_service())
.await.unwrap();
}
}
else {
if with_connect_info {
tokio::spawn(async move {
server
.set_upgrade(force_secure)
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
tokio::spawn(async move {
server
.set_upgrade(force_secure)
.serve(router.into_make_service())
.await.unwrap();
});
}
}
},
Err(e) => tracing::error!("Error starting custom 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(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, service_function: impl FnMut<(Request<Body>,), Output = impl Future<Output = Result<Response, Infallible>> + Send + 'static> + Send + Clone + 'static) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(feature = "tls")]
{
let address = self.config.socket_address(custom_port);
match https_server::create_https_service(address, Some(self.certificates.to_owned())) {
Ok(server) => {
tracing::info!("HTTPS custom server listening on {}", address);
let service = product_os_router::service_fn(service_function);
let make_service = product_os_router::Shared::new(service);
if serve_on_main_thread {
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 {
if with_connect_info {
tokio::spawn(async move {
server.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
tokio::spawn(async move {
server.serve(router.into_make_service())
.await.unwrap();
});
}
}
},
Err(e) => tracing::error!("Error starting custom 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(&mut self, serve_on_main_thread: bool, custom_port: Option<u16>, service_function: impl FnMut<(Request<Body>,), Output = impl Future<Output = Result<Response, Infallible>> + Send + 'static> + Send + Clone + 'static) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
{
let address = self.config.socket_address(custom_port);
match http_server::create_http_service(address) {
Ok(server) => {
tracing::info!("HTTP custom server listening on {}", address);
let service = product_os_router::service_fn(service_function);
let make_service = Shared::new(service);
if serve_on_main_thread {
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 {
if with_connect_info {
tokio::spawn(async move {
server.serve(router.into_make_service_with_connect_info::<SocketAddr>())
.await.unwrap();
});
}
else {
tokio::spawn(async move {
server.serve(router.into_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")]
#[tokio::main]
pub async fn get_relational_store_sync(&mut self) -> Result<Arc<ProductOSRelationalStore>, ()> {
self.get_relational_store()
}
#[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(())
}
}
}
}
#[cfg(feature = "controller")]
#[tokio::main]
pub async fn get_key_value_store_sync(&mut self) -> Result<Arc<ProductOSKeyValueStore>, ()> {
self.get_key_value_store()
}
pub fn get_config(&self) -> Configuration {
self.config.clone()
}
pub fn update_config(&mut self, config: Configuration) {
self.config = config;
}
pub fn get_base_url(&self) -> url::Url {
self.config.url_address().to_owned()
}
#[cfg(feature = "custom")]
#[tokio::main]
pub async fn start_custom_sync(&mut self, service_function: impl FnMut<(Request<Body>,), Output = impl Future<Output = Result<Response, Infallible>> + Send + 'static> + Send + Clone + 'static) -> Result<Handle, Box<dyn std::error::Error + Send + Sync>>
{
self.start_custom(service_function).await
}
#[cfg(feature = "custom")]
pub async fn start_custom(&mut self, service_function: impl FnMut<(Request<Body>,), Output = impl Future<Output = Result<Response, Infallible>> + Send + 'static> + Send + Clone + 'static) -> Result<Handle, 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_force_secure {
self.create_http_server_custom(false, Some(self.config.network.insecure_port), service_function.clone()).await?;
}
else {
self.create_http_server_custom(false, Some(self.config.network.insecure_port), service_function.clone()).await?;
}
}
self.create_https_server_custom(false, None, service_function, false).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, 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) => {
let controller_unlocked = c.clone();
product_os_command_control::run_controller(controller_unlocked).await;
}
}
}
}
}
}
let handle = Handle::current();
Ok(handle)
}
#[tokio::main]
pub async fn start_sync(&mut self) -> Result<Handle, Box<dyn std::error::Error + Send + Sync>> {
self.start().await
}
pub async fn start(&mut self) -> Result<Handle, 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 {
self.create_dual_service_server(false, Some(self.config.insecure_port()), None, false, self.config.network.insecure_force_secure).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) => {
let controller_unlocked = c.clone();
product_os_command_control::run_controller(controller_unlocked).await;
}
}
}
}
}
}
let handle = Handle::current();
Ok(handle)
}
}
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);
Redirect::permanent(url.as_str()).into_response()
}