romp 0.5.2

STOMP server and WebSockets platform
Documentation
use crate::body_parser::{BodyParser, ParserState};
use crate::message::stomp_message::StompMessage;

/// STOMP protocol push parser, parses message body.
///
/// readonly bytes pushed in from the network and copied into the message
///
/// State of the Struct

/// state of the loop
#[derive(Debug, PartialEq)]
enum State {
    Start,
    /// expecting trailing \0
    AlmostDone,
    Done,
    Error,
}

/// Body parser for binary data when we know the expected length of the data, content-len: x was provided.
pub struct FixedLengthBodyParser {
    state: State,
    expected_len: usize,
    remaining_len: usize,
}

/// reads up to content-length: xxx bytes
impl FixedLengthBodyParser {
    pub fn new() -> FixedLengthBodyParser {
        FixedLengthBodyParser {
            state: State::Start,
            expected_len: 0,
            remaining_len: 0,
        }
    }

    /// must be called before use
    pub fn reinit(&mut self, expected_len: usize) {
        self.state = State::Start;
        self.remaining_len = expected_len;
        self.expected_len = expected_len;
    }

    pub fn expected_len(&self) -> usize {
        self.expected_len
    }
}

impl BodyParser for FixedLengthBodyParser {

    fn push(&mut self, buffer: &[u8], message: &mut StompMessage) -> Result<usize, ParserState> {

        let len = buffer.len();

        // TODO might not be necessary if ([0..0]) is sane
        if State::AlmostDone == self.state {
            let zero = buffer[0];
            if zero != b'\0' {
                self.state = State::Error;
                return Err(ParserState::InvalidMessage);
            }
            else {
                self.state = State::Done;
                return Ok(1);
            }
        }
        if len < self.remaining_len {
            message.add_body(buffer);
            self.remaining_len -= len;

            return Ok(len);
        }
        else if len == self.remaining_len {
            message.add_body(buffer);
            self.remaining_len -= len;
            self.state = State::AlmostDone;

            return Ok(len);
        }
        else if len > self.remaining_len {
            let to_read = self.remaining_len;
            message.add_body(&buffer[0..to_read]);
            self.remaining_len -= to_read;

            // verify trailing \0 is present
            match buffer[to_read] {
                b'\0' => {
                    self.state = State::Done;
                    return Ok(to_read + 1);
                }
                _ => {
                    self.state = State::Error;
                    return Err(ParserState::InvalidMessage);
                }
            }
        }

        // unreachable
        return Err(ParserState::InvalidMessage);
    }

    fn is_done(&self) -> bool {
        State::Done == self.state
    }
}

#[cfg(test)]
mod tests {
    use crate::message::stomp_message::Ownership;

    use super::*;

    #[test]
    fn test_fixed_length_happy() {
        let mut p = FixedLengthBodyParser::new();
        let mut message = StompMessage::new(Ownership::Session);

        p.reinit(23);

        match p.push(b"abcdefghij", &mut message) {
            Ok(10) => { assert!(!p.is_done()); },
            Err(_) => panic!("parse error"),
            _ => panic!("unexpected")
        }
        match p.push(b"klmnopqrst", &mut message) {
            Ok(10) => { assert!(!p.is_done()); },
            Err(_) => panic!("parse error"),
            _ => panic!("unexpected")
        }
        match p.push(b"uvw\0", &mut message) {
            Ok(4) => { assert!(p.is_done()); },
            Err(_) => panic!("parse error"),
            _ => panic!("unexpected")
        }
    }


    #[test]
    fn test_fixed_length_unhappy() {
        let mut p = FixedLengthBodyParser::new();
        let mut message = StompMessage::new(Ownership::Session);

        p.reinit(22); // <- bug

        match p.push(b"abcdefghij", &mut message) {
            Ok(10) => { assert!(!p.is_done()); },
            Err(_) => panic!("parse error"),
            _ => panic!("unexpected")
        }
        match p.push(b"klmnopqrst", &mut message) {
            Ok(10) => { assert!(!p.is_done()); },
            Err(_) => panic!("parse error"),
            _ => panic!("unexpected")
        }
        match p.push(b"uvw\0", &mut message) {
            Err(ParserState::InvalidMessage) => {
                assert!(! p.is_done());
                match p.state {
                    State::Error => {
                        // all good
                    }
                    _ => {
                        panic!("unexpected parser state")
                    }
                }
            },
            Ok(4) => { panic!("unexpected") },
            _ => panic!("unexpected")
        }
    }
}