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}