Files
wgcloak/tests/http3.rs
T

122 lines
4.0 KiB
Rust

use std::collections::HashMap;
use wgcloak::transport::http3;
use wgcloak::transport::http3::{
decode_auth_challenge, decode_capsule, decode_http_datagram, encode_auth_challenge,
encode_auth_response, encode_capsule, encode_http_datagram, validate_connect_udp_request,
AuthChallenge, ConnectUdpRequest, Http3Error, CONNECT_UDP_CONTEXT_ID,
};
#[test]
fn validates_extended_connect_request() {
validate_connect_udp_request("CONNECT", "connect-udp", "/tunnel", "/tunnel").unwrap();
ConnectUdpRequest::new("/tunnel")
.validate("/tunnel")
.unwrap();
assert!(matches!(
validate_connect_udp_request("POST", "connect-udp", "/tunnel", "/tunnel"),
Err(Http3Error::InvalidRequest("method must be CONNECT"))
));
assert!(matches!(
validate_connect_udp_request("CONNECT", "webtransport", "/tunnel", "/tunnel"),
Err(Http3Error::InvalidRequest(":protocol must be connect-udp"))
));
assert!(validate_connect_udp_request("CONNECT", "connect-udp", "/other", "/tunnel").is_err());
assert!(validate_connect_udp_request("CONNECT", "connect-udp", "tunnel", "tunnel").is_err());
}
#[test]
fn http_datagram_uses_context_id_and_preserves_opaque_payload() {
for context in [0, 63, 64, 16_383, 16_384, (1 << 30) - 1, 1 << 30] {
let encoded = encode_http_datagram(context, b"opaque", 1200).unwrap();
assert_eq!(
decode_http_datagram(&encoded, context, 1200).unwrap(),
b"opaque"[..]
);
}
assert!(matches!(
decode_http_datagram(
&encode_http_datagram(7, b"x", 1200).unwrap(),
CONNECT_UDP_CONTEXT_ID,
1200
),
Err(Http3Error::ContextMismatch { .. })
));
assert!(matches!(
encode_http_datagram(CONNECT_UDP_CONTEXT_ID, &[0; 1201], 1200),
Err(Http3Error::Oversized {
length: 1201,
maximum: 1200
})
));
}
#[test]
fn capsule_round_trip_and_truncation_are_checked() {
let encoded = encode_capsule(0x2a, b"challenge").unwrap();
assert_eq!(
decode_capsule(&encoded).unwrap(),
(0x2a, bytes::Bytes::from_static(b"challenge"))
);
assert!(matches!(
decode_capsule(&encoded[..encoded.len() - 1]),
Err(Http3Error::Truncated)
));
let mut extra = encoded.to_vec();
extra.push(0);
assert!(matches!(
decode_capsule(&extra),
Err(Http3Error::InvalidCapsule)
));
}
#[test]
fn authentication_is_completed_before_session_activation() {
let challenge = AuthChallenge::random();
let nonce = [5u8; 16];
let secret = [7u8; 32];
let challenge_capsule = encode_auth_challenge(&challenge).unwrap();
assert_eq!(
decode_auth_challenge(&challenge_capsule).unwrap(),
challenge
);
assert_eq!(
decode_capsule(&challenge_capsule).unwrap().0,
http3::AUTH_CHALLENGE_CAPSULE
);
let response = encode_auth_response(&challenge, "laptop", &nonce, &secret).unwrap();
let mut clients = HashMap::new();
clients.insert("laptop".to_owned(), secret.to_vec());
let client = http3::verify_auth_response(&challenge, &response, &clients).unwrap();
assert_eq!(client.id, "laptop");
assert_eq!(client.nonce, nonce);
let bad = encode_auth_response(&challenge, "laptop", &nonce, &[8u8; 32]).unwrap();
assert!(http3::verify_auth_response(&challenge, &bad, &clients).is_err());
assert!(http3::is_auth_accept(&http3::encode_auth_accept().unwrap()).unwrap());
assert!(!http3::is_auth_accept(&http3::encode_auth_reject().unwrap()).unwrap());
}
#[test]
fn datagram_limit_includes_http_context_and_request_stream_overhead() {
use h3::quic::StreamId;
let limit = http3::maximum_application_datagram(
1200,
StreamId::try_from(0).unwrap(),
CONNECT_UDP_CONTEXT_ID,
1200,
)
.unwrap();
assert_eq!(limit, 1198);
assert!(matches!(
http3::maximum_application_datagram(
2,
StreamId::try_from(0).unwrap(),
CONNECT_UDP_CONTEXT_ID,
1200
),
Ok(0)
));
}