T1.5: Migrate emit/parse sites to v2 wire format

This commit is contained in:
Siavash Sameni
2026-05-11 12:36:45 +04:00
parent 9680b6ff34
commit c93d302656
120 changed files with 5953 additions and 2888 deletions

View File

@@ -22,8 +22,8 @@ pub fn server_config() -> (quinn::ServerConfig, Vec<u8>) {
/// Create a server configuration with a deterministic self-signed certificate
/// derived from a 32-byte seed. Same seed = same cert = same TLS fingerprint.
pub fn server_config_from_seed(seed: &[u8; 32]) -> (quinn::ServerConfig, Vec<u8>) {
use ed25519_dalek::pkcs8::EncodePrivateKey;
use ed25519_dalek::SigningKey;
use ed25519_dalek::pkcs8::EncodePrivateKey;
use hkdf::Hkdf;
use sha2::Sha256;
@@ -35,22 +35,23 @@ pub fn server_config_from_seed(seed: &[u8; 32]) -> (quinn::ServerConfig, Vec<u8>
// Create Ed25519 signing key and export as PKCS8 DER
let signing_key = SigningKey::from_bytes(&ed_bytes);
let pkcs8_doc = signing_key.to_pkcs8_der()
let pkcs8_doc = signing_key
.to_pkcs8_der()
.expect("failed to encode Ed25519 key as PKCS8");
let key_der_for_rcgen = rustls::pki_types::PrivateKeyDer::try_from(pkcs8_doc.as_bytes().to_vec())
.expect("failed to wrap PKCS8 DER");
let key_der_for_rcgen =
rustls::pki_types::PrivateKeyDer::try_from(pkcs8_doc.as_bytes().to_vec())
.expect("failed to wrap PKCS8 DER");
// Create rcgen KeyPair from DER
let key_pair = rcgen::KeyPair::from_der_and_sign_algo(
&key_der_for_rcgen,
&rcgen::PKCS_ED25519,
)
.expect("failed to create KeyPair from seed-derived Ed25519 key");
let key_pair = rcgen::KeyPair::from_der_and_sign_algo(&key_der_for_rcgen, &rcgen::PKCS_ED25519)
.expect("failed to create KeyPair from seed-derived Ed25519 key");
// Build self-signed cert with this deterministic keypair
let params = rcgen::CertificateParams::new(vec!["localhost".to_string()])
.expect("failed to create CertificateParams");
let cert = params.self_signed(&key_pair).expect("failed to self-sign cert");
let cert = params
.self_signed(&key_pair)
.expect("failed to self-sign cert");
let cert_der = rustls::pki_types::CertificateDer::from(cert.der().to_vec());
let key_der = rustls::pki_types::PrivateKeyDer::try_from(key_pair.serialize_der())
.expect("failed to serialize key DER");
@@ -62,7 +63,7 @@ pub fn server_config_from_seed(seed: &[u8; 32]) -> (quinn::ServerConfig, Vec<u8>
///
/// Format: `xx:xx:xx:xx:...` (32 bytes = 64 hex chars with colons).
pub fn tls_fingerprint(cert_der: &[u8]) -> String {
use sha2::{Sha256, Digest};
use sha2::{Digest, Sha256};
let hash = Sha256::digest(cert_der);
hash.iter()
.map(|b| format!("{b:02x}"))
@@ -148,7 +149,7 @@ fn transport_config() -> quinn::TransportConfig {
let mut mtu_config = quinn::MtuDiscoveryConfig::default();
mtu_config
.upper_bound(1452)
.interval(Duration::from_secs(300)) // re-probe every 5 min
.interval(Duration::from_secs(300)) // re-probe every 5 min
.black_hole_cooldown(Duration::from_secs(30)); // retry faster on lossy links
config.mtu_discovery_config(Some(mtu_config));
config.initial_mtu(1200); // safe starting point

View File

@@ -28,13 +28,13 @@ pub async fn connect(
server_name: &str,
config: quinn::ClientConfig,
) -> Result<quinn::Connection, TransportError> {
let connecting = endpoint.connect_with(config, addr, server_name).map_err(|e| {
TransportError::Internal(format!("connect error: {e}"))
})?;
let connecting = endpoint
.connect_with(config, addr, server_name)
.map_err(|e| TransportError::Internal(format!("connect error: {e}")))?;
let connection = connecting.await.map_err(|e| {
TransportError::Internal(format!("connection failed: {e}"))
})?;
let connection = connecting
.await
.map_err(|e| TransportError::Internal(format!("connection failed: {e}")))?;
Ok(connection)
}
@@ -111,9 +111,9 @@ pub async fn accept(endpoint: &quinn::Endpoint) -> Result<quinn::Connection, Tra
.await
.ok_or(TransportError::ConnectionLost)?;
let connection = incoming.await.map_err(|e| {
TransportError::Internal(format!("accept failed: {e}"))
})?;
let connection = incoming
.await
.map_err(|e| TransportError::Internal(format!("accept failed: {e}")))?;
Ok(connection)
}

View File

@@ -26,22 +26,20 @@ pub fn max_datagram_payload(connection: &quinn::Connection) -> Option<usize> {
mod tests {
use super::*;
use bytes::Bytes;
use wzp_proto::{CodecId, MediaHeader};
use wzp_proto::{CodecId, MediaHeader, MediaType};
fn test_packet() -> MediaPacket {
MediaPacket {
header: MediaHeader {
version: 0,
is_repair: false,
version: 2,
flags: 0,
media_type: MediaType::Audio,
codec_id: CodecId::Opus16k,
has_quality_report: false,
fec_ratio_encoded: 16,
stream_id: 0,
fec_ratio: 16,
seq: 42,
timestamp: 1000,
fec_block: 1,
fec_symbol: 0,
reserved: 0,
csrc_count: 0,
},
payload: Bytes::from_static(b"fake opus frame data"),
quality_report: None,
@@ -61,7 +59,7 @@ mod tests {
#[test]
fn serialize_deserialize_with_quality_report() {
let mut packet = test_packet();
packet.header.has_quality_report = true;
packet.header.flags |= MediaHeader::FLAG_QUALITY;
packet.quality_report = Some(wzp_proto::QualityReport {
loss_pct: 50,
rtt_4ms: 75,

View File

@@ -30,7 +30,7 @@ pub struct PathMonitor {
first_recv_time_ms: Option<u64>,
last_recv_time_ms: Option<u64>,
/// Sequence tracking for loss detection.
highest_sent_seq: Option<u16>,
highest_sent_seq: Option<u32>,
total_sent: u64,
total_received: u64,
/// Last observed RTT for jitter calculation.
@@ -64,7 +64,7 @@ impl PathMonitor {
}
/// Record that we sent a packet with the given sequence number and timestamp.
pub fn observe_sent(&mut self, seq: u16, timestamp_ms: u64) {
pub fn observe_sent(&mut self, seq: u32, timestamp_ms: u64) {
self.total_sent += 1;
self.highest_sent_seq = Some(seq);
@@ -78,7 +78,7 @@ impl PathMonitor {
}
/// Record that we received a packet with the given sequence number and timestamp.
pub fn observe_received(&mut self, seq: u16, timestamp_ms: u64) {
pub fn observe_received(&mut self, seq: u32, timestamp_ms: u64) {
self.total_received += 1;
if self.first_recv_time_ms.is_none() {
@@ -180,7 +180,12 @@ impl PathMonitor {
return 0.0;
}
let mean = self.rtt_window.iter().sum::<f64>() / n as f64;
let var = self.rtt_window.iter().map(|r| (r - mean).powi(2)).sum::<f64>() / n as f64;
let var = self
.rtt_window
.iter()
.map(|r| (r - mean).powi(2))
.sum::<f64>()
/ n as f64;
var.sqrt()
}
@@ -274,7 +279,7 @@ mod tests {
}
// Receive only 7 of them (30% loss)
for i in [0u16, 1, 2, 3, 5, 7, 9] {
for i in [0u32, 1, 2, 3, 5, 7, 9] {
monitor.observe_received(i, i as u64 * 20 + 50);
}

View File

@@ -127,9 +127,9 @@ impl QuinnTransport {
}
}
self.connection.send_datagram(data).map_err(|e| {
TransportError::Internal(format!("send trunk datagram error: {e}"))
})?;
self.connection
.send_datagram(data)
.map_err(|e| TransportError::Internal(format!("send trunk datagram error: {e}")))?;
Ok(())
}
@@ -146,7 +146,7 @@ impl QuinnTransport {
Err(e) => {
return Err(TransportError::Internal(format!(
"recv trunk datagram error: {e}"
)))
)));
}
};
@@ -177,9 +177,9 @@ impl MediaTransport for QuinnTransport {
monitor.observe_sent(packet.header.seq, packet.header.timestamp as u64);
}
self.connection.send_datagram(data).map_err(|e| {
TransportError::Internal(format!("send datagram error: {e}"))
})?;
self.connection
.send_datagram(data)
.map_err(|e| TransportError::Internal(format!("send datagram error: {e}")))?;
Ok(())
}
@@ -192,7 +192,7 @@ impl MediaTransport for QuinnTransport {
Err(e) => {
return Err(TransportError::Internal(format!(
"recv datagram error: {e}"
)))
)));
}
};
@@ -201,15 +201,15 @@ impl MediaTransport for QuinnTransport {
// Record receive observation
{
let mut monitor = self.path_monitor.lock().unwrap();
monitor.observe_received(
packet.header.seq,
packet.header.timestamp as u64,
);
monitor.observe_received(packet.header.seq, packet.header.timestamp as u64);
}
Ok(Some(packet))
}
None => {
tracing::warn!(len = data.len(), "skipping malformed media datagram, continuing");
tracing::warn!(
len = data.len(),
"skipping malformed media datagram, continuing"
);
// Don't return Ok(None) — that signals connection closed.
// Recurse to read the next datagram instead.
Box::pin(self.recv_media()).await
@@ -241,10 +241,8 @@ impl MediaTransport for QuinnTransport {
}
async fn close(&self) -> Result<(), TransportError> {
self.connection.close(
quinn::VarInt::from_u32(0),
b"normal close",
);
self.connection
.close(quinn::VarInt::from_u32(0), b"normal close");
Ok(())
}
}

View File

@@ -9,10 +9,14 @@ use wzp_proto::{SignalMessage, TransportError};
/// Send a signaling message over a new bidirectional QUIC stream.
///
/// Opens a new bidi stream, writes a length-prefixed JSON frame, then finishes the send side.
pub async fn send_signal(connection: &Connection, msg: &SignalMessage) -> Result<(), TransportError> {
let (mut send, _recv) = connection.open_bi().await.map_err(|e| {
TransportError::Internal(format!("failed to open bidi stream: {e}"))
})?;
pub async fn send_signal(
connection: &Connection,
msg: &SignalMessage,
) -> Result<(), TransportError> {
let (mut send, _recv) = connection
.open_bi()
.await
.map_err(|e| TransportError::Internal(format!("failed to open bidi stream: {e}")))?;
let json = serde_json::to_vec(msg)
.map_err(|e| TransportError::Internal(format!("signal serialize error: {e}")))?;