Skip to main content

vane_core/h2/
connection.rs

1//! HTTP/2 connection state machine (RFC 9113 §4, §5, §6) — sans-io.
2//!
3//! The [`Connection`] consumes inbound bytes and produces outbound
4//! bytes + [`Event`]s; the engine driver owns the socket and forwards
5//! between them. No I/O happens here, which keeps the state machines
6//! fuzzable and runtime-agnostic (they run inside vane-core workers
7//! without a tokio dependency).
8//!
9//! Covers: client connection preface, SETTINGS negotiation + ACK,
10//! stream lifecycle (idle → open → half-closed → closed), stream-id
11//! direction rules, HEADERS + CONTINUATION assembly with HPACK, DATA
12//! relay with connection/stream flow-control accounting, WINDOW_UPDATE
13//! release, PING/PONG, GOAWAY draining, RST_STREAM, unknown-frame
14//! skipping, and uppercase field-name rejection (RFC 9113 §8.2.1).
15
16use std::collections::HashMap;
17
18use super::frame::{
19    DEFAULT_MAX_FRAME_SIZE, FrameFlags, FrameHeader, FrameKind, Setting, parse_header,
20    parse_rst_stream, parse_settings, parse_window_update, validate_payload, write_header,
21    write_setting,
22};
23use super::hpack::{HpackDecoder, HpackEncoder, HpackError};
24
25/// The HTTP/2 client connection preface (RFC 9113 §3.5).
26pub const CLIENT_PREFACE: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
27
28/// HTTP/2 error codes (RFC 9113 §7).
29pub mod error_code {
30    /// No error.
31    pub const NO_ERROR: u32 = 0x0;
32    /// Protocol detected error.
33    pub const PROTOCOL_ERROR: u32 = 0x1;
34    /// Implementation fault.
35    pub const INTERNAL_ERROR: u32 = 0x2;
36    /// Peer violated flow control.
37    pub const FLOW_CONTROL_ERROR: u32 = 0x3;
38    /// Frame on a closed stream.
39    pub const STREAM_CLOSED: u32 = 0x5;
40    /// Frame size invalid.
41    pub const FRAME_SIZE_ERROR: u32 = 0x6;
42    /// Stream refused (load shedding).
43    pub const REFUSED_STREAM: u32 = 0x7;
44    /// Stream cancelled by the receiver.
45    pub const CANCEL: u32 = 0x8;
46    /// HPACK state corrupt.
47    pub const COMPRESSION_ERROR: u32 = 0x9;
48}
49
50/// Which side of the connection this state machine represents.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum Role {
53    /// Receives odd stream ids, allocates even (servers).
54    Server,
55    /// Allocates odd stream ids, receives even (clients; server push
56    /// is refused).
57    Client,
58}
59
60/// Per-stream lifecycle state (RFC 9113 §5.1).
61/// Per-stream lifecycle state (subset of RFC 9113 §5.1 states tracked
62/// by this side of the connection).
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum StreamState {
65    /// HEADERS received (server) / sent (client), awaiting END_STREAM
66    /// handling; relay in both directions.
67    Open,
68    /// We sent END_STREAM: no more request-side data; response may flow.
69    HalfClosedLocal,
70    /// Peer sent END_STREAM: request complete; we may still respond.
71    HalfClosedRemote,
72    /// RST_STREAM sent or received, or END_STREAM both ways.
73    Closed,
74}
75
76/// An assembled request/response body chunk or complete set of headers.
77#[derive(Debug)]
78pub enum Event {
79    /// Complete header block decoded for a stream.
80    Headers {
81        /// Stream the block belongs to.
82        stream_id: u32,
83        /// Peer half-closed with this block.
84        end_stream: bool,
85        /// Decoded header fields.
86        headers: Vec<super::hpack::Header>,
87    },
88    /// Trailers: HEADERS arriving after peer DATA on the same stream
89    /// (gRPC grpc-status, h1-style chunked trailers). Always carries
90    /// the peer's END_STREAM.
91    Trailers {
92        /// Stream the block belongs to.
93        stream_id: u32,
94        /// Decoded header fields.
95        headers: Vec<super::hpack::Header>,
96    },
97    /// Body bytes (flow-control consumed — call
98    /// [`Connection::release_capacity`] to credit the peer).
99    Data {
100        /// Stream the data belongs to.
101        stream_id: u32,
102        /// Peer half-closed with this chunk.
103        end_stream: bool,
104        /// Body bytes.
105        data: Vec<u8>,
106    },
107    /// Peer reset the stream.
108    Reset {
109        /// Reset stream.
110        stream_id: u32,
111        /// Error code from the peer.
112        error_code: u32,
113    },
114    /// Peer is going away; streams above `last_stream_id` will not be
115    /// processed by the peer.
116    GoAway {
117        /// Last stream id the peer will process.
118        last_stream_id: u32,
119        /// Error code from the peer.
120        error_code: u32,
121    },
122    /// Peer ACKed our SETTINGS.
123    SettingsAck,
124    /// Peer granted send credit (a WINDOW_UPDATE frame, or a SETTINGS
125    /// INITIAL_WINDOW_SIZE increase for `stream_id` 0). Drivers holding
126    /// back response bytes on flow control should retry pending sends.
127    /// `stream_id` 0 is connection-level credit (all streams may retry).
128    WindowUpdate {
129        /// Stream credited, or 0 for connection-level credit.
130        stream_id: u32,
131    },
132}
133
134/// Connection-level failure: the driver must send a GOAWAY with this
135/// code and close.
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub struct ConnectionError {
138    /// HTTP/2 error code for the GOAWAY frame.
139    pub code: u32,
140}
141
142/// Negotiated connection parameters.
143#[derive(Debug, Clone)]
144pub struct ConnectionConfig {
145    /// Our SETTINGS_MAX_CONCURRENT_STREAMS (peer's opens are refused
146    /// above this — we RST with REFUSED_STREAM).
147    pub max_concurrent_streams: u32,
148    /// Our SETTINGS_INITIAL_WINDOW_SIZE (per-stream recv credit).
149    pub initial_window_size: u32,
150    /// Our SETTINGS_MAX_FRAME_SIZE (largest frame we accept).
151    pub max_frame_size: u32,
152    /// Our SETTINGS_HEADER_TABLE_SIZE.
153    pub header_table_size: u32,
154    /// Largest header list we accept (approximate, bytes).
155    pub max_header_list_size: u32,
156}
157
158impl Default for ConnectionConfig {
159    fn default() -> Self {
160        Self {
161            max_concurrent_streams: 256,
162            initial_window_size: 512 * 1024,
163            max_frame_size: DEFAULT_MAX_FRAME_SIZE,
164            header_table_size: 4096,
165            max_header_list_size: 128 * 1024,
166        }
167    }
168}
169
170#[derive(Debug, Clone, Copy)]
171struct Stream {
172    state: StreamState,
173    recv_window: i64,
174    send_window: i64,
175    /// Our send side ended with END_STREAM.
176    sent_end: bool,
177    /// The peer sent us DATA (request/response body). HEADERS after
178    /// this are trailers, not new header blocks.
179    peer_data: bool,
180    /// Content-Length declared in the request head (field validated
181    /// against the DATA total at END_STREAM).
182    content_length: Option<u64>,
183    /// DATA bytes received so far.
184    content_received: u64,
185    /// The request head contained a pseudo-header field, a TE field
186    /// with a non-"trailers" value, or an uppercase header name —
187    /// RFC 7540 §8.1.2 violations that must fail the stream with
188    /// PROTOCOL_ERROR (surfaced at END_STREAM/trailers time).
189    head_invalid: bool,
190    /// The peer's request head landed on this stream (a second
191    /// HEADERS without intervening DATA is a protocol violation).
192    head_done: bool,
193}
194
195/// Assembles a HEADERS block across CONTINUATION frames.
196#[derive(Debug, Default)]
197struct HeaderBlockAssembler {
198    fragments: Vec<u8>,
199    stream_id: u32,
200    end_stream: bool,
201    active: bool,
202}
203
204/// HTTP/2 connection state machine (sans-io).
205pub struct Connection {
206    role: Role,
207    cfg: ConnectionConfig,
208    /// Send-window accumulators for retired (closed) streams.
209    retired_windows: std::collections::HashMap<u32, i64>,
210    encoder: HpackEncoder,
211    decoder: HpackDecoder,
212
213    /// Bytes we owe the peer (frames written by this state machine).
214    out: Vec<u8>,
215
216    /// Client preface consumption progress (server role only).
217    preface_pos: usize,
218    preface_done: bool,
219
220    /// Our SETTINGS awaiting peer ACK.
221    settings_acked: bool,
222
223    /// Peer's SETTINGS_MAX_FRAME_SIZE (caps frames we send).
224    peer_max_frame: u32,
225    /// Peer's SETTINGS_INITIAL_WINDOW_SIZE (per-stream send credit).
226    peer_initial_window: i64,
227
228    conn_recv_window: i64,
229    conn_send_window: i64,
230
231    next_stream_id: u32,
232    last_peer_stream_id: u32,
233
234    streams: HashMap<u32, Stream>,
235
236    header_block: HeaderBlockAssembler,
237
238    /// Set when a connection error occurred; out already carries the
239    /// GOAWAY. The driver closes after flushing.
240    connection_error: Option<ConnectionError>,
241    goaway_sent: bool,
242    /// Peer GOAWAY received.
243    peer_goaway: Option<(u32, u32)>,
244}
245
246impl Connection {
247    /// New connection. Emits the client preface + initial SETTINGS
248    /// (client role) or the server's initial SETTINGS (server role)
249    /// into the pending-write buffer.
250    #[must_use]
251    pub fn new(role: Role, cfg: ConnectionConfig) -> Self {
252        let mut conn = Self {
253            role,
254            cfg: cfg.clone(),
255            retired_windows: std::collections::HashMap::new(),
256            encoder: HpackEncoder::new(),
257            decoder: HpackDecoder::new(cfg.header_table_size),
258            out: Vec::new(),
259            preface_pos: 0,
260            preface_done: role == Role::Client,
261            settings_acked: false,
262            peer_max_frame: DEFAULT_MAX_FRAME_SIZE,
263            peer_initial_window: 65_535,
264            // Connection-level receive window: fixed 65535 initial
265            // (RFC 9113 §6.9.1); SETTINGS only raises stream windows.
266            conn_recv_window: 65_535,
267            conn_send_window: 65_535,
268            next_stream_id: match role {
269                Role::Server => 2,
270                Role::Client => 1,
271            },
272            last_peer_stream_id: 0,
273            streams: HashMap::new(),
274            header_block: HeaderBlockAssembler::default(),
275            connection_error: None,
276            goaway_sent: false,
277            peer_goaway: None,
278        };
279
280        match role {
281            Role::Client => {
282                conn.out.extend_from_slice(CLIENT_PREFACE);
283            }
284            Role::Server => {}
285        }
286        conn.write_initial_settings();
287        // Initial SETTINGS are in flight until the peer ACKs them.
288        conn.settings_acked = true;
289        conn
290    }
291
292    fn write_initial_settings(&mut self) {
293        let hdr_pos = self.out.len();
294        write_header(&mut self.out, 0, FrameKind::Settings, FrameFlags::EMPTY, 0);
295        let mut payload = Vec::new();
296        write_setting(&mut payload, 0x3, self.cfg.max_concurrent_streams);
297        write_setting(&mut payload, 0x4, self.cfg.initial_window_size);
298        write_setting(&mut payload, 0x5, self.cfg.max_frame_size);
299        write_setting(&mut payload, 0x6, self.cfg.max_header_list_size);
300        write_setting(&mut payload, 0x1, self.cfg.header_table_size);
301        // Patch the length.
302        let len = payload.len() as u32;
303        self.out[hdr_pos] = (len >> 16) as u8;
304        self.out[hdr_pos + 1] = (len >> 8) as u8;
305        self.out[hdr_pos + 2] = len as u8;
306        self.out.extend_from_slice(&payload);
307    }
308
309    /// Bytes the driver must flush to the peer.
310    #[must_use]
311    pub fn take_pending_writes(&mut self) -> Vec<u8> {
312        std::mem::take(&mut self.out)
313    }
314
315    /// Connection error, if any. The driver sends the GOAWAY (already
316    /// queued in pending writes) and closes.
317    #[must_use]
318    pub fn connection_error(&self) -> Option<ConnectionError> {
319        self.connection_error
320    }
321
322    /// The peer's SETTINGS_MAX_FRAME_SIZE (largest frame payload we
323    /// may send).
324    #[must_use]
325    pub fn peer_max_frame_size(&self) -> usize {
326        self.peer_max_frame as usize
327    }
328
329    /// Available send credit for `stream_id` (min of stream and
330    /// connection windows, clamped at zero).
331    #[must_use]
332    pub fn send_budget(&self, stream_id: u32) -> usize {
333        let stream = self
334            .streams
335            .get(&stream_id)
336            .map_or(i64::MAX, |st| st.send_window);
337        self.conn_send_window
338            .min(stream)
339            .max(0)
340            .try_into()
341            .unwrap_or(0)
342    }
343
344    /// Whether the stream is currently tracked (open or half-closed).
345    #[must_use]
346    pub fn stream_is_open(&self, stream_id: u32) -> bool {
347        self.streams.contains_key(&stream_id)
348    }
349
350    /// Debug: connection send window.
351    #[must_use]
352    pub fn conn_send_window_probe(&self) -> i64 {
353        self.conn_send_window
354    }
355
356    /// Debug: stream send window.
357    #[must_use]
358    pub fn stream_send_window_probe(&self, stream_id: u32) -> i64 {
359        self.streams.get(&stream_id).map_or(0, |st| st.send_window)
360    }
361
362    /// Debug: current send windows (conn, stream if open).
363    #[must_use]
364    pub fn debug_send_windows(&self, stream_id: u32) -> (i64, i64) {
365        (
366            self.conn_send_window,
367            self.streams.get(&stream_id).map_or(0, |st| st.send_window),
368        )
369    }
370
371    /// Test-only: grants `n` bytes of send credit (simulates a peer
372    /// WINDOW_UPDATE on stream + connection without a frame round-trip).
373    pub fn grant_send_for_test(&mut self, stream_id: u32, n: u32) {
374        self.conn_send_window += i64::from(n);
375        if let Some(st) = self.streams.get_mut(&stream_id) {
376            st.send_window += i64::from(n);
377        } else {
378            crate::dbg_trace!("GRANTDBG grant({n}) dropped: stream {stream_id} missing");
379        }
380    }
381
382    /// Reserves `n` bytes of send credit for DATA the driver emits
383    /// directly (frame bytes assembled outside the engine). The engine
384    /// never sees those frames through [`Self::send_data`], so the
385    /// stream and connection windows must be debited here; peer
386    /// WINDOW_UPDATEs replenish via [`Self::handle_read`].
387    /// Returns the amount actually reserved (≤ n).
388    pub fn consume_send_budget(&mut self, stream_id: u32, n: usize) -> usize {
389        let Some(st) = self.streams.get_mut(&stream_id) else {
390            return 0;
391        };
392        let reserved = (n as i64)
393            .min(self.conn_send_window.max(0))
394            .min(st.send_window.max(0));
395        self.conn_send_window -= reserved;
396        st.send_window -= reserved;
397        reserved as usize
398    }
399
400    /// Whether the peer sent GOAWAY.
401    #[must_use]
402    pub fn peer_goaway(&self) -> Option<(u32, u32)> {
403        self.peer_goaway
404    }
405
406    fn conn_error(&mut self, code: u32) -> ConnectionError {
407        eprintln!("CRDBG conn_error code={code:#x}");
408        let err = ConnectionError { code };
409        if !self.goaway_sent {
410            self.goaway_sent = true;
411            let mut payload = Vec::new();
412            payload.extend_from_slice(&self.last_peer_stream_id.to_be_bytes());
413            payload.extend_from_slice(&code.to_be_bytes());
414            write_header(
415                &mut self.out,
416                payload.len() as u32,
417                FrameKind::GoAway,
418                FrameFlags::EMPTY,
419                0,
420            );
421            self.out.extend_from_slice(&payload);
422        }
423        self.connection_error = Some(err);
424        err
425    }
426
427    /// Feeds inbound peer bytes into the state machine. Emits events and
428    /// queues outbound frames (ACKs, WINDOW_UPDATEs, GOAWAY, RSTs).
429    /// Returns the number of bytes consumed by fully-processed frames;
430    /// a trailing partial frame is left unconsumed so drivers can
431    /// buffer the remainder.
432    pub fn handle_read(&mut self, data: &[u8], events: &mut Vec<Event>) -> usize {
433        if self.connection_error.is_some() {
434            return 0;
435        }
436        let total = data.len();
437        let mut off = 0usize;
438
439        // Server: consume the 24-byte client preface first.
440        if !self.preface_done {
441            let need = CLIENT_PREFACE.len() - self.preface_pos;
442            let take = need.min(data.len());
443            if data[..take] != CLIENT_PREFACE[self.preface_pos..self.preface_pos + take] {
444                // Bad preface: RFC 9113 §3.5 — treat as connection error
445                // with PROTOCOL_ERROR.
446                let _ = self.conn_error(error_code::PROTOCOL_ERROR);
447                return 0;
448            }
449            self.preface_pos += take;
450            off += take;
451            if self.preface_pos < CLIENT_PREFACE.len() {
452                return off; // more preface bytes to come
453            }
454            self.preface_done = true;
455        }
456
457        while off < total {
458            if self.header_block.active {
459                // CONTINUATION must be the very next frame.
460                if total - off < 9 {
461                    break; // partial frame header
462                }
463                let hdr_data = &data[off..];
464                let Ok(hdr) = parse_header(hdr_data) else {
465                    break;
466                };
467                if hdr.kind != FrameKind::Continuation
468                    || hdr.stream_id != self.header_block.stream_id
469                {
470                    let _ = self.conn_error(error_code::PROTOCOL_ERROR);
471                    return off;
472                }
473                if total - off < 9 + hdr.length as usize {
474                    break; // partial CONTINUATION payload
475                }
476                let frag = &data[off + 9..off + 9 + hdr.length as usize];
477                self.header_block.fragments.extend_from_slice(frag);
478                off += 9 + hdr.length as usize;
479                if hdr.flags.end_headers() {
480                    if let Err(code) = self.finish_headers(events) {
481                        let _ = self.conn_error(code);
482                        return off;
483                    }
484                }
485                continue;
486            }
487
488            if total - off < 9 {
489                break; // partial frame header
490            }
491            let hdr_data = &data[off..];
492            let Ok(hdr) = parse_header(hdr_data) else {
493                break;
494            };
495            if total - off < 9 + hdr.length as usize {
496                break; // partial frame payload — wait for the rest
497            }
498            let payload = &data[off + 9..off + 9 + hdr.length as usize];
499            off += 9 + hdr.length as usize;
500
501            // Stream-id direction rules (RFC 9113 §5.1.1): clients
502            // initiate streams with odd ids; responses (and any other
503            // peer frames) reference those same odd streams. Even ids
504            // exist only for server push, which we never accept.
505            match hdr.kind {
506                FrameKind::Data
507                | FrameKind::Headers
508                | FrameKind::Continuation
509                | FrameKind::RstStream
510                | FrameKind::PushPromise => {
511                    if hdr.stream_id == 0 || hdr.stream_id & 1 == 0 {
512                        let _ = self.conn_error(error_code::PROTOCOL_ERROR);
513                        return off;
514                    }
515                }
516                FrameKind::Settings | FrameKind::Ping | FrameKind::GoAway => {
517                    if hdr.stream_id != 0 {
518                        let _ = self.conn_error(error_code::PROTOCOL_ERROR);
519                        return off;
520                    }
521                }
522                FrameKind::WindowUpdate => {}
523                FrameKind::Priority => {}
524                FrameKind::Unknown(_) => {}
525            }
526
527            match self.handle_frame(&hdr, payload, events) {
528                Ok(()) => {}
529                Err(code) => {
530                    let _ = self.conn_error(code);
531                    return off;
532                }
533            }
534            if self.connection_error.is_some() {
535                return off;
536            }
537        }
538        off
539    }
540
541    fn handle_frame(
542        &mut self,
543        hdr: &FrameHeader,
544        payload: &[u8],
545        events: &mut Vec<Event>,
546    ) -> Result<(), u32> {
547        match hdr.kind {
548            FrameKind::Settings => self.handle_settings(hdr, payload, events),
549            FrameKind::Ping => self.handle_ping(hdr, payload),
550            FrameKind::WindowUpdate => {
551                eprintln!("CRDBG wu stream={} len={}", hdr.stream_id, payload.len());
552                self.handle_window_update(hdr, payload, events)
553            }
554            FrameKind::GoAway => {
555                if payload.len() < 8 {
556                    return Err(error_code::FRAME_SIZE_ERROR);
557                }
558                let last = u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
559                let code = u32::from_be_bytes([payload[4], payload[5], payload[6], payload[7]]);
560                self.peer_goaway = Some((last, code));
561                events.push(Event::GoAway {
562                    last_stream_id: last,
563                    error_code: code,
564                });
565                Ok(())
566            }
567            FrameKind::RstStream => {
568                let code = parse_rst_stream(payload).map_err(|_| error_code::FRAME_SIZE_ERROR)?;
569                let id = hdr.stream_id;
570                if self.streams.contains_key(&id) {
571                    if let Some(st) = self.streams.get_mut(&id) {
572                        st.state = StreamState::Closed;
573                    }
574                    self.streams.remove(&id);
575                } else if self.is_peer_idle(id) {
576                    // Idle stream: RST_STREAM is a connection error
577                    // (RFC 7540 §5.1 idle state).
578                    return Err(error_code::PROTOCOL_ERROR);
579                }
580                events.push(Event::Reset {
581                    stream_id: id,
582                    error_code: code,
583                });
584                Ok(())
585            }
586            FrameKind::Headers => {
587                eprintln!(
588                    "CRDBG headers stream={} len={}",
589                    hdr.stream_id,
590                    payload.len()
591                );
592                self.handle_headers(hdr, payload, events)?;
593                Ok(())
594            }
595            FrameKind::Continuation => {
596                // Not expected here: handled by the header_block branch in
597                // handle_read; receiving one standalone is a protocol error.
598                Err(error_code::PROTOCOL_ERROR)
599            }
600            FrameKind::Data => {
601                eprintln!("CRDBG data stream={} len={}", hdr.stream_id, payload.len());
602                self.handle_data(hdr, payload, events)
603            }
604            FrameKind::Priority => {
605                // Deprecated but still validated (RFC 7540 §5.1/§6.3):
606                // PRIORITY on stream 0 is PROTOCOL_ERROR; a payload
607                // other than 5 octets is FRAME_SIZE_ERROR; a
608                // self-dependency is PROTOCOL_ERROR.
609                if hdr.stream_id == 0 {
610                    return Err(error_code::PROTOCOL_ERROR);
611                }
612                if payload.len() != 5 {
613                    return Err(error_code::FRAME_SIZE_ERROR);
614                }
615                let dep = u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]])
616                    & 0x7fff_ffff;
617                if dep == hdr.stream_id {
618                    return Err(error_code::PROTOCOL_ERROR);
619                }
620                Ok(())
621            }
622            FrameKind::PushPromise => {
623                // We do not accept server push (client role) and servers
624                // never receive push. Refuse the stream politely.
625                Err(error_code::REFUSED_STREAM)
626            }
627            FrameKind::Unknown(_) => Ok(()),
628        }
629    }
630
631    fn handle_settings(
632        &mut self,
633        hdr: &FrameHeader,
634        payload: &[u8],
635        events: &mut Vec<Event>,
636    ) -> Result<(), u32> {
637        if hdr.flags.ack() {
638            if !self.settings_acked {
639                return Err(error_code::PROTOCOL_ERROR);
640            }
641            self.settings_acked = false;
642            return Ok(());
643        }
644        let settings = parse_settings(payload).map_err(|_| error_code::FRAME_SIZE_ERROR)?;
645        for setting in &settings {
646            match *setting {
647                Setting::MaxFrameSize(v) => {
648                    if !(DEFAULT_MAX_FRAME_SIZE..=16_777_215).contains(&v) {
649                        return Err(error_code::PROTOCOL_ERROR);
650                    }
651                    self.peer_max_frame = v;
652                }
653                Setting::InitialWindowSize(v) => {
654                    if v > (1 << 30) - 1 {
655                        return Err(error_code::FLOW_CONTROL_ERROR);
656                    }
657                    let delta = i64::from(v) - self.peer_initial_window;
658                    self.peer_initial_window = i64::from(v);
659                    if delta > 0 {
660                        for st in self.streams.values_mut() {
661                            st.send_window += delta;
662                        }
663                        // Credit grew for every open stream (connection
664                        // window unchanged) — let drivers retry holds.
665                        events.push(Event::WindowUpdate { stream_id: 0 });
666                    } else {
667                        for st in self.streams.values_mut() {
668                            st.send_window += delta;
669                        }
670                    }
671                }
672                // ENABLE_PUSH is a bool: 0 or 1; anything else is a
673                // connection error (RFC 7540 §6.5.2).
674                Setting::EnablePush(v) if v > 1 => {
675                    return Err(error_code::PROTOCOL_ERROR);
676                }
677                Setting::HeaderTableSize(_) => {
678                    // Our encoder is stateless — nothing to apply.
679                }
680                _ => {}
681            }
682        }
683        // ACK.
684        write_header(
685            &mut self.out,
686            0,
687            FrameKind::Settings,
688            FrameFlags::from_u8(0x01),
689            0,
690        );
691        Ok(())
692    }
693
694    fn handle_ping(&mut self, hdr: &FrameHeader, payload: &[u8]) -> Result<(), u32> {
695        if payload.len() != 8 {
696            return Err(error_code::FRAME_SIZE_ERROR);
697        }
698        if hdr.flags.ack() {
699            return Ok(()); // our PONG acked
700        }
701        write_header(
702            &mut self.out,
703            8,
704            FrameKind::Ping,
705            FrameFlags::from_u8(0x01),
706            0,
707        );
708        self.out.extend_from_slice(payload);
709        Ok(())
710    }
711
712    fn handle_window_update(
713        &mut self,
714        hdr: &FrameHeader,
715        payload: &[u8],
716        events: &mut Vec<Event>,
717    ) -> Result<(), u32> {
718        let inc = parse_window_update(payload).map_err(|_| error_code::FRAME_SIZE_ERROR)?;
719        if inc == 0 {
720            return Err(error_code::PROTOCOL_ERROR);
721        }
722        let inc = i64::from(inc);
723        if hdr.stream_id == 0 {
724            self.conn_send_window += inc;
725            if self.conn_send_window > (1 << 31) - 1 {
726                return Err(error_code::FLOW_CONTROL_ERROR);
727            }
728        } else if let Some(st) = self.streams.get_mut(&hdr.stream_id) {
729            st.send_window += inc;
730        } else {
731            // Retired (closed) stream: keep a shadow accumulator so an
732            // overflowing WINDOW_UPDATE still raises FLOW_CONTROL_ERROR
733            // as an in-band RST_STREAM (RFC 7540 §6.9.1 allows ignoring
734            // ordinary updates).
735            // The accumulator tracks only WINDOW_UPDATE increments —
736            // the initial 65,535 window is not part of the sum.
737            let cur = self
738                .retired_windows
739                .get(&hdr.stream_id)
740                .copied()
741                .unwrap_or(0);
742            let updated = cur + inc;
743            if updated > (1 << 31) - 1 {
744                write_header(
745                    &mut self.out,
746                    4,
747                    FrameKind::RstStream,
748                    FrameFlags::EMPTY,
749                    hdr.stream_id,
750                );
751                self.out
752                    .extend_from_slice(&error_code::FLOW_CONTROL_ERROR.to_be_bytes());
753                return Err(error_code::FLOW_CONTROL_ERROR);
754            }
755            self.retired_windows.insert(hdr.stream_id, updated);
756        }
757        events.push(Event::WindowUpdate {
758            stream_id: hdr.stream_id,
759        });
760        Ok(())
761    }
762
763    fn handle_headers(
764        &mut self,
765        hdr: &FrameHeader,
766        payload: &[u8],
767        events: &mut Vec<Event>,
768    ) -> Result<(), u32> {
769        use super::frame::validate_payload;
770        // Received frames must respect OUR advertised maximum.
771        if payload.len() > self.cfg.max_frame_size as usize {
772            return Err(error_code::FRAME_SIZE_ERROR);
773        }
774        let Ok(split) = validate_payload(hdr, payload, self.peer_max_frame) else {
775            return Err(error_code::FRAME_SIZE_ERROR);
776        };
777        // Inbound HEADERS reference odd ids in both roles: odd are
778        // client-initiated streams (responses ride them); even ids are
779        // server push, which is refused.
780        if hdr.stream_id & 1 == 0 {
781            return Err(error_code::PROTOCOL_ERROR);
782        }
783
784        // Stream id monotonicity + reuse — only for NEW streams. HEADERS
785        // on an already-open stream are trailers (RFC 9113 §8.1) and
786        // keep the original id.
787        let known = self.streams.contains_key(&hdr.stream_id);
788        if !known && hdr.stream_id <= self.last_peer_stream_id {
789            return Err(error_code::PROTOCOL_ERROR);
790        }
791        // HEADERS on a half-closed (remote) or closed stream is a
792        // connection error of type STREAM_CLOSED (RFC 7540 §5.1).
793        if let Some(st) = self.streams.get(&hdr.stream_id) {
794            if matches!(
795                st.state,
796                StreamState::HalfClosedRemote | StreamState::Closed
797            ) {
798                return Err(error_code::STREAM_CLOSED);
799            }
800        }
801        if !known
802            && self.role == Role::Server
803            && self.streams.len() >= self.cfg.max_concurrent_streams as usize
804        {
805            // Over the limit: refuse this stream (stream-level error).
806            write_header(
807                &mut self.out,
808                4,
809                FrameKind::RstStream,
810                FrameFlags::EMPTY,
811                hdr.stream_id,
812            );
813            self.out
814                .extend_from_slice(&error_code::REFUSED_STREAM.to_be_bytes());
815            return Ok(());
816        }
817        self.last_peer_stream_id = hdr.stream_id;
818
819        // Priority hints parsed and ignored (RFC 9113 §5.3 deprecates) —
820        // except the self-dependency, which is a stream error
821        // (RFC 7540 §5.3.1).
822        let mut frag = &payload[split.content_start..split.content_end];
823        if hdr.flags.priority() {
824            if frag.len() < 5 {
825                return Err(error_code::FRAME_SIZE_ERROR);
826            }
827            let dep = u32::from_be_bytes([frag[0], frag[1], frag[2], frag[3]]) & 0x7fff_ffff;
828            if dep == hdr.stream_id {
829                return Err(error_code::PROTOCOL_ERROR);
830            }
831            frag = &frag[5..];
832        }
833
834        let end_stream = hdr.flags.end_stream();
835        if hdr.flags.end_headers() {
836            let headers = {
837                let mut bd = HpackBlockDecoder {
838                    fragments: frag.to_vec(),
839                    decoder: &mut self.decoder,
840                };
841                bd.decode_all()
842            };
843            match headers {
844                Ok(headers) => {
845                    // HEADERS after peer DATA on the same stream are
846                    // trailers (gRPC grpc-status, h1 chunked trailers).
847                    let are_trailers = self
848                        .streams
849                        .get(&hdr.stream_id)
850                        .is_some_and(|st| st.peer_data);
851                    if self.role == Role::Server {
852                        // Second HEADERS on the open request stream
853                        // (no DATA between) violates the message
854                        // sequence → PROTOCOL_ERROR.
855                        let head_dup = self
856                            .streams
857                            .get(&hdr.stream_id)
858                            .is_some_and(|st| st.head_done && !st.peer_data);
859                        if head_dup {
860                            write_header(
861                                &mut self.out,
862                                4,
863                                FrameKind::RstStream,
864                                FrameFlags::EMPTY,
865                                hdr.stream_id,
866                            );
867                            self.out
868                                .extend_from_slice(&error_code::PROTOCOL_ERROR.to_be_bytes());
869                            return Ok(());
870                        }
871                        let head_cl = self.check_request_headers(
872                            hdr.stream_id,
873                            end_stream,
874                            &headers,
875                            are_trailers,
876                        );
877                        if head_cl.is_err() {
878                            // Stream-level refusal (RFC 7540 §8.1.2
879                            // malformed request): RST with the code.
880                            write_header(
881                                &mut self.out,
882                                4,
883                                FrameKind::RstStream,
884                                FrameFlags::EMPTY,
885                                hdr.stream_id,
886                            );
887                            self.out
888                                .extend_from_slice(&error_code::PROTOCOL_ERROR.to_be_bytes());
889                            return Ok(());
890                        }
891                        self.ensure_stream(hdr.stream_id, end_stream);
892                        if let (Some(st), Some(cl)) =
893                            (self.streams.get_mut(&hdr.stream_id), head_cl.unwrap())
894                        {
895                            st.content_length = Some(cl);
896                            st.head_done = true;
897                        }
898                    } else {
899                        self.ensure_stream(hdr.stream_id, end_stream);
900                    }
901                    if end_stream {
902                        if let Some(st) = self.streams.get_mut(&hdr.stream_id) {
903                            st.state = StreamState::HalfClosedRemote;
904                        }
905                    }
906                    if are_trailers {
907                        events.push(Event::Trailers {
908                            stream_id: hdr.stream_id,
909                            headers,
910                        });
911                    } else {
912                        events.push(Event::Headers {
913                            stream_id: hdr.stream_id,
914                            end_stream,
915                            headers,
916                        });
917                    }
918                    return Ok(());
919                }
920                Err(_) => return Err(error_code::COMPRESSION_ERROR),
921            }
922        }
923
924        // Multi-frame block: start assembly.
925        self.header_block = HeaderBlockAssembler {
926            fragments: frag.to_vec(),
927            stream_id: hdr.stream_id,
928            end_stream,
929            active: true,
930        };
931        Ok(())
932    }
933
934    fn finish_headers(&mut self, events: &mut Vec<Event>) -> Result<(), u32> {
935        let (stream_id, end_stream) = (self.header_block.stream_id, self.header_block.end_stream);
936        let mut decoder = HpackBlockDecoder {
937            fragments: std::mem::take(&mut self.header_block.fragments),
938            decoder: &mut self.decoder,
939        };
940        self.header_block.active = false;
941        match decoder.decode_all() {
942            Ok(headers) => {
943                // HEADERS after peer DATA on the same stream are trailers.
944                let are_trailers = self.streams.get(&stream_id).is_some_and(|st| st.peer_data);
945                if self.role == Role::Server {
946                    // Second HEADERS on the open request stream (no
947                    // DATA between) violates the message sequence.
948                    let head_dup = self
949                        .streams
950                        .get(&stream_id)
951                        .is_some_and(|st| st.head_done && !st.peer_data);
952                    if head_dup {
953                        write_header(
954                            &mut self.out,
955                            4,
956                            FrameKind::RstStream,
957                            FrameFlags::EMPTY,
958                            stream_id,
959                        );
960                        self.out
961                            .extend_from_slice(&error_code::PROTOCOL_ERROR.to_be_bytes());
962                        return Ok(());
963                    }
964                    let head_cl =
965                        self.check_request_headers(stream_id, end_stream, &headers, are_trailers);
966                    if head_cl.is_err() {
967                        write_header(
968                            &mut self.out,
969                            4,
970                            FrameKind::RstStream,
971                            FrameFlags::EMPTY,
972                            stream_id,
973                        );
974                        self.out
975                            .extend_from_slice(&error_code::PROTOCOL_ERROR.to_be_bytes());
976                        return Ok(());
977                    }
978                    self.ensure_stream(stream_id, end_stream);
979                    if let (Some(st), Some(cl)) =
980                        (self.streams.get_mut(&stream_id), head_cl.unwrap())
981                    {
982                        st.content_length = Some(cl);
983                        st.head_done = true;
984                    }
985                } else {
986                    self.ensure_stream(stream_id, end_stream);
987                }
988                if end_stream {
989                    if let Some(st) = self.streams.get_mut(&stream_id) {
990                        st.state = StreamState::HalfClosedRemote;
991                    }
992                }
993                if are_trailers {
994                    events.push(Event::Trailers { stream_id, headers });
995                } else {
996                    events.push(Event::Headers {
997                        stream_id,
998                        end_stream,
999                        headers,
1000                    });
1001                }
1002                Ok(())
1003            }
1004            Err(_) => Err(error_code::COMPRESSION_ERROR),
1005        }
1006    }
1007
1008    /// `true` when `stream_id` belongs to the PEER's namespace and is
1009    /// above every id the peer has opened — i.e. the stream is idle
1010    /// (server role: peer streams are odd; client role: even).
1011    fn is_peer_idle(&self, stream_id: u32) -> bool {
1012        let peer_parity = match self.role {
1013            Role::Server => stream_id & 1 == 1,
1014            Role::Client => stream_id & 1 == 0,
1015        };
1016        peer_parity && stream_id > self.last_peer_stream_id
1017    }
1018
1019    /// Server-role header-block validations (RFC 7540 §8.1.2):
1020    /// uppercase names, pseudo-fields after regular ones, TE with a
1021    /// non-"trailers" value, pseudo-fields in trailers, trailers
1022    /// without END_STREAM. Violations are STREAM-level
1023    /// (PROTOCOL_ERROR). Also extracts the declared Content-Length.
1024    fn check_request_headers(
1025        &self,
1026        stream_id: u32,
1027        end_stream: bool,
1028        headers: &[super::hpack::Header],
1029        trailers: bool,
1030    ) -> Result<Option<u64>, ()> {
1031        let _ = stream_id;
1032        let mut pseudo_seen = false;
1033        let mut regular_seen = false;
1034        let mut method_seen = false;
1035        let mut scheme_seen = false;
1036        let mut path_seen = false;
1037        let mut content_length: Option<u64> = None;
1038        for header in headers {
1039            let (name, value) = (&header.name, &header.value);
1040            if name.iter().any(|b| b.is_ascii_uppercase()) {
1041                return Err(());
1042            }
1043            if name.first() == Some(&b':') {
1044                // Pseudo-fields must precede regular ones and appear at
1045                // most once each (RFC 7540 §8.1.2.1).
1046                if regular_seen {
1047                    return Err(());
1048                }
1049                match name.as_slice() {
1050                    b":method" => {
1051                        if method_seen {
1052                            return Err(());
1053                        }
1054                        method_seen = true;
1055                    }
1056                    b":scheme" => {
1057                        if scheme_seen {
1058                            return Err(());
1059                        }
1060                        scheme_seen = true;
1061                    }
1062                    b":path" => {
1063                        if path_seen {
1064                            return Err(());
1065                        }
1066                        path_seen = true;
1067                    }
1068                    // :authority is legal on requests (origin/authority
1069                    // form); it must still be unique.
1070                    b":authority" => {}
1071                    _ => {
1072                        // Unknown pseudo-field → PROTOCOL_ERROR.
1073                        return Err(());
1074                    }
1075                }
1076            } else {
1077                regular_seen = true;
1078                if name == b"te" && !value.eq_ignore_ascii_case(b"trailers") {
1079                    return Err(());
1080                }
1081                if name == b"content-length" {
1082                    content_length = std::str::from_utf8(value)
1083                        .ok()
1084                        .and_then(|s| s.parse::<u64>().ok());
1085                    if content_length.is_none() {
1086                        return Err(());
1087                    }
1088                }
1089            }
1090        }
1091        if trailers && (pseudo_seen || !end_stream) {
1092            return Err(());
1093        }
1094        if !trailers && !(method_seen && scheme_seen && path_seen) {
1095            // A request head must declare method/scheme/path (RFC 7540
1096            // §8.1.2.3; CONNECT's mapping omits scheme/path but h2spec
1097            // cases here are plain requests).
1098            return Err(());
1099        }
1100        Ok(content_length)
1101    }
1102
1103    fn ensure_stream(&mut self, id: u32, end_stream: bool) {
1104        let entry = self.streams.entry(id).or_insert(Stream {
1105            state: StreamState::Open,
1106            recv_window: i64::from(self.cfg.initial_window_size),
1107            send_window: self.peer_initial_window,
1108            sent_end: false,
1109            peer_data: false,
1110            content_length: None,
1111            content_received: 0,
1112            head_invalid: false,
1113            head_done: false,
1114        });
1115        if end_stream {
1116            let retire = entry.sent_end;
1117            entry.state = StreamState::HalfClosedRemote;
1118            // Both halves done (we already sent END_STREAM): retire.
1119            if retire {
1120                self.streams.remove(&id);
1121            }
1122        }
1123    }
1124
1125    fn handle_data(
1126        &mut self,
1127        hdr: &FrameHeader,
1128        payload: &[u8],
1129        events: &mut Vec<Event>,
1130    ) -> Result<(), u32> {
1131        // Received frames must respect OUR advertised maximum.
1132        if payload.len() > self.cfg.max_frame_size as usize {
1133            return Err(error_code::FRAME_SIZE_ERROR);
1134        }
1135        let Ok(split) = validate_payload(hdr, payload, self.peer_max_frame) else {
1136            return Err(error_code::FRAME_SIZE_ERROR);
1137        };
1138        let id = hdr.stream_id;
1139
1140        let Some(st) = self.streams.get_mut(&id) else {
1141            // Unknown/closed stream: connection error STREAM_CLOSED.
1142            return Err(error_code::STREAM_CLOSED);
1143        };
1144        if st.state == StreamState::Closed || st.state == StreamState::HalfClosedRemote {
1145            return Err(error_code::STREAM_CLOSED);
1146        }
1147
1148        let content = &payload[split.content_start..split.content_end];
1149        // Flow control: connection + stream recv windows must cover it.
1150        self.conn_recv_window -= content.len() as i64;
1151        st.recv_window -= content.len() as i64;
1152        if !content.is_empty() {
1153            st.peer_data = true;
1154        }
1155        st.content_received += content.len() as u64;
1156
1157        let end_stream = hdr.flags.end_stream();
1158        if end_stream {
1159            // Declared Content-Length must equal the DATA total.
1160            if let Some(cl) = st.content_length {
1161                if st.content_received != cl {
1162                    return Err(error_code::PROTOCOL_ERROR);
1163                }
1164            }
1165            st.state = StreamState::HalfClosedRemote;
1166        }
1167        events.push(Event::Data {
1168            stream_id: id,
1169            end_stream,
1170            data: content.to_vec(),
1171        });
1172        Ok(())
1173    }
1174
1175    /// Credits the peer for consumed body bytes (emits WINDOW_UPDATEs on
1176    /// connection + stream when the freed amount is significant).
1177    pub fn release_capacity(&mut self, stream_id: u32, n: usize) {
1178        let n = n as i64;
1179        // Credit is returned IMMEDIATELY on every release: batching at
1180        // a half-mark deadlocks transfers sized near a window multiple
1181        // (the peer's remaining credit hits zero while our threshold
1182        // waits for more consumption). The connection-level initial
1183        // window is fixed at 65535 (RFC 9113 §6.9.1); SETTINGS only
1184        // raises per-stream windows.
1185        self.conn_recv_window += n;
1186        if n > 0 {
1187            write_header(
1188                &mut self.out,
1189                4,
1190                FrameKind::WindowUpdate,
1191                FrameFlags::EMPTY,
1192                0,
1193            );
1194            self.out.extend_from_slice(&(n as u32).to_be_bytes());
1195        }
1196        if let Some(st) = self.streams.get_mut(&stream_id) {
1197            st.recv_window += n;
1198            if n > 0 {
1199                write_header(
1200                    &mut self.out,
1201                    4,
1202                    FrameKind::WindowUpdate,
1203                    FrameFlags::EMPTY,
1204                    stream_id,
1205                );
1206                self.out.extend_from_slice(&(n as u32).to_be_bytes());
1207            }
1208        }
1209    }
1210
1211    /// Opens a new outbound stream (client role) or validates the id for
1212    /// a server-initiated response path.
1213    pub fn open_stream(&mut self, stream_id: u32) {
1214        self.streams.entry(stream_id).or_insert(Stream {
1215            state: StreamState::Open,
1216            recv_window: i64::from(self.cfg.initial_window_size),
1217            send_window: self.peer_initial_window,
1218            sent_end: false,
1219            peer_data: false,
1220            content_length: None,
1221            content_received: 0,
1222            head_invalid: false,
1223            head_done: false,
1224        });
1225    }
1226
1227    /// Allocates the next outbound stream id.
1228    pub fn alloc_stream_id(&mut self) -> u32 {
1229        let id = self.next_stream_id;
1230        self.next_stream_id += 2;
1231        self.open_stream(id);
1232        id
1233    }
1234
1235    /// Sends a response/request HEADERS block (HPACK-encoded, split into
1236    /// CONTINUATIONs by the peer's max frame size). `end_stream` closes
1237    /// the send side.
1238    pub fn send_headers(
1239        &mut self,
1240        stream_id: u32,
1241        headers: &[(Vec<u8>, Vec<u8>)],
1242        end_stream: bool,
1243    ) {
1244        let mut block = Vec::new();
1245        self.encoder.encode(headers, &mut block);
1246
1247        // END_STREAM is 0x01, END_HEADERS is 0x04. END_HEADERS may only
1248        // be set on the FINAL fragment of the block (RFC 9113 §4.3):
1249        // on the first frame only when it fits whole.
1250        let fits = block.len() <= self.peer_max_frame as usize;
1251        let max_frag = self.peer_max_frame as usize;
1252        let first = &block[..block.len().min(max_frag)];
1253        let mut flags = if end_stream { 0x04 | 0x01 } else { 0x04 };
1254        if !fits {
1255            flags &= !0x04;
1256        }
1257        write_header(
1258            &mut self.out,
1259            first.len() as u32,
1260            FrameKind::Headers,
1261            FrameFlags::from_u8(flags & 0x04 | if end_stream && fits { 0x01 } else { 0 }),
1262            stream_id,
1263        );
1264        self.out.extend_from_slice(first);
1265
1266        let mut rest = &block[first.len()..];
1267        while !rest.is_empty() {
1268            let n = rest.len().min(max_frag);
1269            let last = n == rest.len();
1270            write_header(
1271                &mut self.out,
1272                n as u32,
1273                FrameKind::Continuation,
1274                FrameFlags::from_u8(if last { 0x04 } else { 0 }),
1275                stream_id,
1276            );
1277            self.out.extend_from_slice(&rest[..n]);
1278            rest = &rest[n..];
1279        }
1280        if end_stream {
1281            let mut retire = false;
1282            if let Some(st) = self.streams.get_mut(&stream_id) {
1283                st.sent_end = true;
1284                st.state = match st.state {
1285                    StreamState::HalfClosedRemote => {
1286                        retire = true; // both halves done
1287                        StreamState::Closed
1288                    }
1289                    _ => StreamState::HalfClosedLocal,
1290                };
1291            }
1292            if retire {
1293                self.streams.remove(&stream_id);
1294            }
1295        }
1296    }
1297
1298    /// Sends DATA on a stream, capped by connection + stream send
1299    /// windows and the peer's max frame size. Returns bytes sent
1300    /// (remainder awaits WINDOW_UPDATE credit).
1301    pub fn send_data(&mut self, stream_id: u32, data: &[u8], end_stream: bool) -> usize {
1302        let cap = self
1303            .streams
1304            .get(&stream_id)
1305            .map(|st| st.send_window.min(self.conn_send_window))
1306            .unwrap_or(0);
1307        let cap = (cap.max(0) as usize).min(self.peer_max_frame as usize);
1308        let n = data.len().min(cap);
1309        if n == 0 {
1310            return 0;
1311        }
1312
1313        let last = n == data.len();
1314        let flags = if last && end_stream { 0x01 } else { 0x00 };
1315        write_header(
1316            &mut self.out,
1317            n as u32,
1318            FrameKind::Data,
1319            FrameFlags::from_u8(flags),
1320            stream_id,
1321        );
1322        self.out.extend_from_slice(&data[..n]);
1323
1324        self.conn_send_window -= n as i64;
1325        let mut retire = false;
1326        if let Some(st) = self.streams.get_mut(&stream_id) {
1327            st.send_window -= n as i64;
1328            if last && end_stream {
1329                st.sent_end = true;
1330                st.state = match st.state {
1331                    StreamState::HalfClosedRemote => {
1332                        retire = true; // both halves done
1333                        StreamState::Closed
1334                    }
1335                    _ => StreamState::HalfClosedLocal,
1336                };
1337            }
1338        }
1339        if retire {
1340            self.streams.remove(&stream_id);
1341        }
1342        n
1343    }
1344
1345    /// Sends RST_STREAM to close a stream with an error.
1346    pub fn send_rst_stream(&mut self, stream_id: u32, code: u32) {
1347        write_header(
1348            &mut self.out,
1349            4,
1350            FrameKind::RstStream,
1351            FrameFlags::EMPTY,
1352            stream_id,
1353        );
1354        self.out.extend_from_slice(&code.to_be_bytes());
1355        self.streams.remove(&stream_id);
1356    }
1357
1358    /// Sends a PING with an opaque 8-byte payload.
1359    pub fn send_ping(&mut self, payload: &[u8; 8]) {
1360        write_header(&mut self.out, 8, FrameKind::Ping, FrameFlags::EMPTY, 0);
1361        self.out.extend_from_slice(payload);
1362    }
1363
1364    /// Sends GOAWAY (draining).
1365    pub fn send_goaway(&mut self, error_code: u32) {
1366        if self.goaway_sent {
1367            return;
1368        }
1369        self.goaway_sent = true;
1370        let mut payload = Vec::new();
1371        payload.extend_from_slice(&self.last_peer_stream_id.to_be_bytes());
1372        payload.extend_from_slice(&error_code.to_be_bytes());
1373        write_header(
1374            &mut self.out,
1375            payload.len() as u32,
1376            FrameKind::GoAway,
1377            FrameFlags::EMPTY,
1378            0,
1379        );
1380        self.out.extend_from_slice(&payload);
1381    }
1382}
1383
1384/// HPACK decode helper over assembled fragments.
1385struct HpackBlockDecoder<'a> {
1386    fragments: Vec<u8>,
1387    decoder: &'a mut HpackDecoder,
1388}
1389
1390impl HpackBlockDecoder<'_> {
1391    fn decode_all(&mut self) -> Result<Vec<super::hpack::Header>, HpackError> {
1392        self.decoder.decode(&self.fragments)
1393    }
1394}
1395
1396#[cfg(test)]
1397mod conn_tests {
1398    use super::*;
1399
1400    fn server() -> Connection {
1401        Connection::new(Role::Server, ConnectionConfig::default())
1402    }
1403
1404    fn client_conn() -> Connection {
1405        Connection::new(Role::Client, ConnectionConfig::default())
1406    }
1407
1408    fn frame_bytes(kind: FrameKind, flags: u8, stream_id: u32, payload: &[u8]) -> Vec<u8> {
1409        let mut out = Vec::new();
1410        write_header(
1411            &mut out,
1412            payload.len() as u32,
1413            kind,
1414            FrameFlags::from_u8(flags),
1415            stream_id,
1416        );
1417        out.extend_from_slice(payload);
1418        out
1419    }
1420
1421    /// Server: client preface (magic + SETTINGS) → SETTINGS ack queued.
1422    #[test]
1423    fn server_acknowledges_client_settings() {
1424        let mut c = server();
1425        let mut events = Vec::new();
1426        // Preface magic + the client's SETTINGS frame, in one read
1427        // (frame header precedes its 6-byte payload).
1428        let mut preface_and_settings = CLIENT_PREFACE.to_vec();
1429        write_header(
1430            &mut preface_and_settings,
1431            6,
1432            FrameKind::Settings,
1433            FrameFlags::EMPTY,
1434            0,
1435        );
1436        write_setting(&mut preface_and_settings, 0x3, 128);
1437        c.handle_read(&preface_and_settings, &mut events);
1438        assert!(events.is_empty());
1439        let writes = c.take_pending_writes();
1440        // Initial server SETTINGS (5 x 6 + 9 hdr) + SETTINGS ACK (9).
1441        assert_eq!(writes.len(), 39 + 9, "expected SETTINGS + ACK: {writes:?}");
1442        // Second frame (offset 39) is the SETTINGS ACK.
1443        assert_eq!(writes[39 + 4] & 0x01, 0x01, "ACK flag");
1444    }
1445
1446    #[test]
1447    fn bad_preface_is_connection_error() {
1448        let mut c = server();
1449        let mut events = Vec::new();
1450        c.handle_read(b"NOT THE PREFACE", &mut events);
1451        assert_eq!(
1452            c.connection_error().map(|e| e.code),
1453            Some(error_code::PROTOCOL_ERROR)
1454        );
1455    }
1456
1457    /// Client HEADERS (END_STREAM) on stream 1 → Headers event.
1458    #[test]
1459    fn server_headers_end_stream() {
1460        let mut c = server();
1461        let mut events = Vec::new();
1462        c.handle_read(CLIENT_PREFACE, &mut events);
1463        let _ = c.take_pending_writes();
1464
1465        // 82 = :method GET indexed; 86 = :scheme https; END_STREAM.
1466        let block = [0x82, 0x86, 0x84]; // :method GET, :scheme, :path /
1467        c.handle_read(
1468            &frame_bytes(FrameKind::Headers, 0x05, 1, &block),
1469            &mut events,
1470        );
1471        assert_eq!(events.len(), 1);
1472        match &events[0] {
1473            Event::Headers {
1474                stream_id,
1475                end_stream,
1476                headers,
1477            } => {
1478                assert_eq!(*stream_id, 1);
1479                assert!(*end_stream);
1480                assert!(!headers.is_empty());
1481            }
1482            other => panic!("expected headers event: {other:?}"),
1483        }
1484    }
1485
1486    /// Even stream id from a client is a connection error.
1487    #[test]
1488    fn even_stream_id_from_client_is_error() {
1489        let mut c = server();
1490        let mut events = Vec::new();
1491        c.handle_read(CLIENT_PREFACE, &mut events);
1492        let _ = c.take_pending_writes();
1493
1494        c.handle_read(
1495            &frame_bytes(FrameKind::Headers, 0x05, 2, &[0x82, 0x86, 0x84]),
1496            &mut events,
1497        );
1498        assert_eq!(
1499            c.connection_error().map(|e| e.code),
1500            Some(error_code::PROTOCOL_ERROR)
1501        );
1502    }
1503
1504    /// DATA on a stream we never saw HEADERS for → STREAM_CLOSED error.
1505    #[test]
1506    fn data_on_unknown_stream_is_error() {
1507        let mut c = server();
1508        let mut events = Vec::new();
1509        c.handle_read(CLIENT_PREFACE, &mut events);
1510        let _ = c.take_pending_writes();
1511
1512        c.handle_read(&frame_bytes(FrameKind::Data, 0x00, 5, b"x"), &mut events);
1513        assert_eq!(
1514            c.connection_error().map(|e| e.code),
1515            Some(error_code::STREAM_CLOSED)
1516        );
1517    }
1518
1519    /// DATA stream: HEADERS (END_STREAM off) → Data events → half-closed.
1520    #[test]
1521    fn data_relay_with_flow_accounting() {
1522        let mut c = server();
1523        let mut events = Vec::new();
1524        c.handle_read(CLIENT_PREFACE, &mut events);
1525        let _ = c.take_pending_writes();
1526
1527        // HEADERS END_STREAM=off: :method GET (82).
1528        c.handle_read(
1529            &frame_bytes(FrameKind::Headers, 0x04, 1, &[0x82, 0x86, 0x84]),
1530            &mut events,
1531        );
1532        assert_eq!(events.len(), 1, "headers event");
1533
1534        // DATA chunk, END_STREAM set.
1535        c.handle_read(
1536            &frame_bytes(FrameKind::Data, 0x01, 1, b"payload"),
1537            &mut events,
1538        );
1539        assert_eq!(events.len(), 2);
1540        match &events[1] {
1541            Event::Data {
1542                stream_id,
1543                end_stream,
1544                data,
1545            } => {
1546                assert_eq!(*stream_id, 1);
1547                assert!(*end_stream);
1548                assert_eq!(data, b"payload");
1549            }
1550            other => panic!("expected data event: {other:?}"),
1551        }
1552    }
1553
1554    /// release_capacity emits WINDOW_UPDATEs once past the half mark.
1555    #[test]
1556    fn release_capacity_emits_window_updates() {
1557        let mut c = server();
1558        let mut events = Vec::new();
1559        c.handle_read(CLIENT_PREFACE, &mut events);
1560        let _ = c.take_pending_writes();
1561
1562        // HEADERS END_STREAM=off on stream 1 (stream recv window =
1563        // 512 KiB; connection window fixed at 65535).
1564        c.handle_read(
1565            &frame_bytes(FrameKind::Headers, 0x04, 1, &[0x82, 0x86, 0x84]),
1566            &mut events,
1567        );
1568
1569        // 8 rounds of 16 KiB (max frame size): each release returns
1570        // credit IMMEDIATELY (both connection and stream) — no
1571        // half-mark batching, which deadlocks transfers sized near a
1572        // window multiple.
1573        let chunk = vec![0u8; 16384];
1574        for _ in 0..8 {
1575            c.handle_read(&frame_bytes(FrameKind::Data, 0x00, 1, &chunk), &mut events);
1576            c.release_capacity(1, chunk.len());
1577        }
1578        let writes = c.take_pending_writes();
1579        assert_eq!(
1580            writes.len(),
1581            16 * 13,
1582            "8 conn + 8 stream WINDOW_UPDATEs (9 hdr + 4 payload each)"
1583        );
1584    }
1585
1586    /// PING (no ACK) → PONG (ACK) with identical payload.
1587    #[test]
1588    fn ping_gets_pong() {
1589        let mut c = server();
1590        let mut events = Vec::new();
1591        c.handle_read(CLIENT_PREFACE, &mut events);
1592        let _ = c.take_pending_writes();
1593
1594        c.handle_read(
1595            &frame_bytes(FrameKind::Ping, 0x00, 0, &[7u8; 8]),
1596            &mut events,
1597        );
1598        let writes = c.take_pending_writes();
1599        assert_eq!(writes.len(), 9 + 8);
1600        // ACK flag set on the pong.
1601        assert_eq!(writes[4], 0x01);
1602        assert_eq!(&writes[9..17], &[7u8; 8]);
1603    }
1604
1605    /// GOAWAY event surfaces last-stream-id + error code.
1606    #[test]
1607    fn goaway_event() {
1608        let mut c = server();
1609        let mut events = Vec::new();
1610        c.handle_read(CLIENT_PREFACE, &mut events);
1611        let _ = c.take_pending_writes();
1612
1613        let mut payload = Vec::new();
1614        payload.extend_from_slice(&7u32.to_be_bytes());
1615        payload.extend_from_slice(&2u32.to_be_bytes());
1616        c.handle_read(
1617            &frame_bytes(FrameKind::GoAway, 0x00, 0, &payload),
1618            &mut events,
1619        );
1620        assert_eq!(events.len(), 1);
1621        match &events[0] {
1622            Event::GoAway {
1623                last_stream_id,
1624                error_code,
1625            } => {
1626                assert_eq!(*last_stream_id, 7);
1627                assert_eq!(*error_code, 2);
1628            }
1629            other => panic!("expected goaway: {other:?}"),
1630        }
1631    }
1632
1633    /// SETTINGS_INITIAL_WINDOW_SIZE adjusts open stream send windows.
1634    #[test]
1635    fn settings_initial_window_delta() {
1636        let mut c = client_conn();
1637        let mut events = Vec::new();
1638        c.handle_read(&frame_bytes(FrameKind::Settings, 0x00, 0, &[]), &mut events);
1639
1640        // Open a stream (client allocates odd id 1).
1641        let id = c.alloc_stream_id();
1642        assert_eq!(id, 1);
1643
1644        // Send SETTINGS raising initial window: old 65535 → new 1_048_576
1645        // (delta +983041).
1646        let mut payload = Vec::new();
1647        write_setting(&mut payload, 0x4, 1_048_576);
1648        c.handle_read(
1649            &frame_bytes(FrameKind::Settings, 0x00, 0, &payload),
1650            &mut events,
1651        );
1652
1653        // Stream 1 send window: 65535 + 983041 = 1048576. Each send_data
1654        // call is frame-capped (peer MAX_FRAME_SIZE 16384); drain the
1655        // old window to prove the delta accounting kept it intact.
1656        let mut sent = 0usize;
1657        while sent < 65535 {
1658            let want = 16384.min(65535 - sent);
1659            let data = vec![0u8; want];
1660            let n = c.send_data(id, &data, false);
1661            assert_eq!(n, want, "frame at {sent}");
1662            sent += n;
1663        }
1664        // The stream window grew, but DATA also consumed the
1665        // connection-level window (65535, now exhausted): a stream-level
1666        // SETTINGS delta alone does not unblock sends.
1667        let n2 = c.send_data(id, &[0u8; 16384], false);
1668        assert_eq!(n2, 0, "connection window exhausted");
1669
1670        // Replenish the connection window; the delta-adjusted stream
1671        // window (1048576) then allows sends to resume.
1672        let conn_payload = {
1673            let mut p = 100_000u32.to_be_bytes().to_vec();
1674            p[0] &= 0x7f;
1675            p
1676        };
1677        c.handle_read(
1678            &frame_bytes(FrameKind::WindowUpdate, 0x00, 0, &conn_payload),
1679            &mut events,
1680        );
1681        let n3 = c.send_data(id, &[0u8; 16384], false);
1682        assert_eq!(n3, 16384, "send resumes once both windows have credit");
1683    }
1684
1685    /// Client role: server even-id HEADERS produce events.
1686    /// Client role: server response HEADERS arrive on the client's own
1687    /// ODD streams; EVEN ids are server push and are refused.
1688    #[test]
1689    fn client_receives_response_headers() {
1690        let mut c = client_conn();
1691        // Client preface is queued in pending writes at construction.
1692        let _ = c.take_pending_writes();
1693
1694        // The client opened stream 1; the server responds on it.
1695        c.open_stream(1);
1696        let mut events = Vec::new();
1697        // :status 200 indexed = 0x88, END_STREAM.
1698        c.handle_read(
1699            &frame_bytes(FrameKind::Headers, 0x05, 1, &[0x88]),
1700            &mut events,
1701        );
1702        assert_eq!(events.len(), 1);
1703        match &events[0] {
1704            Event::Headers {
1705                stream_id,
1706                end_stream,
1707                headers,
1708            } => {
1709                assert_eq!(*stream_id, 1);
1710                assert!(*end_stream);
1711                assert_eq!(headers[0].name, b":status");
1712                assert_eq!(headers[0].value, b"200");
1713            }
1714            other => panic!("expected headers: {other:?}"),
1715        }
1716    }
1717
1718    /// Client role: response trailers (HEADERS after server DATA with
1719    /// END_STREAM — gRPC grpc-status) surface as Event::Trailers.
1720    #[test]
1721    fn client_receives_response_trailers() {
1722        let mut c = client_conn();
1723        let _ = c.take_pending_writes();
1724        let mut events = Vec::new();
1725
1726        // Server HEADERS on stream 2? No — client streams are odd: we
1727        // opened stream 1; the server responds on it.
1728        c.open_stream(1);
1729        // :status 200 (0x88), END_HEADERS off END_STREAM off.
1730        c.handle_read(
1731            &frame_bytes(FrameKind::Headers, 0x04, 1, &[0x88]),
1732            &mut events,
1733        );
1734        // DATA (no END_STREAM).
1735        c.handle_read(
1736            &frame_bytes(FrameKind::Data, 0x00, 1, b"grpc-payload"),
1737            &mut events,
1738        );
1739        // Trailers: grpc-status: 0, END_STREAM.
1740        let mut trailers = Vec::new();
1741        trailers.extend_from_slice(&[0x00, 0x0b]);
1742        trailers.extend_from_slice(b"grpc-status");
1743        trailers.extend_from_slice(&[0x01, b'0']);
1744        c.handle_read(
1745            &frame_bytes(FrameKind::Headers, 0x05, 1, &trailers),
1746            &mut events,
1747        );
1748
1749        let kinds: Vec<&str> = events
1750            .iter()
1751            .filter_map(|ev| match ev {
1752                Event::Headers { .. } => Some("headers"),
1753                Event::Data { .. } => Some("data"),
1754                Event::Trailers { .. } => Some("trailers"),
1755                _ => None,
1756            })
1757            .collect();
1758        assert_eq!(kinds, vec!["headers", "data", "trailers"], "{events:?}");
1759        match &events[2] {
1760            Event::Trailers { headers, .. } => {
1761                assert_eq!(headers[0].name, b"grpc-status");
1762            }
1763            other => panic!("expected trailers: {other:?}"),
1764        }
1765    }
1766
1767    /// Request trailers: HEADERS arriving after DATA on the same
1768    /// stream surface as Event::Trailers (with END_STREAM semantics).
1769    #[test]
1770    fn request_trailers_surfaces_as_trailers_event() {
1771        let mut c = server();
1772        let mut events = Vec::new();
1773        // Preface + client SETTINGS.
1774        let mut flight = CLIENT_PREFACE.to_vec();
1775        flight.extend_from_slice(&frame_bytes(FrameKind::Settings, 0x00, 0, &[]));
1776        c.handle_read(&flight, &mut events);
1777        let _ = c.take_pending_writes();
1778
1779        // HEADERS (POST, END_STREAM off) + DATA + trailers HEADERS.
1780        // Pseudo set: :method (82→wait, 0x83 is :method POST? — static
1781        // index 3 = :method POST), :scheme https (86), :path / (84).
1782        c.handle_read(
1783            &frame_bytes(FrameKind::Headers, 0x04, 1, &[0x83, 0x86, 0x84]),
1784            &mut events,
1785        );
1786        c.handle_read(
1787            &frame_bytes(FrameKind::Data, 0x00, 1, b"payload"),
1788            &mut events,
1789        );
1790        // Trailers: HEADERS END_STREAM with a literal header
1791        // (0x00 0x0a "grpc-status" 0x01 "0" — name len 11, value len 1).
1792        let mut trailers = Vec::new();
1793        trailers.extend_from_slice(&[0x00, 0x0b]);
1794        trailers.extend_from_slice(b"grpc-status");
1795        trailers.extend_from_slice(&[0x01, b'0']);
1796        c.handle_read(
1797            &frame_bytes(FrameKind::Headers, 0x05, 1, &trailers),
1798            &mut events,
1799        );
1800
1801        let kinds: Vec<&str> = events
1802            .iter()
1803            .filter_map(|ev| match ev {
1804                Event::Headers { .. } => Some("headers"),
1805                Event::Data { .. } => Some("data"),
1806                Event::Trailers { .. } => Some("trailers"),
1807                _ => None,
1808            })
1809            .collect();
1810        assert_eq!(kinds, vec!["headers", "data", "trailers"], "{events:?}");
1811        match &events[2] {
1812            Event::Trailers { stream_id, headers } => {
1813                assert_eq!(*stream_id, 1);
1814                assert_eq!(headers[0].name, b"grpc-status");
1815                assert_eq!(headers[0].value, b"0");
1816            }
1817            other => panic!("expected trailers: {other:?}"),
1818        }
1819    }
1820
1821    /// Client role: even-stream HEADERS are server push (banned) —
1822    /// connection error.
1823    #[test]
1824    fn client_rejects_even_stream_headers() {
1825        let mut c = client_conn();
1826        let _ = c.take_pending_writes();
1827        let mut events = Vec::new();
1828        c.handle_read(
1829            &frame_bytes(FrameKind::Headers, 0x05, 2, &[0x88]),
1830            &mut events,
1831        );
1832        assert_eq!(
1833            c.connection_error().map(|e| e.code),
1834            Some(error_code::PROTOCOL_ERROR)
1835        );
1836    }
1837
1838    /// CONTINUATION assembles multi-frame header blocks.
1839    #[test]
1840    fn continuation_assembles_block() {
1841        let mut c = server();
1842        let mut events = Vec::new();
1843        c.handle_read(CLIENT_PREFACE, &mut events);
1844        let _ = c.take_pending_writes();
1845
1846        // HEADERS without END_HEADERS, with END_STREAM: :method GET +
1847        // :scheme (82, 86); :path rides the CONTINUATION.
1848        c.handle_read(
1849            &frame_bytes(FrameKind::Headers, 0x01, 1, &[0x82, 0x86]),
1850            &mut events,
1851        );
1852        assert!(events.is_empty(), "block not yet complete");
1853
1854        // CONTINUATION END_HEADERS=on: :path / (84) completes the set.
1855        c.handle_read(
1856            &frame_bytes(FrameKind::Continuation, 0x04, 1, &[0x84]),
1857            &mut events,
1858        );
1859        assert_eq!(events.len(), 1);
1860        match &events[0] {
1861            Event::Headers {
1862                end_stream,
1863                headers,
1864                ..
1865            } => {
1866                assert!(*end_stream);
1867                assert!(headers.len() >= 2);
1868            }
1869            other => panic!("expected headers: {other:?}"),
1870        }
1871    }
1872
1873    /// A non-CONTINUATION frame while assembling is a connection error.
1874    #[test]
1875    fn interrupted_header_block_is_error() {
1876        let mut c = server();
1877        let mut events = Vec::new();
1878        c.handle_read(CLIENT_PREFACE, &mut events);
1879        let _ = c.take_pending_writes();
1880
1881        c.handle_read(
1882            &frame_bytes(FrameKind::Headers, 0x01, 1, &[0x82, 0x86, 0x84]),
1883            &mut events,
1884        );
1885        c.handle_read(
1886            &frame_bytes(FrameKind::Ping, 0x00, 0, &[7u8; 8]),
1887            &mut events,
1888        );
1889        assert_eq!(
1890            c.connection_error().map(|e| e.code),
1891            Some(error_code::PROTOCOL_ERROR)
1892        );
1893    }
1894
1895    /// HPACK errors surface as COMPRESSION_ERROR connection failures.
1896    #[test]
1897    fn hpack_corruption_is_compression_error() {
1898        let mut c = server();
1899        let mut events = Vec::new();
1900        c.handle_read(CLIENT_PREFACE, &mut events);
1901        let _ = c.take_pending_writes();
1902
1903        // Indexed header with index 0 — invalid per RFC 7541 §6.1.
1904        c.handle_read(
1905            &frame_bytes(FrameKind::Headers, 0x05, 1, &[0x80]),
1906            &mut events,
1907        );
1908        assert_eq!(
1909            c.connection_error().map(|e| e.code),
1910            Some(error_code::COMPRESSION_ERROR)
1911        );
1912    }
1913
1914    /// HEADERS split across CONTINUATIONs on SEND: a header block
1915    /// larger than the peer's max frame size is emitted as HEADERS
1916    /// (END_HEADERS off) followed by CONTINUATION (END_HEADERS on).
1917    #[test]
1918    fn send_headers_splits_into_continuations() {
1919        let mut c = client_conn();
1920        let _ = c.take_pending_writes(); // drain preface + initial SETTINGS
1921        let id = c.alloc_stream_id();
1922        // ~40 KiB of header value forces a split at 16384.
1923        let big = vec![b'a'; 40_960];
1924        let headers = vec![
1925            (b":method".to_vec(), b"GET".to_vec()),
1926            (b"x-big".to_vec(), big),
1927        ];
1928        c.send_headers(id, &headers, true);
1929        let writes = c.take_pending_writes();
1930        // Walk the emitted frames: first HEADERS, then CONTINUATIONs.
1931        let mut off = 0usize;
1932        let mut kinds = Vec::new();
1933        while off < writes.len() {
1934            let len =
1935                u32::from_be_bytes([0, writes[off], writes[off + 1], writes[off + 2]]) as usize;
1936            kinds.push((writes[off + 3], writes[off + 4]));
1937            off += 9 + len;
1938        }
1939        assert!(kinds.len() >= 2, "expected split frames: {kinds:?}");
1940        let (first_kind, first_flags) = kinds[0];
1941        assert_eq!(first_kind, 0x01, "HEADERS");
1942        assert_eq!(first_flags & 0x04, 0, "END_HEADERS clear on first frame");
1943        let (last_kind, last_flags) = *kinds.last().expect("nonempty");
1944        assert_eq!(last_kind, 0x09, "CONTINUATION");
1945        assert_eq!(last_flags & 0x04, 0x04, "END_HEADERS on last frame");
1946    }
1947
1948    /// DATA on a stream closed by END_STREAM is a STREAM_CLOSED error.
1949    #[test]
1950    fn data_after_end_stream_is_stream_closed() {
1951        let mut c = server();
1952        let mut events = Vec::new();
1953        c.handle_read(CLIENT_PREFACE, &mut events);
1954        let _ = c.take_pending_writes();
1955
1956        // HEADERS with END_STREAM closes stream 1.
1957        c.handle_read(
1958            &frame_bytes(FrameKind::Headers, 0x05, 1, &[0x82, 0x86, 0x84]),
1959            &mut events,
1960        );
1961        assert_eq!(events.len(), 1);
1962        // Late DATA on the closed stream.
1963        c.handle_read(&frame_bytes(FrameKind::Data, 0x00, 1, b"late"), &mut events);
1964        assert_eq!(
1965            c.connection_error().map(|e| e.code),
1966            Some(error_code::STREAM_CLOSED)
1967        );
1968    }
1969
1970    /// SETTINGS ACK arriving without a pending ACK is a PROTOCOL_ERROR.
1971    #[test]
1972    fn unexpected_settings_ack_is_protocol_error() {
1973        let mut c = server();
1974        let mut events = Vec::new();
1975        // Preface + client SETTINGS + ACK of ours: a legitimate
1976        // exchange (our initial SETTINGS were in flight).
1977        let mut preface = CLIENT_PREFACE.to_vec();
1978        preface.extend_from_slice(&frame_bytes(FrameKind::Settings, 0x00, 0, &[]));
1979        preface.extend_from_slice(&frame_bytes(FrameKind::Settings, 0x01, 0, &[]));
1980        c.handle_read(&preface, &mut events);
1981        assert!(c.connection_error().is_none());
1982        let _ = c.take_pending_writes();
1983
1984        // A SECOND ACK with no SETTINGS in flight is a protocol error.
1985        c.handle_read(&frame_bytes(FrameKind::Settings, 0x01, 0, &[]), &mut events);
1986        assert_eq!(
1987            c.connection_error().map(|e| e.code),
1988            Some(error_code::PROTOCOL_ERROR)
1989        );
1990    }
1991
1992    /// send_data respects stream + connection windows.
1993    #[test]
1994    fn send_data_respects_flow_control() {
1995        let mut c = client_conn();
1996        let id = c.alloc_stream_id();
1997
1998        // Initial peer stream window 65535; connection window 65535.
1999        // Each send_data call is capped by the peer's MAX_FRAME_SIZE
2000        // (16384), so a full window needs 4 frames.
2001        // 65535 = 3 x 16384 + 16383.
2002        for expected in [16384usize, 16384, 16384, 16383] {
2003            let data = vec![0u8; expected];
2004            let n = c.send_data(id, &data, false);
2005            assert_eq!(n, expected);
2006        }
2007        // Window exhausted: next send is refused (0 bytes).
2008        assert_eq!(c.send_data(id, &[0u8; 1000], false), 0, "window exhausted");
2009
2010        // Peer grants 200000 STREAM-level credit via WINDOW_UPDATE, but
2011        // DATA consumes both windows — sending stays blocked until the
2012        // CONNECTION-level window is replenished too.
2013        let mut events = Vec::new();
2014        let mut payload = 200_000u32.to_be_bytes().to_vec();
2015        payload[0] &= 0x7f; // reserved bit zero
2016        c.handle_read(
2017            &frame_bytes(FrameKind::WindowUpdate, 0x00, id, &payload),
2018            &mut events,
2019        );
2020        assert_eq!(c.send_data(id, &[0u8; 100], false), 0, "conn window empty");
2021
2022        // Connection-level WINDOW_UPDATE (stream_id 0) unblocks sends.
2023        let conn_payload = {
2024            let mut p = (65_535u32 + 200_000).to_be_bytes().to_vec();
2025            p[0] &= 0x7f;
2026            p
2027        };
2028        c.handle_read(
2029            &frame_bytes(FrameKind::WindowUpdate, 0x00, 0, &conn_payload),
2030            &mut events,
2031        );
2032        let n = c.send_data(id, &[0u8; 16384], false);
2033        assert_eq!(n, 16384, "send resumes after credit");
2034    }
2035}