Coverage Report

Created: 2026-10-17 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2/src/proto/streams/send.rs
Line
Count
Source
1
use super::{
2
    store, Buffer, BufferStatus, Codec, Config, Counts, Frame, Prioritize, Prioritized, Store,
3
    Stream, StreamId, StreamIdOverflow, WindowSize,
4
};
5
use crate::codec::UserError;
6
use crate::frame::{self, Reason};
7
use crate::proto::{self, Error, Initiator};
8
9
use bytes::Buf;
10
use tokio::io::AsyncWrite;
11
12
use std::cmp::Ordering;
13
use std::io;
14
use std::task::{Context, Poll, Waker};
15
16
/// Manages state transitions related to outbound frames.
17
#[derive(Debug)]
18
pub(super) struct Send {
19
    /// Stream identifier to use for next initialized stream.
20
    next_stream_id: Result<StreamId, StreamIdOverflow>,
21
22
    /// Any streams with a higher ID are ignored.
23
    ///
24
    /// This starts as MAX, but is lowered when a GOAWAY is received.
25
    ///
26
    /// > After sending a GOAWAY frame, the sender can discard frames for
27
    /// > streams initiated by the receiver with identifiers higher than
28
    /// > the identified last stream.
29
    max_stream_id: StreamId,
30
31
    /// Initial window size of locally initiated streams
32
    init_window_sz: WindowSize,
33
34
    /// Prioritization layer
35
    prioritize: Prioritize,
36
37
    is_push_enabled: bool,
38
39
    /// If extended connect protocol is enabled.
40
    is_extended_connect_protocol_enabled: bool,
41
}
42
43
/// A value to detect which public API has called `poll_reset`.
44
#[derive(Debug)]
45
pub(crate) enum PollReset {
46
    AwaitingHeaders,
47
    Streaming,
48
}
49
50
impl Send {
51
    /// Create a new `Send`
52
13.2k
    pub fn new(config: &Config) -> Self {
53
13.2k
        Send {
54
13.2k
            init_window_sz: config.remote_init_window_sz,
55
13.2k
            max_stream_id: StreamId::MAX,
56
13.2k
            next_stream_id: Ok(config.local_next_stream_id),
57
13.2k
            prioritize: Prioritize::new(config),
58
13.2k
            is_push_enabled: true,
59
13.2k
            is_extended_connect_protocol_enabled: false,
60
13.2k
        }
61
13.2k
    }
62
63
    /// Returns the initial send window size
64
411k
    pub fn init_window_sz(&self) -> WindowSize {
65
411k
        self.init_window_sz
66
411k
    }
67
68
410k
    pub fn open(&mut self) -> Result<StreamId, UserError> {
69
410k
        let stream_id = self.ensure_next_stream_id()?;
70
410k
        self.next_stream_id = stream_id.next_id();
71
410k
        Ok(stream_id)
72
410k
    }
73
74
0
    pub fn reserve_local(&mut self) -> Result<StreamId, UserError> {
75
0
        let stream_id = self.ensure_next_stream_id()?;
76
0
        self.next_stream_id = stream_id.next_id();
77
0
        Ok(stream_id)
78
0
    }
79
80
410k
    fn check_headers(fields: &http::HeaderMap) -> Result<(), UserError> {
81
        // 8.1.2.2. Connection-Specific Header Fields
82
410k
        if fields.contains_key(http::header::CONNECTION)
83
410k
            || fields.contains_key(http::header::TRANSFER_ENCODING)
84
410k
            || fields.contains_key(http::header::UPGRADE)
85
410k
            || fields.contains_key("keep-alive")
86
410k
            || fields.contains_key("proxy-connection")
87
        {
88
3
            tracing::debug!("illegal connection-specific headers found");
89
3
            return Err(UserError::MalformedHeaders);
90
410k
        } else if let Some(te) = fields.get(http::header::TE) {
91
93
            if te != "trailers" {
92
92
                tracing::debug!("illegal connection-specific headers found");
93
92
                return Err(UserError::MalformedHeaders);
94
1
            }
95
409k
        }
96
409k
        Ok(())
97
410k
    }
98
99
0
    pub fn send_push_promise<B>(
100
0
        &mut self,
101
0
        frame: frame::PushPromise,
102
0
        buffer: &mut Buffer<Frame<B>>,
103
0
        stream: &mut store::Ptr,
104
0
        task: &mut Option<Waker>,
105
0
    ) -> Result<(), UserError> {
106
0
        if !self.is_push_enabled {
107
0
            return Err(UserError::PeerDisabledServerPush);
108
0
        }
109
110
0
        tracing::trace!(
111
0
            "send_push_promise; frame={:?}; init_window={:?}",
112
            frame,
113
            self.init_window_sz
114
        );
115
116
0
        Self::check_headers(frame.fields())?;
117
118
        // Queue the frame for sending
119
0
        self.prioritize
120
0
            .queue_frame(frame.into(), buffer, stream, task);
121
122
0
        Ok(())
123
0
    }
124
125
410k
    pub fn send_headers<B>(
126
410k
        &mut self,
127
410k
        frame: frame::Headers,
128
410k
        buffer: &mut Buffer<Frame<B>>,
129
410k
        stream: &mut store::Ptr,
130
410k
        counts: &mut Counts,
131
410k
        task: &mut Option<Waker>,
132
410k
    ) -> Result<(), UserError> {
133
410k
        tracing::trace!(
134
0
            "send_headers; frame={:?}; init_window={:?}",
135
            frame,
136
            self.init_window_sz
137
        );
138
139
410k
        Self::check_headers(frame.fields())?;
140
141
409k
        let end_stream = frame.is_end_stream();
142
143
        // Update the state
144
409k
        stream.state.send_open(end_stream)?;
145
146
409k
        let mut pending_open = false;
147
409k
        if counts.peer().is_local_init(frame.stream_id()) && !stream.is_pending_push {
148
409k
            self.prioritize.queue_open(stream);
149
409k
            pending_open = true;
150
409k
        }
151
152
        // Queue the frame for sending
153
        //
154
        // This call expects that, since new streams are in the open queue, new
155
        // streams won't be pushed on pending_send.
156
409k
        self.prioritize
157
409k
            .queue_frame(frame.into(), buffer, stream, task);
158
159
        // Need to notify the connection when pushing onto pending_open since
160
        // queue_frame only notifies for pending_send.
161
409k
        if pending_open {
162
409k
            if let Some(task) = task.take() {
163
3.38k
                task.wake();
164
406k
            }
165
0
        }
166
167
409k
        Ok(())
168
410k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::send_headers::<_>
<h2::proto::streams::send::Send>::send_headers::<bytes::bytes::Bytes>
Line
Count
Source
125
795
    pub fn send_headers<B>(
126
795
        &mut self,
127
795
        frame: frame::Headers,
128
795
        buffer: &mut Buffer<Frame<B>>,
129
795
        stream: &mut store::Ptr,
130
795
        counts: &mut Counts,
131
795
        task: &mut Option<Waker>,
132
795
    ) -> Result<(), UserError> {
133
795
        tracing::trace!(
134
0
            "send_headers; frame={:?}; init_window={:?}",
135
            frame,
136
            self.init_window_sz
137
        );
138
139
795
        Self::check_headers(frame.fields())?;
140
141
700
        let end_stream = frame.is_end_stream();
142
143
        // Update the state
144
700
        stream.state.send_open(end_stream)?;
145
146
700
        let mut pending_open = false;
147
700
        if counts.peer().is_local_init(frame.stream_id()) && !stream.is_pending_push {
148
700
            self.prioritize.queue_open(stream);
149
700
            pending_open = true;
150
700
        }
151
152
        // Queue the frame for sending
153
        //
154
        // This call expects that, since new streams are in the open queue, new
155
        // streams won't be pushed on pending_send.
156
700
        self.prioritize
157
700
            .queue_frame(frame.into(), buffer, stream, task);
158
159
        // Need to notify the connection when pushing onto pending_open since
160
        // queue_frame only notifies for pending_send.
161
700
        if pending_open {
162
700
            if let Some(task) = task.take() {
163
0
                task.wake();
164
700
            }
165
0
        }
166
167
700
        Ok(())
168
795
    }
<h2::proto::streams::send::Send>::send_headers::<bytes::bytes::Bytes>
Line
Count
Source
125
409k
    pub fn send_headers<B>(
126
409k
        &mut self,
127
409k
        frame: frame::Headers,
128
409k
        buffer: &mut Buffer<Frame<B>>,
129
409k
        stream: &mut store::Ptr,
130
409k
        counts: &mut Counts,
131
409k
        task: &mut Option<Waker>,
132
409k
    ) -> Result<(), UserError> {
133
409k
        tracing::trace!(
134
0
            "send_headers; frame={:?}; init_window={:?}",
135
            frame,
136
            self.init_window_sz
137
        );
138
139
409k
        Self::check_headers(frame.fields())?;
140
141
409k
        let end_stream = frame.is_end_stream();
142
143
        // Update the state
144
409k
        stream.state.send_open(end_stream)?;
145
146
409k
        let mut pending_open = false;
147
409k
        if counts.peer().is_local_init(frame.stream_id()) && !stream.is_pending_push {
148
409k
            self.prioritize.queue_open(stream);
149
409k
            pending_open = true;
150
409k
        }
151
152
        // Queue the frame for sending
153
        //
154
        // This call expects that, since new streams are in the open queue, new
155
        // streams won't be pushed on pending_send.
156
409k
        self.prioritize
157
409k
            .queue_frame(frame.into(), buffer, stream, task);
158
159
        // Need to notify the connection when pushing onto pending_open since
160
        // queue_frame only notifies for pending_send.
161
409k
        if pending_open {
162
409k
            if let Some(task) = task.take() {
163
3.38k
                task.wake();
164
405k
            }
165
0
        }
166
167
409k
        Ok(())
168
409k
    }
169
170
    /// Send interim informational headers (1xx responses) without changing stream state.
171
    /// This allows multiple interim informational responses to be sent before the final response.
172
0
    pub fn send_interim_informational_headers<B>(
173
0
        &mut self,
174
0
        frame: frame::Headers,
175
0
        buffer: &mut Buffer<Frame<B>>,
176
0
        stream: &mut store::Ptr,
177
0
        _counts: &mut Counts,
178
0
        task: &mut Option<Waker>,
179
0
    ) -> Result<(), UserError> {
180
0
        tracing::trace!(
181
0
            "send_interim_informational_headers; frame={:?}; stream_id={:?}",
182
            frame,
183
0
            frame.stream_id()
184
        );
185
186
        // Validate headers
187
0
        Self::check_headers(frame.fields())?;
188
189
0
        debug_assert!(frame.is_informational(),
190
0
            "Frame must be informational (1xx status code) at this point. Validation should happen at the public API boundary.");
191
0
        debug_assert!(!frame.is_end_stream(),
192
0
            "Informational frames must not have end_stream flag set. Validation should happen at the internal send informational header streams.");
193
194
        // Queue the frame for sending WITHOUT changing stream state
195
        // This is the key difference from send_headers - we don't call stream.state.send_open()
196
0
        self.prioritize
197
0
            .queue_frame(frame.into(), buffer, stream, task);
198
199
0
        Ok(())
200
0
    }
201
202
    /// Send an explicit RST_STREAM frame
203
41.1k
    pub fn send_reset<B>(
204
41.1k
        &mut self,
205
41.1k
        reason: Reason,
206
41.1k
        initiator: Initiator,
207
41.1k
        buffer: &mut Buffer<Frame<B>>,
208
41.1k
        stream: &mut store::Ptr,
209
41.1k
        counts: &mut Counts,
210
41.1k
        task: &mut Option<Waker>,
211
41.1k
    ) {
212
41.1k
        let is_reset = stream.state.is_reset();
213
41.1k
        let is_closed = stream.state.is_closed();
214
41.1k
        let is_empty = stream.pending_send.is_empty();
215
41.1k
        let stream_id = stream.id;
216
217
41.1k
        tracing::trace!(
218
0
            "send_reset(..., reason={:?}, initiator={:?}, stream={:?}, ..., \
219
0
             is_reset={:?}; is_closed={:?}; pending_send.is_empty={:?}; \
220
0
             state={:?} \
221
0
             ",
222
            reason,
223
            initiator,
224
            stream_id,
225
            is_reset,
226
            is_closed,
227
            is_empty,
228
0
            stream.state
229
        );
230
231
41.1k
        if is_reset {
232
            // Don't double reset
233
20.1k
            tracing::trace!(
234
0
                " -> not sending RST_STREAM ({:?} is already reset)",
235
                stream_id
236
            );
237
20.1k
            return;
238
21.0k
        }
239
240
        // Transition the state to reset no matter what.
241
21.0k
        stream.set_reset(reason, initiator);
242
243
        // If closed AND the send queue is flushed, then the stream cannot be
244
        // reset explicitly, either. Implicit resets can still be queued.
245
21.0k
        if is_closed && is_empty {
246
23
            tracing::trace!(
247
0
                " -> not sending explicit RST_STREAM ({:?} was closed \
248
0
                 and send queue was flushed)",
249
                stream_id
250
            );
251
23
            return;
252
20.9k
        }
253
254
        // If the stream hasn't been opened yet (its initial HEADERS are still
255
        // sitting in `pending_open`/`pending_send`), clearing the queue would
256
        // drop those HEADERS and let a RST_STREAM become the first frame on an
257
        // idle stream. HTTP/2 forbids that: ยง5.1 allows only HEADERS/PRIORITY
258
        // on idle streams and ยง6.4 says RST_STREAM on idle is a PROTOCOL_ERROR.
259
        // Keep the queued HEADERS so the stream opens, then send the reset
260
        // immediately after.
261
20.9k
        if !stream.is_pending_open {
262
20.8k
            // Otherwise, drop any buffered DATA/HEADERS and only send the
263
20.8k
            // reset.
264
20.8k
            //
265
20.8k
            // Note that we don't call `self.recv_err` because we want to enqueue
266
20.8k
            // the reset frame before transitioning the stream inside
267
20.8k
            // `reclaim_all_capacity`.
268
20.8k
            self.prioritize.clear_queue(buffer, stream);
269
20.8k
        }
270
271
20.9k
        let frame = frame::Reset::new(stream.id, reason);
272
273
20.9k
        tracing::trace!("send_reset -- queueing; frame={:?}", frame);
274
20.9k
        self.prioritize
275
20.9k
            .queue_frame(frame.into(), buffer, stream, task);
276
20.9k
        self.prioritize.reclaim_all_capacity(stream, counts);
277
41.1k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::send_reset::<_>
<h2::proto::streams::send::Send>::send_reset::<bytes::bytes::Bytes>
Line
Count
Source
203
41.1k
    pub fn send_reset<B>(
204
41.1k
        &mut self,
205
41.1k
        reason: Reason,
206
41.1k
        initiator: Initiator,
207
41.1k
        buffer: &mut Buffer<Frame<B>>,
208
41.1k
        stream: &mut store::Ptr,
209
41.1k
        counts: &mut Counts,
210
41.1k
        task: &mut Option<Waker>,
211
41.1k
    ) {
212
41.1k
        let is_reset = stream.state.is_reset();
213
41.1k
        let is_closed = stream.state.is_closed();
214
41.1k
        let is_empty = stream.pending_send.is_empty();
215
41.1k
        let stream_id = stream.id;
216
217
41.1k
        tracing::trace!(
218
0
            "send_reset(..., reason={:?}, initiator={:?}, stream={:?}, ..., \
219
0
             is_reset={:?}; is_closed={:?}; pending_send.is_empty={:?}; \
220
0
             state={:?} \
221
0
             ",
222
            reason,
223
            initiator,
224
            stream_id,
225
            is_reset,
226
            is_closed,
227
            is_empty,
228
0
            stream.state
229
        );
230
231
41.1k
        if is_reset {
232
            // Don't double reset
233
20.1k
            tracing::trace!(
234
0
                " -> not sending RST_STREAM ({:?} is already reset)",
235
                stream_id
236
            );
237
20.1k
            return;
238
21.0k
        }
239
240
        // Transition the state to reset no matter what.
241
21.0k
        stream.set_reset(reason, initiator);
242
243
        // If closed AND the send queue is flushed, then the stream cannot be
244
        // reset explicitly, either. Implicit resets can still be queued.
245
21.0k
        if is_closed && is_empty {
246
23
            tracing::trace!(
247
0
                " -> not sending explicit RST_STREAM ({:?} was closed \
248
0
                 and send queue was flushed)",
249
                stream_id
250
            );
251
23
            return;
252
20.9k
        }
253
254
        // If the stream hasn't been opened yet (its initial HEADERS are still
255
        // sitting in `pending_open`/`pending_send`), clearing the queue would
256
        // drop those HEADERS and let a RST_STREAM become the first frame on an
257
        // idle stream. HTTP/2 forbids that: ยง5.1 allows only HEADERS/PRIORITY
258
        // on idle streams and ยง6.4 says RST_STREAM on idle is a PROTOCOL_ERROR.
259
        // Keep the queued HEADERS so the stream opens, then send the reset
260
        // immediately after.
261
20.9k
        if !stream.is_pending_open {
262
20.8k
            // Otherwise, drop any buffered DATA/HEADERS and only send the
263
20.8k
            // reset.
264
20.8k
            //
265
20.8k
            // Note that we don't call `self.recv_err` because we want to enqueue
266
20.8k
            // the reset frame before transitioning the stream inside
267
20.8k
            // `reclaim_all_capacity`.
268
20.8k
            self.prioritize.clear_queue(buffer, stream);
269
20.8k
        }
270
271
20.9k
        let frame = frame::Reset::new(stream.id, reason);
272
273
20.9k
        tracing::trace!("send_reset -- queueing; frame={:?}", frame);
274
20.9k
        self.prioritize
275
20.9k
            .queue_frame(frame.into(), buffer, stream, task);
276
20.9k
        self.prioritize.reclaim_all_capacity(stream, counts);
277
41.1k
    }
278
279
76.0k
    pub fn schedule_implicit_reset(
280
76.0k
        &mut self,
281
76.0k
        stream: &mut store::Ptr,
282
76.0k
        reason: Reason,
283
76.0k
        counts: &mut Counts,
284
76.0k
        task: &mut Option<Waker>,
285
76.0k
    ) {
286
76.0k
        if stream.state.is_closed() {
287
            // Stream is already closed, nothing more to do
288
0
            return;
289
76.0k
        }
290
291
76.0k
        stream.state.set_scheduled_reset(reason);
292
293
76.0k
        self.prioritize.reclaim_reserved_capacity(stream, counts);
294
76.0k
        self.prioritize.schedule_send(stream, task);
295
76.0k
    }
296
297
409k
    pub fn send_data<B>(
298
409k
        &mut self,
299
409k
        frame: frame::Data<B>,
300
409k
        buffer: &mut Buffer<Frame<B>>,
301
409k
        stream: &mut store::Ptr,
302
409k
        counts: &mut Counts,
303
409k
        task: &mut Option<Waker>,
304
409k
    ) -> Result<(), UserError>
305
409k
    where
306
409k
        B: Buf,
307
    {
308
409k
        self.prioritize
309
409k
            .send_data(frame, buffer, stream, counts, task)
310
409k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::send_data::<_>
<h2::proto::streams::send::Send>::send_data::<bytes::bytes::Bytes>
Line
Count
Source
297
409k
    pub fn send_data<B>(
298
409k
        &mut self,
299
409k
        frame: frame::Data<B>,
300
409k
        buffer: &mut Buffer<Frame<B>>,
301
409k
        stream: &mut store::Ptr,
302
409k
        counts: &mut Counts,
303
409k
        task: &mut Option<Waker>,
304
409k
    ) -> Result<(), UserError>
305
409k
    where
306
409k
        B: Buf,
307
    {
308
409k
        self.prioritize
309
409k
            .send_data(frame, buffer, stream, counts, task)
310
409k
    }
311
312
0
    pub fn send_trailers<B>(
313
0
        &mut self,
314
0
        frame: frame::Headers,
315
0
        buffer: &mut Buffer<Frame<B>>,
316
0
        stream: &mut store::Ptr,
317
0
        counts: &mut Counts,
318
0
        task: &mut Option<Waker>,
319
0
    ) -> Result<(), UserError> {
320
        // Trailers are carried in a HEADERS frame and are therefore subject to the
321
        // same prohibition on connection-specific header fields (RFC 9113 ยง8.2.2)
322
        // as any other outbound HEADERS block. `send_headers`, `send_push_promise`
323
        // and `send_interim_informational_headers` all validate this, and the
324
        // receive path treats such a header as malformed. Validate here too so a
325
        // caller cannot make this crate *generate* a message ยง8.2.2 forbids.
326
        // Checked before the state transition so a rejected call leaves the stream
327
        // untouched and still able to send valid trailers.
328
0
        Self::check_headers(frame.fields())?;
329
330
        // TODO: Should this logic be moved into state.rs?
331
0
        if !stream.state.is_send_streaming() {
332
0
            return Err(UserError::UnexpectedFrameType);
333
0
        }
334
335
0
        stream.state.send_close();
336
337
0
        tracing::trace!("send_trailers -- queuing; frame={:?}", frame);
338
0
        self.prioritize
339
0
            .queue_frame(frame.into(), buffer, stream, task);
340
341
        // Release any excess capacity
342
0
        self.prioritize.reserve_capacity(0, stream, counts);
343
344
0
        Ok(())
345
0
    }
346
347
800k
    pub fn buffer_pending<T, B>(
348
800k
        &mut self,
349
800k
        buffer: &mut Buffer<Frame<B>>,
350
800k
        store: &mut Store,
351
800k
        counts: &mut Counts,
352
800k
        dst: &mut Codec<T, Prioritized<B>>,
353
800k
    ) -> io::Result<BufferStatus>
354
800k
    where
355
800k
        T: AsyncWrite + Unpin,
356
800k
        B: Buf,
357
    {
358
800k
        self.prioritize.buffer_pending(buffer, store, counts, dst)
359
800k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::buffer_pending::<_, _>
<h2::proto::streams::send::Send>::buffer_pending::<fuzz_e2e::MockIo, bytes::bytes::Bytes>
Line
Count
Source
347
800k
    pub fn buffer_pending<T, B>(
348
800k
        &mut self,
349
800k
        buffer: &mut Buffer<Frame<B>>,
350
800k
        store: &mut Store,
351
800k
        counts: &mut Counts,
352
800k
        dst: &mut Codec<T, Prioritized<B>>,
353
800k
    ) -> io::Result<BufferStatus>
354
800k
    where
355
800k
        T: AsyncWrite + Unpin,
356
800k
        B: Buf,
357
    {
358
800k
        self.prioritize.buffer_pending(buffer, store, counts, dst)
359
800k
    }
360
361
535k
    pub fn reclaim_written_frame<T, B>(
362
535k
        &mut self,
363
535k
        buffer: &mut Buffer<Frame<B>>,
364
535k
        store: &mut Store,
365
535k
        dst: &mut Codec<T, Prioritized<B>>,
366
535k
    ) -> bool
367
535k
    where
368
535k
        B: Buf,
369
    {
370
535k
        self.prioritize.reclaim_written_frame(buffer, store, dst)
371
535k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::reclaim_written_frame::<_, _>
<h2::proto::streams::send::Send>::reclaim_written_frame::<fuzz_e2e::MockIo, bytes::bytes::Bytes>
Line
Count
Source
361
535k
    pub fn reclaim_written_frame<T, B>(
362
535k
        &mut self,
363
535k
        buffer: &mut Buffer<Frame<B>>,
364
535k
        store: &mut Store,
365
535k
        dst: &mut Codec<T, Prioritized<B>>,
366
535k
    ) -> bool
367
535k
    where
368
535k
        B: Buf,
369
    {
370
535k
        self.prioritize.reclaim_written_frame(buffer, store, dst)
371
535k
    }
372
373
    /// Request capacity to send data
374
0
    pub fn reserve_capacity(
375
0
        &mut self,
376
0
        capacity: WindowSize,
377
0
        stream: &mut store::Ptr,
378
0
        counts: &mut Counts,
379
0
    ) {
380
0
        self.prioritize.reserve_capacity(capacity, stream, counts)
381
0
    }
382
383
0
    pub fn poll_capacity(
384
0
        &mut self,
385
0
        cx: &Context,
386
0
        stream: &mut store::Ptr,
387
0
    ) -> Poll<Option<Result<WindowSize, UserError>>> {
388
0
        if !stream.state.is_send_streaming() {
389
0
            return Poll::Ready(None);
390
0
        }
391
392
0
        if !stream.send_capacity_inc {
393
0
            stream.wait_send(cx);
394
0
            return Poll::Pending;
395
0
        }
396
397
0
        stream.send_capacity_inc = false;
398
399
0
        let capacity = self.capacity(stream);
400
401
        // If capacity has been reduced to zero, for example due to a race
402
        // with a SETTINGS frame, return Pending instead of Ready(Ok(0)).
403
0
        if capacity == 0 {
404
0
            stream.wait_send(cx);
405
0
            return Poll::Pending;
406
0
        }
407
408
0
        Poll::Ready(Some(Ok(capacity)))
409
0
    }
410
411
    /// Current available stream send capacity
412
0
    pub fn capacity(&self, stream: &mut store::Ptr) -> WindowSize {
413
0
        stream.capacity(self.prioritize.max_buffer_size())
414
0
    }
415
416
0
    pub fn poll_reset(
417
0
        &self,
418
0
        cx: &Context,
419
0
        stream: &mut Stream,
420
0
        mode: PollReset,
421
0
    ) -> Poll<Result<Reason, crate::Error>> {
422
0
        match stream.state.ensure_reason(mode)? {
423
0
            Some(reason) => Poll::Ready(Ok(reason)),
424
            None => {
425
0
                stream.wait_send(cx);
426
0
                Poll::Pending
427
            }
428
        }
429
0
    }
430
431
954
    pub fn recv_connection_window_update(
432
954
        &mut self,
433
954
        frame: frame::WindowUpdate,
434
954
        store: &mut Store,
435
954
        counts: &mut Counts,
436
954
    ) -> Result<(), Reason> {
437
954
        self.prioritize
438
954
            .recv_connection_window_update(frame.size_increment(), store, counts)
439
954
    }
440
441
126k
    pub fn recv_stream_window_update<B>(
442
126k
        &mut self,
443
126k
        sz: WindowSize,
444
126k
        buffer: &mut Buffer<Frame<B>>,
445
126k
        stream: &mut store::Ptr,
446
126k
        counts: &mut Counts,
447
126k
        task: &mut Option<Waker>,
448
126k
    ) -> Result<(), Reason> {
449
126k
        if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
450
32
            tracing::debug!("recv_stream_window_update !!; err={:?}", e);
451
452
32
            self.send_reset(
453
                Reason::FLOW_CONTROL_ERROR,
454
32
                Initiator::Library,
455
32
                buffer,
456
32
                stream,
457
32
                counts,
458
32
                task,
459
            );
460
461
32
            return Err(e);
462
126k
        }
463
464
126k
        Ok(())
465
126k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::recv_stream_window_update::<_>
<h2::proto::streams::send::Send>::recv_stream_window_update::<bytes::bytes::Bytes>
Line
Count
Source
441
126k
    pub fn recv_stream_window_update<B>(
442
126k
        &mut self,
443
126k
        sz: WindowSize,
444
126k
        buffer: &mut Buffer<Frame<B>>,
445
126k
        stream: &mut store::Ptr,
446
126k
        counts: &mut Counts,
447
126k
        task: &mut Option<Waker>,
448
126k
    ) -> Result<(), Reason> {
449
126k
        if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
450
32
            tracing::debug!("recv_stream_window_update !!; err={:?}", e);
451
452
32
            self.send_reset(
453
                Reason::FLOW_CONTROL_ERROR,
454
32
                Initiator::Library,
455
32
                buffer,
456
32
                stream,
457
32
                counts,
458
32
                task,
459
            );
460
461
32
            return Err(e);
462
126k
        }
463
464
126k
        Ok(())
465
126k
    }
466
467
1.18k
    pub(super) fn recv_go_away(&mut self, last_stream_id: StreamId) -> Result<(), Error> {
468
1.18k
        if last_stream_id > self.max_stream_id {
469
            // The remote endpoint sent a `GOAWAY` frame indicating a stream
470
            // that we never sent, or that we have already terminated on account
471
            // of previous `GOAWAY` frame. In either case, that is illegal.
472
            // (When sending multiple `GOAWAY`s, "Endpoints MUST NOT increase
473
            // the value they send in the last stream identifier, since the
474
            // peers might already have retried unprocessed requests on another
475
            // connection.")
476
30
            proto_err!(conn:
477
                "recv_go_away: last_stream_id ({:?}) > max_stream_id ({:?})",
478
                last_stream_id, self.max_stream_id,
479
            );
480
30
            return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
481
1.15k
        }
482
483
1.15k
        self.max_stream_id = last_stream_id;
484
1.15k
        Ok(())
485
1.18k
    }
486
487
449k
    pub fn handle_error<B>(
488
449k
        &mut self,
489
449k
        buffer: &mut Buffer<Frame<B>>,
490
449k
        stream: &mut store::Ptr,
491
449k
        counts: &mut Counts,
492
449k
    ) {
493
        // Clear all pending outbound frames
494
449k
        self.prioritize.clear_queue(buffer, stream);
495
449k
        self.prioritize.reclaim_all_capacity(stream, counts);
496
449k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::handle_error::<_>
<h2::proto::streams::send::Send>::handle_error::<bytes::bytes::Bytes>
Line
Count
Source
487
700
    pub fn handle_error<B>(
488
700
        &mut self,
489
700
        buffer: &mut Buffer<Frame<B>>,
490
700
        stream: &mut store::Ptr,
491
700
        counts: &mut Counts,
492
700
    ) {
493
        // Clear all pending outbound frames
494
700
        self.prioritize.clear_queue(buffer, stream);
495
700
        self.prioritize.reclaim_all_capacity(stream, counts);
496
700
    }
<h2::proto::streams::send::Send>::handle_error::<bytes::bytes::Bytes>
Line
Count
Source
487
449k
    pub fn handle_error<B>(
488
449k
        &mut self,
489
449k
        buffer: &mut Buffer<Frame<B>>,
490
449k
        stream: &mut store::Ptr,
491
449k
        counts: &mut Counts,
492
449k
    ) {
493
        // Clear all pending outbound frames
494
449k
        self.prioritize.clear_queue(buffer, stream);
495
449k
        self.prioritize.reclaim_all_capacity(stream, counts);
496
449k
    }
497
498
5.68k
    pub fn apply_remote_settings<B>(
499
5.68k
        &mut self,
500
5.68k
        settings: &frame::Settings,
501
5.68k
        buffer: &mut Buffer<Frame<B>>,
502
5.68k
        store: &mut Store,
503
5.68k
        counts: &mut Counts,
504
5.68k
        task: &mut Option<Waker>,
505
5.68k
    ) -> Result<(), Error> {
506
5.68k
        if let Some(val) = settings.is_extended_connect_protocol_enabled() {
507
44
            self.is_extended_connect_protocol_enabled = val;
508
5.63k
        }
509
510
        // Applies an update to the remote endpoint's initial window size.
511
        //
512
        // Per RFC 7540 ยง6.9.2:
513
        //
514
        // In addition to changing the flow-control window for streams that are
515
        // not yet active, a SETTINGS frame can alter the initial flow-control
516
        // window size for streams with active flow-control windows (that is,
517
        // streams in the "open" or "half-closed (remote)" state). When the
518
        // value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust
519
        // the size of all stream flow-control windows that it maintains by the
520
        // difference between the new value and the old value.
521
        //
522
        // A change to `SETTINGS_INITIAL_WINDOW_SIZE` can cause the available
523
        // space in a flow-control window to become negative. A sender MUST
524
        // track the negative flow-control window and MUST NOT send new
525
        // flow-controlled frames until it receives WINDOW_UPDATE frames that
526
        // cause the flow-control window to become positive.
527
5.68k
        if let Some(val) = settings.initial_window_size() {
528
3.31k
            let old_val = self.init_window_sz;
529
3.31k
            self.init_window_sz = val;
530
531
3.31k
            match val.cmp(&old_val) {
532
                Ordering::Less => {
533
                    // We must decrease the (remote) window on every open stream.
534
1.67k
                    let dec = old_val - val;
535
1.67k
                    tracing::trace!("decrementing all windows; dec={}", dec);
536
537
1.67k
                    let mut total_reclaimed = 0;
538
147k
                    store.try_for_each(|mut stream| {
539
147k
                        let stream = &mut *stream;
540
541
147k
                        if stream.state.is_send_closed() && stream.buffered_send_data == 0 {
542
4.19k
                            tracing::trace!(
543
0
                                "skipping send-closed stream; id={:?}; flow={:?}",
544
                                stream.id,
545
                                stream.send_flow
546
                            );
547
548
4.19k
                            return Ok(());
549
143k
                        }
550
551
143k
                        tracing::trace!(
552
0
                            "decrementing stream window; id={:?}; decr={}; flow={:?}",
553
                            stream.id,
554
                            dec,
555
                            stream.send_flow
556
                        );
557
558
                        // TODO: this decrement can underflow based on received frames!
559
143k
                        stream
560
143k
                            .send_flow
561
143k
                            .dec_send_window(dec)
562
143k
                            .map_err(proto::Error::library_go_away)?;
563
564
                        // It's possible that decreasing the window causes
565
                        // `window_size` (the stream-specific window) to fall below
566
                        // `available` (the portion of the connection-level window
567
                        // that we have allocated to the stream).
568
                        // In this case, we should take that excess allocation away
569
                        // and reassign it to other streams.
570
143k
                        let window_size = stream.send_flow.window_size();
571
143k
                        let available = stream.send_flow.available().as_size();
572
143k
                        let reclaimed = if available > window_size {
573
                            // Drop down to `window_size`.
574
15.6k
                            let reclaim = available - window_size;
575
15.6k
                            stream
576
15.6k
                                .send_flow
577
15.6k
                                .claim_capacity(reclaim)
578
15.6k
                                .map_err(proto::Error::library_go_away)?;
579
15.6k
                            total_reclaimed += reclaim;
580
15.6k
                            reclaim
581
                        } else {
582
128k
                            0
583
                        };
584
585
143k
                        tracing::trace!(
586
0
                            "decremented stream window; id={:?}; decr={}; reclaimed={}; flow={:?}",
587
                            stream.id,
588
                            dec,
589
                            reclaimed,
590
                            stream.send_flow
591
                        );
592
593
                        // TODO: Should this notify the producer when the capacity
594
                        // of a stream is reduced? Maybe it should if the capacity
595
                        // is reduced to zero, allowing the producer to stop work.
596
597
143k
                        Ok::<_, proto::Error>(())
598
147k
                    })?;
Unexecuted instantiation: <h2::proto::streams::send::Send>::apply_remote_settings::<_>::{closure#0}
<h2::proto::streams::send::Send>::apply_remote_settings::<bytes::bytes::Bytes>::{closure#0}
Line
Count
Source
538
147k
                    store.try_for_each(|mut stream| {
539
147k
                        let stream = &mut *stream;
540
541
147k
                        if stream.state.is_send_closed() && stream.buffered_send_data == 0 {
542
4.19k
                            tracing::trace!(
543
0
                                "skipping send-closed stream; id={:?}; flow={:?}",
544
                                stream.id,
545
                                stream.send_flow
546
                            );
547
548
4.19k
                            return Ok(());
549
143k
                        }
550
551
143k
                        tracing::trace!(
552
0
                            "decrementing stream window; id={:?}; decr={}; flow={:?}",
553
                            stream.id,
554
                            dec,
555
                            stream.send_flow
556
                        );
557
558
                        // TODO: this decrement can underflow based on received frames!
559
143k
                        stream
560
143k
                            .send_flow
561
143k
                            .dec_send_window(dec)
562
143k
                            .map_err(proto::Error::library_go_away)?;
563
564
                        // It's possible that decreasing the window causes
565
                        // `window_size` (the stream-specific window) to fall below
566
                        // `available` (the portion of the connection-level window
567
                        // that we have allocated to the stream).
568
                        // In this case, we should take that excess allocation away
569
                        // and reassign it to other streams.
570
143k
                        let window_size = stream.send_flow.window_size();
571
143k
                        let available = stream.send_flow.available().as_size();
572
143k
                        let reclaimed = if available > window_size {
573
                            // Drop down to `window_size`.
574
15.6k
                            let reclaim = available - window_size;
575
15.6k
                            stream
576
15.6k
                                .send_flow
577
15.6k
                                .claim_capacity(reclaim)
578
15.6k
                                .map_err(proto::Error::library_go_away)?;
579
15.6k
                            total_reclaimed += reclaim;
580
15.6k
                            reclaim
581
                        } else {
582
128k
                            0
583
                        };
584
585
143k
                        tracing::trace!(
586
0
                            "decremented stream window; id={:?}; decr={}; reclaimed={}; flow={:?}",
587
                            stream.id,
588
                            dec,
589
                            reclaimed,
590
                            stream.send_flow
591
                        );
592
593
                        // TODO: Should this notify the producer when the capacity
594
                        // of a stream is reduced? Maybe it should if the capacity
595
                        // is reduced to zero, allowing the producer to stop work.
596
597
143k
                        Ok::<_, proto::Error>(())
598
147k
                    })?;
599
600
1.67k
                    self.prioritize
601
1.67k
                        .assign_connection_capacity(total_reclaimed, store, counts);
602
                }
603
                Ordering::Greater => {
604
1.31k
                    let inc = val - old_val;
605
606
125k
                    store.try_for_each(|mut stream| {
607
125k
                        self.recv_stream_window_update(inc, buffer, &mut stream, counts, task)
608
125k
                            .map_err(Error::library_go_away)
609
125k
                    })?;
Unexecuted instantiation: <h2::proto::streams::send::Send>::apply_remote_settings::<_>::{closure#1}
<h2::proto::streams::send::Send>::apply_remote_settings::<bytes::bytes::Bytes>::{closure#1}
Line
Count
Source
606
125k
                    store.try_for_each(|mut stream| {
607
125k
                        self.recv_stream_window_update(inc, buffer, &mut stream, counts, task)
608
125k
                            .map_err(Error::library_go_away)
609
125k
                    })?;
610
                }
611
335
                Ordering::Equal => (),
612
            }
613
2.36k
        }
614
615
5.68k
        if let Some(val) = settings.is_push_enabled() {
616
286
            self.is_push_enabled = val
617
5.39k
        }
618
619
5.68k
        Ok(())
620
5.68k
    }
Unexecuted instantiation: <h2::proto::streams::send::Send>::apply_remote_settings::<_>
<h2::proto::streams::send::Send>::apply_remote_settings::<bytes::bytes::Bytes>
Line
Count
Source
498
5.68k
    pub fn apply_remote_settings<B>(
499
5.68k
        &mut self,
500
5.68k
        settings: &frame::Settings,
501
5.68k
        buffer: &mut Buffer<Frame<B>>,
502
5.68k
        store: &mut Store,
503
5.68k
        counts: &mut Counts,
504
5.68k
        task: &mut Option<Waker>,
505
5.68k
    ) -> Result<(), Error> {
506
5.68k
        if let Some(val) = settings.is_extended_connect_protocol_enabled() {
507
44
            self.is_extended_connect_protocol_enabled = val;
508
5.63k
        }
509
510
        // Applies an update to the remote endpoint's initial window size.
511
        //
512
        // Per RFC 7540 ยง6.9.2:
513
        //
514
        // In addition to changing the flow-control window for streams that are
515
        // not yet active, a SETTINGS frame can alter the initial flow-control
516
        // window size for streams with active flow-control windows (that is,
517
        // streams in the "open" or "half-closed (remote)" state). When the
518
        // value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust
519
        // the size of all stream flow-control windows that it maintains by the
520
        // difference between the new value and the old value.
521
        //
522
        // A change to `SETTINGS_INITIAL_WINDOW_SIZE` can cause the available
523
        // space in a flow-control window to become negative. A sender MUST
524
        // track the negative flow-control window and MUST NOT send new
525
        // flow-controlled frames until it receives WINDOW_UPDATE frames that
526
        // cause the flow-control window to become positive.
527
5.68k
        if let Some(val) = settings.initial_window_size() {
528
3.31k
            let old_val = self.init_window_sz;
529
3.31k
            self.init_window_sz = val;
530
531
3.31k
            match val.cmp(&old_val) {
532
                Ordering::Less => {
533
                    // We must decrease the (remote) window on every open stream.
534
1.67k
                    let dec = old_val - val;
535
1.67k
                    tracing::trace!("decrementing all windows; dec={}", dec);
536
537
1.67k
                    let mut total_reclaimed = 0;
538
1.67k
                    store.try_for_each(|mut stream| {
539
                        let stream = &mut *stream;
540
541
                        if stream.state.is_send_closed() && stream.buffered_send_data == 0 {
542
                            tracing::trace!(
543
                                "skipping send-closed stream; id={:?}; flow={:?}",
544
                                stream.id,
545
                                stream.send_flow
546
                            );
547
548
                            return Ok(());
549
                        }
550
551
                        tracing::trace!(
552
                            "decrementing stream window; id={:?}; decr={}; flow={:?}",
553
                            stream.id,
554
                            dec,
555
                            stream.send_flow
556
                        );
557
558
                        // TODO: this decrement can underflow based on received frames!
559
                        stream
560
                            .send_flow
561
                            .dec_send_window(dec)
562
                            .map_err(proto::Error::library_go_away)?;
563
564
                        // It's possible that decreasing the window causes
565
                        // `window_size` (the stream-specific window) to fall below
566
                        // `available` (the portion of the connection-level window
567
                        // that we have allocated to the stream).
568
                        // In this case, we should take that excess allocation away
569
                        // and reassign it to other streams.
570
                        let window_size = stream.send_flow.window_size();
571
                        let available = stream.send_flow.available().as_size();
572
                        let reclaimed = if available > window_size {
573
                            // Drop down to `window_size`.
574
                            let reclaim = available - window_size;
575
                            stream
576
                                .send_flow
577
                                .claim_capacity(reclaim)
578
                                .map_err(proto::Error::library_go_away)?;
579
                            total_reclaimed += reclaim;
580
                            reclaim
581
                        } else {
582
                            0
583
                        };
584
585
                        tracing::trace!(
586
                            "decremented stream window; id={:?}; decr={}; reclaimed={}; flow={:?}",
587
                            stream.id,
588
                            dec,
589
                            reclaimed,
590
                            stream.send_flow
591
                        );
592
593
                        // TODO: Should this notify the producer when the capacity
594
                        // of a stream is reduced? Maybe it should if the capacity
595
                        // is reduced to zero, allowing the producer to stop work.
596
597
                        Ok::<_, proto::Error>(())
598
0
                    })?;
599
600
1.67k
                    self.prioritize
601
1.67k
                        .assign_connection_capacity(total_reclaimed, store, counts);
602
                }
603
                Ordering::Greater => {
604
1.31k
                    let inc = val - old_val;
605
606
1.31k
                    store.try_for_each(|mut stream| {
607
                        self.recv_stream_window_update(inc, buffer, &mut stream, counts, task)
608
                            .map_err(Error::library_go_away)
609
1
                    })?;
610
                }
611
335
                Ordering::Equal => (),
612
            }
613
2.36k
        }
614
615
5.68k
        if let Some(val) = settings.is_push_enabled() {
616
286
            self.is_push_enabled = val
617
5.39k
        }
618
619
5.68k
        Ok(())
620
5.68k
    }
621
622
17.1k
    pub fn clear_queues(&mut self, store: &mut Store, counts: &mut Counts) {
623
17.1k
        self.prioritize.clear_pending_capacity(store, counts);
624
17.1k
        self.prioritize.clear_pending_send(store, counts);
625
17.1k
        self.prioritize.clear_pending_open(store, counts);
626
17.1k
    }
627
628
835
    pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> {
629
835
        if let Ok(next) = self.next_stream_id {
630
417
            if id >= next {
631
56
                return Err(Reason::PROTOCOL_ERROR);
632
361
            }
633
418
        }
634
        // if next_stream_id is overflowed, that's ok.
635
636
779
        Ok(())
637
835
    }
638
639
1.23M
    pub fn ensure_next_stream_id(&self) -> Result<StreamId, UserError> {
640
1.23M
        self.next_stream_id
641
1.23M
            .map_err(|_| UserError::OverflowedStreamId)
642
1.23M
    }
643
644
3.96k
    pub fn may_have_created_stream(&self, id: StreamId) -> bool {
645
3.96k
        if let Ok(next_id) = self.next_stream_id {
646
            // Peer::is_local_init should have been called beforehand
647
3.31k
            debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated(),);
648
3.31k
            id < next_id
649
        } else {
650
656
            true
651
        }
652
3.96k
    }
653
654
7.98k
    pub(super) fn maybe_reset_next_stream_id(&mut self, id: StreamId) {
655
7.98k
        if let Ok(next_id) = self.next_stream_id {
656
            // Peer::is_local_init should have been called beforehand
657
7.11k
            debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated());
658
7.11k
            if id >= next_id {
659
2.71k
                self.next_stream_id = id.next_id();
660
4.40k
            }
661
868
        }
662
7.98k
    }
663
664
0
    pub(crate) fn is_extended_connect_protocol_enabled(&self) -> bool {
665
0
        self.is_extended_connect_protocol_enabled
666
0
    }
667
}