use std::io;
use std::num::Wrapping;
use super::HeaderTable;
use super::STATIC_TABLE;
pub fn encode_integer_into<W: io::Write>(
mut value: usize,
prefix_size: u8,
leading_bits: u8,
writer: &mut W,
) -> io::Result<()> {
let Wrapping(mask) = if prefix_size >= 8 {
Wrapping(0xFF)
} else {
Wrapping(1u8 << prefix_size) - Wrapping(1)
};
let leading_bits = leading_bits & (!mask);
let mask = mask as usize;
if value < mask {
writer.write_all(&[leading_bits | value as u8])?;
return Ok(());
}
writer.write_all(&[leading_bits | mask as u8])?;
value -= mask;
while value >= 128 {
writer.write_all(&[((value % 128) + 128) as u8])?;
value /= 128;
}
writer.write_all(&[value as u8])?;
Ok(())
}
pub fn encode_integer(value: usize, prefix_size: u8) -> Vec<u8> {
let mut res = Vec::new();
encode_integer_into(value, prefix_size, 0, &mut res).unwrap();
res
}
pub struct Encoder<'a> {
header_table: HeaderTable<'a>,
}
impl<'a> Default for Encoder<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Encoder<'a> {
pub fn new() -> Encoder<'a> {
Encoder {
header_table: HeaderTable::with_static_table(STATIC_TABLE),
}
}
pub fn set_max_table_size(&mut self, new_max_size: usize) {
self.header_table
.dynamic_table
.set_max_table_size(new_max_size);
}
pub fn encode<'b, I>(&mut self, headers: I) -> Vec<u8>
where
I: IntoIterator<Item = (&'b [u8], &'b [u8])>,
{
let mut encoded: Vec<u8> = Vec::new();
self.encode_into(headers, &mut encoded).unwrap();
encoded
}
pub fn encode_into<'b, I, W>(&mut self, headers: I, writer: &mut W) -> io::Result<()>
where
I: IntoIterator<Item = (&'b [u8], &'b [u8])>,
W: io::Write,
{
for header in headers {
self.encode_header_into(header, writer)?;
}
Ok(())
}
pub fn encode_header_into<W: io::Write>(
&mut self,
header: (&[u8], &[u8]),
writer: &mut W,
) -> io::Result<()> {
match self.header_table.find_header(header) {
None => {
self.encode_literal(&header, true, writer)?;
self.header_table
.add_header(header.0.to_vec(), header.1.to_vec());
}
Some((index, false)) => {
self.encode_indexed_name((index, header.1), false, writer)?;
}
Some((index, true)) => {
self.encode_indexed(index, writer)?;
}
};
Ok(())
}
fn encode_literal<W: io::Write>(
&mut self,
header: &(&[u8], &[u8]),
should_index: bool,
buf: &mut W,
) -> io::Result<()> {
let mask = if should_index { 0x40 } else { 0x0 };
buf.write_all(&[mask])?;
self.encode_string_literal(header.0, buf)?;
self.encode_string_literal(header.1, buf)?;
Ok(())
}
fn encode_string_literal<W: io::Write>(
&mut self,
octet_str: &[u8],
buf: &mut W,
) -> io::Result<()> {
encode_integer_into(octet_str.len(), 7, 0, buf)?;
buf.write_all(octet_str)?;
Ok(())
}
fn encode_indexed_name<W: io::Write>(
&mut self,
header: (usize, &[u8]),
should_index: bool,
buf: &mut W,
) -> io::Result<()> {
let (mask, prefix) = if should_index { (0x40, 6) } else { (0x0, 4) };
encode_integer_into(header.0, prefix, mask, buf)?;
self.encode_string_literal(header.1, buf)?;
Ok(())
}
fn encode_indexed<W: io::Write>(&self, index: usize, buf: &mut W) -> io::Result<()> {
encode_integer_into(index, 7, 0x80, buf)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use tracing::debug;
use super::encode_integer;
use super::Encoder;
use super::super::Decoder;
#[test]
fn test_encode_integer() {
assert_eq!(encode_integer(10, 5), [10]);
assert_eq!(encode_integer(1337, 5), [31, 154, 10]);
assert_eq!(encode_integer(127, 7), [127, 0]);
assert_eq!(encode_integer(255, 8), [255, 0]);
assert_eq!(encode_integer(254, 8), [254]);
assert_eq!(encode_integer(1, 8), [1]);
assert_eq!(encode_integer(0, 8), [0]);
assert_eq!(encode_integer(255, 7), [127, 128, 1]);
}
fn is_decodable(buf: &[u8], headers: &Vec<(Vec<u8>, Vec<u8>)>) -> bool {
let mut decoder = Decoder::new();
match decoder.decode(buf).ok() {
Some(h) => h == *headers,
None => false,
}
}
#[test]
fn test_encode_only_method() {
let mut encoder: Encoder = Encoder::new();
let headers = vec![(b":method".to_vec(), b"GET".to_vec())];
let result = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
debug!("{:?}", result);
assert!(is_decodable(&result, &headers));
}
#[test]
fn test_custom_header_gets_indexed() {
let mut encoder: Encoder = Encoder::new();
let headers = vec![(b"custom-key".to_vec(), b"custom-value".to_vec())];
let result = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
assert!(is_decodable(&result, &headers));
assert_eq!(encoder.header_table.dynamic_table.to_vec(), headers);
assert!(0x40 == (0x40 & result[0]));
debug!("{:?}", result);
}
#[test]
fn test_uses_index_on_second_iteration() {
let mut encoder: Encoder = Encoder::new();
let headers = vec![(b"custom-key".to_vec(), b"custom-value".to_vec())];
let _ = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
let result = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
assert_eq!(encoder.header_table.dynamic_table.to_vec(), headers);
assert_eq!(result.len(), 1);
assert_eq!(0x80 & result[0], 0x80);
assert_eq!(result[0] ^ 0x80, 62);
assert_eq!(
encoder.header_table.get_from_table(62).unwrap(),
(&headers[0].0[..], &headers[0].1[..])
);
}
#[test]
fn test_name_indexed_value_not() {
{
let mut encoder: Encoder = Encoder::new();
let headers = [(b":method", b"PUT")];
let result = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
assert_eq!(result[0], 3);
assert_eq!(&result[1..], &[3, b'P', b'U', b'T']);
}
{
let mut encoder: Encoder = Encoder::new();
let headers = [(b":authority".to_vec(), b"example.com".to_vec())];
let result = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
assert_eq!(result[0], 1);
assert_eq!(
&result[1..],
&[11, b'e', b'x', b'a', b'm', b'p', b'l', b'e', b'.', b'c', b'o', b'm']
)
}
}
#[test]
fn test_multiple_headers_encoded() {
let mut encoder = Encoder::new();
let headers = vec![
(b"custom-key".to_vec(), b"custom-value".to_vec()),
(b":method".to_vec(), b"GET".to_vec()),
(b":path".to_vec(), b"/some/path".to_vec()),
];
let result = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
assert!(is_decodable(&result, &headers));
}
}