#![allow(clippy::vec_init_then_push, clippy::too_many_arguments, dead_code)]
pub const FRAME_TYPE_DATA: u8 = 0x00;
pub const FRAME_TYPE_HEADERS: u8 = 0x01;
pub const FRAME_TYPE_PRIORITY: u8 = 0x02;
pub const FRAME_TYPE_RST_STREAM: u8 = 0x03;
pub const FRAME_TYPE_SETTINGS: u8 = 0x04;
pub const FRAME_TYPE_PUSH_PROMISE: u8 = 0x05;
pub const FRAME_TYPE_PING: u8 = 0x06;
pub const FRAME_TYPE_GOAWAY: u8 = 0x07;
pub const FRAME_TYPE_WINDOW_UPDATE: u8 = 0x08;
pub const FRAME_TYPE_CONTINUATION: u8 = 0x09;
pub const FLAG_END_STREAM: u8 = 0x01;
pub const FLAG_END_HEADERS: u8 = 0x04;
pub const FLAG_PADDED: u8 = 0x08;
pub const FLAG_PRIORITY: u8 = 0x20;
pub const FLAG_ACK: u8 = 0x01;
pub const CONNECTION_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
fn build_frame_header(length: u32, frame_type: u8, flags: u8, stream_id: u32) -> Vec<u8> {
let mut header = Vec::with_capacity(9);
header.push((length >> 16) as u8);
header.push((length >> 8) as u8);
header.push(length as u8);
header.push(frame_type);
header.push(flags);
header.push((stream_id >> 24) as u8 & 0x7F);
header.push((stream_id >> 16) as u8);
header.push((stream_id >> 8) as u8);
header.push(stream_id as u8);
header
}
pub fn build_data_frame(stream_id: u32, data: &[u8], end_stream: bool) -> Vec<u8> {
let flags = if end_stream { FLAG_END_STREAM } else { 0 };
let mut frame = build_frame_header(data.len() as u32, FRAME_TYPE_DATA, flags, stream_id);
frame.extend_from_slice(data);
frame
}
pub fn build_data_frame_padded(
stream_id: u32,
data: &[u8],
padding_len: u8,
end_stream: bool,
) -> Vec<u8> {
let mut flags = FLAG_PADDED;
if end_stream {
flags |= FLAG_END_STREAM;
}
let total_len = 1 + data.len() + padding_len as usize;
let mut frame = build_frame_header(total_len as u32, FRAME_TYPE_DATA, flags, stream_id);
frame.push(padding_len);
frame.extend_from_slice(data);
frame.extend(std::iter::repeat_n(0u8, padding_len as usize));
frame
}
pub fn build_headers_frame(stream_id: u32, hpack_block: &[u8], flags: u8) -> Vec<u8> {
let mut frame = build_frame_header(
hpack_block.len() as u32,
FRAME_TYPE_HEADERS,
flags,
stream_id,
);
frame.extend_from_slice(hpack_block);
frame
}
pub fn build_complete_headers_frame(stream_id: u32, hpack_block: &[u8]) -> Vec<u8> {
build_headers_frame(stream_id, hpack_block, FLAG_END_HEADERS | FLAG_END_STREAM)
}
pub fn build_headers_frame_with_body(stream_id: u32, hpack_block: &[u8]) -> Vec<u8> {
build_headers_frame(stream_id, hpack_block, FLAG_END_HEADERS)
}
pub fn build_headers_frame_padded(
stream_id: u32,
hpack_block: &[u8],
padding_len: u8,
end_stream: bool,
end_headers: bool,
) -> Vec<u8> {
let mut flags = FLAG_PADDED;
if end_stream {
flags |= FLAG_END_STREAM;
}
if end_headers {
flags |= FLAG_END_HEADERS;
}
let total_len = 1 + hpack_block.len() + padding_len as usize;
let mut frame = build_frame_header(total_len as u32, FRAME_TYPE_HEADERS, flags, stream_id);
frame.push(padding_len);
frame.extend_from_slice(hpack_block);
frame.extend(std::iter::repeat_n(0u8, padding_len as usize));
frame
}
pub fn build_headers_frame_priority(
stream_id: u32,
hpack_block: &[u8],
stream_dependency: u32,
exclusive: bool,
weight: u8,
end_stream: bool,
end_headers: bool,
) -> Vec<u8> {
let mut flags = FLAG_PRIORITY;
if end_stream {
flags |= FLAG_END_STREAM;
}
if end_headers {
flags |= FLAG_END_HEADERS;
}
let total_len = 5 + hpack_block.len();
let mut frame = build_frame_header(total_len as u32, FRAME_TYPE_HEADERS, flags, stream_id);
let dep = if exclusive {
stream_dependency | 0x80000000
} else {
stream_dependency
};
frame.extend_from_slice(&dep.to_be_bytes());
frame.push(weight);
frame.extend_from_slice(hpack_block);
frame
}
pub fn build_headers_frame_padded_priority(
stream_id: u32,
hpack_block: &[u8],
padding_len: u8,
stream_dependency: u32,
exclusive: bool,
weight: u8,
end_stream: bool,
end_headers: bool,
) -> Vec<u8> {
let mut flags = FLAG_PADDED | FLAG_PRIORITY;
if end_stream {
flags |= FLAG_END_STREAM;
}
if end_headers {
flags |= FLAG_END_HEADERS;
}
let total_len = 1 + 5 + hpack_block.len() + padding_len as usize;
let mut frame = build_frame_header(total_len as u32, FRAME_TYPE_HEADERS, flags, stream_id);
frame.push(padding_len);
let dep = if exclusive {
stream_dependency | 0x80000000
} else {
stream_dependency
};
frame.extend_from_slice(&dep.to_be_bytes());
frame.push(weight);
frame.extend_from_slice(hpack_block);
frame.extend(std::iter::repeat_n(0u8, padding_len as usize));
frame
}
pub fn build_continuation_frame(stream_id: u32, hpack_block: &[u8], end_headers: bool) -> Vec<u8> {
let flags = if end_headers { FLAG_END_HEADERS } else { 0 };
let mut frame = build_frame_header(
hpack_block.len() as u32,
FRAME_TYPE_CONTINUATION,
flags,
stream_id,
);
frame.extend_from_slice(hpack_block);
frame
}
pub fn build_settings_frame(settings: &[(u16, u32)]) -> Vec<u8> {
let payload_len = settings.len() * 6;
let mut frame = build_frame_header(payload_len as u32, FRAME_TYPE_SETTINGS, 0, 0);
for (id, value) in settings {
frame.extend_from_slice(&id.to_be_bytes());
frame.extend_from_slice(&value.to_be_bytes());
}
frame
}
pub fn build_empty_settings_frame() -> Vec<u8> {
build_frame_header(0, FRAME_TYPE_SETTINGS, 0, 0)
}
pub fn build_settings_ack_frame() -> Vec<u8> {
build_frame_header(0, FRAME_TYPE_SETTINGS, FLAG_ACK, 0)
}
pub fn build_window_update_frame(stream_id: u32, increment: u32) -> Vec<u8> {
let mut frame = build_frame_header(4, FRAME_TYPE_WINDOW_UPDATE, 0, stream_id);
frame.extend_from_slice(&(increment & 0x7FFFFFFF).to_be_bytes());
frame
}
pub fn build_ping_frame(data: &[u8; 8], ack: bool) -> Vec<u8> {
let flags = if ack { FLAG_ACK } else { 0 };
let mut frame = build_frame_header(8, FRAME_TYPE_PING, flags, 0);
frame.extend_from_slice(data);
frame
}
pub fn build_goaway_frame(last_stream_id: u32, error_code: u32) -> Vec<u8> {
let mut frame = build_frame_header(8, FRAME_TYPE_GOAWAY, 0, 0);
frame.extend_from_slice(&(last_stream_id & 0x7FFFFFFF).to_be_bytes());
frame.extend_from_slice(&error_code.to_be_bytes());
frame
}
pub fn build_rst_stream_frame(stream_id: u32, error_code: u32) -> Vec<u8> {
let mut frame = build_frame_header(4, FRAME_TYPE_RST_STREAM, 0, stream_id);
frame.extend_from_slice(&error_code.to_be_bytes());
frame
}
pub fn hpack_literal_with_indexing(name: &str, value: &str) -> Vec<u8> {
let mut encoded = Vec::new();
encoded.push(0x40);
encoded.push(name.len() as u8);
encoded.extend_from_slice(name.as_bytes());
encoded.push(value.len() as u8);
encoded.extend_from_slice(value.as_bytes());
encoded
}
pub fn hpack_literal_without_indexing(name: &str, value: &str) -> Vec<u8> {
let mut encoded = Vec::new();
encoded.push(0x00);
encoded.push(name.len() as u8);
encoded.extend_from_slice(name.as_bytes());
encoded.push(value.len() as u8);
encoded.extend_from_slice(value.as_bytes());
encoded
}
pub fn hpack_indexed(index: u8) -> Vec<u8> {
vec![0x80 | index]
}
pub mod hpack_static {
pub fn method_get() -> Vec<u8> {
vec![0x82]
}
pub fn method_post() -> Vec<u8> {
vec![0x83]
}
pub fn path_root() -> Vec<u8> {
vec![0x84]
}
pub fn path_index_html() -> Vec<u8> {
vec![0x85]
}
pub fn scheme_http() -> Vec<u8> {
vec![0x86]
}
pub fn scheme_https() -> Vec<u8> {
vec![0x87]
}
pub fn status_200() -> Vec<u8> {
vec![0x88]
}
pub fn status_204() -> Vec<u8> {
vec![0x89]
}
pub fn status_206() -> Vec<u8> {
vec![0x8a]
}
pub fn status_304() -> Vec<u8> {
vec![0x8b]
}
pub fn status_400() -> Vec<u8> {
vec![0x8c]
}
pub fn status_404() -> Vec<u8> {
vec![0x8d]
}
pub fn status_500() -> Vec<u8> {
vec![0x8e]
}
}
pub fn hpack_get_request(path: &str, authority: &str) -> Vec<u8> {
let mut block = Vec::new();
block.extend(hpack_static::method_get());
block.extend(hpack_static::scheme_https());
if path == "/" {
block.extend(hpack_static::path_root());
} else {
block.extend(hpack_literal_without_indexing(":path", path));
}
block.extend(hpack_literal_without_indexing(":authority", authority));
block
}
pub fn hpack_post_request(path: &str, authority: &str) -> Vec<u8> {
let mut block = Vec::new();
block.extend(hpack_static::method_post());
block.extend(hpack_static::scheme_https());
if path == "/" {
block.extend(hpack_static::path_root());
} else {
block.extend(hpack_literal_without_indexing(":path", path));
}
block.extend(hpack_literal_without_indexing(":authority", authority));
block
}
pub fn connection_start() -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(CONNECTION_PREFACE);
data.extend(build_empty_settings_frame());
data
}
pub mod hpack_huffman {
fn huffman_length(len: usize) -> Vec<u8> {
if len < 127 {
vec![0x80 | len as u8]
} else {
let mut result = vec![0xFF]; let mut remaining = len - 127;
while remaining >= 128 {
result.push(0x80 | (remaining & 0x7F) as u8);
remaining >>= 7;
}
result.push(remaining as u8);
result
}
}
pub fn www_example_com() -> Vec<u8> {
vec![
0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
]
}
pub fn no_cache() -> Vec<u8> {
vec![0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf]
}
pub fn custom_key() -> Vec<u8> {
vec![0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f]
}
pub fn custom_value() -> Vec<u8> {
vec![0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf]
}
pub fn literal_huffman(name_huffman: &[u8], value_huffman: &[u8]) -> Vec<u8> {
let mut encoded = Vec::new();
encoded.push(0x00);
encoded.extend(huffman_length(name_huffman.len()));
encoded.extend_from_slice(name_huffman);
encoded.extend(huffman_length(value_huffman.len()));
encoded.extend_from_slice(value_huffman);
encoded
}
pub fn literal_indexed_name_huffman_value(name_index: u8, value_huffman: &[u8]) -> Vec<u8> {
let mut encoded = Vec::new();
encoded.push(name_index & 0x0F);
encoded.extend(huffman_length(value_huffman.len()));
encoded.extend_from_slice(value_huffman);
encoded
}
}
pub fn hpack_fill_dynamic_table(table_size: usize) -> Vec<u8> {
let mut block = Vec::new();
let mut total_size = 0;
let mut i = 0;
while total_size < table_size + 100 {
let name = format!("x-header-{:04}", i);
let value = format!("value-{:04}", i);
let entry_size = name.len() + value.len() + 32;
block.extend(super::hpack_literal_with_indexing(&name, &value));
total_size += entry_size;
i += 1;
}
block
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_data_frame() {
let frame = build_data_frame(1, b"hello", false);
assert_eq!(frame.len(), 9 + 5); assert_eq!(frame[3], FRAME_TYPE_DATA);
assert_eq!(frame[4], 0); assert_eq!(&frame[9..], b"hello");
}
#[test]
fn test_build_data_frame_end_stream() {
let frame = build_data_frame(1, b"hello", true);
assert_eq!(frame[4], FLAG_END_STREAM);
}
#[test]
fn test_build_settings_frame() {
let frame = build_settings_frame(&[(0x04, 65535)]);
assert_eq!(frame.len(), 9 + 6);
assert_eq!(frame[3], FRAME_TYPE_SETTINGS);
}
#[test]
fn test_build_window_update_frame() {
let frame = build_window_update_frame(0, 1000);
assert_eq!(frame.len(), 9 + 4);
assert_eq!(frame[3], FRAME_TYPE_WINDOW_UPDATE);
}
#[test]
fn test_hpack_indexed() {
let encoded = hpack_indexed(2);
assert_eq!(encoded, vec![0x82]); }
}