1mod priority;
4pub mod web_transport;
5use crate::{
6 ArcHandler, ArcedQuicEndpoint, BoxedBidiStream, QuicConnection, QuicTransportReceive,
7 QuicTransportSend, RuntimeTrait, unmap_ipv4,
8};
9use priority::{PrioritizedStream, PriorityRegistry, transport_priority};
10use std::sync::Arc;
11use trillium::{Handler, KnownHeaderName, Listener, Upgrade};
12use trillium_http::{
13 HttpContext,
14 h3::{H3Connection, H3Error, H3ErrorCode, H3StreamResult, UniStreamResult},
15};
16use web_transport::{WebTransportDispatcher, WebTransportStream};
17
18#[derive(Clone, Copy, Debug)]
20pub struct StreamId(u64);
21impl From<StreamId> for u64 {
22 fn from(val: StreamId) -> Self {
23 val.0
24 }
25}
26
27impl From<u64> for StreamId {
28 fn from(value: u64) -> Self {
29 Self(value)
30 }
31}
32
33pub(crate) async fn run_h3(
34 quic_binding: ArcedQuicEndpoint,
35 context: Arc<HttpContext>,
36 handler: ArcHandler<impl Handler>,
37 runtime: impl RuntimeTrait,
38 listener: Option<Listener>,
39 local_alt_svc: Option<&'static str>,
40) {
41 let swansong = context.swansong();
42 while let Some(connection) = swansong.interrupt(quic_binding.accept()).await.flatten() {
43 let h3 = H3Connection::new(context.clone());
44 let handler = handler.clone();
45 let runtime = runtime.clone();
46 runtime.clone().spawn_detached(run_h3_connection(
47 connection,
48 h3,
49 handler,
50 runtime,
51 listener.clone(),
52 local_alt_svc,
53 ));
54 }
55}
56
57async fn run_h3_connection(
58 connection: QuicConnection,
59 h3: Arc<H3Connection>,
60 handler: ArcHandler<impl Handler>,
61 runtime: impl RuntimeTrait,
62 listener: Option<Listener>,
63 local_alt_svc: Option<&'static str>,
64) {
65 let wt_dispatcher = h3
66 .context()
67 .config()
68 .webtransport_enabled()
69 .then(WebTransportDispatcher::new);
70
71 log::trace!("new quic connection from {}", connection.remote_address());
72
73 let priorities = PriorityRegistry::default();
74 h3.register_priority_callback({
75 let priorities = priorities.clone();
76 move |stream_id, priority, is_update| {
77 priorities.apply(stream_id, transport_priority(priority), is_update)
78 }
79 });
80
81 spawn_outbound_control_stream(&connection, &h3, &runtime);
82 spawn_qpack_encoder_stream(&connection, &h3, &runtime);
83 spawn_qpack_decoder_stream(&connection, &h3, &runtime);
84 spawn_inbound_uni_streams(&connection, &h3, &runtime, &wt_dispatcher);
85 handle_inbound_bidi_streams(
86 connection,
87 h3.clone(),
88 handler,
89 runtime,
90 wt_dispatcher,
91 listener,
92 local_alt_svc,
93 priorities,
94 )
95 .await;
96}
97
98#[allow(clippy::too_many_arguments)]
99async fn handle_inbound_bidi_streams(
100 connection: QuicConnection,
101 h3: Arc<H3Connection>,
102 handler: ArcHandler<impl Handler>,
103 runtime: impl RuntimeTrait,
104 wt_dispatcher: Option<WebTransportDispatcher>,
105 listener: Option<Listener>,
106 local_alt_svc: Option<&'static str>,
107 priorities: PriorityRegistry,
108) {
109 loop {
110 match h3.swansong().interrupt(connection.accept_bidi()).await {
111 None => {
112 log::trace!("H3 bidi accept loop: interrupted by swansong shutdown");
113 break;
114 }
115 Some(Err(e)) => {
116 log::debug!("H3 bidi accept loop: accept_bidi error: {e}");
117 break;
118 }
119 Some(Ok((stream_id, transport))) => {
120 handle_bidi_stream(
121 stream_id,
122 transport,
123 &h3,
124 &handler,
125 &connection,
126 &runtime,
127 &wt_dispatcher,
128 listener.clone(),
129 local_alt_svc,
130 &priorities,
131 );
132 }
133 }
134 }
135
136 h3.shut_down();
137}
138
139#[allow(clippy::too_many_arguments)]
140fn handle_bidi_stream(
141 stream_id: u64,
142 transport: BoxedBidiStream,
143 h3: &Arc<H3Connection>,
144 handler: &ArcHandler<impl Handler>,
145 connection: &QuicConnection,
146 runtime: &impl RuntimeTrait,
147 wt_dispatcher: &Option<WebTransportDispatcher>,
148 listener: Option<Listener>,
149 local_alt_svc: Option<&'static str>,
150 priorities: &PriorityRegistry,
151) {
152 log::trace!("H3 bidi stream {stream_id}: spawning handler task");
153 let (h3, handler, connection, wt_dispatcher, priorities) = (
154 h3.clone(),
155 handler.clone(),
156 connection.clone(),
157 wt_dispatcher.clone(),
158 priorities.clone(),
159 );
160
161 let slot = priorities.register(stream_id);
165
166 let peer_gone = transport.stopped();
169
170 let transport: BoxedBidiStream = Box::new(PrioritizedStream::new(transport, slot, stream_id));
171
172 runtime.spawn_detached(async move {
173 let peer_ip = unmap_ipv4(connection.remote_address().ip());
177 let quic_connection = connection.clone();
178 let wt_dispatcher = wt_dispatcher.clone();
179
180 let handler_fn = {
181 let handler = handler.clone();
182 let wt_dispatcher = wt_dispatcher.clone();
183 move |mut conn: trillium_http::Conn<_>| async move {
184 conn.set_peer_ip(Some(peer_ip));
185 conn.set_secure(true);
186
187 let state = conn.state_mut();
188 state.insert(quic_connection);
189 state.insert(StreamId(stream_id));
190 if let Some(listener) = listener {
191 if let Some(addr) = listener.socket_addr() {
192 state.insert(addr);
193 }
194 state.insert(listener);
195 }
196 if let Some(dispatcher) = wt_dispatcher {
197 state.insert(dispatcher);
198 }
199 if let Some(alt_svc) = local_alt_svc {
200 conn.response_headers_mut()
201 .try_insert(KnownHeaderName::AltSvc, alt_svc);
202 }
203
204 let conn = handler.run(conn.into()).await;
205 let conn = handler.before_send(conn).await;
206
207 conn.into_inner()
208 }
209 };
210
211 let result = h3
212 .clone()
213 .process_inbound_bidi(transport, handler_fn, stream_id)
214 .with_reset(|t, code| {
215 let raw = u64::from(code);
220 t.stop(raw);
221 t.reset(raw);
222 });
223
224 let result = match peer_gone {
225 Some(peer_gone) => result.with_peer_gone(peer_gone).await,
226 None => result.await,
227 };
228
229 match result {
230 Ok(H3StreamResult::Request(conn)) if conn.should_upgrade() => {
231 let upgrade = Upgrade::from(conn);
232 if handler.has_upgrade(&upgrade) {
233 log::debug!("upgrading h3 stream");
234 handler.upgrade(upgrade).await;
235 } else {
236 log::error!("h3 upgrade specified but no upgrade handler provided");
237 }
238 }
239
240 Ok(H3StreamResult::Request(_)) => {}
241
242 Ok(H3StreamResult::WebTransport {
243 session_id,
244 mut transport,
245 buffer,
246 }) => {
247 if let Some(dispatcher) = &wt_dispatcher {
248 dispatcher.dispatch(WebTransportStream::Bidi {
249 session_id,
250 stream: Box::new(transport),
251 buffer: buffer.into(),
252 });
253 } else {
254 transport.stop(H3ErrorCode::StreamCreationError.into());
255 transport.reset(H3ErrorCode::StreamCreationError.into());
256 }
257 }
258
259 Err(error) => {
260 log::debug!("H3 bidi stream {stream_id}: error: {error}");
261 handle_h3_error(error, &connection, &h3);
262 }
263 }
264
265 priorities.deregister(stream_id);
266 });
267}
268
269fn spawn_inbound_uni_streams(
270 connection: &QuicConnection,
271 h3: &Arc<H3Connection>,
272 runtime: &impl RuntimeTrait,
273 wt_dispatcher: &Option<WebTransportDispatcher>,
274) {
275 let (connection, h3, runtime, wt_dispatcher) = (
276 connection.clone(),
277 h3.clone(),
278 runtime.clone(),
279 wt_dispatcher.clone(),
280 );
281 runtime.clone().spawn_detached(async move {
282 while let Some(Ok((_stream_id, recv))) =
283 h3.swansong().interrupt(connection.accept_uni()).await
284 {
285 let (connection, h3, wt_dispatcher) =
286 (connection.clone(), h3.clone(), wt_dispatcher.clone());
287
288 runtime.spawn_detached(async move {
289 let close_connection = {
297 let connection = connection.clone();
298 let h3 = h3.clone();
299 move |code: H3ErrorCode| {
300 connection.close(code.into(), code.reason().as_bytes());
301 h3.shut_down();
302 }
303 };
304 let result = h3
305 .process_inbound_uni_with_close(recv, close_connection)
306 .await;
307
308 match result {
309 Ok(UniStreamResult::Handled) => {}
310 Ok(UniStreamResult::WebTransport {
311 session_id,
312 mut stream,
313 buffer,
314 }) => {
315 if let Some(dispatcher) = &wt_dispatcher {
316 dispatcher.dispatch(WebTransportStream::Uni {
317 session_id,
318 stream: Box::new(stream),
319 buffer: buffer.into(),
320 });
321 } else {
322 stream.stop(H3ErrorCode::StreamCreationError.into());
323 }
324 }
325
326 Ok(UniStreamResult::Unknown { mut stream, .. }) => {
327 stream.stop(H3ErrorCode::StreamCreationError.into());
328 }
329
330 Err(error) => {
331 handle_h3_error(error, &connection, &h3);
335 }
336 }
337 });
338 }
339
340 h3.shut_down();
341 });
342}
343
344fn spawn_qpack_decoder_stream(
345 connection: &QuicConnection,
346 h3: &Arc<H3Connection>,
347 runtime: &impl RuntimeTrait,
348) {
349 let (connection, h3) = (connection.clone(), h3.clone());
350
351 runtime.spawn_detached(async move {
352 log::trace!("H3: opening outbound QPACK decoder stream");
353 let stream = match connection.open_uni().await {
354 Ok((_stream_id, stream)) => stream,
355 Err(err) => {
356 log::error!("H3: open_uni for QPACK decoder stream failed: {err:?}");
357 h3.shut_down();
358 return;
359 }
360 };
361
362 let result = h3.run_decoder(stream).await;
363
364 if let Err(error) = result {
365 handle_h3_error(error, &connection, &h3);
366 }
367 });
369}
370
371fn spawn_qpack_encoder_stream(
372 connection: &QuicConnection,
373 h3: &Arc<H3Connection>,
374 runtime: &impl RuntimeTrait,
375) {
376 let (connection, h3) = (connection.clone(), h3.clone());
377 runtime.spawn_detached(async move {
378 log::trace!("H3: opening outbound QPACK encoder stream");
379 let stream = match connection.open_uni().await {
380 Ok((_stream_id, stream)) => stream,
381 Err(err) => {
382 log::error!("H3: open_uni for QPACK encoder stream failed: {err:?}");
383 h3.shut_down();
384 return;
385 }
386 };
387
388 let result = h3.run_encoder(stream).await;
389
390 if let Err(error) = result {
391 handle_h3_error(error, &connection, &h3);
392 }
393 });
395}
396
397fn spawn_outbound_control_stream(
398 connection: &QuicConnection,
399 h3: &Arc<H3Connection>,
400 runtime: &impl RuntimeTrait,
401) {
402 let (connection, h3) = (connection.clone(), h3.clone());
403 runtime.spawn_detached(async move {
404 log::trace!("H3: opening outbound control stream");
405 let stream = match connection.open_uni().await {
406 Ok((_stream_id, stream)) => stream,
407 Err(err) => {
408 log::error!("H3: open_uni for outbound control stream failed: {err:?}");
409 h3.shut_down();
410 return;
411 }
412 };
413
414 let result = h3.run_outbound_control(stream).await;
415
416 if let Err(error) = result {
417 handle_h3_error(error, &connection, &h3);
418 }
419 });
421}
422
423fn handle_h3_error(error: H3Error, connection: &QuicConnection, h3: &H3Connection) {
424 log::debug!("H3 error: {error}");
425 if let H3Error::Protocol(code) = error
426 && code.is_connection_error()
427 {
428 connection.close(code.into(), code.reason().as_bytes());
429 h3.shut_down();
430 }
431}