Skip to main content

vibeio_http/h2/connection/
mod.rs

1//! HTTP/2 connection (RFC 9113 Sections 3.5, 5.1, 6.5, 8.1).
2//!
3//! Drives one HTTP/2 connection over an async I/O stream: reads the
4//! client's 24-octet preface (with a timeout), sends the server
5//! SETTINGS, maintains the peer's SETTINGS state (applying
6//! `SETTINGS_HEADER_TABLE_SIZE` to the HPACK encoder and
7//! `SETTINGS_MAX_FRAME_SIZE` to the outgoing frame writer), answers
8//! SETTINGS frames with ACK, echoes PING, and reports protocol
9//! violations with GOAWAY before closing.
10//!
11//! Frame parsing and validation happen in [`super::codec`]; this module
12//! adds the connection- and stream-level wiring: per-stream state,
13//! request/response header parsing, request dispatch to the handler,
14//! response framing, and the connection-level error handling that
15//! h2spec's `http2/4.3`, `http2/5.1`, `http2/6.1`, `http2/6.2`,
16//! `http2/6.4` and `http2/8.1` groups cover.
17
18use std::{
19    future::Future,
20    pin::Pin,
21    sync::{atomic::AtomicBool, Arc},
22    task::{Context, Poll},
23    time::Duration,
24};
25
26use bytes::Bytes;
27use futures_util::{pin_mut, FutureExt};
28use http::{Request, Response, StatusCode};
29use http_body::Body;
30use rustc_hash::{FxHashMap, FxHashSet};
31use tokio_util::sync::CancellationToken;
32
33use super::codec::{
34    Frame, FrameDecoder, FrameWriter, Setting, CLIENT_PREFACE, DEFAULT_INITIAL_WINDOW_SIZE,
35    DEFAULT_MAX_FRAME_SIZE, MAX_FRAME_SIZE_LIMIT,
36};
37use super::date::DateCache;
38use super::error::Reason;
39use super::hpack::{Decoder as HpackDecoder, Encoder, Header as HpackHeader, HpackError};
40use super::sanitize_response;
41use super::stream::{
42    BodyMsg, H2Body, MalformedRequest, ParsedRequest, StreamDriver, StreamEntry, StreamMsg,
43};
44use crate::early_hints::EarlyHints;
45use crate::Incoming;
46
47/// Per-connection behavior options.
48#[derive(Debug, Clone, Copy)]
49pub struct ConnectionOptions {
50    /// Answer requests with `100 Continue` when they carry
51    /// `expect: 100-continue`.
52    pub send_continue_response: bool,
53    /// Add a `Date` header to responses.
54    pub send_date_header: bool,
55    /// `SETTINGS_MAX_CONCURRENT_STREAMS` announced to the peer.
56    pub max_concurrent_streams: u32,
57    /// `SETTINGS_INITIAL_WINDOW_SIZE`: per-stream DATA credit we start with.
58    pub initial_stream_window_size: u32,
59    /// Connection-level DATA credit we start with (RFC 9113 Section 6.9.1).
60    pub initial_connection_window_size: u32,
61    /// Largest frame (payload) we send or receive (`SETTINGS_MAX_FRAME_SIZE`).
62    pub max_frame_size: u32,
63    /// Largest decoded header list we accept (`SETTINGS_MAX_HEADER_LIST_SIZE`).
64    pub max_header_list_size: u32,
65    /// Whether to enable Extended CONNECT
66    pub enable_connect_protocol: bool,
67    /// Close the connection after this long with no frame from the peer
68    /// (RFC 9113 Section 10.5). `None` disables the idle timeout.
69    pub idle_timeout: Option<Duration>,
70    /// Maximum number of RST_STREAM frames we send in response to
71    /// protocol errors made by the peer over the connection's lifetime.
72    /// `None` disables the limit; when it is exceeded the connection
73    /// closes with GOAWAY `ENHANCE_YOUR_CALM` (RFC 9113 Section 10.5.2).
74    pub max_local_error_reset_streams: Option<usize>,
75    /// Maximum number of streams the peer reset before we accepted them.
76    /// `None` disables the limit; when it is exceeded the connection
77    /// closes with GOAWAY `ENHANCE_YOUR_CALM` (RFC 9113 Section 10.5.2).
78    pub max_pending_accept_reset_streams: Option<usize>,
79    /// Maximum number of frames that may compose a single, not-yet-finalized
80    /// header field block (HEADERS/CONTINUATION). Beyond this a stream is
81    /// reset as a CONTINUATION flood (CVE-2024-27919).
82    pub max_continuation_frames: usize,
83}
84
85impl Default for ConnectionOptions {
86    #[inline]
87    fn default() -> Self {
88        ConnectionOptions {
89            send_continue_response: false,
90            send_date_header: true,
91            max_concurrent_streams: 100,
92            initial_stream_window_size: DEFAULT_INITIAL_WINDOW_SIZE,
93            initial_connection_window_size: DEFAULT_INITIAL_WINDOW_SIZE,
94            max_frame_size: DEFAULT_MAX_FRAME_SIZE as u32,
95            max_header_list_size: u32::MAX,
96            enable_connect_protocol: false,
97            idle_timeout: None,
98            max_local_error_reset_streams: Some(1024),
99            max_pending_accept_reset_streams: Some(20),
100            max_continuation_frames: 16,
101        }
102    }
103}
104
105/// The peer's current SETTINGS values. Defaults are the RFC 9113
106/// Section 6.5.2 initial values; they change as non-ACK SETTINGS frames
107/// arrive.
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct PeerSettings {
110    pub(crate) header_table_size: u32,
111    pub(crate) enable_push: u32,
112    pub(crate) initial_window_size: u32,
113    pub(crate) max_frame_size: usize,
114    /// Kept for the field block decoder's bomb protection (C2).
115    #[allow(dead_code)]
116    pub(crate) max_header_list_size: u32,
117}
118
119impl Default for PeerSettings {
120    #[inline]
121    fn default() -> Self {
122        PeerSettings {
123            header_table_size: 4096,
124            enable_push: 1,
125            initial_window_size: DEFAULT_INITIAL_WINDOW_SIZE,
126            max_frame_size: DEFAULT_MAX_FRAME_SIZE,
127            max_header_list_size: u32::MAX,
128        }
129    }
130}
131
132/// An HTTP/2 server connection.
133///
134/// `Io` must be a raw transport: the preface is read byte-exact, so
135/// any buffering layer between the socket and this type breaks the
136/// initial read.
137pub struct Connection<Io> {
138    io: Io,
139    decoder: FrameDecoder,
140    writer: FrameWriter,
141    out: Vec<u8>,
142    /// Encoder for the field blocks this connection sends; its table
143    /// size follows the peer's `SETTINGS_HEADER_TABLE_SIZE`.
144    encoder: Encoder,
145    /// Decoder for the peer's field blocks; sees every header block on
146    /// this connection (RFC 9113 Section 4.3).
147    request_decoder: HpackDecoder,
148    /// The peer's settings, updated by non-ACK SETTINGS frames.
149    peer: PeerSettings,
150    /// The settings this connection announced (public RFC defaults
151    /// plus `SETTINGS_MAX_CONCURRENT_STREAMS`).
152    #[allow(dead_code)]
153    local: PeerSettings,
154    /// Bounds the wait for the client's 24-octet preface. A peer that
155    /// is too slow is disconnected without a GOAWAY.
156    preface_timeout: Option<Duration>,
157    /// Active streams, keyed by stream id (RFC 9113 Section 5.1).
158    streams: FxHashMap<u32, StreamEntry>,
159    /// Connection-level send window for DATA payloads (RFC 9113
160    /// Section 6.9.1); initial 65,535 octets.
161    conn_window: i64,
162    /// Stream ids whose streams have ended (closed state per RFC 9113
163    /// Section 5.1); used to tell closed-stream frames apart from
164    /// idle-stream frames.
165    closed_streams: FxHashSet<u32>,
166    /// Behavior options for this connection (used by [`Connection::handle`]).
167    opts: ConnectionOptions,
168    /// RST_STREAM frames this endpoint has sent in response to the
169    /// peer's protocol errors (bounded by `opts.max_local_error_reset_streams`).
170    local_error_resets: usize,
171    /// Streams the peer reset before this endpoint accepted them
172    /// (bounded by `opts.max_pending_accept_reset_streams`).
173    pending_accept_resets: usize,
174    /// Wakes the drive loop when a stream task fills its outbound
175    /// channel; the loop drains channels between reads.
176    wake_tx: Option<kanal::AsyncSender<()>>,
177    /// Stream ids whose FIELD_BLOCK completed (END_HEADERS seen) and
178    /// awaits finalization; drained one per frame by
179    /// [`Connection::process_frames`].
180    complete_blocks: Vec<u32>,
181    /// Maximum number of frames a single header field block may span before
182    /// it is treated as a CONTINUATION flood and reset (CVE-2024-27919).
183    max_continuation_frames: usize,
184    /// Scratch buffer reused by [`Connection::drain_pending_data`] to
185    /// snapshot stream ids before pumping (avoids a per-call
186    /// allocation).
187    drain_ids: Vec<u32>,
188    /// Highest stream id opened by the peer (RFC 9113 Section 5.1.1).
189    highest_stream_id: u32,
190    /// A connection error is pending; the loop stops after flushing.
191    closing: bool,
192    /// Graceful shutdown is in progress: the first GOAWAY is sent and we
193    /// only drain already-open streams until they finish or the drain
194    /// window elapses (RFC 9113 Section 6.8).
195    graceful: bool,
196    /// Last stream id advertised in the graceful-shutdown GOAWAY; peers
197    /// must not open streams beyond it.
198    graceful_last_stream: u32,
199    /// Optional token that triggers graceful shutdown when cancelled.
200    shutdown: Option<CancellationToken>,
201    /// Shared `Date` header value, refreshed periodically for the
202    /// responses the stream tasks emit.
203    date_cache: Arc<DateCache>,
204}
205
206impl<Io> Connection<Io>
207where
208    Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
209{
210    /// Creates a connection over `io`.
211    #[inline]
212    pub fn new(io: Io, preface_timeout: Option<Duration>) -> Connection<Io> {
213        Connection {
214            io,
215            decoder: FrameDecoder::new(DEFAULT_MAX_FRAME_SIZE),
216            writer: FrameWriter::new(DEFAULT_MAX_FRAME_SIZE),
217            out: Vec::new(),
218            encoder: Encoder::new(4096),
219            request_decoder: HpackDecoder::new(4096),
220            peer: PeerSettings::default(),
221            local: PeerSettings::default(),
222            preface_timeout,
223            streams: FxHashMap::default(),
224            conn_window: DEFAULT_INITIAL_WINDOW_SIZE as i64,
225            closed_streams: FxHashSet::default(),
226            opts: ConnectionOptions::default(),
227            local_error_resets: 0,
228            pending_accept_resets: 0,
229            wake_tx: None,
230            complete_blocks: Vec::new(),
231            max_continuation_frames: 16,
232            drain_ids: Vec::new(),
233            highest_stream_id: 0,
234            closing: false,
235            graceful: false,
236            graceful_last_stream: 0,
237            shutdown: None,
238            date_cache: Arc::new(DateCache::new()),
239        }
240    }
241
242    /// Arms a [`CancellationToken`] that triggers a graceful shutdown
243    /// when cancelled: the connection sends GOAWAY, stops opening new
244    /// streams, drains in-flight responses, then closes (RFC 9113
245    /// Section 6.8).
246    #[inline]
247    pub fn with_shutdown(mut self, token: CancellationToken) -> Self {
248        self.shutdown = Some(token);
249        self
250    }
251
252    /// Drives a connection that never serves requests: the preface
253    /// handshake, SETTINGS/PING maintenance and error handling, but no
254    /// request dispatch (any request stream is refused).
255    ///
256    /// Equivalent to [`Connection::handle`] with a handler that never
257    /// completes; kept for tests and for callers that only want the
258    /// connection-level behavior.
259    #[inline]
260    pub async fn drive(self) -> std::io::Result<()> {
261        self.handle(
262            Arc::new(|_| std::future::pending::<Result<Response<Incoming>, std::io::Error>>()),
263            ConnectionOptions::default(),
264        )
265        .await
266    }
267
268    /// Serves requests to completion: peer EOF, GOAWAY received,
269    /// preface timeout, or an unrecoverable protocol error.
270    ///
271    /// Each decoded request starts a stream task running `request_fn`
272    /// (a clone-free sequential borrow: the loop owns the closure for
273    /// the connection's lifetime). Responses are framed onto the wire
274    /// by this task as the stream tasks emit them.
275    #[inline]
276    pub async fn handle<F, Fut, ResB, ResBE, ResE>(
277        mut self,
278        request_fn: Arc<F>,
279        options: ConnectionOptions,
280    ) -> std::io::Result<()>
281    where
282        F: Fn(Request<Incoming>) -> Fut + 'static,
283        Fut: Future<Output = Result<Response<ResB>, ResE>> + 'static,
284        ResB: Body<Data = Bytes, Error = ResBE> + Unpin + 'static,
285        ResBE: std::error::Error + 'static,
286        ResE: std::error::Error + 'static,
287    {
288        self.opts = options;
289        // Apply the negotiated native settings before the handshake.
290        self.request_decoder
291            .set_max_header_list_size(self.opts.max_header_list_size as usize);
292        self.decoder
293            .set_max_frame_size(self.opts.max_frame_size as usize);
294        self.writer.max_frame_size = self.opts.max_frame_size as usize;
295        self.peer.max_frame_size = self.opts.max_frame_size as usize;
296        self.conn_window = self.opts.initial_connection_window_size as i64;
297        // Resolved CONTINUATION-flood limit (CVE-2024-27919); `Http2Options`
298        // already applies the safe default when none was configured.
299        self.max_continuation_frames = options.max_continuation_frames;
300        match self.read_preface().await? {
301            None => return Ok(()), // preface timeout: close quietly
302            Some(false) => {
303                // Invalid preface: connection error PROTOCOL_ERROR
304                // (RFC 9113 Section 3.5), then close.
305                self.goaway(Reason::ProtocolError, b"invalid connection preface");
306                self.flush().await?;
307                return Ok(());
308            }
309            Some(true) => {}
310        }
311
312        // Our connection preface: SETTINGS announcing our flow-control
313        // windows, frame/header limits and concurrency (RFC 9113
314        // Sections 3.5 and 6.5.2).
315        self.writer.write_settings(
316            &mut self.out,
317            &[
318                Setting {
319                    id: 0x03,
320                    value: self.opts.max_concurrent_streams,
321                },
322                Setting {
323                    id: 0x04,
324                    value: self.opts.initial_stream_window_size,
325                },
326                Setting {
327                    id: 0x05,
328                    value: self.opts.max_frame_size,
329                },
330                Setting {
331                    id: 0x08,
332                    value: if self.opts.enable_connect_protocol {
333                        1
334                    } else {
335                        0
336                    },
337                },
338            ],
339        );
340        self.flush().await?;
341
342        let (wake_tx, wake_rx) = kanal::bounded_async(1);
343        self.wake_tx = Some(wake_tx);
344
345        let mut buf = [0u8; 8192];
346        let mut peer_goaway = false;
347        while !peer_goaway && !(self.graceful && self.streams.is_empty()) {
348            let wake_recv = wake_rx.recv().fuse();
349            let read = tokio::io::AsyncReadExt::read(&mut self.io, &mut buf).fuse();
350            // Graceful-shutdown signal: never fires without a token. The
351            // clone lives for the loop iteration so the boxed future can
352            // borrow it.
353            let shutdown_token = self.shutdown.clone();
354            let shutdown_fut: Pin<Box<dyn futures_util::future::FusedFuture<Output = ()> + Send>> =
355                match &shutdown_token {
356                    Some(token) => Box::pin(token.cancelled().fuse()),
357                    None => Box::pin(futures_util::future::pending().fuse()),
358                };
359            pin_mut!(wake_recv);
360            pin_mut!(read);
361            pin_mut!(shutdown_fut);
362            // Idle timeout (RFC 9113 Section 10.5): no frame received from the
363            // peer within `idle_timeout` => graceful shutdown. Recreated each
364            // iteration so it measures the gap since the last received frame.
365            let mut idle: Pin<Box<dyn futures_util::future::FusedFuture<Output = ()>>> =
366                match self.opts.idle_timeout {
367                    Some(d) => Box::pin(
368                        vibeio::time::timeout(d, futures_util::future::pending::<()>())
369                            .map(|_| ())
370                            .fuse(),
371                    ),
372                    None => Box::pin(futures_util::future::pending::<()>().fuse()),
373                };
374            futures_util::select! {
375                n = read => {
376                    let n = match n {
377                        Ok(n) => n,
378                        Err(e) if self.streams.is_empty()
379                            && matches!(
380                                e.kind(),
381                                std::io::ErrorKind::BrokenPipe
382                                    | std::io::ErrorKind::ConnectionReset
383                                    | std::io::ErrorKind::ConnectionAborted
384                                    | std::io::ErrorKind::UnexpectedEof
385                            ) => {
386                            // Connection abruptly closed while idle (no streams)...
387                            return Ok(())
388                        }
389                        Err(e) => Err(e)?
390                    };
391                    if n == 0 {
392                        break; // peer closed; nothing more to say
393                    }
394                    self.decoder.extend(&buf[..n]);
395                    peer_goaway = self.process_frames(&request_fn).await?;
396                    self.drain_outbound();
397                    self.flush().await?;
398                }
399                _ = wake_recv => {
400                    // A stream task parked on a full channel; drain it.
401                    self.drain_outbound();
402                    self.flush().await?;
403                }
404                _ = shutdown_fut => {
405                    self.begin_graceful_shutdown();
406                    self.flush().await?;
407                }
408                _ = idle => {
409                    // The peer was silent for `idle_timeout`; close the
410                    // connection gracefully (GOAWAY) and stop.
411                    self.begin_graceful_shutdown();
412                    self.flush().await?;
413                    break;
414                }
415            }
416        }
417        if self.graceful {
418            self.finish_graceful_shutdown();
419        }
420        self.flush().await?;
421        Ok(())
422    }
423
424    /// Reads the 24-octet client preface.
425    ///
426    /// Returns `Ok(None)` on timeout (the connection closes quietly —
427    /// answering a peer that never spoke is meaningless), `Ok(Some(
428    /// true))` on a match, and `Ok(Some(false))` when the peer sent
429    /// something else (the caller answers with GOAWAY).
430    #[inline]
431    async fn read_preface(&mut self) -> std::io::Result<Option<bool>> {
432        let mut magic = [0u8; CLIENT_PREFACE.len()];
433        match self.preface_timeout {
434            Some(timeout) => {
435                match vibeio::time::timeout(
436                    timeout,
437                    tokio::io::AsyncReadExt::read_exact(&mut self.io, &mut magic),
438                )
439                .await
440                {
441                    Ok(result) => {
442                        result?;
443                    }
444                    Err(_elapsed) => return Ok(None),
445                }
446            }
447            None => {
448                tokio::io::AsyncReadExt::read_exact(&mut self.io, &mut magic).await?;
449            }
450        }
451        Ok(Some(magic == CLIENT_PREFACE))
452    }
453
454    /// Decodes and handles every frame currently buffered. Returns
455    /// `Ok(true)` when the connection should end (peer GOAWAY or an
456    /// error we answered with GOAWAY).
457    #[inline]
458    async fn process_frames<F, Fut, ResB, ResBE, ResE>(
459        &mut self,
460        request_fn: &Arc<F>,
461    ) -> std::io::Result<bool>
462    where
463        F: Fn(Request<Incoming>) -> Fut + 'static,
464        Fut: Future<Output = Result<Response<ResB>, ResE>> + 'static,
465        ResB: Body<Data = Bytes, Error = ResBE> + Unpin + 'static,
466        ResBE: std::error::Error + 'static,
467        ResE: std::error::Error + 'static,
468    {
469        loop {
470            let frame = match self.decoder.next_frame() {
471                Ok(Some(frame)) => frame,
472                Ok(None) => return Ok(false),
473                Err(error) => {
474                    // Frame-level violation: GOAWAY with the code the
475                    // codec determined (RFC 9113 Sections 6.10, 6.5.2),
476                    // then close.
477                    self.goaway(error.reason, b"frame error");
478                    self.flush().await?;
479                    return Ok(true);
480                }
481            };
482            match frame {
483                Frame::Settings {
484                    ack: false,
485                    settings,
486                } => {
487                    self.apply_peer_settings(&settings);
488                    self.writer.write_settings_ack(&mut self.out);
489                }
490                Frame::Settings { ack: true, .. } => {}
491                Frame::Ping {
492                    ack: false,
493                    payload,
494                } => {
495                    self.writer.write_ping_ack(&mut self.out, &payload);
496                }
497                Frame::Ping { ack: true, .. } => {}
498                Frame::GoAway { .. } => return Ok(true),
499                Frame::Headers {
500                    stream_id,
501                    end_stream,
502                    end_headers,
503                    block,
504                    ..
505                } => {
506                    self.handle_headers_frame(stream_id, end_stream, end_headers, &block);
507                }
508                Frame::Continuation {
509                    stream_id,
510                    end_headers,
511                    block,
512                } => {
513                    self.handle_continuation(stream_id, end_headers, &block);
514                }
515                Frame::Data {
516                    stream_id,
517                    end_stream,
518                    data,
519                } => {
520                    self.handle_data_frame(stream_id, end_stream, data).await;
521                }
522                Frame::Reset {
523                    stream_id,
524                    error_code,
525                } => {
526                    self.handle_reset_frame(stream_id, error_code);
527                }
528                Frame::Priority { .. } => {}
529                Frame::WindowUpdate {
530                    stream_id,
531                    increment,
532                } => self.handle_window_update(stream_id, increment),
533                Frame::PushPromise { .. } => {
534                    self.goaway(Reason::ProtocolError, b"push promise to server");
535                }
536                Frame::Unknown { .. } => {}
537            }
538            // One HEADERS/CONTINUATION chain completes at most one
539            // field block per frame arrival; act on it while the
540            // handler closure is in scope.
541            if let Some(id) = self.take_complete_block() {
542                self.finalize_field_block(id, request_fn).await;
543            }
544            if self.closing {
545                self.flush().await?;
546                return Ok(true);
547            }
548        }
549    }
550}
551/// A boxed response body: the handler's body type is erased at the
552/// stream boundary so `Connection` stays monomorphic.
553struct ConnBody {
554    inner: Pin<Box<dyn Body<Data = Bytes, Error = std::io::Error>>>,
555}
556
557impl ConnBody {
558    #[inline]
559    fn new<ResB, ResBE>(body: ResB) -> Self
560    where
561        ResB: Body<Data = Bytes, Error = ResBE> + Unpin + 'static,
562        ResBE: std::error::Error + 'static,
563    {
564        ConnBody {
565            inner: Box::pin(BodyAdapter(Some(Box::pin(body)))),
566        }
567    }
568}
569
570impl Body for ConnBody {
571    type Data = Bytes;
572    type Error = std::io::Error;
573
574    #[inline]
575    fn poll_frame(
576        mut self: Pin<&mut Self>,
577        cx: &mut Context<'_>,
578    ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
579        self.inner.as_mut().poll_frame(cx)
580    }
581
582    #[inline]
583    fn size_hint(&self) -> http_body::SizeHint {
584        self.inner.size_hint()
585    }
586}
587
588/// Converts any displayable error into an [`io::Error`] without requiring
589/// `Send + Sync` (the native connection layer only needs the message, not the
590/// source; this keeps the public trait free of `Send`/`Sync` so it works on
591/// runtimes such as `vibeio` that do not demand them).
592#[inline]
593fn e2io<E: std::fmt::Display>(e: E) -> std::io::Error {
594    #[derive(Debug)]
595    struct Msg(String);
596    impl std::fmt::Display for Msg {
597        #[inline]
598        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
599            f.write_str(&self.0)
600        }
601    }
602    impl std::error::Error for Msg {}
603    std::io::Error::other(Msg(format!("{e}")))
604}
605
606/// Adapts an arbitrary body whose error is not `io::Error` into one
607/// that is (the stream layer only deals in `io::Error`).
608struct BodyAdapter<ResB>(Option<Pin<Box<ResB>>>);
609
610impl<ResB, ResBE> Body for BodyAdapter<ResB>
611where
612    ResB: Body<Data = Bytes, Error = ResBE>,
613    ResBE: std::error::Error + 'static,
614{
615    type Data = Bytes;
616    type Error = std::io::Error;
617
618    #[inline]
619    fn poll_frame(
620        self: Pin<&mut Self>,
621        cx: &mut Context<'_>,
622    ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
623        let this = self.get_mut();
624        let Some(inner) = this.0.as_mut() else {
625            return Poll::Ready(None);
626        };
627        match inner.as_mut().poll_frame(cx) {
628            Poll::Ready(Some(Ok(frame))) => Poll::Ready(Some(Ok(frame))),
629            Poll::Ready(Some(Err(error))) => Poll::Ready(Some(Err(e2io(error)))),
630            Poll::Ready(None) => {
631                this.0 = None;
632                Poll::Ready(None)
633            }
634            Poll::Pending => Poll::Pending,
635        }
636    }
637
638    #[inline]
639    fn size_hint(&self) -> http_body::SizeHint {
640        match &self.0 {
641            Some(body) => body.size_hint(),
642            None => http_body::SizeHint::default(),
643        }
644    }
645}
646
647#[derive(Clone, Copy, Debug, PartialEq, Eq)]
648enum StreamDataState {
649    Idle,
650    Closed,
651    Bad,
652    Gone,
653    Ok,
654}
655
656mod handlers;
657
658#[cfg(test)]
659mod tests;