#[cfg(test)]
mod tests {
use std::fs;
use std::path::{Path, PathBuf};
struct ProtocolGoldenTester {
test_name: String,
base_path: PathBuf,
}
impl ProtocolGoldenTester {
fn new(test_name: &str) -> Self {
let base_path = Path::new("tests/golden").join("protocol");
Self {
test_name: test_name.to_string(),
base_path,
}
}
fn assert_golden(&self, actual: &str) {
let golden_path = self.base_path.join(format!("{}.golden", self.test_name));
if std::env::var("UPDATE_GOLDENS").is_ok() {
fs::create_dir_all(golden_path.parent().unwrap()).unwrap();
fs::write(&golden_path, actual).unwrap();
eprintln!("[PROTOCOL GOLDEN] Updated: {}", golden_path.display());
return;
}
let expected = fs::read_to_string(&golden_path).unwrap_or_else(|_| {
panic!(
"Protocol golden file missing: {}\n\
Run with UPDATE_GOLDENS=1 to create it",
golden_path.display()
)
});
let expected = self.canonicalize(&expected);
if actual != expected {
let actual_path = golden_path.with_extension("actual");
fs::write(&actual_path, actual).unwrap();
panic!(
"PROTOCOL GOLDEN MISMATCH: {}\n\
Expected length: {}, Actual length: {}\n\
To update: UPDATE_GOLDENS=1 cargo test -- {}\n\
To review: diff {} {}",
self.test_name,
expected.len(),
actual.len(),
self.test_name,
golden_path.display(),
actual_path.display(),
);
}
}
fn assert_binary_golden(&self, actual_bytes: &[u8]) {
let hex_output = hex::encode(actual_bytes);
let formatted = hex_output
.chars()
.collect::<Vec<_>>()
.chunks(64)
.map(|chunk| chunk.iter().collect::<String>())
.collect::<Vec<_>>()
.join("\n");
self.assert_golden(&formatted);
}
fn canonicalize(&self, output: &str) -> String {
output
.replace("\r\n", "\n")
.lines()
.map(|l| l.trim_end())
.collect::<Vec<_>>()
.join("\n")
.trim_end_matches('\n')
.to_string()
}
}
#[test]
fn golden_hpack_literal_header_encoding() {
let tester = ProtocolGoldenTester::new("hpack_literal_header_encoding");
let test_cases = [
("custom-header", "custom-value"),
(":method", "DELETE"),
(":path", "/search?q=test"),
("user-agent", "asupersync/0.1"),
("accept-encoding", "gzip, br"),
];
let mut output = String::new();
output.push_str("# HPACK Literal Header Field Encoding (RFC 7541 §6.2.1)\n\n");
for (name, value) in &test_cases {
let name_bytes = name.as_bytes();
let value_bytes = value.as_bytes();
let mut encoded = Vec::new();
encoded.push(0b01000000); encoded.push(name_bytes.len() as u8); encoded.extend_from_slice(name_bytes);
encoded.push(value_bytes.len() as u8); encoded.extend_from_slice(value_bytes);
output.push_str(&format!("Header: {} = {}\n", name, value));
output.push_str(&format!("Encoded: {}\n", hex::encode(&encoded)));
output.push_str(&format!("Length: {} bytes\n\n", encoded.len()));
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_hpack_indexed_header_encoding() {
let tester = ProtocolGoldenTester::new("hpack_indexed_header_encoding");
let indexed_headers = [
(2, ":method", "GET"), (4, ":path", "/"), (6, ":scheme", "http"), (8, ":status", "200"), (14, ":status", "303"), ];
let mut output = String::new();
output.push_str("# HPACK Indexed Header Field Encoding (RFC 7541 §6.1)\n\n");
for (index, name, value) in &indexed_headers {
let encoded_byte = 0b10000000 | (index & 0b01111111);
output.push_str(&format!("Index {}: {} = {}\n", index, name, value));
output.push_str(&format!("Encoded: {:02x}\n", encoded_byte));
output.push_str(&format!("Binary: {:08b}\n\n", encoded_byte));
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_hpack_huffman_encoding_examples() {
let tester = ProtocolGoldenTester::new("hpack_huffman_encoding_examples");
let huffman_examples = [
("302", "302"), ("private", "private"), ("Mon, 21 Oct 2013 20:13:21 GMT", "date"), ("https://www.example.com", "url"), ];
let mut output = String::new();
output.push_str("# HPACK Huffman Encoding Examples (RFC 7541 Appendix C)\n\n");
for (plaintext, category) in &huffman_examples {
let encoded = huffman_encode_simple(plaintext);
output.push_str(&format!("Category: {}\n", category));
output.push_str(&format!("Plaintext: {}\n", plaintext));
output.push_str(&format!(
"Plain bytes: {}\n",
hex::encode(plaintext.as_bytes())
));
output.push_str(&format!("Huffman encoded: {}\n", hex::encode(&encoded)));
output.push_str(&format!(
"Compression ratio: {:.1}%\n\n",
(encoded.len() as f64 / plaintext.len() as f64) * 100.0
));
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_h2_frame_headers() {
let tester = ProtocolGoldenTester::new("h2_frame_headers");
let frame_examples = [
(
"SETTINGS",
0x4,
0x0,
0,
vec![0x00, 0x01, 0x00, 0x00, 0x10, 0x00],
),
("WINDOW_UPDATE", 0x8, 0x0, 1, vec![0x00, 0x00, 0x40, 0x00]),
("HEADERS", 0x1, 0x4, 1, vec![0x82, 0x86, 0x84, 0x41, 0x0f]),
("DATA", 0x0, 0x1, 1, vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]),
];
let mut output = String::new();
output.push_str("# HTTP/2 Frame Headers (RFC 7540 §4.1)\n\n");
output.push_str(
"# Frame format: [Length:24][Type:8][Flags:8][R:1][Stream ID:31][Payload]\n\n",
);
for (name, frame_type, flags, stream_id, payload) in &frame_examples {
let length = payload.len() as u32;
let mut frame = Vec::new();
frame.push((length >> 16) as u8);
frame.push((length >> 8) as u8);
frame.push(length as u8);
frame.push(*frame_type);
frame.push(*flags);
frame.push((stream_id >> 24) as u8);
frame.push((stream_id >> 16) as u8);
frame.push((stream_id >> 8) as u8);
frame.push(*stream_id as u8);
frame.extend_from_slice(payload);
output.push_str(&format!("Frame: {}\n", name));
output.push_str(&format!(
"Type: 0x{:02x}, Flags: 0x{:02x}, Stream: {}\n",
frame_type, flags, stream_id
));
output.push_str(&format!("Length: {} bytes\n", payload.len()));
output.push_str(&format!("Wire format: {}\n", hex::encode(&frame)));
let header = &frame[0..9];
output.push_str(&format!("Header breakdown:\n"));
output.push_str(&format!(
" Length: {:02x}{:02x}{:02x} ({} bytes)\n",
header[0], header[1], header[2], length
));
output.push_str(&format!(" Type: {:02x} ({})\n", header[3], name));
output.push_str(&format!(" Flags: {:02x}\n", header[4]));
output.push_str(&format!(
" Stream: {:02x}{:02x}{:02x}{:02x} ({})\n",
header[5], header[6], header[7], header[8], stream_id
));
output.push_str("\n");
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_h2_settings_frame_canonical() {
let tester = ProtocolGoldenTester::new("h2_settings_frame_canonical");
let settings_parameters = [
(0x1, 4096), (0x2, 1), (0x3, 100), (0x4, 65535), (0x5, 16384), (0x6, 100), ];
let mut payload = Vec::new();
for (identifier, value) in &settings_parameters {
payload.push((identifier >> 8) as u8);
payload.push(*identifier as u8);
payload.push((value >> 24) as u8);
payload.push((value >> 16) as u8);
payload.push((value >> 8) as u8);
payload.push(*value as u8);
}
let length = payload.len() as u32;
let mut frame = Vec::new();
frame.extend_from_slice(&length.to_be_bytes()[1..4]); frame.push(0x4); frame.push(0x0); frame.extend_from_slice(&0u32.to_be_bytes()); frame.extend_from_slice(&payload);
let mut output = String::new();
output.push_str("# HTTP/2 SETTINGS Frame Canonical Format (RFC 7540 §6.5)\n\n");
output.push_str(&format!("Frame length: {} bytes\n", length));
output.push_str(&format!(
"Parameters count: {}\n\n",
settings_parameters.len()
));
for (i, (identifier, value)) in settings_parameters.iter().enumerate() {
let setting_name = match identifier {
0x1 => "HEADER_TABLE_SIZE",
0x2 => "ENABLE_PUSH",
0x3 => "MAX_CONCURRENT_STREAMS",
0x4 => "INITIAL_WINDOW_SIZE",
0x5 => "MAX_FRAME_SIZE",
0x6 => "MAX_HEADER_LIST_SIZE",
_ => "UNKNOWN",
};
let offset = 9 + i * 6; let setting_bytes = &frame[offset..offset + 6];
output.push_str(&format!(
"Setting {}: {} = {}\n",
i + 1,
setting_name,
value
));
output.push_str(&format!(" ID: 0x{:04x}, Value: {}\n", identifier, value));
output.push_str(&format!(" Bytes: {}\n\n", hex::encode(setting_bytes)));
}
output.push_str(&format!("Complete frame: {}\n", hex::encode(&frame)));
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_h3_frame_types() {
let tester = ProtocolGoldenTester::new("h3_frame_types");
let h3_frame_types = [
(0x0, "DATA", "Application data"),
(0x1, "HEADERS", "Header block"),
(0x3, "CANCEL_PUSH", "Cancel server push"),
(0x4, "SETTINGS", "Connection settings"),
(0x5, "PUSH_PROMISE", "Push promise"),
(0x7, "GOAWAY", "Graceful connection close"),
(0xd, "MAX_PUSH_ID", "Maximum push identifier"),
];
let mut output = String::new();
output.push_str("# HTTP/3 Frame Types (RFC 9114 §7.2)\n\n");
output.push_str("# Frame format: [Type:varint][Length:varint][Payload]\n\n");
for (frame_type, name, description) in &h3_frame_types {
let type_varint = encode_varint(*frame_type);
output.push_str(&format!("Type: 0x{:02x} - {}\n", frame_type, name));
output.push_str(&format!("Description: {}\n", description));
output.push_str(&format!("Type varint: {}\n", hex::encode(&type_varint)));
output.push_str(&format!("Type binary: {:08b}\n\n", frame_type));
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_h3_settings_frame() {
let tester = ProtocolGoldenTester::new("h3_settings_frame");
let h3_settings = [
(0x1, 100, "QPACK_MAX_TABLE_CAPACITY"),
(0x6, 100, "QPACK_BLOCKED_STREAMS"),
(0x8, 3, "H3_DATAGRAM"),
];
let mut frame_payload = Vec::new();
for (identifier, value, _name) in &h3_settings {
frame_payload.extend_from_slice(&encode_varint(*identifier));
frame_payload.extend_from_slice(&encode_varint(*value));
}
let frame_type = encode_varint(0x4); let frame_length = encode_varint(frame_payload.len() as u64);
let mut output = String::new();
output.push_str("# HTTP/3 SETTINGS Frame (RFC 9114 §7.2.4)\n\n");
output.push_str("Frame components:\n");
output.push_str(&format!("Type: {} (SETTINGS)\n", hex::encode(&frame_type)));
output.push_str(&format!(
"Length: {} ({} bytes payload)\n",
hex::encode(&frame_length),
frame_payload.len()
));
output.push_str("\nSettings parameters:\n");
for (identifier, value, name) in &h3_settings {
output.push_str(&format!(" {} (0x{:x}): {}\n", name, identifier, value));
output.push_str(&format!(
" ID varint: {}\n",
hex::encode(&encode_varint(*identifier))
));
output.push_str(&format!(
" Value varint: {}\n",
hex::encode(&encode_varint(*value))
));
}
let mut complete_frame = Vec::new();
complete_frame.extend_from_slice(&frame_type);
complete_frame.extend_from_slice(&frame_length);
complete_frame.extend_from_slice(&frame_payload);
output.push_str(&format!(
"\nComplete frame: {}\n",
hex::encode(&complete_frame)
));
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_websocket_frame_masking_algorithm() {
let tester = ProtocolGoldenTester::new("websocket_frame_masking_algorithm");
let test_payloads = [
b"Hello".to_vec(),
b"WebSocket".to_vec(),
b"The quick brown fox jumps over the lazy dog".to_vec(),
(0..16).collect::<Vec<u8>>(), ];
let masking_keys = [
[0x37, 0xfa, 0x21, 0x3d],
[0x12, 0x34, 0x56, 0x78],
[0xff, 0x00, 0xaa, 0x55],
[0x00, 0x00, 0x00, 0x00], ];
let mut output = String::new();
output.push_str("# WebSocket Frame Masking Algorithm (RFC 6455 §5.3)\n\n");
output.push_str("# Algorithm: transformed-octet-i = original-octet-i XOR masking-key-octet-[i MOD 4]\n\n");
for (payload_idx, payload) in test_payloads.iter().enumerate() {
let masking_key = masking_keys[payload_idx % masking_keys.len()];
let mut masked_payload = payload.clone();
for (i, byte) in masked_payload.iter_mut().enumerate() {
*byte ^= masking_key[i % 4];
}
output.push_str(&format!("Test case {}:\n", payload_idx + 1));
output.push_str(&format!("Original bytes: {}\n", hex::encode(payload)));
output.push_str(&format!(
"Masking key: {:02x}{:02x}{:02x}{:02x}\n",
masking_key[0], masking_key[1], masking_key[2], masking_key[3]
));
output.push_str(&format!("Masked: {}\n", hex::encode(&masked_payload)));
let mut unmasked = masked_payload.clone();
for (i, byte) in unmasked.iter_mut().enumerate() {
*byte ^= masking_key[i % 4];
}
output.push_str(&format!("Unmasked: {}\n", hex::encode(&unmasked)));
output.push_str(&format!("Round-trip: {}\n\n", payload == &unmasked));
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_websocket_frame_format() {
let tester = ProtocolGoldenTester::new("websocket_frame_format");
let extended_text_payload = [b'a'; 126];
let frame_examples: [(bool, u8, bool, &[u8], [u8; 4]); 5] = [
(true, 0x1, true, b"Hello", [0x12, 0x34, 0x56, 0x78]),
(
false,
0x2,
false,
b"\x01\x02\x03\x04",
[0x00, 0x00, 0x00, 0x00],
),
(true, 0x9, true, b"ping", [0xab, 0xcd, 0xef, 0x01]),
(false, 0x8, false, &[0x03, 0xe8], [0x00, 0x00, 0x00, 0x00]), (
true,
0x1,
false,
&extended_text_payload,
[0x00, 0x00, 0x00, 0x00],
),
];
let mut output = String::new();
output.push_str("# WebSocket Frame Format (RFC 6455 §5.2)\n\n");
output.push_str("# Frame format: [FIN:1][RSV:3][Opcode:4][MASK:1][Payload len:7|16|64][Masking key:32][Payload]\n\n");
for (fin, opcode, masked, payload, mask_key) in &frame_examples {
let mut frame = Vec::new();
let first_byte = if *fin { 0x80 } else { 0x00 } | (opcode & 0x0f);
frame.push(first_byte);
let mask_bit = if *masked { 0x80 } else { 0x00 };
if payload.len() < 126 {
frame.push(mask_bit | (payload.len() as u8));
} else {
frame.push(mask_bit | 126);
frame.extend_from_slice(&(payload.len() as u16).to_be_bytes());
}
if *masked {
frame.extend_from_slice(mask_key);
}
if *masked {
for (i, &byte) in payload.iter().enumerate() {
frame.push(byte ^ mask_key[i % 4]);
}
} else {
frame.extend_from_slice(payload);
}
let opcode_name = match opcode {
0x0 => "CONTINUATION",
0x1 => "TEXT",
0x2 => "BINARY",
0x8 => "CLOSE",
0x9 => "PING",
0xa => "PONG",
_ => "UNKNOWN",
};
output.push_str(&format!(
"Frame: {} (0x{:x}), FIN={}, MASK={}\n",
opcode_name, opcode, fin, masked
));
output.push_str(&format!("Payload length: {} bytes\n", payload.len()));
output.push_str(&format!("Original payload: {}\n", hex::encode(payload)));
if *masked {
output.push_str(&format!(
"Masking key: {:02x}{:02x}{:02x}{:02x}\n",
mask_key[0], mask_key[1], mask_key[2], mask_key[3]
));
}
output.push_str(&format!("Wire format: {}\n", hex::encode(&frame)));
output.push_str(&format!("Frame length: {} bytes\n\n", frame.len()));
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_websocket_extended_payload_wire_bytes() {
let tester = ProtocolGoldenTester::new("websocket_extended_payload_wire_bytes");
let payload = [b'a'; 126];
let mut frame = vec![0x81, 0x7e];
frame.extend_from_slice(&(payload.len() as u16).to_be_bytes());
frame.extend_from_slice(&payload);
tester.assert_binary_golden(&frame);
}
#[test]
fn golden_raptorq_rfc6330_lookup_tables() {
let tester = ProtocolGoldenTester::new("raptorq_rfc6330_lookup_tables");
let v0_sample: &[u32] = &[
251291136, 3952231631, 3370958628, 4070167936, 123631495, 3351110283, 3218676425,
2011642291,
];
let v1_sample: &[u32] = &[
807385413, 2043073223, 3336749796, 1302105833, 2278607931, 541015020, 1684564270,
372709334,
];
let v2_sample: &[u32] = &[
1629829892, 282540176, 2794583710, 496504798, 2990494426, 3070701851, 2575963183,
4094823972,
];
let mut output = String::new();
output.push_str("# RaptorQ RFC 6330 Lookup Tables (Section 5.5)\n\n");
output.push_str("# These tables implement the pseudo-random number generator\n");
output.push_str("# used for systematic and repair symbol generation\n\n");
output.push_str("V0 Table (first 8 entries):\n");
for (i, &value) in v0_sample.iter().enumerate() {
output.push_str(&format!(" V0[{:3}] = 0x{:08x} = {}\n", i, value, value));
}
output.push_str("\nV1 Table (first 8 entries):\n");
for (i, &value) in v1_sample.iter().enumerate() {
output.push_str(&format!(" V1[{:3}] = 0x{:08x} = {}\n", i, value, value));
}
output.push_str("\nV2 Table (first 8 entries):\n");
for (i, &value) in v2_sample.iter().enumerate() {
output.push_str(&format!(" V2[{:3}] = 0x{:08x} = {}\n", i, value, value));
}
output.push_str("\nPRNG Function Test (X=1, I=0..7):\n");
for i in 0..8 {
let x = 1u32;
let prng_value = prng_function(x, i, v0_sample, v1_sample);
output.push_str(&format!(" PRNG(X=1, I={}): 0x{:08x}\n", i, prng_value));
}
tester.assert_golden(&tester.canonicalize(&output));
}
#[test]
fn golden_raptorq_systematic_index_verification() {
let tester = ProtocolGoldenTester::new("raptorq_systematic_index_verification");
let test_k_values = [4, 8, 16, 32, 64];
let mut output = String::new();
output.push_str("# RaptorQ Systematic Index Calculation (RFC 6330 §5.3.3.4.1)\n\n");
for &k in &test_k_values {
output.push_str(&format!("K = {} source symbols:\n", k));
let s = calculate_s(k);
let h = s / 2;
output.push_str(&format!(" S (LDPC symbols): {}\n", s));
output.push_str(&format!(" H (Half symbols): {}\n", h));
output.push_str(&format!(" L (Intermediate): {}\n", k + s + h));
output.push_str(" Systematic indices: ");
let systematic: Vec<u32> = (0..k).collect();
output.push_str(&format!("{:?}\n", systematic));
output.push_str(" First repair indices: ");
let repair: Vec<u32> = (k..k + 4).collect();
output.push_str(&format!("{:?}\n\n", repair));
}
tester.assert_golden(&tester.canonicalize(&output));
}
fn huffman_encode_simple(input: &str) -> Vec<u8> {
let bytes = input.as_bytes();
let mut encoded = Vec::new();
for byte in bytes {
match byte {
b' ' => encoded.push(0x80), b'e' => encoded.push(0x81), b't' => encoded.push(0x82), _ => encoded.push(*byte), }
}
encoded
}
fn encode_varint(value: u64) -> Vec<u8> {
if value < 0x40 {
vec![value as u8]
} else if value < 0x4000 {
vec![0x40 | (value >> 8) as u8, value as u8]
} else if value < 0x40000000 {
vec![
0x80 | (value >> 24) as u8,
(value >> 16) as u8,
(value >> 8) as u8,
value as u8,
]
} else {
vec![
0xc0 | (value >> 56) as u8,
(value >> 48) as u8,
(value >> 40) as u8,
(value >> 32) as u8,
(value >> 24) as u8,
(value >> 16) as u8,
(value >> 8) as u8,
value as u8,
]
}
}
fn prng_function(x: u32, i: u32, v0: &[u32], v1: &[u32]) -> u32 {
let v0_idx = ((x + i) % v0.len() as u32) as usize;
let v1_idx = (i % v1.len() as u32) as usize;
v0[v0_idx] ^ v1[v1_idx]
}
fn calculate_s(k: u32) -> u32 {
match k {
1..=4 => 2,
5..=8 => 3,
9..=16 => 4,
17..=32 => 5,
33..=64 => 6,
65..=128 => 7,
129..=256 => 8,
257..=512 => 9,
_ => 10,
}
}
}