tokio_quiche/http3/driver/
datagram.rs1use datagram_socket::DgramBuffer;
28use quiche::h3::NameValue;
29use quiche::h3::{
30 self,
31};
32
33use super::InboundFrame;
34use crate::buf_factory::BufFactory;
35use crate::quic::QuicheConnection;
36
37pub(crate) fn extract_quarter_stream_id(
40 stream_id: u64, headers: &[h3::Header],
41) -> Option<u64> {
42 let mut method = None;
43 let mut datagram_flow_id: Option<u64> = None;
44 let mut protocol = None;
45
46 for header in headers {
47 match header.name() {
48 b":method" => method = Some(header.value()),
49 b":protocol" => protocol = Some(header.value()),
50 b"datagram-flow-id" =>
51 datagram_flow_id = std::str::from_utf8(header.value())
52 .ok()
53 .and_then(|v| v.parse().ok()),
54 _ => {},
55 };
56
57 if method.is_some() && (datagram_flow_id.is_some() || protocol.is_some())
60 {
61 break;
62 }
63 }
64
65 if method == Some(b"CONNECT-UDP") && datagram_flow_id.is_some() {
67 datagram_flow_id
68 } else if method == Some(b"CONNECT") && protocol.is_some() {
70 Some(stream_id / 4)
73 } else {
74 None
75 }
76}
77
78#[inline]
81pub(crate) fn send_h3_dgram(
82 conn: &mut QuicheConnection, quarter_stream_id: u64, dgram: DgramBuffer,
83) -> quiche::Result<()> {
84 conn.dgram_send_buf(h3_dgram_add_quarter_stream_id(quarter_stream_id, dgram)?)
85}
86
87fn h3_dgram_add_quarter_stream_id(
89 quarter_stream_id: u64, mut dgram: DgramBuffer,
90) -> quiche::Result<DgramBuffer> {
91 let mut prefix_buf = [0u8; 8];
92 let mut enc = octets::OctetsMut::with_slice(&mut prefix_buf);
93 let prefix = enc.put_varint(quarter_stream_id)?;
94
95 if dgram.try_add_prefix(prefix).is_err() {
96 const {
100 assert!(BufFactory::DGRAM_HEADROOM >= 8);
102 }
103 dgram.splice_headroom(BufFactory::DGRAM_HEADROOM);
104 dgram.try_add_prefix(prefix).unwrap();
105 }
106 Ok(dgram)
107}
108
109fn h3_dgram_remove_quarter_stream_id(
113 mut dgram: DgramBuffer,
114) -> quiche::Result<(u64, DgramBuffer)> {
115 let mut b = octets::Octets::with_slice(dgram.as_slice());
116 let quarter_stream_id = b.get_varint()?;
117 let advance = b.off();
119 dgram.advance(advance);
120 Ok((quarter_stream_id, dgram))
121}
122
123#[inline]
127pub(crate) fn receive_h3_dgram(
128 conn: &mut QuicheConnection,
129) -> quiche::Result<(u64, InboundFrame)> {
130 let dgram = conn.dgram_recv_buf()?;
131 let (quarter_stream_id, dgram) = h3_dgram_remove_quarter_stream_id(dgram)?;
132 Ok((quarter_stream_id, InboundFrame::Datagram(dgram)))
133}
134
135#[cfg(test)]
136mod tests {
137 use bytes::BufMut;
138 use datagram_socket::DgramBuffer;
139
140 use super::*;
141
142 #[test]
143 fn h3_dgram_add_quarter_stream_id_enough_headroom() {
144 let mut dgram = DgramBuffer::with_capacity_and_headroom(16, 8);
145 dgram.put_slice(&[0xaa, 0xbb, 0xcc]);
146
147 let result = h3_dgram_add_quarter_stream_id(67, dgram).unwrap();
149 assert_eq!(result.as_slice(), &[64, 67, 0xaa, 0xbb, 0xcc]);
150 }
151
152 #[test]
154 fn h3_dgram_add_quarter_stream_id_need_more_headroom() {
155 let dgram = DgramBuffer::from_slice(&[1, 2]);
156
157 let result = h3_dgram_add_quarter_stream_id(42, dgram).unwrap();
159 assert_eq!(result.as_slice(), &[42, 1, 2]);
160 }
161
162 #[test]
163 fn h3_dgram_remove_quarter_stream_id_tests() {
164 let dgram = DgramBuffer::from_slice(&[1, 2, 3, 4]);
165 let dgram = h3_dgram_add_quarter_stream_id(67, dgram).unwrap();
166 let (quarter_stream_id, rest) =
167 h3_dgram_remove_quarter_stream_id(dgram).unwrap();
168
169 assert_eq!(quarter_stream_id, 67);
170 assert_eq!(rest.as_slice(), &[1, 2, 3, 4]);
171 }
172
173 #[test]
174 fn h3_dgram_remove_quarter_stream_id_non_minimal_varint() {
175 let dgram = DgramBuffer::from_slice(&[0x40, 0x00, 1, 2, 3, 4]);
180 let (qsid, rest) = h3_dgram_remove_quarter_stream_id(dgram).unwrap();
181 assert_eq!(qsid, 0);
182
183 assert_eq!(
184 rest.as_slice(),
185 &[1, 2, 3, 4],
186 "leftover non-minimal varint byte leaked into payload prefix"
187 );
188 }
189
190 #[test]
193 fn remove_quarter_stream_id_empty_buffer_errors() {
194 let dgram = DgramBuffer::new();
195 assert!(h3_dgram_remove_quarter_stream_id(dgram).is_err());
196 }
197}