viral32111_stomp/
frame.rs1use flate2::read::GzDecoder;
2use std::{error::Error, io::Read, str::from_utf8};
3
4use crate::header::Headers;
5
6pub struct Frame {
10 pub command: String,
11 pub headers: Vec<(String, String)>,
12 pub body: Option<String>,
13}
14
15pub fn create(command: &str, headers: Option<Vec<(&str, &str)>>, body: Option<&str>) -> String {
17 if headers.is_none() {
19 return format!("{}\n\n{}\0", command, body.unwrap_or(""));
20 }
21
22 let header_lines = headers
24 .unwrap()
25 .iter()
26 .map(|(name, value)| format!("{}:{}", name, value))
27 .collect::<Vec<String>>()
28 .join("\n");
29
30 return format!("{}\n{}\n\n{}\0", command, header_lines, body.unwrap_or(""));
32}
33
34pub fn parse(buffer: &mut Vec<u8>) -> Result<Option<(Frame, usize)>, Box<dyn Error>> {
36 if buffer.len() < 2 {
40 return Ok(None); }
42
43 let separator_position = buffer.windows(2).position(|bytes| bytes == [b'\n', b'\n']);
45 if separator_position.is_none() {
46 return Ok(None); }
48
49 let command_end_position = buffer.iter().position(|&byte| byte == b'\n').unwrap();
51 let command = from_utf8(&buffer[..command_end_position])?
52 .trim_end() .to_string();
54
55 let headers_start_position = command_end_position + 1;
57 let headers_end_position = separator_position.unwrap() + 1;
58 if headers_end_position > buffer.len() {
59 return Ok(None); }
61 let headers = from_utf8(&buffer[headers_start_position..headers_end_position])?
62 .lines()
63 .filter_map(|line| {
64 if line.is_empty() {
66 return None;
67 }
68
69 let (name, value) = line.split_once(":")?;
71
72 if name.is_empty() {
74 return None;
75 }
76
77 let name = name.to_lowercase();
79
80 let value = value
82 .replace("\\r", "\r")
83 .replace("\\n", "\n")
84 .replace("\\c", ":")
85 .replace("\\\\", "\\");
86
87 Some((name, value.to_string()))
89 })
90 .collect::<Vec<(String, String)>>();
91
92 let content_length = headers.iter().find_map(|(name, value)| {
94 if name.eq(Headers::ContentLength.as_str()) {
95 return value.parse::<usize>().ok();
96 }
97
98 None
99 });
100
101 if content_length.is_none() {
103 if buffer.len() < headers_end_position + 2 {
105 return Ok(None); }
107 if buffer[headers_end_position + 1] != 0x00 {
108 return Err("Frame not null terminated".into());
109 }
110 if buffer[headers_end_position + 2] != b'\n' {
111 return Err("Frame not terminated with a new line".into());
112 }
113
114 return Ok(Some((
116 Frame {
117 command,
118 headers,
119 body: None,
120 },
121 headers_end_position + 2, )));
123 }
124
125 let body_start_position = headers_end_position + 1; let body_length = content_length.unwrap();
128 let body_end_position = body_start_position + body_length;
129 if body_end_position > buffer.len() {
130 return Ok(None); }
132 let mut decompressor = GzDecoder::new(&buffer[body_start_position..body_end_position]);
133 let mut body = String::new();
134 decompressor.read_to_string(&mut body)?;
135
136 if buffer.len() < body_end_position + 2 {
138 return Ok(None); }
140 if buffer[body_end_position] != 0x00 {
141 return Err("Frame not null terminated".into());
142 }
143 if buffer[body_end_position + 1] != b'\n' {
144 return Err("Frame not terminated with a new line".into());
145 }
146
147 Ok(Some((
149 Frame {
150 command,
151 headers,
152 body: Some(body),
153 },
154 body_end_position + 1, )))
156}