romp 0.5.2

STOMP server and WebSockets platform
Documentation
//! Context object for the message workflow through the filter chain.

use std::collections::HashMap;
use std::sync::{Arc, RwLock};

use crate::session::stomp_session::{StompSession, FLAG_ADMIN, FLAG_DOWNSTREAM};
use chrono::{Utc, DateTime};

//. TODO provide access to SERVER and CONFIG items here, rather than globals

pub struct Context {
    /// StompSession is valid for the duration of a STOMP TCP connection or WebSocket
    pub session: Arc<RwLock<StompSession>>,
    /// Request attributes, valid for the life of a single message, to avoid collisions prefix the attributes with the cargo project name e.g. "romp."
    pub attributes: HashMap<&'static str, String>,
    // TODO how to determine this if SSL is upstream
    pub is_secure: bool,
    /// flag is true if incoming request has upgraded to WebSockets
    pub is_web_sockets: bool,
    // TODO finer grained security
    /// True if this request has admin privs, from port 61616 or login credentials
    is_admin: Option<bool>,
    /// If true session is upstream
    is_downstream: Option<bool>,
    /// Cached value of time now
    timestamp: Option<DateTime<Utc>>,
}

impl Context {
    pub(crate) fn new(session: Arc<RwLock<StompSession>>) -> Context {
        Context {
            session,
            attributes: Default::default(),
            is_secure: false,
            is_web_sockets: false,
            is_admin: None,
            is_downstream: None,
            timestamp: None,
        }
    }

    // for use in tests
    pub fn mock() -> Context {
        Context {
            session: Arc::new(RwLock::new(StompSession::new())),
            attributes: Default::default(),
            is_secure: false,
            is_web_sockets: false,
            is_admin: None,
            is_downstream: None,
            timestamp: None,
        }
    }

    /// returns (cached) timestamp
    pub fn now(&mut self) -> DateTime<Utc> {
        if let None = self.timestamp {
            self.timestamp = Some(Utc::now());
        }
        self.timestamp.unwrap()
    }

    /// returns (cached) admin flag from the session
    pub fn is_admin(&mut self) -> bool {
        if let None = self.is_admin {
            self.is_admin = Some(self.session.read().unwrap().get_flag(FLAG_ADMIN));
        }
        self.is_admin.unwrap()
    }

    /// returns (cached) downstream flag from the session
    pub fn is_downstream(&mut self) -> bool {
        if let None = self.is_admin {
            self.is_downstream = Some(self.session.read().unwrap().get_flag(FLAG_DOWNSTREAM));
        }
        self.is_downstream.unwrap()
    }

    // OW horrible, have to clone a string to get it out from behind two read locks :(
    pub(crate) fn downstream(&self) -> String {
        self.session.read().unwrap().downstream_connector.as_ref().unwrap().read().unwrap().downstream.name.clone()
    }
}