Coverage Report

Created: 2026-09-04 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2/src/proto/streams/counts.rs
Line
Count
Source
1
use super::*;
2
3
#[derive(Debug)]
4
struct Budget {
5
    available: usize,
6
    max: usize,
7
}
8
9
#[derive(Debug)]
10
pub(super) struct BudgetExhausted;
11
12
impl Budget {
13
12.7k
    fn new(max: usize) -> Self {
14
12.7k
        Budget {
15
12.7k
            available: max,
16
12.7k
            max,
17
12.7k
        }
18
12.7k
    }
19
20
696
    fn consume(&mut self, amount: usize) -> Result<(), BudgetExhausted> {
21
696
        self.available = self.available.checked_sub(amount).ok_or(BudgetExhausted)?;
22
696
        Ok(())
23
696
    }
24
25
456
    fn replenish(&mut self, amount: usize) {
26
456
        self.available = self.available.saturating_add(amount).min(self.max);
27
456
    }
28
}
29
30
#[derive(Debug)]
31
pub(super) struct Counts {
32
    /// Acting as a client or server. This allows us to track which values to
33
    /// inc / dec.
34
    peer: peer::Dyn,
35
36
    /// Maximum number of locally initiated streams
37
    max_send_streams: usize,
38
39
    /// Current number of remote initiated streams
40
    num_send_streams: usize,
41
42
    /// Maximum number of remote initiated streams
43
    max_recv_streams: usize,
44
45
    /// Current number of locally initiated streams
46
    num_recv_streams: usize,
47
48
    /// Maximum number of pending locally reset streams
49
    max_local_reset_streams: usize,
50
51
    /// Current number of pending locally reset streams
52
    num_local_reset_streams: usize,
53
54
    /// Max number of "pending accept" streams that were remotely reset
55
    max_remote_reset_streams: usize,
56
57
    /// Current number of "pending accept" streams that were remotely reset
58
    num_remote_reset_streams: usize,
59
60
    /// Maximum number of locally reset streams due to protocol error across
61
    /// the lifetime of the connection.
62
    ///
63
    /// When this gets exceeded, we issue GOAWAYs.
64
    max_local_error_reset_streams: Option<usize>,
65
66
    /// Total number of locally reset streams due to protocol error across the
67
    /// lifetime of the connection.
68
    num_local_error_reset_streams: usize,
69
70
    /// connection-level budget for DATA framing overhead.
71
    data_frame_budget: Budget,
72
73
    /// Number of empty, non-final DATA frames received over the lifetime of
74
    /// the connection.
75
    num_recv_empty_data_frames: usize,
76
}
77
78
impl Counts {
79
    /// Create a new `Counts` using the provided configuration values.
80
12.7k
    pub fn new(peer: peer::Dyn, config: &Config) -> Self {
81
12.7k
        Counts {
82
12.7k
            peer,
83
12.7k
            max_send_streams: config.initial_max_send_streams,
84
12.7k
            num_send_streams: 0,
85
12.7k
            max_recv_streams: config.remote_max_initiated.unwrap_or(usize::MAX),
86
12.7k
            num_recv_streams: 0,
87
12.7k
            max_local_reset_streams: config.local_reset_max,
88
12.7k
            num_local_reset_streams: 0,
89
12.7k
            max_remote_reset_streams: config.remote_reset_max,
90
12.7k
            num_remote_reset_streams: 0,
91
12.7k
            max_local_error_reset_streams: config.local_max_error_reset_streams,
92
12.7k
            num_local_error_reset_streams: 0,
93
12.7k
            data_frame_budget: Budget::new(config.data_frame_budget),
94
12.7k
            num_recv_empty_data_frames: 0,
95
12.7k
        }
96
12.7k
    }
97
98
    /// Records the framing overhead of a DATA frame.
99
1.65k
    pub fn record_data_frame(&mut self, payload_len: usize) -> Result<(), BudgetExhausted> {
100
1.65k
        if payload_len == 0 {
101
935
            self.num_recv_empty_data_frames = self
102
935
                .num_recv_empty_data_frames
103
935
                .checked_add(1)
104
935
                .ok_or(BudgetExhausted)?;
105
935
            if self.num_recv_empty_data_frames > MAX_RECV_EMPTY_DATA_FRAMES {
106
2
                return Err(BudgetExhausted);
107
933
            }
108
933
            Ok(())
109
719
        } else if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
110
696
            self.data_frame_budget
111
696
                .consume(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len)
112
        } else {
113
23
            self.data_frame_budget
114
23
                .replenish(payload_len - DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD);
115
23
            Ok(())
116
        }
117
1.65k
    }
118
119
    /// Releases the framing overhead of a DATA frame that is no longer
120
    /// buffered internally.
121
439
    pub fn release_data_frame(&mut self, payload_len: usize) {
122
439
        if payload_len != 0 && payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
123
433
            self.data_frame_budget
124
433
                .replenish(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len);
125
433
        }
126
439
    }
127
128
    /// Returns true when the next opened stream will reach capacity of outbound streams
129
    ///
130
    /// The number of client send streams is incremented in prioritize; send_request has to guess if
131
    /// it should wait before allowing another request to be sent.
132
406k
    pub fn next_send_stream_will_reach_capacity(&self) -> bool {
133
406k
        self.max_send_streams <= (self.num_send_streams + 1)
134
406k
    }
135
136
    /// Returns the current peer
137
904k
    pub fn peer(&self) -> peer::Dyn {
138
904k
        self.peer
139
904k
    }
140
141
3.59M
    pub fn has_streams(&self) -> bool {
142
3.59M
        self.num_send_streams != 0 || self.num_recv_streams != 0
143
3.59M
    }
144
145
    /// Returns true if we can issue another local reset due to protocol error.
146
67.3k
    pub fn can_inc_num_local_error_resets(&self) -> bool {
147
67.3k
        if let Some(max) = self.max_local_error_reset_streams {
148
67.3k
            max > self.num_local_error_reset_streams
149
        } else {
150
0
            true
151
        }
152
67.3k
    }
153
154
33.6k
    pub fn inc_num_local_error_resets(&mut self) {
155
33.6k
        assert!(self.can_inc_num_local_error_resets());
156
157
        // Increment the number of remote initiated streams
158
33.6k
        self.num_local_error_reset_streams += 1;
159
33.6k
    }
160
161
0
    pub(crate) fn max_local_error_resets(&self) -> Option<usize> {
162
0
        self.max_local_error_reset_streams
163
0
    }
164
165
    /// Returns true if the receive stream concurrency can be incremented
166
914
    pub fn can_inc_num_recv_streams(&self) -> bool {
167
914
        self.max_recv_streams > self.num_recv_streams
168
914
    }
169
170
    /// Increments the number of concurrent receive streams.
171
    ///
172
    /// # Panics
173
    ///
174
    /// Panics on failure as this should have been validated before hand.
175
3
    pub fn inc_num_recv_streams(&mut self, stream: &mut store::Ptr) {
176
3
        assert!(self.can_inc_num_recv_streams());
177
3
        assert!(!stream.is_counted);
178
179
        // Increment the number of remote initiated streams
180
3
        self.num_recv_streams += 1;
181
3
        stream.is_counted = true;
182
3
    }
183
184
    /// Returns true if the send stream concurrency can be incremented
185
874k
    pub fn can_inc_num_send_streams(&self) -> bool {
186
874k
        self.max_send_streams > self.num_send_streams
187
874k
    }
188
189
    /// Increments the number of concurrent send streams.
190
    ///
191
    /// # Panics
192
    ///
193
    /// Panics on failure as this should have been validated before hand.
194
201k
    pub fn inc_num_send_streams(&mut self, stream: &mut store::Ptr) {
195
201k
        assert!(self.can_inc_num_send_streams());
196
201k
        assert!(!stream.is_counted);
197
198
        // Increment the number of remote initiated streams
199
201k
        self.num_send_streams += 1;
200
201k
        stream.is_counted = true;
201
201k
    }
202
203
    /// Returns true if the number of pending reset streams can be incremented.
204
129k
    pub fn can_inc_num_reset_streams(&self) -> bool {
205
129k
        self.max_local_reset_streams > self.num_local_reset_streams
206
129k
    }
207
208
    /// Increments the number of pending reset streams.
209
    ///
210
    /// # Panics
211
    ///
212
    /// Panics on failure as this should have been validated before hand.
213
43.0k
    pub fn inc_num_reset_streams(&mut self) {
214
43.0k
        assert!(self.can_inc_num_reset_streams());
215
216
43.0k
        self.num_local_reset_streams += 1;
217
43.0k
    }
218
219
0
    pub(crate) fn max_remote_reset_streams(&self) -> usize {
220
0
        self.max_remote_reset_streams
221
0
    }
222
223
    /// Returns true if the number of pending REMOTE reset streams can be
224
    /// incremented.
225
0
    pub(crate) fn can_inc_num_remote_reset_streams(&self) -> bool {
226
0
        self.max_remote_reset_streams > self.num_remote_reset_streams
227
0
    }
228
229
    /// Increments the number of pending REMOTE reset streams.
230
    ///
231
    /// # Panics
232
    ///
233
    /// Panics on failure as this should have been validated before hand.
234
0
    pub(crate) fn inc_num_remote_reset_streams(&mut self) {
235
0
        assert!(self.can_inc_num_remote_reset_streams());
236
237
0
        self.num_remote_reset_streams += 1;
238
0
    }
239
240
0
    pub(crate) fn dec_num_remote_reset_streams(&mut self) {
241
0
        assert!(self.num_remote_reset_streams > 0);
242
243
0
        self.num_remote_reset_streams -= 1;
244
0
    }
245
246
5.30k
    pub fn apply_remote_settings(&mut self, settings: &frame::Settings, is_initial: bool) {
247
5.30k
        match settings.max_concurrent_streams() {
248
542
            Some(val) => self.max_send_streams = val as usize,
249
662
            None if is_initial => self.max_send_streams = usize::MAX,
250
4.10k
            None => {}
251
        }
252
5.30k
    }
253
254
    /// Run a block of code that could potentially transition a stream's state.
255
    ///
256
    /// If the stream state transitions to closed, this function will perform
257
    /// all necessary cleanup.
258
    ///
259
    /// TODO: Is this function still needed?
260
1.77M
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
1.77M
    where
262
1.77M
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
1.77M
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
1.77M
        let ret = f(self, &mut stream);
269
270
1.77M
        self.transition_after(stream, is_pending_reset);
271
272
1.77M
        ret
273
1.77M
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::prioritize::Prioritize>::assign_connection_capacity<h2::proto::streams::store::Ptr>::{closure#0}, ()>
Line
Count
Source
260
25.2k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
25.2k
    where
262
25.2k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
25.2k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
25.2k
        let ret = f(self, &mut stream);
269
270
25.2k
        self.transition_after(stream, is_pending_reset);
271
272
25.2k
        ret
273
25.2k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::prioritize::Prioritize>::assign_connection_capacity<h2::proto::streams::store::Store>::{closure#0}, ()>
Line
Count
Source
260
42.1k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
42.1k
    where
262
42.1k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
42.1k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
42.1k
        let ret = f(self, &mut stream);
269
270
42.1k
        self.transition_after(stream, is_pending_reset);
271
272
42.1k
        ret
273
42.1k
    }
<h2::proto::streams::counts::Counts>::transition::<h2::proto::streams::streams::drop_stream_ref::{closure#0}::{closure#0}, ()>
Line
Count
Source
260
169
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
169
    where
262
169
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
169
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
169
        let ret = f(self, &mut stream);
269
270
169
        self.transition_after(stream, is_pending_reset);
271
272
169
        ret
273
169
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::prioritize::Prioritize>::clear_pending_capacity::{closure#0}, ()>
Line
Count
Source
260
12.9k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
12.9k
    where
262
12.9k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
12.9k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
12.9k
        let ret = f(self, &mut stream);
269
270
12.9k
        self.transition_after(stream, is_pending_reset);
271
272
12.9k
        ret
273
12.9k
    }
Unexecuted instantiation: <h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::recv::Recv>::clear_stream_window_update_queue::{closure#0}, ()>
<h2::proto::streams::counts::Counts>::transition::<h2::proto::streams::streams::drop_stream_ref::{closure#0}, ()>
Line
Count
Source
260
814k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
814k
    where
262
814k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
814k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
814k
        let ret = f(self, &mut stream);
269
270
814k
        self.transition_after(stream, is_pending_reset);
271
272
814k
        ret
273
814k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::recv_eof<bytes::bytes::Bytes>::{closure#0}::{closure#0}, ()>
Line
Count
Source
260
713
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
713
    where
262
713
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
713
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
713
        let ret = f(self, &mut stream);
269
270
713
        self.transition_after(stream, is_pending_reset);
271
272
713
        ret
273
713
    }
Unexecuted instantiation: <h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::recv::Recv>::send_stream_window_updates<fuzz_e2e::MockIo, bytes::bytes::Bytes>::{closure#0}, ()>
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::recv_reset<bytes::bytes::Bytes>::{closure#0}, core::result::Result<(), h2::proto::error::Error>>
Line
Count
Source
260
321
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
321
    where
262
321
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
321
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
321
        let ret = f(self, &mut stream);
269
270
321
        self.transition_after(stream, is_pending_reset);
271
272
321
        ret
273
321
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::recv_headers<bytes::bytes::Bytes>::{closure#0}, core::result::Result<(), h2::proto::error::Error>>
Line
Count
Source
260
2.00k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
2.00k
    where
262
2.00k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
2.00k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
2.00k
        let ret = f(self, &mut stream);
269
270
2.00k
        self.transition_after(stream, is_pending_reset);
271
272
2.00k
        ret
273
2.00k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::recv_push_promise<bytes::bytes::Bytes>::{closure#0}, core::result::Result<core::option::Option<h2::proto::streams::store::Key>, h2::proto::error::Error>>
Line
Count
Source
260
911
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
911
    where
262
911
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
911
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
911
        let ret = f(self, &mut stream);
269
270
911
        self.transition_after(stream, is_pending_reset);
271
272
911
        ret
273
911
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::recv_data<bytes::bytes::Bytes>::{closure#0}, core::result::Result<(), h2::proto::error::Error>>
Line
Count
Source
260
1.91k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
1.91k
    where
262
1.91k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
1.91k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
1.91k
        let ret = f(self, &mut stream);
269
270
1.91k
        self.transition_after(stream, is_pending_reset);
271
272
1.91k
        ret
273
1.91k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Actions>::send_reset<bytes::bytes::Bytes>::{closure#0}, core::result::Result<(), h2::proto::error::GoAway>>
Line
Count
Source
260
32.8k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
32.8k
    where
262
32.8k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
32.8k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
32.8k
        let ret = f(self, &mut stream);
269
270
32.8k
        self.transition_after(stream, is_pending_reset);
271
272
32.8k
        ret
273
32.8k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::handle_error<bytes::bytes::Bytes>::{closure#0}::{closure#0}, ()>
Line
Count
Source
260
237k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
237k
    where
262
237k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
237k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
237k
        let ret = f(self, &mut stream);
269
270
237k
        self.transition_after(stream, is_pending_reset);
271
272
237k
        ret
273
237k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::recv_go_away<bytes::bytes::Bytes>::{closure#0}::{closure#0}, ()>
Line
Count
Source
260
4.00k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
4.00k
    where
262
4.00k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
4.00k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
4.00k
        let ret = f(self, &mut stream);
269
270
4.00k
        self.transition_after(stream, is_pending_reset);
271
272
4.00k
        ret
273
4.00k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::Inner>::recv_eof<bytes::bytes::Bytes>::{closure#0}::{closure#0}, ()>
Line
Count
Source
260
190k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
190k
    where
262
190k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
190k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
190k
        let ret = f(self, &mut stream);
269
270
190k
        self.transition_after(stream, is_pending_reset);
271
272
190k
        ret
273
190k
    }
<h2::proto::streams::counts::Counts>::transition::<<h2::proto::streams::streams::StreamRef<bytes::bytes::Bytes>>::send_data::{closure#0}, core::result::Result<(), h2::codec::error::UserError>>
Line
Count
Source
260
406k
    pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261
406k
    where
262
406k
        F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263
    {
264
        // TODO: Does this need to be computed before performing the action?
265
406k
        let is_pending_reset = stream.is_pending_reset_expiration();
266
267
        // Run the action
268
406k
        let ret = f(self, &mut stream);
269
270
406k
        self.transition_after(stream, is_pending_reset);
271
272
406k
        ret
273
406k
    }
274
275
    // TODO: move this to macro?
276
2.42M
    pub fn transition_after(&mut self, mut stream: store::Ptr, is_reset_counted: bool) {
277
2.42M
        tracing::trace!(
278
0
            "transition_after; stream={:?}; state={:?}; is_closed={:?}; \
279
0
             pending_send_empty={:?}; buffered_send_data={}; \
280
0
             num_recv={}; num_send={}",
281
0
            stream.id,
282
0
            stream.state,
283
0
            stream.is_closed(),
284
0
            stream.pending_send.is_empty(),
285
0
            stream.buffered_send_data,
286
            self.num_recv_streams,
287
            self.num_send_streams
288
        );
289
290
2.42M
        if stream.is_closed() {
291
1.22M
            if !stream.is_pending_reset_expiration() {
292
1.16M
                stream.unlink();
293
1.16M
                if is_reset_counted {
294
43.0k
                    self.dec_num_reset_streams();
295
1.12M
                }
296
57.3k
            }
297
298
1.22M
            if !stream.state.is_scheduled_reset() && stream.is_counted {
299
201k
                tracing::trace!("dec_num_streams; stream={:?}", stream.id);
300
                // Decrement the number of active streams.
301
201k
                self.dec_num_streams(&mut stream);
302
1.02M
            }
303
1.20M
        }
304
305
        // Release the stream if it requires releasing
306
2.42M
        if stream.is_released() {
307
421k
            stream.remove();
308
2.00M
        }
309
2.42M
    }
310
311
    /// Returns the maximum number of streams that can be initiated by this
312
    /// peer.
313
0
    pub(crate) fn max_send_streams(&self) -> usize {
314
0
        self.max_send_streams
315
0
    }
316
317
    /// Returns the maximum number of streams that can be initiated by the
318
    /// remote peer.
319
0
    pub(crate) fn max_recv_streams(&self) -> usize {
320
0
        self.max_recv_streams
321
0
    }
322
323
201k
    fn dec_num_streams(&mut self, stream: &mut store::Ptr) {
324
201k
        assert!(stream.is_counted);
325
326
201k
        if self.peer.is_local_init(stream.id) {
327
201k
            assert!(self.num_send_streams > 0);
328
201k
            self.num_send_streams -= 1;
329
201k
            stream.is_counted = false;
330
        } else {
331
3
            assert!(self.num_recv_streams > 0);
332
3
            self.num_recv_streams -= 1;
333
3
            stream.is_counted = false;
334
        }
335
201k
    }
336
337
43.0k
    fn dec_num_reset_streams(&mut self) {
338
43.0k
        assert!(self.num_local_reset_streams > 0);
339
43.0k
        self.num_local_reset_streams -= 1;
340
43.0k
    }
341
}
342
343
impl Drop for Counts {
344
12.7k
    fn drop(&mut self) {
345
        use std::thread;
346
347
12.7k
        if !thread::panicking() {
348
12.7k
            debug_assert!(!self.has_streams());
349
0
        }
350
12.7k
    }
351
}
352
353
#[cfg(test)]
354
mod tests {
355
    use super::*;
356
    use crate::frame::DEFAULT_INITIAL_WINDOW_SIZE;
357
358
    fn counts() -> Counts {
359
        Counts::new(
360
            peer::Dyn::Server,
361
            &Config {
362
                initial_max_send_streams: 0,
363
                local_max_buffer_size: 0,
364
                local_next_stream_id: 2.into(),
365
                local_push_enabled: false,
366
                extended_connect_protocol_enabled: false,
367
                local_reset_duration: Duration::ZERO,
368
                local_reset_max: 0,
369
                remote_reset_max: 0,
370
                remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
371
                remote_max_initiated: None,
372
                local_max_error_reset_streams: None,
373
                data_frame_budget: DEFAULT_DATA_FRAME_BUDGET,
374
            },
375
        )
376
    }
377
378
    #[test]
379
    fn budget_is_bounded() {
380
        let mut budget = Budget::new(10);
381
382
        budget.consume(4).unwrap();
383
        budget.replenish(20);
384
        assert_eq!(budget.available, 10);
385
    }
386
387
    #[test]
388
    fn budget_reports_exhaustion_without_underflowing() {
389
        let mut budget = Budget::new(10);
390
391
        budget.consume(10).unwrap();
392
        assert!(budget.consume(1).is_err());
393
        assert_eq!(budget.available, 0);
394
    }
395
396
    #[test]
397
    fn good_sized_data_frames_do_not_exhaust_budget() {
398
        let mut counts = counts();
399
400
        for _ in 0..1_000_000 {
401
            counts
402
                .record_data_frame(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD)
403
                .unwrap();
404
        }
405
    }
406
407
    #[test]
408
    fn consumed_small_data_frames_do_not_exhaust_budget() {
409
        let mut counts = counts();
410
411
        for _ in 0..1_000_000 {
412
            counts.record_data_frame(1).unwrap();
413
            counts.release_data_frame(1);
414
        }
415
    }
416
417
    #[test]
418
    fn empty_data_frames_do_not_consume_data_frame_budget() {
419
        let mut counts = counts();
420
        counts.data_frame_budget = Budget::new(0);
421
422
        for _ in 0..MAX_RECV_EMPTY_DATA_FRAMES {
423
            counts.record_data_frame(0).unwrap();
424
        }
425
426
        // Empty frames have their own limit, while a non-empty small frame
427
        // still consumes the independently configured DATA frame budget.
428
        assert!(counts.record_data_frame(0).is_err());
429
        assert!(counts.record_data_frame(1).is_err());
430
    }
431
432
    #[test]
433
    fn large_data_frames_do_not_replenish_empty_data_frame_limit() {
434
        let mut counts = counts();
435
436
        for _ in 0..MAX_RECV_EMPTY_DATA_FRAMES {
437
            counts.record_data_frame(0).unwrap();
438
            counts
439
                .record_data_frame(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD * 2)
440
                .unwrap();
441
        }
442
        assert!(counts.record_data_frame(0).is_err());
443
    }
444
}