///|
pub fn assert_ok(result : Result[DecodedFrame, ProxyError]) -> DecodedFrame {
match result {
Ok(frame) => frame
Err(error) =>
abort("expected Ok, got error at \{error.offset}: \{error.context}")
}
}
///|
pub fn assert_err_kind(
result : Result[DecodedFrame, ProxyError],
expected : ProxyErrorKind,
) -> Unit {
match result {
Err(error) =>
if error.kind != expected {
abort("unexpected proxy error kind")
}
Ok(_) => abort("expected proxy error")
}
}
///|
pub fn assert_bytes_eq(actual : Bytes, expected : Bytes) -> Unit {
if !bytes_equal_constant_time(actual, expected) {
abort(
"byte sequences differ: \{hex_encode(actual)} != \{hex_encode(expected)}",
)
}
}
///|
fn raw_tlv_eq(left : RawTlv, right : RawTlv) -> Bool {
left.type_code == right.type_code &&
bytes_equal_constant_time(left.value, right.value)
}
///|
pub fn header_semantic_eq(left : ProxyHeader, right : ProxyHeader) -> Bool {
if left.version != right.version ||
left.command != right.command ||
left.family != right.family ||
left.transport != right.transport ||
left.tlvs.length() != right.tlvs.length() {
return false
}
for index = 0; index < left.tlvs.length(); index = index + 1 {
if !raw_tlv_eq(left.tlvs[index], right.tlvs[index]) {
return false
}
}
match (left.address, right.address) {
(NoAddress, NoAddress) => true
(RawUnsupported(a), RawUnsupported(b)) => bytes_equal_constant_time(a, b)
(Ipv4(a), Ipv4(b)) =>
bytes_equal_constant_time(
Bytes::from_array(a.source_address.octets),
Bytes::from_array(b.source_address.octets),
) &&
bytes_equal_constant_time(
Bytes::from_array(a.destination_address.octets),
Bytes::from_array(b.destination_address.octets),
) &&
a.source_port == b.source_port &&
a.destination_port == b.destination_port
(Ipv6(a), Ipv6(b)) =>
bytes_equal_constant_time(
Bytes::from_array(a.source_address.octets),
Bytes::from_array(b.source_address.octets),
) &&
bytes_equal_constant_time(
Bytes::from_array(a.destination_address.octets),
Bytes::from_array(b.destination_address.octets),
) &&
a.source_port == b.source_port &&
a.destination_port == b.destination_port
(UnixAddress(a), UnixAddress(b)) =>
bytes_equal_constant_time(a.source, b.source) &&
bytes_equal_constant_time(a.destination, b.destination)
_ => false
}
}
///|
pub fn assert_header_eq(actual : ProxyHeader, expected : ProxyHeader) -> Unit {
if !header_semantic_eq(actual, expected) {
abort("proxy headers are not semantically equal")
}
}
///|
pub fn assert_remaining_eq(frame : DecodedFrame, expected : Bytes) -> Unit {
assert_bytes_eq(frame.remaining, expected)
}
///|
pub fn assert_roundtrip_v1(header : ProxyHeader) -> Unit {
let encoded = encode_v1(header).unwrap()
let decoded = decode_v1(encoded, DecodePolicy::v1_only()).unwrap()
assert_header_eq(decoded.header, header)
if decoded.remaining.length() != 0 {
abort("v1 roundtrip produced payload")
}
}
///|
pub fn assert_roundtrip_v2(header : ProxyHeader) -> Unit {
let encoded = encode_v2(header).unwrap()
let decoded = decode_v2(encoded, DecodePolicy::v2_only()).unwrap()
assert_header_eq(decoded.header, header)
if decoded.remaining.length() != 0 {
abort("v2 roundtrip produced payload")
}
}