use super::super::error::H2ParseError;
use super::huffman;
use super::integer::decode_integer;
pub fn decode_string(buf: &[u8]) -> Result<(String, &[u8]), H2ParseError> {
if buf.is_empty() {
return Err(H2ParseError::MalformedHeaders);
}
let huffman = buf[0] & 0x80 != 0;
let (len, rest) = decode_integer(buf, 7)?;
let len_usize = usize::try_from(len).map_err(|_| H2ParseError::MalformedHeaders)?;
if rest.len() < len_usize {
return Err(H2ParseError::MalformedHeaders);
}
let payload = &rest[..len_usize];
let after = &rest[len_usize..];
if huffman {
let s = huffman::decode(payload)?;
Ok((s, after))
} else {
let s = std::str::from_utf8(payload)
.map_err(|_| H2ParseError::NonAsciiAuthority)?
.to_string();
Ok((s, after))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_literal_round_trips() {
let buf: Vec<u8> = std::iter::once(5u8)
.chain(b"hello".iter().copied())
.collect();
let (s, rest) = decode_string(&buf).unwrap();
assert_eq!(s, "hello");
assert!(rest.is_empty());
}
#[test]
fn huffman_literal_round_trips() {
let payload = huffman::encode("api.example.com");
let mut buf = Vec::new();
assert!(payload.len() < 0x7F);
buf.push(0x80 | payload.len() as u8);
buf.extend_from_slice(&payload);
let (s, rest) = decode_string(&buf).unwrap();
assert_eq!(s, "api.example.com");
assert!(rest.is_empty());
}
#[test]
fn malformed_truncated_payload() {
let buf = vec![5u8, b'a', b'b', b'c'];
let err = decode_string(&buf).unwrap_err();
assert_eq!(err, H2ParseError::MalformedHeaders);
}
}