1pub fn unescape_header_value(input: &[u8]) -> Result<Vec<u8>, String> {
13 let mut result = Vec::with_capacity(input.len());
14 let mut i = 0;
15 while i < input.len() {
16 if input[i] == b'\\' {
17 if i + 1 >= input.len() {
18 return Err("incomplete escape sequence at end of header value".to_string());
19 }
20 match input[i + 1] {
21 b'\\' => result.push(b'\\'),
22 b'n' => result.push(b'\n'),
23 b'r' => result.push(b'\r'),
24 b'c' => result.push(b':'),
25 other => {
26 return Err(format!(
27 "invalid escape sequence '\\{}' in header value",
28 other as char
29 ));
30 }
31 }
32 i += 2;
33 } else {
34 result.push(input[i]);
35 i += 1;
36 }
37 }
38 Ok(result)
39}
40
41type ParseResult =
48 Result<Option<(Vec<u8>, Vec<(Vec<u8>, Vec<u8>)>, Option<Vec<u8>>, usize)>, String>;
49
50fn get_content_length(headers: &[(Vec<u8>, Vec<u8>)]) -> Result<Option<usize>, String> {
51 for (k, v) in headers {
52 if k.eq_ignore_ascii_case(&b"content-length"[..]) {
53 let s =
54 std::str::from_utf8(v).map_err(|e| format!("content-length not utf8: {}", e))?;
55 let trimmed = s.trim();
56 if trimmed.is_empty() {
57 return Err("empty content-length".to_string());
58 }
59 match trimmed.parse::<usize>() {
60 Ok(n) => return Ok(Some(n)),
61 Err(e) => return Err(format!("invalid content-length '{}': {}", trimmed, e)),
62 }
63 }
64 }
65 Ok(None)
66}
67
68pub fn parse_frame_slice(input: &[u8]) -> ParseResult {
74 let mut pos = 0usize;
75 let len = input.len();
76
77 while pos < len && input[pos] == b'\n' {
79 pos += 1;
82 }
83
84 let cmd_end_opt = input[pos..].iter().position(|&b| b == b'\n');
86 let mut command: Vec<u8>;
87 if let Some(cmd_end_rel) = cmd_end_opt {
88 command = input[pos..pos + cmd_end_rel].to_vec();
89 if command.last() == Some(&b'\r') {
91 command.pop();
93 }
94 pos += cmd_end_rel + 1;
95 } else {
96 if let Some(nul_rel) = input[pos..].iter().position(|&b| b == 0) {
99 let body = input[pos..pos + nul_rel].to_vec();
100 pos += nul_rel + 1;
101 if pos < len && input[pos] == b'\n' {
102 pos += 1;
103 }
104 let body_opt = if body.is_empty() { None } else { Some(body) };
105 return Ok(Some((Vec::new(), Vec::new(), body_opt, pos)));
106 }
107 return Ok(None);
108 }
109
110 let mut headers: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
112 loop {
113 if pos >= len {
114 return Ok(None);
115 }
116 if input[pos] == b'\n' {
117 pos += 1; break;
119 }
120 let line_end_rel = match input[pos..].iter().position(|&b| b == b'\n') {
122 Some(i) => i,
123 None => return Ok(None),
124 };
125 let mut line = &input[pos..pos + line_end_rel];
126 if !line.is_empty() && line[line.len() - 1] == b'\r' {
128 line = &line[..line.len() - 1];
129 }
130 if let Some(colon) = line.iter().position(|&b| b == b':') {
132 let key = line[..colon].to_vec();
133 let val = line[colon + 1..].to_vec();
134 headers.push((key, val));
135 } else {
136 return Err(format!(
137 "malformed header line: {:?}",
138 String::from_utf8_lossy(line)
139 ));
140 }
141 pos += line_end_rel + 1;
142 }
143
144 match get_content_length(&headers) {
146 Ok(Some(content_len)) => {
147 if pos + content_len + 1 > len {
149 Ok(None)
150 } else {
151 let body = input[pos..pos + content_len].to_vec();
152 pos += content_len;
153 if pos >= len || input[pos] != 0 {
155 Err("missing NUL terminator after content-length body".to_string())
156 } else {
157 pos += 1;
158 if pos < len && input[pos] == b'\n' {
160 pos += 1;
161 }
162 Ok(Some((command, headers, Some(body), pos)))
163 }
164 }
165 }
166 Ok(None) => {
167 match input[pos..].iter().position(|&b| b == 0) {
169 Some(nul_rel) => {
170 let body = input[pos..pos + nul_rel].to_vec();
171 pos += nul_rel + 1;
172 if pos < len && input[pos] == b'\n' {
174 pos += 1;
175 }
176 let body_opt = if body.is_empty() { None } else { Some(body) };
177 Ok(Some((command, headers, body_opt, pos)))
178 }
179 None => Ok(None),
180 }
181 }
182 Err(e) => Err(e),
183 }
184}