Skip to main content

h2/proto/streams/
send.rs

1use super::{
2    store, Buffer, BufferStatus, Codec, Config, Counts, Frame, Prioritize, Prioritized, Store,
3    Stream, StreamId, StreamIdOverflow, WindowSize,
4};
5use crate::codec::UserError;
6use crate::frame::{self, Reason};
7use crate::proto::{self, Error, Initiator};
8
9use bytes::Buf;
10use tokio::io::AsyncWrite;
11
12use std::cmp::Ordering;
13use std::io;
14use std::task::{Context, Poll, Waker};
15
16/// Manages state transitions related to outbound frames.
17#[derive(Debug)]
18pub(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)]
45pub(crate) enum PollReset {
46    AwaitingHeaders,
47    Streaming,
48}
49
50impl Send {
51    /// Create a new `Send`
52    pub fn new(config: &Config) -> Self {
53        Send {
54            init_window_sz: config.remote_init_window_sz,
55            max_stream_id: StreamId::MAX,
56            next_stream_id: Ok(config.local_next_stream_id),
57            prioritize: Prioritize::new(config),
58            is_push_enabled: true,
59            is_extended_connect_protocol_enabled: false,
60        }
61    }
62
63    /// Returns the initial send window size
64    pub fn init_window_sz(&self) -> WindowSize {
65        self.init_window_sz
66    }
67
68    pub fn open(&mut self) -> Result<StreamId, UserError> {
69        let stream_id = self.ensure_next_stream_id()?;
70        self.next_stream_id = stream_id.next_id();
71        Ok(stream_id)
72    }
73
74    pub fn reserve_local(&mut self) -> Result<StreamId, UserError> {
75        let stream_id = self.ensure_next_stream_id()?;
76        self.next_stream_id = stream_id.next_id();
77        Ok(stream_id)
78    }
79
80    fn check_headers(fields: &http::HeaderMap) -> Result<(), UserError> {
81        // 8.1.2.2. Connection-Specific Header Fields
82        if fields.contains_key(http::header::CONNECTION)
83            || fields.contains_key(http::header::TRANSFER_ENCODING)
84            || fields.contains_key(http::header::UPGRADE)
85            || fields.contains_key("keep-alive")
86            || fields.contains_key("proxy-connection")
87        {
88            tracing::debug!("illegal connection-specific headers found");
89            return Err(UserError::MalformedHeaders);
90        } else if let Some(te) = fields.get(http::header::TE) {
91            if te != "trailers" {
92                tracing::debug!("illegal connection-specific headers found");
93                return Err(UserError::MalformedHeaders);
94            }
95        }
96        Ok(())
97    }
98
99    pub fn send_push_promise<B>(
100        &mut self,
101        frame: frame::PushPromise,
102        buffer: &mut Buffer<Frame<B>>,
103        stream: &mut store::Ptr,
104        task: &mut Option<Waker>,
105    ) -> Result<(), UserError> {
106        if !self.is_push_enabled {
107            return Err(UserError::PeerDisabledServerPush);
108        }
109
110        tracing::trace!(
111            "send_push_promise; frame={:?}; init_window={:?}",
112            frame,
113            self.init_window_sz
114        );
115
116        Self::check_headers(frame.fields())?;
117
118        // Queue the frame for sending
119        self.prioritize
120            .queue_frame(frame.into(), buffer, stream, task);
121
122        Ok(())
123    }
124
125    pub fn send_headers<B>(
126        &mut self,
127        frame: frame::Headers,
128        buffer: &mut Buffer<Frame<B>>,
129        stream: &mut store::Ptr,
130        counts: &mut Counts,
131        task: &mut Option<Waker>,
132    ) -> Result<(), UserError> {
133        tracing::trace!(
134            "send_headers; frame={:?}; init_window={:?}",
135            frame,
136            self.init_window_sz
137        );
138
139        Self::check_headers(frame.fields())?;
140
141        let end_stream = frame.is_end_stream();
142
143        // Update the state
144        stream.state.send_open(end_stream)?;
145
146        let mut pending_open = false;
147        if counts.peer().is_local_init(frame.stream_id()) && !stream.is_pending_push {
148            self.prioritize.queue_open(stream);
149            pending_open = true;
150        }
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        self.prioritize
157            .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        if pending_open {
162            if let Some(task) = task.take() {
163                task.wake();
164            }
165        }
166
167        Ok(())
168    }
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    pub fn send_interim_informational_headers<B>(
173        &mut self,
174        frame: frame::Headers,
175        buffer: &mut Buffer<Frame<B>>,
176        stream: &mut store::Ptr,
177        _counts: &mut Counts,
178        task: &mut Option<Waker>,
179    ) -> Result<(), UserError> {
180        tracing::trace!(
181            "send_interim_informational_headers; frame={:?}; stream_id={:?}",
182            frame,
183            frame.stream_id()
184        );
185
186        // Validate headers
187        Self::check_headers(frame.fields())?;
188
189        debug_assert!(frame.is_informational(),
190            "Frame must be informational (1xx status code) at this point. Validation should happen at the public API boundary.");
191        debug_assert!(!frame.is_end_stream(),
192            "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        self.prioritize
197            .queue_frame(frame.into(), buffer, stream, task);
198
199        Ok(())
200    }
201
202    /// Send an explicit RST_STREAM frame
203    pub fn send_reset<B>(
204        &mut self,
205        reason: Reason,
206        initiator: Initiator,
207        buffer: &mut Buffer<Frame<B>>,
208        stream: &mut store::Ptr,
209        counts: &mut Counts,
210        task: &mut Option<Waker>,
211    ) {
212        let is_reset = stream.state.is_reset();
213        let is_closed = stream.state.is_closed();
214        let is_empty = stream.pending_send.is_empty();
215        let stream_id = stream.id;
216
217        tracing::trace!(
218            "send_reset(..., reason={:?}, initiator={:?}, stream={:?}, ..., \
219             is_reset={:?}; is_closed={:?}; pending_send.is_empty={:?}; \
220             state={:?} \
221             ",
222            reason,
223            initiator,
224            stream_id,
225            is_reset,
226            is_closed,
227            is_empty,
228            stream.state
229        );
230
231        if is_reset {
232            // Don't double reset
233            tracing::trace!(
234                " -> not sending RST_STREAM ({:?} is already reset)",
235                stream_id
236            );
237            return;
238        }
239
240        // Transition the state to reset no matter what.
241        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        if is_closed && is_empty {
246            tracing::trace!(
247                " -> not sending explicit RST_STREAM ({:?} was closed \
248                 and send queue was flushed)",
249                stream_id
250            );
251            return;
252        }
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        if !stream.is_pending_open {
262            // Otherwise, drop any buffered DATA/HEADERS and only send the
263            // reset.
264            //
265            // Note that we don't call `self.recv_err` because we want to enqueue
266            // the reset frame before transitioning the stream inside
267            // `reclaim_all_capacity`.
268            self.prioritize.clear_queue(buffer, stream);
269        }
270
271        let frame = frame::Reset::new(stream.id, reason);
272
273        tracing::trace!("send_reset -- queueing; frame={:?}", frame);
274        self.prioritize
275            .queue_frame(frame.into(), buffer, stream, task);
276        self.prioritize.reclaim_all_capacity(stream, counts);
277    }
278
279    pub fn schedule_implicit_reset(
280        &mut self,
281        stream: &mut store::Ptr,
282        reason: Reason,
283        counts: &mut Counts,
284        task: &mut Option<Waker>,
285    ) {
286        if stream.state.is_closed() {
287            // Stream is already closed, nothing more to do
288            return;
289        }
290
291        stream.state.set_scheduled_reset(reason);
292
293        self.prioritize.reclaim_reserved_capacity(stream, counts);
294        self.prioritize.schedule_send(stream, task);
295    }
296
297    pub fn send_data<B>(
298        &mut self,
299        frame: frame::Data<B>,
300        buffer: &mut Buffer<Frame<B>>,
301        stream: &mut store::Ptr,
302        counts: &mut Counts,
303        task: &mut Option<Waker>,
304    ) -> Result<(), UserError>
305    where
306        B: Buf,
307    {
308        self.prioritize
309            .send_data(frame, buffer, stream, counts, task)
310    }
311
312    pub fn send_trailers<B>(
313        &mut self,
314        frame: frame::Headers,
315        buffer: &mut Buffer<Frame<B>>,
316        stream: &mut store::Ptr,
317        counts: &mut Counts,
318        task: &mut Option<Waker>,
319    ) -> 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        Self::check_headers(frame.fields())?;
329
330        // TODO: Should this logic be moved into state.rs?
331        if !stream.state.is_send_streaming() {
332            return Err(UserError::UnexpectedFrameType);
333        }
334
335        stream.state.send_close();
336
337        tracing::trace!("send_trailers -- queuing; frame={:?}", frame);
338        self.prioritize
339            .queue_frame(frame.into(), buffer, stream, task);
340
341        // Release any excess capacity
342        self.prioritize.reserve_capacity(0, stream, counts);
343
344        Ok(())
345    }
346
347    pub fn buffer_pending<T, B>(
348        &mut self,
349        buffer: &mut Buffer<Frame<B>>,
350        store: &mut Store,
351        counts: &mut Counts,
352        dst: &mut Codec<T, Prioritized<B>>,
353    ) -> io::Result<BufferStatus>
354    where
355        T: AsyncWrite + Unpin,
356        B: Buf,
357    {
358        self.prioritize.buffer_pending(buffer, store, counts, dst)
359    }
360
361    pub fn reclaim_written_frame<T, B>(
362        &mut self,
363        buffer: &mut Buffer<Frame<B>>,
364        store: &mut Store,
365        dst: &mut Codec<T, Prioritized<B>>,
366    ) -> bool
367    where
368        B: Buf,
369    {
370        self.prioritize.reclaim_written_frame(buffer, store, dst)
371    }
372
373    /// Request capacity to send data
374    pub fn reserve_capacity(
375        &mut self,
376        capacity: WindowSize,
377        stream: &mut store::Ptr,
378        counts: &mut Counts,
379    ) {
380        self.prioritize.reserve_capacity(capacity, stream, counts)
381    }
382
383    pub fn poll_capacity(
384        &mut self,
385        cx: &Context,
386        stream: &mut store::Ptr,
387    ) -> Poll<Option<Result<WindowSize, UserError>>> {
388        if !stream.state.is_send_streaming() {
389            return Poll::Ready(None);
390        }
391
392        if !stream.send_capacity_inc {
393            stream.wait_send(cx);
394            return Poll::Pending;
395        }
396
397        stream.send_capacity_inc = false;
398
399        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        if capacity == 0 {
404            stream.wait_send(cx);
405            return Poll::Pending;
406        }
407
408        Poll::Ready(Some(Ok(capacity)))
409    }
410
411    /// Current available stream send capacity
412    pub fn capacity(&self, stream: &mut store::Ptr) -> WindowSize {
413        stream.capacity(self.prioritize.max_buffer_size())
414    }
415
416    pub fn poll_reset(
417        &self,
418        cx: &Context,
419        stream: &mut Stream,
420        mode: PollReset,
421    ) -> Poll<Result<Reason, crate::Error>> {
422        match stream.state.ensure_reason(mode)? {
423            Some(reason) => Poll::Ready(Ok(reason)),
424            None => {
425                stream.wait_send(cx);
426                Poll::Pending
427            }
428        }
429    }
430
431    pub fn recv_connection_window_update(
432        &mut self,
433        frame: frame::WindowUpdate,
434        store: &mut Store,
435        counts: &mut Counts,
436    ) -> Result<(), Reason> {
437        self.prioritize
438            .recv_connection_window_update(frame.size_increment(), store, counts)
439    }
440
441    pub fn recv_stream_window_update<B>(
442        &mut self,
443        sz: WindowSize,
444        buffer: &mut Buffer<Frame<B>>,
445        stream: &mut store::Ptr,
446        counts: &mut Counts,
447        task: &mut Option<Waker>,
448    ) -> Result<(), Reason> {
449        if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
450            tracing::debug!("recv_stream_window_update !!; err={:?}", e);
451
452            self.send_reset(
453                Reason::FLOW_CONTROL_ERROR,
454                Initiator::Library,
455                buffer,
456                stream,
457                counts,
458                task,
459            );
460
461            return Err(e);
462        }
463
464        Ok(())
465    }
466
467    pub(super) fn recv_go_away(&mut self, last_stream_id: StreamId) -> Result<(), Error> {
468        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            proto_err!(conn:
477                "recv_go_away: last_stream_id ({:?}) > max_stream_id ({:?})",
478                last_stream_id, self.max_stream_id,
479            );
480            return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
481        }
482
483        self.max_stream_id = last_stream_id;
484        Ok(())
485    }
486
487    pub fn handle_error<B>(
488        &mut self,
489        buffer: &mut Buffer<Frame<B>>,
490        stream: &mut store::Ptr,
491        counts: &mut Counts,
492    ) {
493        // Clear all pending outbound frames
494        self.prioritize.clear_queue(buffer, stream);
495        self.prioritize.reclaim_all_capacity(stream, counts);
496    }
497
498    pub fn apply_remote_settings<B>(
499        &mut self,
500        settings: &frame::Settings,
501        buffer: &mut Buffer<Frame<B>>,
502        store: &mut Store,
503        counts: &mut Counts,
504        task: &mut Option<Waker>,
505    ) -> Result<(), Error> {
506        if let Some(val) = settings.is_extended_connect_protocol_enabled() {
507            self.is_extended_connect_protocol_enabled = val;
508        }
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        if let Some(val) = settings.initial_window_size() {
528            let old_val = self.init_window_sz;
529            self.init_window_sz = val;
530
531            match val.cmp(&old_val) {
532                Ordering::Less => {
533                    // We must decrease the (remote) window on every open stream.
534                    let dec = old_val - val;
535                    tracing::trace!("decrementing all windows; dec={}", dec);
536
537                    let mut total_reclaimed = 0;
538                    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                    })?;
599
600                    self.prioritize
601                        .assign_connection_capacity(total_reclaimed, store, counts);
602                }
603                Ordering::Greater => {
604                    let inc = val - old_val;
605
606                    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                    })?;
610                }
611                Ordering::Equal => (),
612            }
613        }
614
615        if let Some(val) = settings.is_push_enabled() {
616            self.is_push_enabled = val
617        }
618
619        Ok(())
620    }
621
622    pub fn clear_queues(&mut self, store: &mut Store, counts: &mut Counts) {
623        self.prioritize.clear_pending_capacity(store, counts);
624        self.prioritize.clear_pending_send(store, counts);
625        self.prioritize.clear_pending_open(store, counts);
626    }
627
628    pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> {
629        if let Ok(next) = self.next_stream_id {
630            if id >= next {
631                return Err(Reason::PROTOCOL_ERROR);
632            }
633        }
634        // if next_stream_id is overflowed, that's ok.
635
636        Ok(())
637    }
638
639    pub fn ensure_next_stream_id(&self) -> Result<StreamId, UserError> {
640        self.next_stream_id
641            .map_err(|_| UserError::OverflowedStreamId)
642    }
643
644    pub fn may_have_created_stream(&self, id: StreamId) -> bool {
645        if let Ok(next_id) = self.next_stream_id {
646            // Peer::is_local_init should have been called beforehand
647            debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated(),);
648            id < next_id
649        } else {
650            true
651        }
652    }
653
654    pub(super) fn maybe_reset_next_stream_id(&mut self, id: StreamId) {
655        if let Ok(next_id) = self.next_stream_id {
656            // Peer::is_local_init should have been called beforehand
657            debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated());
658            if id >= next_id {
659                self.next_stream_id = id.next_id();
660            }
661        }
662    }
663
664    pub(crate) fn is_extended_connect_protocol_enabled(&self) -> bool {
665        self.is_extended_connect_protocol_enabled
666    }
667}