tokio_quiche/http3/
settings.rs1use std::future::poll_fn;
28use std::task::Context;
29use std::task::Poll;
30use std::time::Duration;
31
32use crate::http3::driver::H3ConnectionError;
33use crate::quic::QuicheConnection;
34
35use foundations::telemetry::log;
36use tokio_util::time::delay_queue::DelayQueue;
37use tokio_util::time::delay_queue::{
38 self,
39};
40
41#[derive(Default, Clone, Debug)]
44pub struct Http3Settings {
45 pub max_requests_per_connection: Option<u64>,
49 pub max_header_list_size: Option<u64>,
51 pub qpack_max_table_capacity: Option<u64>,
54 pub qpack_blocked_streams: Option<u64>,
57 pub post_accept_timeout: Option<Duration>,
61 pub enable_extended_connect: bool,
64}
65
66impl From<&Http3Settings> for quiche::h3::Config {
67 fn from(value: &Http3Settings) -> Self {
68 let mut config = Self::new().unwrap();
69
70 if let Some(v) = value.max_header_list_size {
71 config.set_max_field_section_size(v);
72 }
73
74 if let Some(v) = value.qpack_max_table_capacity {
75 config.set_qpack_max_table_capacity(v);
76 }
77
78 if let Some(v) = value.qpack_blocked_streams {
79 config.set_qpack_blocked_streams(v);
80 }
81
82 if value.enable_extended_connect {
83 config.enable_extended_connect(value.enable_extended_connect)
84 }
85
86 config
87 }
88}
89
90pub(crate) struct TimeoutKey(delay_queue::Key);
92
93pub(crate) struct Http3SettingsEnforcer {
94 limits: Http3Limits,
95 timeouts: Http3Timeouts,
96}
97
98impl From<&Http3Settings> for Http3SettingsEnforcer {
99 fn from(value: &Http3Settings) -> Self {
100 Self {
101 limits: Http3Limits {
102 max_requests_per_connection: value.max_requests_per_connection,
103 },
104 timeouts: Http3Timeouts {
105 post_accept_timeout: value.post_accept_timeout,
106 delay_queue: DelayQueue::new(),
107 },
108 }
109 }
110}
111
112impl Http3SettingsEnforcer {
113 pub fn enforce_requests_limit(&self, request_count: u64) -> bool {
116 if let Some(limit) = self.limits.max_requests_per_connection {
117 return request_count >= limit;
118 }
119
120 false
121 }
122
123 pub fn post_accept_timeout(&self) -> Option<Duration> {
125 self.timeouts.post_accept_timeout
126 }
127
128 pub fn add_timeout(
130 &mut self, typ: Http3TimeoutType, duration: Duration,
131 ) -> TimeoutKey {
132 let key = self.timeouts.delay_queue.insert(typ, duration);
133 TimeoutKey(key)
134 }
135
136 pub fn has_pending_timeouts(&self) -> bool {
139 !self.timeouts.delay_queue.is_empty()
140 }
141
142 fn poll_timeouts(&mut self, cx: &mut Context) -> Poll<TimeoutCheckResult> {
144 let mut changed = false;
145 let mut result = TimeoutCheckResult::default();
146
147 while let Poll::Ready(Some(exp)) =
148 self.timeouts.delay_queue.poll_expired(cx)
149 {
150 changed |= result.set_expired(exp.into_inner());
151 }
152
153 if changed {
154 return Poll::Ready(result);
155 }
156 Poll::Pending
157 }
158
159 pub async fn enforce_timeouts(
164 &mut self, qconn: &mut QuicheConnection,
165 ) -> Result<(), H3ConnectionError> {
166 let result = poll_fn(|cx| self.poll_timeouts(cx)).await;
167
168 if result.connection_timed_out {
169 log::debug!("connection timed out due to post-accept-timeout"; "scid" => ?qconn.source_id());
170 qconn.close(true, quiche::h3::WireErrorCode::NoError as u64, &[])?;
171 }
172
173 Ok(())
174 }
175
176 pub fn cancel_timeout(&mut self, key: TimeoutKey) {
178 self.timeouts.delay_queue.remove(&key.0);
179 }
180}
181
182struct Http3Limits {
185 max_requests_per_connection: Option<u64>,
186}
187
188struct Http3Timeouts {
189 post_accept_timeout: Option<Duration>,
190 delay_queue: DelayQueue<Http3TimeoutType>,
191}
192
193#[derive(Clone, Copy, Debug)]
194pub(crate) enum Http3TimeoutType {
195 PostAccept,
196}
197
198#[derive(Default, Eq, PartialEq)]
199struct TimeoutCheckResult {
200 connection_timed_out: bool,
201}
202
203impl TimeoutCheckResult {
204 fn set_expired(&mut self, typ: Http3TimeoutType) -> bool {
205 use Http3TimeoutType::*;
206 let field = match typ {
207 PostAccept => &mut self.connection_timed_out,
208 };
209
210 *field = true;
211 true
212 }
213}