pub fn unescape_header_value(input: &[u8]) -> Result<Vec<u8>, String> {
let mut result = Vec::with_capacity(input.len());
let mut i = 0;
while i < input.len() {
if input[i] == b'\\' {
if i + 1 >= input.len() {
return Err("incomplete escape sequence at end of header value".to_string());
}
match input[i + 1] {
b'\\' => result.push(b'\\'),
b'n' => result.push(b'\n'),
b'r' => result.push(b'\r'),
b'c' => result.push(b':'),
other => {
return Err(format!(
"invalid escape sequence '\\{}' in header value",
other as char
));
}
}
i += 2;
} else {
result.push(input[i]);
i += 1;
}
}
Ok(result)
}
type ParseResult =
Result<Option<(Vec<u8>, Vec<(Vec<u8>, Vec<u8>)>, Option<Vec<u8>>, usize)>, String>;
fn get_content_length(headers: &[(Vec<u8>, Vec<u8>)]) -> Result<Option<usize>, String> {
for (k, v) in headers {
if k.eq_ignore_ascii_case(&b"content-length"[..]) {
let s =
std::str::from_utf8(v).map_err(|e| format!("content-length not utf8: {}", e))?;
let trimmed = s.trim();
if trimmed.is_empty() {
return Err("empty content-length".to_string());
}
match trimmed.parse::<usize>() {
Ok(n) => return Ok(Some(n)),
Err(e) => return Err(format!("invalid content-length '{}': {}", trimmed, e)),
}
}
}
Ok(None)
}
pub const DEFAULT_MAX_FRAME_SIZE: usize = 16 * 1024 * 1024;
pub fn parse_frame_slice(input: &[u8]) -> ParseResult {
parse_frame_slice_bounded(input, DEFAULT_MAX_FRAME_SIZE)
}
pub fn parse_frame_slice_bounded(input: &[u8], max_frame_size: usize) -> ParseResult {
let mut pos = 0usize;
let len = input.len();
while pos < len && input[pos] == b'\n' {
pos += 1;
}
let cmd_end_opt = input[pos..].iter().position(|&b| b == b'\n');
let mut command: Vec<u8>;
if let Some(cmd_end_rel) = cmd_end_opt {
command = input[pos..pos + cmd_end_rel].to_vec();
if command.last() == Some(&b'\r') {
command.pop();
}
pos += cmd_end_rel + 1;
} else {
if let Some(nul_rel) = input[pos..].iter().position(|&b| b == 0) {
if nul_rel > max_frame_size {
return Err(format!(
"frame body {} exceeds maximum frame size {}",
nul_rel, max_frame_size
));
}
let body = input[pos..pos + nul_rel].to_vec();
pos += nul_rel + 1;
if pos < len && input[pos] == b'\n' {
pos += 1;
}
let body_opt = if body.is_empty() { None } else { Some(body) };
return Ok(Some((Vec::new(), Vec::new(), body_opt, pos)));
}
return Ok(None);
}
let mut headers: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
loop {
if pos >= len {
return Ok(None);
}
if input[pos] == b'\n' {
pos += 1; break;
}
let line_end_rel = match input[pos..].iter().position(|&b| b == b'\n') {
Some(i) => i,
None => return Ok(None),
};
let mut line = &input[pos..pos + line_end_rel];
if !line.is_empty() && line[line.len() - 1] == b'\r' {
line = &line[..line.len() - 1];
}
if let Some(colon) = line.iter().position(|&b| b == b':') {
let key = line[..colon].to_vec();
let val = line[colon + 1..].to_vec();
headers.push((key, val));
} else {
return Err(format!(
"malformed header line: {:?}",
String::from_utf8_lossy(line)
));
}
pos += line_end_rel + 1;
}
match get_content_length(&headers) {
Ok(Some(content_len)) => {
if content_len > max_frame_size {
return Err(format!(
"content-length {} exceeds maximum frame size {}",
content_len, max_frame_size
));
}
let needed = match pos.checked_add(content_len).and_then(|n| n.checked_add(1)) {
Some(n) => n,
None => return Err("content-length too large".to_string()),
};
if needed > len {
Ok(None)
} else {
let body = input[pos..pos + content_len].to_vec();
pos += content_len;
if pos >= len || input[pos] != 0 {
Err("missing NUL terminator after content-length body".to_string())
} else {
pos += 1;
if pos < len && input[pos] == b'\n' {
pos += 1;
}
Ok(Some((command, headers, Some(body), pos)))
}
}
}
Ok(None) => {
match input[pos..].iter().position(|&b| b == 0) {
Some(nul_rel) => {
if nul_rel > max_frame_size {
return Err(format!(
"frame body {} exceeds maximum frame size {}",
nul_rel, max_frame_size
));
}
let body = input[pos..pos + nul_rel].to_vec();
pos += nul_rel + 1;
if pos < len && input[pos] == b'\n' {
pos += 1;
}
let body_opt = if body.is_empty() { None } else { Some(body) };
Ok(Some((command, headers, body_opt, pos)))
}
None => Ok(None),
}
}
Err(e) => Err(e),
}
}