1use crate::{Error, Limits, Method};
10
11const FORBIDDEN_HEADERS: &[&[u8]] = &[
12 b"connection",
13 b"expect",
14 b"host",
15 b"keep-alive",
16 b"proxy-authenticate",
17 b"proxy-authentication-info",
18 b"proxy-authorization",
19 b"proxy-connection",
20 b"te",
21 b"trailer",
22 b"transfer-encoding",
23 b"upgrade",
24];
25
26#[derive(Clone, Debug, Eq, PartialEq)]
28pub struct HeaderField {
29 pub name: Vec<u8>,
31 pub value: Vec<u8>,
33}
34
35#[derive(Clone, Debug, Eq, PartialEq)]
37pub struct Request {
38 pub method: Method,
40 pub authority: Vec<u8>,
42 pub path: Vec<u8>,
44 pub headers: Vec<HeaderField>,
46 pub body: Vec<u8>,
48}
49
50#[derive(Clone, Debug, Eq, PartialEq)]
53pub struct RequestHead {
54 pub method: Method,
56 pub authority: Vec<u8>,
58 pub path: Vec<u8>,
60 pub headers: Vec<HeaderField>,
62}
63
64pub(crate) fn encode_stream_head(head: &RequestHead, limits: Limits) -> Result<Vec<u8>, Error> {
65 validate_target(head.method, &head.authority, &head.path, limits)?;
66 validate_payload(&head.headers, &[], limits)?;
67 stream_content_length(&head.headers)?;
68 let fields = encode_fields(&head.headers)?;
69 let mut output = Vec::with_capacity(fields.len() + head.authority.len() + head.path.len() + 64);
70 write_vector(&mut output, head.method.as_bytes())?;
72 write_vector(&mut output, &head.authority)?;
73 write_vector(&mut output, &head.path)?;
74 write_vector(&mut output, &fields)?;
75 Ok(output)
76}
77
78pub(crate) fn decode_stream_head(input: &[u8], limits: Limits) -> Result<RequestHead, Error> {
79 let mut reader = SliceReader::new(input);
80 let method = Method::from_bytes(reader.read_vector()?)?;
81 let authority = reader.read_vector()?;
82 let path = reader.read_vector()?;
83 validate_target(method, authority, path, limits).map_err(decode_validation_error)?;
84 let headers = decode_fields(reader.read_vector()?, limits).map_err(decode_validation_error)?;
85 if !reader.is_finished() {
86 return Err(Error::MalformedEnvelope);
87 }
88 stream_content_length(&headers).map_err(decode_validation_error)?;
89 Ok(RequestHead {
90 method,
91 authority: authority.to_vec(),
92 path: path.to_vec(),
93 headers,
94 })
95}
96
97pub(crate) fn stream_content_length(headers: &[HeaderField]) -> Result<Option<u64>, Error> {
98 let mut values = headers
99 .iter()
100 .filter(|field| field.name == b"content-length");
101 let Some(field) = values.next() else {
102 return Ok(None);
103 };
104 if values.next().is_some()
105 || field.value.is_empty()
106 || !field.value.iter().all(u8::is_ascii_digit)
107 {
108 return Err(Error::InvalidConfiguration);
109 }
110 let mut length = 0_u64;
111 for digit in &field.value {
112 length = length
113 .checked_mul(10)
114 .and_then(|value| value.checked_add(u64::from(digit - b'0')))
115 .ok_or(Error::LimitExceeded)?;
116 }
117 Ok(Some(length))
118}
119
120#[derive(Clone, Debug, Eq, PartialEq)]
122pub struct Response {
123 pub status: u16,
125 pub headers: Vec<HeaderField>,
127 pub body: Vec<u8>,
129}
130
131pub(crate) fn validate_decoded_response_body(
132 response: &Response,
133 method: Method,
134) -> Result<(), Error> {
135 validate_response_content_length(
136 response.status,
137 &response.headers,
138 response.body.len(),
139 method,
140 )
141 .map_err(decode_validation_error)
142}
143
144const fn decode_validation_error(error: Error) -> Error {
145 match error {
146 Error::InvalidConfiguration => Error::MalformedEnvelope,
147 other => other,
148 }
149}
150
151pub(crate) fn encode_fields(headers: &[HeaderField]) -> Result<Vec<u8>, Error> {
152 let mut output = Vec::new();
153 for field in headers {
154 write_vector(&mut output, &field.name)?;
155 write_vector(&mut output, &field.value)?;
156 }
157 Ok(output)
158}
159
160pub(crate) fn decode_fields(input: &[u8], limits: Limits) -> Result<Vec<HeaderField>, Error> {
161 let mut reader = SliceReader::new(input);
162 let mut headers: Vec<HeaderField> = Vec::new();
163 let mut header_bytes = 0_usize;
164 while !reader.is_finished() {
165 if headers.len() >= limits.max_header_count {
166 return Err(Error::LimitExceeded);
167 }
168 let name = reader.read_vector()?;
169 let value = reader.read_vector()?;
170 header_bytes = header_bytes
171 .checked_add(name.len())
172 .and_then(|length| length.checked_add(value.len()))
173 .ok_or(Error::LimitExceeded)?;
174 if header_bytes > limits.max_header_bytes {
175 return Err(Error::LimitExceeded);
176 }
177 validate_header_parts(name, value)?;
178 headers.push(HeaderField {
179 name: name.to_vec(),
180 value: value.to_vec(),
181 });
182 }
183 Ok(headers)
184}
185
186pub(crate) fn validate_request(request: &Request, limits: Limits) -> Result<(), Error> {
187 validate_target(request.method, &request.authority, &request.path, limits)?;
188 validate_payload(&request.headers, &request.body, limits)?;
189 validate_content_length(
190 &request.headers,
191 request.body.len(),
192 ContentLengthRule::Exact,
193 )
194}
195
196pub(crate) fn validate_response_content_length(
197 status: u16,
198 headers: &[HeaderField],
199 body_len: usize,
200 method: Method,
201) -> Result<(), Error> {
202 validate_content_length(
203 headers,
204 body_len,
205 response_content_length_rule(method, status),
206 )
207}
208
209pub(crate) fn validate_response_head(
210 status: u16,
211 headers: &[HeaderField],
212 limits: Limits,
213) -> Result<(), Error> {
214 if !(200..=599).contains(&status) {
215 return Err(Error::InvalidConfiguration);
216 }
217 validate_payload(headers, &[], limits)
218}
219
220fn validate_target(
221 method: Method,
222 authority: &[u8],
223 path: &[u8],
224 limits: Limits,
225) -> Result<(), Error> {
226 let target_len = authority
227 .len()
228 .checked_add(path.len())
229 .ok_or(Error::LimitExceeded)?;
230 if target_len > limits.max_target_len {
231 return Err(Error::LimitExceeded);
232 }
233 if !valid_authority(authority) || !valid_request_target(method, path) {
234 return Err(Error::InvalidConfiguration);
235 }
236 Ok(())
237}
238
239fn valid_authority(authority: &[u8]) -> bool {
240 if authority.is_empty() || !authority.iter().all(u8::is_ascii_graphic) {
241 return false;
242 }
243 if authority[0] == b'[' {
244 return valid_ip_literal_authority(authority);
245 }
246
247 let (host, port) = authority
248 .iter()
249 .rposition(|byte| *byte == b':')
250 .map_or((authority, None), |separator| {
251 (&authority[..separator], Some(&authority[separator + 1..]))
252 });
253 if host.contains(&b':') || !valid_reg_name(host) || !port.is_none_or(valid_port) {
254 return false;
255 }
256 let numeric_ipv4_candidate = host.split(|byte| *byte == b'.').count() == 4
257 && host
258 .iter()
259 .all(|byte| byte.is_ascii_digit() || *byte == b'.');
260 !numeric_ipv4_candidate
261 || std::str::from_utf8(host)
262 .ok()
263 .and_then(|value| value.parse::<std::net::Ipv4Addr>().ok())
264 .is_some()
265}
266
267fn valid_ip_literal_authority(authority: &[u8]) -> bool {
268 let Some(close) = authority.iter().position(|byte| *byte == b']') else {
269 return false;
270 };
271 let literal = &authority[1..close];
272 let suffix = &authority[close + 1..];
273 let valid_suffix = suffix.is_empty() || suffix.strip_prefix(b":").is_some_and(valid_port);
274 if literal.is_empty() || !valid_suffix {
275 return false;
276 }
277 let Ok(value) = std::str::from_utf8(literal) else {
278 return false;
279 };
280 value.parse::<std::net::Ipv6Addr>().is_ok() || valid_ipv_future(literal)
281}
282
283fn valid_ipv_future(value: &[u8]) -> bool {
284 let Some(version_and_address) = value
285 .strip_prefix(b"v")
286 .or_else(|| value.strip_prefix(b"V"))
287 else {
288 return false;
289 };
290 let Some(separator) = version_and_address.iter().position(|byte| *byte == b'.') else {
291 return false;
292 };
293 let version = &version_and_address[..separator];
294 let address = &version_and_address[separator + 1..];
295 !version.is_empty()
296 && version.iter().all(u8::is_ascii_hexdigit)
297 && !address.is_empty()
298 && address
299 .iter()
300 .copied()
301 .all(|byte| is_unreserved(byte) || is_sub_delim(byte) || byte == b':')
302}
303
304fn valid_reg_name(value: &[u8]) -> bool {
305 if value.is_empty() {
306 return false;
307 }
308 let mut position = 0;
309 while position < value.len() {
310 let byte = value[position];
311 if is_unreserved(byte) || is_sub_delim(byte) {
312 position += 1;
313 } else if byte == b'%'
314 && value
315 .get(position + 1..position + 3)
316 .is_some_and(|encoded| encoded.iter().all(u8::is_ascii_hexdigit))
317 {
318 position += 3;
319 } else {
320 return false;
321 }
322 }
323 true
324}
325
326fn valid_port(value: &[u8]) -> bool {
327 if value.is_empty() || !value.iter().all(u8::is_ascii_digit) {
328 return false;
329 }
330 let significant = value
331 .iter()
332 .position(|byte| *byte != b'0')
333 .map_or(&[][..], |position| &value[position..]);
334 significant.is_empty()
335 || (significant.len() <= 5
336 && std::str::from_utf8(significant)
337 .ok()
338 .and_then(|port| port.parse::<u16>().ok())
339 .is_some())
340}
341
342const fn is_unreserved(byte: u8) -> bool {
343 byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~')
344}
345
346const fn is_sub_delim(byte: u8) -> bool {
347 matches!(
348 byte,
349 b'!' | b'$' | b'&' | b'\'' | b'(' | b')' | b'*' | b'+' | b',' | b';' | b'='
350 )
351}
352
353fn validate_payload(headers: &[HeaderField], body: &[u8], limits: Limits) -> Result<(), Error> {
354 if headers.len() > limits.max_header_count || body.len() > limits.max_body_len {
355 return Err(Error::LimitExceeded);
356 }
357 let mut header_bytes = 0_usize;
358 for field in headers {
359 validate_header_parts(&field.name, &field.value)?;
360 header_bytes = header_bytes
361 .checked_add(field.name.len())
362 .and_then(|length| length.checked_add(field.value.len()))
363 .ok_or(Error::LimitExceeded)?;
364 }
365 if header_bytes > limits.max_header_bytes {
366 return Err(Error::LimitExceeded);
367 }
368 Ok(())
369}
370
371#[derive(Clone, Copy)]
372enum ContentLengthRule {
373 Exact,
374 Metadata,
375 Forbidden,
376}
377
378fn response_content_length_rule(method: Method, status: u16) -> ContentLengthRule {
379 if status == 204 {
380 ContentLengthRule::Forbidden
381 } else if method == Method::Head || status == 304 {
382 ContentLengthRule::Metadata
383 } else {
384 ContentLengthRule::Exact
385 }
386}
387
388fn validate_content_length(
389 headers: &[HeaderField],
390 body_len: usize,
391 rule: ContentLengthRule,
392) -> Result<(), Error> {
393 let mut values = headers
394 .iter()
395 .filter(|field| field.name == b"content-length")
396 .map(|field| field.value.as_slice());
397 let Some(value) = values.next() else {
398 return Ok(());
399 };
400 if values.next().is_some() || value.is_empty() || !value.iter().all(u8::is_ascii_digit) {
401 return Err(Error::InvalidConfiguration);
402 }
403 match rule {
404 ContentLengthRule::Forbidden => Err(Error::InvalidConfiguration),
405 ContentLengthRule::Metadata => Ok(()),
406 ContentLengthRule::Exact => {
407 let significant = value
408 .iter()
409 .position(|byte| *byte != b'0')
410 .map_or(&[][..], |position| &value[position..]);
411 let expected = body_len.to_string();
412 let matches = if significant.is_empty() {
413 body_len == 0
414 } else {
415 significant == expected.as_bytes()
416 };
417 if matches {
418 Ok(())
419 } else {
420 Err(Error::InvalidConfiguration)
421 }
422 }
423 }
424}
425
426fn validate_header_parts(name: &[u8], value: &[u8]) -> Result<(), Error> {
427 if name.is_empty()
428 || !name.iter().copied().all(is_lower_tchar)
429 || FORBIDDEN_HEADERS.contains(&name)
430 || !value
431 .iter()
432 .all(|byte| *byte == b'\t' || (b' '..=b'~').contains(byte))
433 || value.first().is_some_and(|byte| is_ows(*byte))
434 || value.last().is_some_and(|byte| is_ows(*byte))
435 {
436 return Err(Error::InvalidConfiguration);
437 }
438 Ok(())
439}
440
441fn valid_request_target(method: Method, path: &[u8]) -> bool {
442 if path == b"*" {
443 return method == Method::Options;
444 }
445 if !path.starts_with(b"/")
446 || path.contains(&b'#')
447 || path.contains(&b'\\')
448 || !path.iter().all(u8::is_ascii_graphic)
449 || !valid_percent_encoding(path)
450 {
451 return false;
452 }
453 let path_only = path.split(|byte| *byte == b'?').next().unwrap_or_default();
454 let Some(decoded_path) = decode_path(path_only) else {
455 return false;
456 };
457 std::str::from_utf8(&decoded_path).is_ok()
458 && !decoded_path
459 .iter()
460 .any(|byte| byte.is_ascii_control() || *byte == b'\\')
461 && !decoded_path.split(|byte| *byte == b'/').any(is_dot_segment)
462}
463
464fn decode_path(path: &[u8]) -> Option<Vec<u8>> {
465 let mut decoded = Vec::with_capacity(path.len());
466 let mut position = 0;
467 while position < path.len() {
468 if path[position] == b'%' {
469 let high = hex_value(*path.get(position + 1)?)?;
470 let low = hex_value(*path.get(position + 2)?)?;
471 let byte = high.checked_mul(16)?.checked_add(low)?;
472 if byte == b'/' {
473 return None;
474 }
475 decoded.push(byte);
476 position += 3;
477 } else {
478 decoded.push(path[position]);
479 position += 1;
480 }
481 }
482 Some(decoded)
483}
484
485const fn hex_value(byte: u8) -> Option<u8> {
486 match byte {
487 b'0'..=b'9' => Some(byte - b'0'),
488 b'a'..=b'f' => Some(byte - b'a' + 10),
489 b'A'..=b'F' => Some(byte - b'A' + 10),
490 _ => None,
491 }
492}
493
494fn valid_percent_encoding(value: &[u8]) -> bool {
495 let mut position = 0;
496 while position < value.len() {
497 if value[position] == b'%' {
498 let Some(encoded) = value.get(position + 1..position + 3) else {
499 return false;
500 };
501 if !encoded.iter().all(u8::is_ascii_hexdigit) {
502 return false;
503 }
504 position += 3;
505 } else {
506 position += 1;
507 }
508 }
509 true
510}
511
512fn is_dot_segment(segment: &[u8]) -> bool {
513 segment == b"." || segment == b".."
514}
515
516const fn is_ows(byte: u8) -> bool {
517 matches!(byte, b' ' | b'\t')
518}
519
520const fn is_lower_tchar(byte: u8) -> bool {
521 byte.is_ascii_lowercase()
522 || byte.is_ascii_digit()
523 || matches!(
524 byte,
525 b'!' | b'#'
526 | b'$'
527 | b'%'
528 | b'&'
529 | b'\''
530 | b'*'
531 | b'+'
532 | b'-'
533 | b'.'
534 | b'^'
535 | b'_'
536 | b'`'
537 | b'|'
538 | b'~'
539 )
540}
541
542fn write_vector(output: &mut Vec<u8>, value: &[u8]) -> Result<(), Error> {
543 let length = u64::try_from(value.len()).map_err(|_| Error::LimitExceeded)?;
544 write_varint(output, length)?;
545 output.extend_from_slice(value);
546 Ok(())
547}
548
549fn write_varint(output: &mut Vec<u8>, value: u64) -> Result<(), Error> {
550 match value {
551 0..=0x3f => output.push(u8::try_from(value).map_err(|_| Error::LimitExceeded)?),
552 0x40..=0x3fff => {
553 let encoded = u16::try_from(value).map_err(|_| Error::LimitExceeded)? | 0x4000;
554 output.extend_from_slice(&encoded.to_be_bytes());
555 }
556 0x4000..=0x3fff_ffff => {
557 let encoded = u32::try_from(value).map_err(|_| Error::LimitExceeded)? | 0x8000_0000;
558 output.extend_from_slice(&encoded.to_be_bytes());
559 }
560 0x4000_0000..=0x3fff_ffff_ffff_ffff => {
561 output.extend_from_slice(&(value | 0xc000_0000_0000_0000).to_be_bytes());
562 }
563 _ => return Err(Error::LimitExceeded),
564 }
565 Ok(())
566}
567
568struct SliceReader<'a> {
569 input: &'a [u8],
570 position: usize,
571}
572
573impl<'a> SliceReader<'a> {
574 const fn new(input: &'a [u8]) -> Self {
575 Self { input, position: 0 }
576 }
577
578 fn read_varint(&mut self) -> Result<u64, Error> {
579 let first = *self
580 .input
581 .get(self.position)
582 .ok_or(Error::MalformedEnvelope)?;
583 let length = 1_usize << (first >> 6);
584 let end = self
585 .position
586 .checked_add(length)
587 .ok_or(Error::MalformedEnvelope)?;
588 let encoded = self
589 .input
590 .get(self.position..end)
591 .ok_or(Error::MalformedEnvelope)?;
592 let mut value = u64::from(first & 0x3f);
593 for byte in &encoded[1..] {
594 value = (value << 8) | u64::from(*byte);
595 }
596 if varint_length(value) != length {
597 return Err(Error::MalformedEnvelope);
598 }
599 self.position = end;
600 Ok(value)
601 }
602
603 fn read_vector(&mut self) -> Result<&'a [u8], Error> {
604 let length = usize::try_from(self.read_varint()?).map_err(|_| Error::MalformedEnvelope)?;
605 let end = self
606 .position
607 .checked_add(length)
608 .ok_or(Error::MalformedEnvelope)?;
609 let value = self
610 .input
611 .get(self.position..end)
612 .ok_or(Error::MalformedEnvelope)?;
613 self.position = end;
614 Ok(value)
615 }
616
617 const fn is_finished(&self) -> bool {
618 self.position == self.input.len()
619 }
620}
621
622const fn varint_length(value: u64) -> usize {
623 match value {
624 0..=0x3f => 1,
625 0x40..=0x3fff => 2,
626 0x4000..=0x3fff_ffff => 4,
627 _ => 8,
628 }
629}
630
631#[cfg(test)]
632mod tests {
633 use super::{SliceReader, decode_fields, decode_stream_head, encode_stream_head, write_varint};
634 use crate::{Error, HeaderField, Limits, Method, RequestHead};
635
636 #[test]
637 fn field_decoder_stops_at_the_configured_count() {
638 let limits = Limits {
639 max_header_count: 2,
640 ..Limits::default()
641 };
642 let encoded = [1, b'a', 0, 1, b'b', 0, 1, b'c', 0];
643 assert_eq!(decode_fields(&encoded, limits), Err(Error::LimitExceeded));
644 }
645
646 #[test]
647 fn varint_round_trips_boundaries_and_rejects_non_minimal_values() -> Result<(), Error> {
648 let cases: &[(u64, &[u8])] = &[
649 (0, &[0x00]),
650 (63, &[0x3f]),
651 (64, &[0x40, 0x40]),
652 (16_383, &[0x7f, 0xff]),
653 (16_384, &[0x80, 0x00, 0x40, 0x00]),
654 (0x3fff_ffff, &[0xbf, 0xff, 0xff, 0xff]),
655 (
656 0x4000_0000,
657 &[0xc0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00],
658 ),
659 ];
660 for (value, expected) in cases {
661 let mut encoded = Vec::new();
662 write_varint(&mut encoded, *value)?;
663 assert_eq!(&encoded, expected);
664 let mut reader = SliceReader::new(expected);
665 assert_eq!(reader.read_varint()?, *value);
666 assert!(reader.is_finished());
667 }
668 let mut non_minimal = SliceReader::new(&[0x40, 0x01]);
669 assert_eq!(non_minimal.read_varint(), Err(Error::MalformedEnvelope));
670 Ok(())
671 }
672
673 #[test]
674 fn stream_head_decoder_rechecks_authenticated_content_length() -> Result<(), Error> {
675 let request = RequestHead {
676 method: Method::Post,
677 authority: b"api.example.test".to_vec(),
678 path: b"/items".to_vec(),
679 headers: vec![HeaderField {
680 name: b"content-length".to_vec(),
681 value: b"4".to_vec(),
682 }],
683 };
684 let mut encoded = encode_stream_head(&request, Limits::default())?;
685 let name_offset = encoded
686 .windows(b"content-length".len())
687 .position(|window| window == b"content-length")
688 .ok_or(Error::MalformedEnvelope)?;
689 let value_offset = name_offset + b"content-length".len() + 1;
690 encoded[value_offset] = b'x';
691 assert_eq!(
692 decode_stream_head(&encoded, Limits::default()).err(),
693 Some(Error::MalformedEnvelope)
694 );
695 Ok(())
696 }
697
698 #[test]
699 fn stream_head_decoder_rechecks_authenticated_authority_syntax() -> Result<(), Error> {
700 let request = RequestHead {
701 method: Method::Get,
702 authority: b"api.example".to_vec(),
703 path: b"/items".to_vec(),
704 headers: Vec::new(),
705 };
706 let mut encoded = encode_stream_head(&request, Limits::default())?;
707 let authority_offset = encoded
708 .windows(b"api.example".len())
709 .position(|window| window == b"api.example")
710 .ok_or(Error::MalformedEnvelope)?;
711 encoded[authority_offset] = b'[';
712 assert_eq!(
713 decode_stream_head(&encoded, Limits::default()).err(),
714 Some(Error::MalformedEnvelope)
715 );
716 Ok(())
717 }
718}