use crate::error::ParseError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HeaderField<'a> {
pub name: &'a [u8],
pub value: &'a [u8],
}
impl<'a> HeaderField<'a> {
pub fn parse(
input: &'a [u8]
) -> Result<(alloc::vec::Vec<(alloc::vec::Vec<u8>, alloc::vec::Vec<u8>)>, &'a [u8]), ParseError> {
use alloc::vec::Vec;
let mut headers = Vec::new();
let mut remaining = input;
if !remaining.is_empty() {
let first_byte = remaining.first().copied();
if first_byte == Some(b' ') || first_byte == Some(b'\t') {
return Err(ParseError::WhitespaceBeforeHeaders);
}
}
loop {
let byte0 = remaining.first().copied();
let byte1 = remaining.get(1).copied();
if remaining.len() >= 2 && byte0 == Some(b'\r') && byte1 == Some(b'\n') {
remaining = remaining.get(2..).ok_or(ParseError::MissingCrlf)?;
break;
}
if !remaining.is_empty() && byte0 == Some(b'\n') {
remaining = remaining.get(1..).ok_or(ParseError::MissingCrlf)?;
break;
}
let Some(colon_pos) = remaining.iter().position(|&b| b == b':') else {
return Err(ParseError::InvalidHeaderName);
};
if colon_pos == 0 {
return Err(ParseError::InvalidHeaderName);
}
let name = remaining
.get(..colon_pos)
.ok_or(ParseError::InvalidHeaderName)?;
if name.iter().any(|&b| b == b' ' || b == b'\t') {
return Err(ParseError::InvalidHeaderName);
}
for &b in name {
if !is_token_char(b) {
return Err(ParseError::InvalidHeaderName);
}
}
let mut value_bytes = Vec::new();
remaining = remaining
.get(colon_pos + 1..)
.ok_or(ParseError::InvalidHeaderValue)?;
while !remaining.is_empty() {
let first_byte = remaining.first().copied();
if first_byte == Some(b' ') || first_byte == Some(b'\t') {
remaining = remaining.get(1..).ok_or(ParseError::InvalidHeaderValue)?;
} else {
break;
}
}
loop {
let mut line_end = 0;
while line_end < remaining.len() {
let byte_at_end = remaining.get(line_end).copied();
if byte_at_end == Some(b'\r') || byte_at_end == Some(b'\n') {
break;
}
line_end += 1;
}
if line_end > 0 {
let line_value = remaining
.get(..line_end)
.ok_or(ParseError::InvalidHeaderValue)?;
value_bytes.extend_from_slice(line_value);
}
remaining = remaining.get(line_end..).ok_or(ParseError::MissingCrlf)?;
let next_byte0 = remaining.first().copied();
let next_byte1 = remaining.get(1).copied();
let next_byte2 = remaining.get(2).copied();
if remaining.len() >= 3
&& next_byte0 == Some(b'\r')
&& next_byte1 == Some(b'\n')
&& (next_byte2 == Some(b' ') || next_byte2 == Some(b'\t'))
{
value_bytes.push(b' ');
remaining = remaining.get(2..).ok_or(ParseError::MissingCrlf)?;
while !remaining.is_empty() {
let ws_byte = remaining.first().copied();
if ws_byte == Some(b' ') || ws_byte == Some(b'\t') {
remaining = remaining.get(1..).ok_or(ParseError::InvalidHeaderValue)?;
} else {
break;
}
}
continue; }
if remaining.len() >= 2 && next_byte0 == Some(b'\n') && (next_byte1 == Some(b' ') || next_byte1 == Some(b'\t'))
{
value_bytes.push(b' ');
remaining = remaining.get(1..).ok_or(ParseError::MissingCrlf)?;
while !remaining.is_empty() {
let ws_byte = remaining.first().copied();
if ws_byte == Some(b' ') || ws_byte == Some(b'\t') {
remaining = remaining.get(1..).ok_or(ParseError::InvalidHeaderValue)?;
} else {
break;
}
}
continue; }
if remaining.len() >= 2 && next_byte0 == Some(b'\r') && next_byte1 == Some(b'\n') {
remaining = remaining.get(2..).ok_or(ParseError::MissingCrlf)?;
break;
}
if !remaining.is_empty() && next_byte0 == Some(b'\n') {
remaining = remaining.get(1..).ok_or(ParseError::MissingCrlf)?;
break;
}
return Err(ParseError::MissingCrlf);
}
while !value_bytes.is_empty() {
let len = value_bytes.len();
let last_byte = value_bytes.get(len - 1).copied();
if last_byte == Some(b' ') || last_byte == Some(b'\t') {
value_bytes.pop();
} else {
break;
}
}
headers.push((name.to_vec(), value_bytes));
}
Ok((headers, remaining))
}
}
const fn is_token_char(b: u8) -> bool {
matches!(b,
b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' |
b'0'..=b'9' | b'A'..=b'Z' | b'^' | b'_' | b'`' | b'a'..=b'z' | b'|' | b'~'
)
}