viral32111_stomp/
frame.rsuse flate2::read::GzDecoder;
use std::{error::Error, io::Read, str::from_utf8};
use crate::header::Headers;
pub struct Frame {
pub command: String,
pub headers: Vec<(String, String)>,
pub body: Option<String>,
}
pub fn create(command: &str, headers: Option<Vec<(&str, &str)>>, body: Option<&str>) -> String {
if headers.is_none() {
return format!("{}\n\n{}\0", command, body.unwrap_or(""));
}
let header_lines = headers
.unwrap()
.iter()
.map(|(name, value)| format!("{}:{}", name, value))
.collect::<Vec<String>>()
.join("\n");
return format!("{}\n{}\n\n{}\0", command, header_lines, body.unwrap_or(""));
}
pub fn parse(buffer: &mut Vec<u8>) -> Result<Option<(Frame, usize)>, Box<dyn Error>> {
if buffer.len() < 2 {
return Ok(None); }
let separator_position = buffer.windows(2).position(|bytes| bytes == [b'\n', b'\n']);
if separator_position.is_none() {
return Ok(None); }
let command_end_position = buffer.iter().position(|&byte| byte == b'\n').unwrap();
let command = from_utf8(&buffer[..command_end_position])?
.trim_end() .to_string();
let headers_start_position = command_end_position + 1;
let headers_end_position = separator_position.unwrap() + 1;
if headers_end_position > buffer.len() {
return Ok(None); }
let headers = from_utf8(&buffer[headers_start_position..headers_end_position])?
.lines()
.filter_map(|line| {
if line.is_empty() {
return None;
}
let (name, value) = line.split_once(":")?;
if name.is_empty() {
return None;
}
let name = name.to_lowercase();
let value = value
.replace("\\r", "\r")
.replace("\\n", "\n")
.replace("\\c", ":")
.replace("\\\\", "\\");
Some((name, value.to_string()))
})
.collect::<Vec<(String, String)>>();
let content_length = headers.iter().find_map(|(name, value)| {
if name.eq(Headers::ContentLength.as_str()) {
return value.parse::<usize>().ok();
}
None
});
if content_length.is_none() {
if buffer.len() < headers_end_position + 2 {
return Ok(None); }
if buffer[headers_end_position + 1] != 0x00 {
return Err("Frame not null terminated".into());
}
if buffer[headers_end_position + 2] != b'\n' {
return Err("Frame not terminated with a new line".into());
}
return Ok(Some((
Frame {
command,
headers,
body: None,
},
headers_end_position + 2, )));
}
let body_start_position = headers_end_position + 1; let body_length = content_length.unwrap();
let body_end_position = body_start_position + body_length;
if body_end_position > buffer.len() {
return Ok(None); }
let mut decompressor = GzDecoder::new(&buffer[body_start_position..body_end_position]);
let mut body = String::new();
decompressor.read_to_string(&mut body)?;
if buffer.len() < body_end_position + 2 {
return Ok(None); }
if buffer[body_end_position] != 0x00 {
return Err("Frame not null terminated".into());
}
if buffer[body_end_position + 1] != b'\n' {
return Err("Frame not terminated with a new line".into());
}
Ok(Some((
Frame {
command,
headers,
body: Some(body),
},
body_end_position + 1, )))
}