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
//! HTTPS server implementation module
//!
//! This module provides the HTTPS (TLS-secured) server implementation using Hyper,
//! Tokio, and Rustls.
//!
//! # Overview
//!
//! The HTTPS server creates a TLS-secured TCP listener that handles incoming
//! connections with full TLS handshake support. It uses Rustls for modern,
//! safe TLS implementation.
//!
//! # Features
//!
//! - **TLS 1.2/1.3**: Modern TLS protocol support via Rustls
//! - **ALPN**: Application-Layer Protocol Negotiation for HTTP/2 and HTTP/1.1
//! - **Connection Info**: Optional extraction of client connection information
//! - **Certificate Flexibility**: Support for self-signed and CA-signed certificates
//!
//! # ALPN Configuration
//!
//! The server is configured to prefer HTTP/2 over HTTP/1.1 via ALPN negotiation.
//!
//! # Requirements
//!
//! This module requires both `tls` and `executor_tokio` features to be enabled.

#![cfg(all(feature = "tls", feature = "executor_tokio"))]

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

#[cfg(feature = "framework_axum")]
use core::error::Error;
use std::sync;

#[cfg(feature = "framework_axum")]
use product_os_net::SocketAddr;
use rustls::ServerConfig;
#[cfg(feature = "framework_axum")]
use hyper::service::Service;
#[cfg(feature = "framework_axum")]
use product_os_async_executor::{Executor, TokioExecutor};
#[cfg(feature = "framework_axum")]
use product_os_router::{Request, Service as RouterService, Router};
use product_os_security::certificates::{
    Certificates, CertificateDer, CertificatesFormat, PrivateKeyDer, PrivatePkcs8KeyDer,
};
use rustls::pki_types::pem::PemObject;

/// Creates an HTTPS server bound to the given socket address using the Tokio runtime.
///
/// # 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.
#[cfg(feature = "framework_axum")]
pub async fn create_https_tokio_service(socket_address: SocketAddr, certs: Option<Certificates>, router: Router, with_connect_info: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
    let tls_config = 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().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;
                        }
                        Err(e) => tracing::error!("Failed to get executor for connection from {}: {:?}", addr, e)
                    }
                });
            }
        }
        false => {
            let tower_service = router;

            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;
                        }
                        Err(e) => tracing::error!("Failed to get executor for connection from {}: {:?}", addr, e)
                    }
                });
            }
        }
    }
}

/// Creates a Rustls `ServerConfig` from optional certificates.
///
/// If no certificates are provided, generates a self-signed certificate for "localhost".
///
/// # Errors
///
/// Returns an error if the certificate or key is invalid.
pub fn create_rustls_config(certs: Option<Certificates>) -> Result<sync::Arc<ServerConfig>, Box<dyn std::error::Error + Send + Sync>> {
    let cert_data = certs.unwrap_or_else(|| {
        Certificates::new_self_signed(
            vec![],
            Some(vec![String::from("localhost"), String::from("127.0.0.1")]),
            None,
            None,
            None,
            None,
        )
    });

    let private_key = load_private_key(&cert_data)?;
    let certificates = load_certificates(&cert_data)?;

    let mut config = ServerConfig::builder()
        .with_no_client_auth()
        .with_single_cert(certificates, private_key)
        .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) })?;

    config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];

    Ok(sync::Arc::new(config))
}

fn load_private_key(
    cert_data: &Certificates,
) -> Result<PrivateKeyDer<'static>, Box<dyn std::error::Error + Send + Sync>> {
    match cert_data.format {
        CertificatesFormat::Pem => PrivateKeyDer::from_pem_slice(&cert_data.private)
            .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) }),
        CertificatesFormat::Pkcs8 | CertificatesFormat::Der | CertificatesFormat::Raw => Ok(
            PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(cert_data.private.clone())),
        ),
    }
}

fn load_certificates(
    cert_data: &Certificates,
) -> Result<Vec<CertificateDer<'static>>, Box<dyn std::error::Error + Send + Sync>> {
    match cert_data.format {
        CertificatesFormat::Pem => cert_data
            .certificates
            .iter()
            .map(|cert| {
                CertificateDer::from_pem_slice(cert)
                    .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) })
            })
            .collect(),
        CertificatesFormat::Pkcs8 | CertificatesFormat::Der | CertificatesFormat::Raw => {
            Ok(cert_data
                .certificates
                .iter()
                .cloned()
                .map(CertificateDer::from)
                .collect())
        }
    }
}