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