Skip to main content

vane_core/
l4.rs

1use std::net::SocketAddr;
2use std::sync::Arc;
3
4use bytes::Bytes;
5use tokio::net::{TcpStream, UdpSocket};
6
7use crate::fetch::AsyncReadWrite;
8
9#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, serde::Serialize, serde::Deserialize)]
10pub struct QuicAssocId(pub u64);
11
12pub enum L4Conn {
13	Tcp(TcpStream),
14	/// Cleartext stream that the listener-side peek prelude has already
15	/// drained part of, with those bytes rewound into the read side via
16	/// `PeekedStream`. Type-erased so `vane-core` doesn't need to know
17	/// the concrete adapter; downstream consumers see the connection
18	/// from byte zero.
19	Peeked(Box<dyn AsyncReadWrite + Send>),
20	/// TLS-terminated stream after a server-side handshake completed.
21	/// The trait object erases the concrete `tokio_rustls::TlsStream`
22	/// type so that `vane-core` doesn't need to depend on rustls
23	/// (the parsing + termination live in `vane-engine`). `AsyncReadWrite`
24	/// is the same trait `L4ForwardFetch` uses for byte-tunnel I/O,
25	/// auto-impl'd on any `AsyncRead + AsyncWrite + Unpin`. See
26	/// `spec/architecture/08-tls.md` § _TLS termination (L4 → L7
27	/// upgrade)_.
28	Tls(Box<dyn AsyncReadWrite + Send>),
29	Udp(UdpAssoc),
30}
31
32pub struct UdpAssoc {
33	/// Physical listener socket — vane-owned, shared via `Arc` with the
34	/// listener's recv loop. The fetch sends responses back to the peer
35	/// through this socket; the listener demuxes inbound datagrams to
36	/// the per-session forwarder via the dispatch table. See
37	/// `spec/architecture/06-l4.md` § _UDP socket multiplexing_.
38	pub socket: Arc<UdpSocket>,
39	pub peer: SocketAddr,
40	/// Datagram that triggered the cold-path `FlowGraph` entry. The
41	/// `L4Forward` fetch sends this verbatim as the upstream session's
42	/// first packet so no inbound bytes are lost between dispatch
43	/// table miss and forwarder registration.
44	pub first_packet: Bytes,
45	/// `None` on the cold-path entry; populated only when an existing
46	/// QUIC session takes over (post-MVP — see
47	/// `spec/architecture/06-l4.md` § _`udp_dispatch`_).
48	pub quic: Option<QuicAssocId>,
49}
50
51#[cfg(test)]
52mod tests {
53	use super::*;
54
55	// Compile-gate: if L4Conn's variant shape changes, this signature fails
56	// to type-check. No runtime assertion is warranted.
57	fn _accepts_l4_conn(_: &L4Conn) {}
58
59	#[test]
60	fn quic_assoc_id_serde_round_trip() {
61		let id = QuicAssocId(0xdead_beef_cafe_babe);
62		let encoded = serde_json::to_string(&id).expect("serialize");
63		let decoded: QuicAssocId = serde_json::from_str(&encoded).expect("deserialize");
64		assert_eq!(decoded, id);
65	}
66
67	#[test]
68	fn quic_assoc_id_equality_is_structural() {
69		assert_eq!(QuicAssocId(42), QuicAssocId(42));
70		assert_ne!(QuicAssocId(42), QuicAssocId(43));
71	}
72}