product-os-server 0.0.55

Product OS : Server provides a full functioning advanced server capable of acting as a web server, command and control distributed network, authentication server, crawling server and more. Fully featured with high level of flexibility.
Documentation
//! TLS server implementation module (dual protocol naming retained for API compatibility)
//!
//! This module provides a TLS-secured server implementation using Hyper, Tokio, and Rustls.
//! Despite the module name, the current implementation serves TLS connections only.
//! All incoming connections are handled through a TLS acceptor.
//!
//! # Overview
//!
//! The server creates a TLS-secured TCP listener that handles incoming connections
//! with full TLS handshake support.
//!
//! # Features
//!
//! - **TLS 1.2/1.3**: Modern TLS protocol support via Rustls
//! - **Connection Info**: Optional extraction of client connection information
//! - **HTTP Upgrade Support**: Supports protocol upgrades (e.g., WebSocket over TLS)
//!
//! # Note
//!
//! The function and module are named "dual" for historical reasons. The implementation
//! wraps all connections in TLS. For actual HTTP (non-TLS) fallback, use a separate
//! HTTP server on a different port via `create_http_tokio_service`.
//!
//! # Requirements
//!
//! This module requires the `dual_server` feature (which implies `tls` and `tokio`).

#![cfg(feature="dual_server")]

use std::prelude::v1::*;

use core::error::Error;

use product_os_net::SocketAddr;
use hyper::service::Service;
use product_os_async_executor::{Executor, TokioExecutor};
use product_os_router::{Request, Service as RouterService, Router, UpgradeHttpLayer, Protocol};
use product_os_security::certificates::Certificates;

/// Creates a TLS Tokio-based server service bound to the given socket address.
///
/// All connections are served over TLS. The module name "dual" is retained for
/// backwards compatibility but does not imply HTTP/HTTPS protocol detection.
///
/// # Arguments
///
/// * `socket_address` - The address and port to bind to
/// * `certs` - Optional TLS certificates (will generate self-signed if None)
/// * `router` - The axum router to handle requests
/// * `with_connect_info` - Whether to extract connection info for handlers
///
/// # Errors
///
/// Returns an error if TLS configuration fails or the TCP listener cannot bind.
pub async fn create_dual_tokio_service(socket_address: SocketAddr, certs: Option<Certificates>, router: Router, with_connect_info: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
    let tls_config = crate::https_server::create_rustls_config(certs)?;
    tracing::info!("Starting HTTPS server listening on {}", socket_address);
    let tls_acceptor = tokio_rustls::TlsAcceptor::from(tls_config);

    let listener = tokio::net::TcpListener::bind(socket_address).await
        .map_err(|e| -> Box<dyn Error + Send + Sync> { Box::new(e) })?;

    match with_connect_info {
        true => {
            let router_service = router.clone()
                .layer(UpgradeHttpLayer {})
                .layer(tower::util::MapRequestLayer::new(|mut req: Request<hyper::body::Incoming>| {
                    req.extensions_mut().insert(Protocol::Tls);
                    req
                }))
                .into_make_service_with_connect_info::<SocketAddr>();
            let make_service = hyper_util::service::TowerToHyperService::new(router_service);
            let tower_service = make_service.call(socket_address).await
                .map_err(|e| -> Box<dyn Error + Send + Sync> { Box::new(e) })?;

            loop {
                let (cnx, addr) = match listener.accept().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        tracing::error!("Failed to accept connection: {:?}", e);
                        continue;
                    }
                };
                let tls_acceptor = tls_acceptor.clone();
                let tower_service = tower_service.clone();

                tokio::spawn(async move {
                    let stream = match tls_acceptor.accept(cnx).await {
                        Ok(s) => s,
                        Err(e) => {
                            tracing::error!("error during tls handshake connection from {}: {:?}", addr, e);
                            return;
                        }
                    };

                    let stream = hyper_util::rt::TokioIo::new(stream);

                    let hyper_service = hyper::service::service_fn(move |request: Request<hyper::body::Incoming>| {
                        tower_service.clone().call(request)
                    });

                    match TokioExecutor::context_sync() {
                        Ok(executor) => {
                            let _ret = hyper_util::server::conn::auto::Builder::new(executor)
                                .serve_connection_with_upgrades(stream, hyper_service)
                                .await;
                            if let Err(err) = _ret {
                                tracing::warn!("error serving connection from {}: {}", addr, err);
                            }
                        }
                        Err(e) => tracing::error!("Failed to get executor for connection from {}: {:?}", addr, e)
                    }
                });
            }
        }
        false => {
            let tower_service = router
                .layer(UpgradeHttpLayer {})
                .layer(tower::util::MapRequestLayer::new(|mut req: Request<hyper::body::Incoming>| {
                    req.extensions_mut().insert(Protocol::Tls);
                    req
                }));

            loop {
                let (cnx, addr) = match listener.accept().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        tracing::error!("Failed to accept connection: {:?}", e);
                        continue;
                    }
                };
                let tls_acceptor = tls_acceptor.clone();
                let tower_service = tower_service.clone();

                tokio::spawn(async move {
                    let Ok(stream) = tls_acceptor.accept(cnx).await else {
                        tracing::error!("error during tls handshake connection from {}", addr);
                        return;
                    };

                    let stream = hyper_util::rt::TokioIo::new(stream);

                    let hyper_service = hyper::service::service_fn(move |request: Request<hyper::body::Incoming>| {
                        tower_service.clone().call(request)
                    });

                    match TokioExecutor::context_sync() {
                        Ok(executor) => {
                            let _ret = hyper_util::server::conn::auto::Builder::new(executor)
                                .serve_connection_with_upgrades(stream, hyper_service)
                                .await;
                            if let Err(err) = _ret {
                                tracing::warn!("error serving connection from {}: {}", addr, err);
                            }
                        }
                        Err(e) => tracing::error!("Failed to get executor for connection from {}: {:?}", addr, e)
                    }
                });
            }
        }
    }
}