romp 0.5.2

STOMP server and WebSockets platform
Documentation

use log::*;

use crate::message::stomp_message::StompMessage;
use crate::workflow::context::Context;
use crate::workflow::filter::{HttpFilter, HttpError};
use crate::workflow::filter::http::get::GetFilter;
use crate::workflow::filter::http::upgrade::UpgradeFilter;
use crate::workflow::filter::http::version::VersionFilter;
use crate::workflow::filter::http::health_check::HealthCheckFilter;
use crate::workflow::filter::http::not_found::NotFoundFilter;
use crate::workflow::filter::http::closure::ClosureFilter;

// route an Http message through the Http filters

lazy_static! {

    pub static ref FILTER_CHAIN_STATIC: FilterChain = FilterChain {
        chain: build_filter_chain()
    };

}

// bespoke filter chain additions

static mut CHAIN: Vec<(Box<dyn HttpFilter + Sync>, usize)> = Vec::new();
static mut INITIALIZED: bool = false;
static DEFAULT_INSERT_POINT: usize = 3;

/// Insert a bespoke filter into the filter chain, filter supports `init()`
///
///  # Panics
///
/// if called after bootstrapping the server.
///
pub fn insert_http_filter(filter: Box<dyn HttpFilter + Sync>) {
    unsafe {
        if INITIALIZED {
            panic!("Cannot insert filters after the server is initialized");
        }
        CHAIN.push((filter, DEFAULT_INSERT_POINT));
    }
}

/// Voodoo inserts: populate the filter chain at a different point,
/// This is not safe across different versions as the default chain changes
/// However, with a bit of care it does allow hooking into the filter chain anywhere.
///
/// # Panics
///
/// if `at > FILTER_CHAIN_STATIC.len`, which is not known until runtime
pub fn insert_http_filter_at(filter: Box<dyn HttpFilter + Sync>, at: usize) {
    unsafe {
        if INITIALIZED {
            panic!("Cannot insert filters after the server is initialized");
        }
        CHAIN.push((filter, at));
    }
}

/// Insert a bespoke filter as a closure into the filter chain
///
///  # Panics
///
/// if called after bootstrapping the server.
///
pub fn insert_http(closure: fn(&mut Context, message: &mut StompMessage) -> Result<bool, HttpError>) {
    unsafe {
        if INITIALIZED {
            panic!("Cannot insert filters after the server is initialized");
        }
        insert_http_filter(Box::new(ClosureFilter::new(closure)));
    }
}

pub struct FilterChain {
    chain: Vec<Box<dyn HttpFilter + Sync>>,
}

pub fn build_filter_chain() -> Vec<Box<dyn HttpFilter + Sync>> {
    let mut route: Vec<Box<dyn HttpFilter + Sync>> = vec![
        Box::new(VersionFilter::new()),
        Box::new(UpgradeFilter::new()),
        Box::new(GetFilter::new()),
        // bespoke filters inserted here
        Box::new(HealthCheckFilter::new()),
        Box::new(NotFoundFilter::new()),
    ];

    unsafe {
        while CHAIN.len() > 0 {
            let filter = CHAIN.remove(CHAIN.len() - 1);
            route.insert(filter.1, filter.0);
        }
        INITIALIZED = true;
    }

    for filter in route.iter_mut() {
        filter.init();
        info!("{:?}.init()", filter);
    }

    debug!("HTTP init finished");
    route
}

/// route incoming HTTP messages
pub fn route_http(mut ctx: &mut Context, message: &mut StompMessage) -> Result<(), ()> {

    for f in &FILTER_CHAIN_STATIC.chain {
        match f.do_filter(&mut ctx, message) {
            Ok(false) => {
                // loop
            },
            Ok(true) => {
                break;
            },
            Err(fe) => {
                if let Some(log_message) = fe.log_message {
                    warn!("{}", log_message);
                }
                return Err(());
            }
        }
    }
    return Ok(());

}