70 lines
1.6 KiB
Rust
70 lines
1.6 KiB
Rust
use anyhow::{anyhow, Result};
|
|
|
|
pub struct ParsedMessage<'a> {
|
|
pub headers: Vec<ParsedHeader<'a>>,
|
|
pub body: &'a [u8],
|
|
}
|
|
|
|
impl<'a> ParsedMessage<'a> {
|
|
pub fn from_bytes(data: &'a [u8]) -> Result<Self> {
|
|
let (mut raw_headers, body) = match data.windows(4).position(|w| w == b"\r\n\r\n") {
|
|
Some(body_index) => (&data[..body_index + 2], &data[body_index + 4..]),
|
|
None => return Err(anyhow!("message body not found")),
|
|
};
|
|
let mut headers = Vec::with_capacity(128);
|
|
while !raw_headers.is_empty() {
|
|
let end_index = header_end_pos(raw_headers)?;
|
|
let h = ParsedHeader::from_bytes(&raw_headers[..end_index])?;
|
|
headers.push(h);
|
|
raw_headers = &raw_headers[end_index..];
|
|
}
|
|
headers.shrink_to_fit();
|
|
Ok(Self { headers, body })
|
|
}
|
|
}
|
|
|
|
fn is_wsp(c: u8) -> bool {
|
|
c == b' ' || c == b'\t'
|
|
}
|
|
|
|
fn header_end_pos(data: &[u8]) -> Result<usize> {
|
|
let mut ret = 0;
|
|
let max_len = data.len();
|
|
loop {
|
|
ret += data[ret..]
|
|
.windows(2)
|
|
.position(|w| w == b"\r\n")
|
|
.ok_or(anyhow!("end of header not found"))?
|
|
+ 2;
|
|
if ret >= max_len {
|
|
return Ok(max_len);
|
|
}
|
|
if !is_wsp(data[ret]) {
|
|
return Ok(ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ParsedHeader<'a> {
|
|
pub name: &'a [u8],
|
|
pub name_lower: String,
|
|
pub value: &'a [u8],
|
|
pub raw: &'a [u8],
|
|
}
|
|
|
|
impl<'a> ParsedHeader<'a> {
|
|
fn from_bytes(data: &'a [u8]) -> Result<Self> {
|
|
let colon_pos = data
|
|
.iter()
|
|
.position(|&w| w == b':')
|
|
.ok_or(anyhow!("colon not found in header"))?;
|
|
let name = &data[..colon_pos];
|
|
let value = &data[colon_pos + 1..];
|
|
Ok(Self {
|
|
name,
|
|
name_lower: String::from_utf8(name.to_vec())?.to_lowercase(),
|
|
value,
|
|
raw: data,
|
|
})
|
|
}
|
|
}
|