romp 0.5.2

STOMP server and WebSockets platform
Documentation
//! `heart-beat` functions.

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

use log::*;

use crate::init::CONFIG;

use crate::message::stomp_message::{Header, StompMessage};
use crate::session::stomp_session::StompSession;

// contains code for heart-beats, (keep-alive pings)

pub fn get_request_header(message: &StompMessage) -> Option<&Header> {
    for hdr in message.headers() {
        if hdr.name.eq("heart-beat") {
            return Some(hdr);
        }
    }
    None
}

/// get the header value to add to a CONNECTED reply
pub fn get_response_header(session: &Arc<RwLock<StompSession>>) -> Header {
    let session = session.read().unwrap();
    Header {
        name: String::from("heart-beat"),
        value: format!("{},{}", session.heart_beat_write, session.heart_beat_read)
    }
}

/// parse header and update the StompSession with expected heart-beat config
pub fn parse_header(hdr: &Header, session: &Arc<RwLock<StompSession>>) -> Result<(), &'static str> {

    // Rust is sooo ugly sometimes this parses "x,y"

    let comma_idx = (*hdr.value).find(',');
    match comma_idx {
        Some(comma_idx) => {
            let (cx, cy) = (*hdr.value).split_at(comma_idx);
            if cy.len() < 2 {
                return Err("ecg syntax");
            }
            // atoi(cx, cy)
            match (cx.parse::<u32>(), cy[1..].parse::<u32>()) {
                (Ok(cx), Ok(cy)) => {
                    let mut s = session.write().unwrap();

                    if cx == 0 {
                        s.heart_beat_read = 0;
                    } else if cx > CONFIG.heart_beat_read {
                        // error client can only guarantee beats every cx but we need beats every  CONFIG.heart_beat_read
                        debug!("ecg incompat");
                        return Err("ecg incompat");
                    } else {
                        s.heart_beat_read = CONFIG.heart_beat_read;
                    }

                    if cy == 0 || CONFIG.heart_beat_write_max == 0 {
                        s.heart_beat_write = 0;
                    } else if cy < CONFIG.heart_beat_write_min {
                        debug!("ecg incompat");
                        return Err("ecg incompat");
                    } else if cy > CONFIG.heart_beat_write_max {
                        s.heart_beat_write = CONFIG.heart_beat_write_max;
                    } else {
                        s.heart_beat_write = cy;
                    }

                    return Ok(());
                },
                _ => return Err("ecg syntax"),
            }
        },
        _ => {
            return Err("ecg syntax");
        },
    }
}



#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_parse_header() {
        let session = Arc::new(RwLock::new(StompSession::new()));
        let hdr = Header::new("heart-beat", "60000,60000");
        match parse_header(&hdr, &session) {
            Ok(()) => {},
            Err(e) => panic!("parse failed '{}'", e),
        }

        let session = Arc::new(RwLock::new(StompSession::new()));
        let hdr = Header::new("heart-beat", "60000,");
        match parse_header(&hdr, &session) {
            Ok(()) => panic!("expected parse fail"),
            Err(_e) => {},
        }

        let session = Arc::new(RwLock::new(StompSession::new()));
        let hdr = Header::new("heart-beat", ",60000");
        match parse_header(&hdr, &session) {
            Ok(()) => panic!("expected parse fail"),
            Err(_e) => {},
        }

        let session = Arc::new(RwLock::new(StompSession::new()));
        let hdr = Header::new("heart-beat", "60000,600000");
        match parse_header(&hdr, &session) {
            Ok(()) => {
                let session = session.read().unwrap();
                assert_eq!(session.heart_beat_read, 120000); // N.B. heart_beat_read set to server config, client will send us more often than this, thats OK
                assert_eq!(session.heart_beat_write, CONFIG.heart_beat_write_max);
            },
            Err(_e) => {},
        }

        let session = Arc::new(RwLock::new(StompSession::new()));
        let hdr = Header::new("heart-beat", "60000,6000");
        match parse_header(&hdr, &session) {
            Ok(()) => {
                let session = session.read().unwrap();
                assert_eq!(session.heart_beat_read, 120000);
                assert_eq!(session.heart_beat_write, CONFIG.heart_beat_write_min);
            },
            Err(_e) => {},
        }

        let session = Arc::new(RwLock::new(StompSession::new()));
        let hdr = Header::new("heart-beat", "0,0");
        match parse_header(&hdr, &session) {
            Ok(()) => {
                let session = session.read().unwrap();
                assert_eq!(session.heart_beat_read, 0);
                assert_eq!(session.heart_beat_write, 0);
            },
            Err(_e) => {},
        }
    }

    #[test]
    fn test_parse_header_read_range() {
        let session = Arc::new(RwLock::new(StompSession::new()));
        let hdr = Header::new("heart-beat", "120001,60000");
        match parse_header(&hdr, &session) {
            Ok(()) => panic!("expect read out of range"),
            Err(_e) => {},
        }
    }
}