romp 0.5.2

STOMP server and WebSockets platform
Documentation
use std::sync::{Arc, RwLock};

use log::*;

use crate::message::response::get_response_error;
use crate::message::stomp_message::StompMessage;
use crate::session::stomp_session::StompSession;
use crate::workflow::context::Context;
use crate::workflow::filter::connect::ConnectFilter;
use crate::workflow::filter::disconnect::DisconnectFilter;
use crate::workflow::filter::{MessageFilter, FilterError};
use crate::workflow::filter::ack::AckFilter;
use crate::workflow::filter::log::LogFilter;
use crate::workflow::filter::receipt::ReceiptFilter;
use crate::workflow::filter::requires_auth::RequiresAuthFilter;
use crate::workflow::filter::subscribe::SubscribeFilter;
use crate::workflow::filter::send::SendFilter;
use crate::workflow::filter::admin::AdminFilter;
use crate::workflow::filter::closure::ClosureFilter;
use crate::workflow::filter::http::HttpFilter;
use crate::workflow;
// route messages through the application

lazy_static! {

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

}

// bespoke filter chain additions
static mut CHAIN: Vec<(Box<dyn MessageFilter + Sync>, usize)> = Vec::new();
static mut INITIALIZED: bool = false;
static DEFAULT_INSERT_POINT: usize = 6;
pub static PRE_SEND_INSERT_POINT: usize = 5;

/// Insert a bespoke filter into the filter chain, filter supports `init()`
///
///  # Panics
///
/// if called after bootstrapping the server.
///
pub fn insert_stomp_filter(filter: Box<dyn MessageFilter + 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_stomp_filter_at(filter: Box<dyn MessageFilter + 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_stomp(closure: fn(&mut Context, message: &mut StompMessage) -> Result<bool, FilterError>) {
    unsafe {
        if INITIALIZED {
            panic!("Cannot insert filters after the server is initialized");
        }
        insert_stomp_filter(Box::new(ClosureFilter::new(closure)));
    }
}

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

pub fn build_filter_chain() -> Vec<Box<dyn MessageFilter + Sync>> {

    let mut route: Vec<Box<dyn MessageFilter + Sync>> = vec![
        Box::new(LogFilter::new()),
        Box::new(RequiresAuthFilter::new()),
        Box::new(ConnectFilter::new()),
        Box::new(SubscribeFilter::new()),
        Box::new(AckFilter::new()),
        // pre-send insert point for bespoke filters, e.g. security filters
        Box::new(SendFilter::new()), // does not handle /virt/
        // default insert point for bespoke filters
        Box::new(HttpFilter::new()), // routes HTTP messages
        Box::new(AdminFilter::new()),
        Box::new(ReceiptFilter::new()),
        Box::new(DisconnectFilter::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!("STOMP init finished");
    route
}

/// route incoming messages
pub fn route(mut message: StompMessage, session: Arc<RwLock<StompSession>>, web_socket: bool) {

    let mut ctx: Context = Context::new(session.clone());
    ctx.is_web_sockets = web_socket;

    let mut fatal = false;
    let mut abort_message = None;

    for f in &FILTER_CHAIN_STATIC.chain {
        match f.do_filter(&mut ctx, &mut message) {
            Ok(workflow::CONTINUE) => {
                // loop
            },
            Ok(workflow::HANDLED) => {
                break;
            },
            Err(fe) => {
                if let Some(log_message) = fe.log_message {
                    warn!("{}", log_message);
                }
                abort_message = fe.reply_message;
                fatal |= fe.fatal;
                break;
            }
        }
    }

    if let Some(abort_message) = abort_message {
        session.read().unwrap().send_message(get_response_error(abort_message.as_str()));
    }

    if fatal {
        session.write().unwrap().shutdown();
    }

}