use super::{
FieldSection, INDEXED_FIELD_LINE, INDEXED_STATIC_FLAG, LITERAL_WITH_LITERAL_NAME,
LITERAL_WITH_NAME_REF, NAME_REF_STATIC_FLAG, huffman,
static_table::{StaticLookup, static_table_lookup},
varint,
};
use crate::{Method, Status};
impl FieldSection<'_> {
pub fn encode(&self, buf: &mut Vec<u8>) {
let Self {
pseudo_headers,
headers,
} = self;
buf.extend_from_slice(&varint::encode(0, 8));
buf.extend_from_slice(&varint::encode(0, 7));
if let Some(method) = pseudo_headers.method {
encode_method(method, buf);
}
if let Some(status) = pseudo_headers.status {
encode_status(status, buf);
}
if let Some(path) = &pseudo_headers.path {
encode_pseudo_string(b":path", path.as_ref(), 1, buf);
}
if let Some(scheme) = &pseudo_headers.scheme {
encode_pseudo_string(b":scheme", scheme.as_ref(), 22, buf);
}
if let Some(authority) = &pseudo_headers.authority {
encode_pseudo_string(b":authority", authority.as_ref(), 0, buf);
}
if let Some(protocol) = &pseudo_headers.protocol {
encode_literal_with_literal_name(b":protocol", protocol.as_ref().as_bytes(), buf);
}
for (name, values) in &**headers {
let name_bytes = name.as_ref().as_bytes();
for value in values {
let value_bytes: &[u8] = value.as_ref();
let lookup = static_table_lookup(&name, value);
encode_by_lookup(lookup, name_bytes, value_bytes, buf);
}
}
}
}
fn encode_indexed(index: u8, buf: &mut Vec<u8>) {
let start = buf.len();
buf.extend_from_slice(&varint::encode(index as usize, 6));
buf[start] |= INDEXED_FIELD_LINE | INDEXED_STATIC_FLAG;
}
fn encode_literal_with_name_ref(index: u8, value: &[u8], buf: &mut Vec<u8>) {
let start = buf.len();
buf.extend_from_slice(&varint::encode(index as usize, 4));
buf[start] |= LITERAL_WITH_NAME_REF | NAME_REF_STATIC_FLAG;
encode_string(value, 7, buf);
}
fn encode_literal_with_literal_name(name: &[u8], value: &[u8], buf: &mut Vec<u8>) {
let start = buf.len();
encode_string(name, 3, buf);
buf[start] |= LITERAL_WITH_LITERAL_NAME;
encode_string(value, 7, buf);
}
fn encode_method(method: Method, buf: &mut Vec<u8>) {
let index = match method {
Method::Connect => Some(15),
Method::Delete => Some(16),
Method::Get => Some(17),
Method::Head => Some(18),
Method::Options => Some(19),
Method::Post => Some(20),
Method::Put => Some(21),
_ => None,
};
match index {
Some(i) => encode_indexed(i, buf),
None => encode_literal_with_name_ref(15, method.as_str().as_bytes(), buf),
}
}
fn encode_status(status: Status, buf: &mut Vec<u8>) {
let index = match status {
Status::EarlyHints => Some(24),
Status::Ok => Some(25),
Status::NotModified => Some(26),
Status::NotFound => Some(27),
Status::ServiceUnavailable => Some(28),
Status::Continue => Some(63),
Status::NoContent => Some(64),
Status::PartialContent => Some(65),
Status::Found => Some(66),
Status::BadRequest => Some(67),
Status::Forbidden => Some(68),
Status::MisdirectedRequest => Some(69),
Status::TooEarly => Some(70),
Status::InternalServerError => Some(71),
_ => None,
};
match index {
Some(i) => encode_indexed(i, buf),
None => encode_literal_with_name_ref(24, status.code().as_bytes(), buf),
}
}
fn encode_pseudo_string(name: &[u8], value: &str, name_match_index: u8, buf: &mut Vec<u8>) {
let full_match = match (name, value) {
(b":path", "/") => Some(1),
(b":scheme", "http") => Some(22),
(b":scheme", "https") => Some(23),
_ => None,
};
match full_match {
Some(i) => encode_indexed(i, buf),
None => encode_literal_with_name_ref(name_match_index, value.as_bytes(), buf),
}
}
fn encode_by_lookup(lookup: StaticLookup, name: &[u8], value: &[u8], buf: &mut Vec<u8>) {
match lookup {
StaticLookup::FullMatch(index) => encode_indexed(index, buf),
StaticLookup::NameMatch(index) => encode_literal_with_name_ref(index, value, buf),
StaticLookup::NoMatch => encode_literal_with_literal_name(name, value, buf),
}
}
fn encode_string(value: &[u8], prefix_size: u8, buf: &mut Vec<u8>) {
let huffman_encoded = huffman::encode(value);
let (bytes, huffman_flag) = if huffman_encoded.len() < value.len() {
(huffman_encoded.as_slice(), 1_u8 << prefix_size)
} else {
(value, 0)
};
let start = buf.len();
buf.extend_from_slice(&varint::encode(bytes.len(), prefix_size));
buf[start] |= huffman_flag;
buf.extend_from_slice(bytes);
}