Skip to main content

micro_h2/
conn.rs

1//! A client connection: one HPACK context, a few streams, and flow control.
2//!
3//! # Flow control is the part that bites
4//!
5//! An HTTP/2 receiver advertises how much it is willing to accept, and a sender
6//! that has used up that allowance simply stops. Both windows start at 65535
7//! bytes — one for the connection, one for each stream — and neither grows
8//! unless the receiver says so with `WINDOW_UPDATE`.
9//!
10//! For a client that fetches small responses this never comes up, which is
11//! exactly why it is dangerous: the netmap long-poll streams megabytes, and a
12//! client that never sends `WINDOW_UPDATE` receives precisely 65535 bytes and
13//! then hangs forever. It looks like the server stopped talking. It did, because
14//! we told it to.
15//!
16//! So this connection raises the connection window immediately after the
17//! preface, advertises a large per-stream window in its `SETTINGS`, and returns
18//! a `WINDOW_UPDATE` for every byte of `DATA` it consumes.
19//!
20//! # Sans-io
21//!
22//! The caller reads [`frame::HEADER_LEN`] bytes, learns the payload length,
23//! reads that, and hands the whole frame to [`Connection::recv`]. Anything that
24//! must be sent in reply is written to the caller's buffer.
25
26use crate::frame::{self, FrameHeader, FrameType, flags, settings};
27use crate::hpack::{self, Decoder};
28use crate::{Error, headers, hpack::encode};
29
30/// How many requests may be in flight at once. Registration and the map
31/// long-poll, with one spare.
32pub const MAX_STREAMS: usize = 4;
33
34/// The largest header block this client will reassemble across `CONTINUATION`
35/// frames.
36pub const MAX_HEADER_BLOCK: usize = 2048;
37
38/// What we advertise as our per-stream receive window, and what we top the
39/// connection window up to.
40///
41/// Large enough that a streaming response is never throttled by the round trip
42/// of a `WINDOW_UPDATE`, and it costs nothing: the window bounds what the peer
43/// may have in flight, not what we must buffer, because every `DATA` frame is
44/// handed to the caller as it arrives.
45pub const RECEIVE_WINDOW: u32 = 1 << 20;
46
47/// The default both windows start at, before anything is negotiated.
48const DEFAULT_WINDOW: u32 = 65_535;
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51struct Stream {
52    id: u32,
53    /// Bytes received since the last `WINDOW_UPDATE` for this stream.
54    consumed: u32,
55    open: bool,
56    send_window: i64,
57    head_request: bool,
58    connect_request: bool,
59    response_started: bool,
60    no_body: bool,
61    content_length: Option<u64>,
62    received: u64,
63}
64
65/// What arrived.
66#[derive(Debug, PartialEq, Eq)]
67pub enum Event<'a> {
68    /// Nothing the caller needs to act on: a settings exchange, a ping, an
69    /// unknown frame type, or a late frame on a closed stream. Anything that
70    /// needed a reply is already in `out`.
71    Nothing,
72    /// A complete header block. The fields were passed to the callback.
73    Headers { stream: u32, end_stream: bool },
74    /// Response body bytes. Borrowed from the caller's own frame buffer, so
75    /// nothing is copied.
76    Data {
77        stream: u32,
78        data: &'a [u8],
79        end_stream: bool,
80    },
81    /// The peer reset one stream. The connection is still usable.
82    Reset { stream: u32, code: u32 },
83    /// The peer is shutting the connection down.
84    GoAway { code: u32 },
85}
86
87pub struct Connection {
88    decoder: Decoder,
89    streams: heapless::Vec<Stream, MAX_STREAMS>,
90    next_stream: u32,
91    /// Reassembly buffer for a header block split across `CONTINUATION` frames.
92    header_block: heapless::Vec<u8, MAX_HEADER_BLOCK>,
93    /// Which stream the block being reassembled belongs to.
94    header_stream: u32,
95    header_end_stream: bool,
96    /// Bytes received on the connection since the last `WINDOW_UPDATE`.
97    consumed: u32,
98    /// What the peer said it will accept in one frame.
99    peer_max_frame: usize,
100    send_window: u32,
101    peer_initial_window: u32,
102    peer_max_streams: u32,
103    goaway: bool,
104}
105
106impl Default for Connection {
107    fn default() -> Self {
108        Self::new()
109    }
110}
111
112impl Connection {
113    pub fn new() -> Self {
114        Self {
115            decoder: Decoder::new(hpack::DEFAULT_TABLE_SIZE),
116            streams: heapless::Vec::new(),
117            // Client-initiated streams are odd-numbered.
118            next_stream: 1,
119            header_block: heapless::Vec::new(),
120            header_stream: 0,
121            header_end_stream: false,
122            consumed: 0,
123            peer_max_frame: frame::DEFAULT_MAX_FRAME,
124            send_window: DEFAULT_WINDOW,
125            peer_initial_window: DEFAULT_WINDOW,
126            peer_max_streams: u32::MAX,
127            goaway: false,
128        }
129    }
130
131    /// Write the client preface, our settings, and the connection-level window
132    /// update that stops a large response stalling at 65535 bytes.
133    pub fn start(&mut self, out: &mut [u8]) -> Result<usize, Error> {
134        let preface = frame::CLIENT_PREFACE;
135        out.get_mut(..preface.len())
136            .ok_or(Error::BufferTooSmall)?
137            .copy_from_slice(preface);
138        let mut len = preface.len();
139
140        // Push is refused outright rather than handled: a client that never
141        // accepts a promise needs no state for one.
142        let mut payload = [0u8; 24];
143        write_setting(
144            &mut payload[0..6],
145            settings::HEADER_TABLE_SIZE,
146            hpack::DEFAULT_TABLE_SIZE as u32,
147        );
148        write_setting(&mut payload[6..12], settings::ENABLE_PUSH, 0);
149        write_setting(
150            &mut payload[12..18],
151            settings::INITIAL_WINDOW_SIZE,
152            RECEIVE_WINDOW,
153        );
154        write_setting(
155            &mut payload[18..24],
156            settings::MAX_CONCURRENT_STREAMS,
157            MAX_STREAMS as u32,
158        );
159        len += frame::write_frame(FrameType::Settings, 0, 0, &payload, &mut out[len..])?;
160
161        // SETTINGS_INITIAL_WINDOW_SIZE applies to streams only; the connection
162        // window can be raised only by an explicit update.
163        let increment = (RECEIVE_WINDOW - DEFAULT_WINDOW).to_be_bytes();
164        len += frame::write_frame(FrameType::WindowUpdate, 0, 0, &increment, &mut out[len..])?;
165        Ok(len)
166    }
167
168    /// Open a stream and send a request, headers and body together.
169    ///
170    /// Returns the stream identifier and how many bytes were written.
171    /// The authority argument is authoritative; redundant `host` fields are
172    /// omitted. If it is empty, a single `host` field supplies `:authority`.
173    #[allow(clippy::too_many_arguments)]
174    pub fn request(
175        &mut self,
176        method: &str,
177        path: &str,
178        authority: &str,
179        scheme: &str,
180        extra: &[(&str, &str)],
181        body: &[u8],
182        out: &mut [u8],
183    ) -> Result<(u32, usize), Error> {
184        if self.goaway {
185            return Err(Error::GoAway);
186        }
187        let id = self.next_stream;
188        if self.streams.len() as u32 >= self.peer_max_streams {
189            return Err(Error::TooManyStreams);
190        }
191        if self.streams.is_full() || id > 0x7fff_ffff {
192            return Err(Error::TooManyStreams);
193        }
194
195        if method.is_empty()
196            || !method.bytes().all(headers::token)
197            || (method != "CONNECT"
198                && (!headers::path_valid(method, path) || !headers::scheme_valid(scheme)))
199        {
200            return Err(Error::Protocol);
201        }
202        let mut content_length = None;
203        let mut host = None;
204        for &(name, value) in extra {
205            headers::field(name, value, true)?;
206            if name == "host" && authority.is_empty() && host.replace(value).is_some() {
207                return Err(Error::Protocol);
208            }
209            if name == "content-length" {
210                headers::content_length(value, &mut content_length)?;
211            }
212        }
213        let authority = if authority.is_empty() {
214            host.unwrap_or("")
215        } else {
216            authority
217        };
218        if !headers::authority_valid(authority, scheme, method == "CONNECT") {
219            return Err(Error::Protocol);
220        }
221        if content_length.is_some_and(|length| length != body.len() as u64) {
222            return Err(Error::Protocol);
223        }
224
225        // Pseudo-headers must come first and in this order; a server is entitled
226        // to reject a block that interleaves them with ordinary fields.
227        let mut block = [0u8; MAX_HEADER_BLOCK];
228        let mut block_len = 0;
229        block_len = encode::encode_header(":method", method, &mut block, block_len)?;
230        if method != "CONNECT" {
231            block_len = encode::encode_header(":path", path, &mut block, block_len)?;
232            block_len = encode::encode_header(":scheme", scheme, &mut block, block_len)?;
233        }
234        if !authority.is_empty() {
235            block_len = encode::encode_header(":authority", authority, &mut block, block_len)?;
236        }
237        let mut length_written = false;
238        for (name, value) in extra {
239            // Direct HTTP/2 clients convey routing through :authority. Omit
240            // Host so callers cannot emit conflicting routing identities.
241            if *name == "host" {
242                continue;
243            }
244            if *name == "content-length" {
245                if length_written {
246                    continue;
247                }
248                // Emit the already-validated length once in canonical form.
249                // Receivers need not normalize duplicate/list forms themselves.
250                use core::fmt::Write;
251                let mut canonical = heapless::String::<20>::new();
252                write!(&mut canonical, "{}", body.len()).map_err(|_| Error::BufferTooSmall)?;
253                block_len = encode::encode_header(name, &canonical, &mut block, block_len)?;
254                length_written = true;
255            } else {
256                let value = if *name == "te" { "trailers" } else { value };
257                block_len = encode::encode_header(name, value, &mut block, block_len)?;
258            }
259        }
260
261        // No CONTINUATION is emitted: a header block that does not fit one frame
262        // would need one, and these two request shapes never come close.
263        if block_len > self.peer_max_frame {
264            return Err(Error::FrameTooLarge);
265        }
266
267        if body.len() > self.peer_max_frame {
268            return Err(Error::FrameTooLarge);
269        }
270        if body.len() > self.send_window as usize || body.len() > self.peer_initial_window as usize
271        {
272            return Err(Error::FlowControl);
273        }
274        let required = frame::HEADER_LEN
275            + block_len
276            + if body.is_empty() {
277                0
278            } else {
279                frame::HEADER_LEN + body.len()
280            };
281        if out.len() < required {
282            return Err(Error::BufferTooSmall);
283        }
284
285        let end_stream = if body.is_empty() {
286            flags::END_STREAM
287        } else {
288            0
289        };
290        let mut len = frame::write_frame(
291            FrameType::Headers,
292            flags::END_HEADERS | end_stream,
293            id,
294            &block[..block_len],
295            out,
296        )?;
297
298        if !body.is_empty() {
299            len += frame::write_frame(
300                FrameType::Data,
301                flags::END_STREAM,
302                id,
303                body,
304                &mut out[len..],
305            )?;
306        }
307        self.streams
308            .push(Stream {
309                id,
310                consumed: 0,
311                open: true,
312                send_window: self.peer_initial_window as i64 - body.len() as i64,
313                head_request: method == "HEAD",
314                connect_request: method == "CONNECT",
315                response_started: false,
316                no_body: false,
317                content_length: None,
318                received: 0,
319            })
320            .map_err(|_| Error::TooManyStreams)?;
321        self.next_stream += 2;
322        self.send_window -= body.len() as u32;
323        Ok((id, len))
324    }
325
326    /// Consume one whole frame.
327    ///
328    /// `frame` is the nine-byte header followed by exactly its payload. Replies
329    /// this connection owes — settings and ping acknowledgements, window updates
330    /// — are written to `out`, and the caller must send them.
331    pub fn recv<'a>(
332        &mut self,
333        bytes: &'a [u8],
334        mut on_header: impl FnMut(&str, &str),
335        out: &mut [u8],
336    ) -> Result<(Event<'a>, usize), Error> {
337        let header = FrameHeader::parse(bytes)?;
338        let payload = bytes
339            .get(frame::HEADER_LEN..frame::HEADER_LEN + header.length)
340            .ok_or(Error::Incomplete)?;
341        if header.length > frame::DEFAULT_MAX_FRAME {
342            return Err(Error::FrameTooLarge);
343        }
344        if self.header_stream != 0 {
345            if header.kind != FrameType::Continuation || header.stream != self.header_stream {
346                return Err(Error::Protocol);
347            }
348        } else if header.kind == FrameType::Continuation {
349            return Err(Error::Protocol);
350        }
351        match header.kind {
352            FrameType::Settings
353                if header.stream != 0 || (header.has(flags::ACK) && !payload.is_empty()) =>
354            {
355                return Err(Error::Protocol);
356            }
357            FrameType::Ping if header.stream != 0 || payload.len() != 8 => {
358                return Err(Error::Protocol);
359            }
360            FrameType::GoAway if header.stream != 0 || payload.len() < 8 => {
361                return Err(Error::Protocol);
362            }
363            FrameType::Headers | FrameType::Data | FrameType::Continuation
364                if header.stream == 0 =>
365            {
366                return Err(Error::Protocol);
367            }
368            FrameType::RstStream if header.stream == 0 || payload.len() != 4 => {
369                return Err(Error::Protocol);
370            }
371            FrameType::Priority if header.stream == 0 || payload.len() != 5 => {
372                return Err(Error::Protocol);
373            }
374            FrameType::WindowUpdate
375                if payload.len() != 4 || read_u32(payload).unwrap() & 0x7fff_ffff == 0 =>
376            {
377                return Err(Error::Protocol);
378            }
379            _ => {}
380        }
381        if matches!(
382            header.kind,
383            FrameType::Headers | FrameType::Data | FrameType::RstStream | FrameType::WindowUpdate
384        ) && header.stream != 0
385        {
386            self.known_stream(header.stream)?;
387        }
388        let mut written = 0;
389
390        match header.kind {
391            FrameType::Settings => {
392                if !header.has(flags::ACK) {
393                    self.apply_settings(payload)?;
394                    // An unacknowledged SETTINGS blocks the peer indefinitely.
395                    written = frame::write_frame(FrameType::Settings, flags::ACK, 0, &[], out)?;
396                }
397                Ok((Event::Nothing, written))
398            }
399
400            FrameType::Ping => {
401                if !header.has(flags::ACK) {
402                    // The payload must be echoed exactly.
403                    written = frame::write_frame(FrameType::Ping, flags::ACK, 0, payload, out)?;
404                }
405                Ok((Event::Nothing, written))
406            }
407
408            FrameType::Headers | FrameType::Continuation => {
409                let block = if header.kind == FrameType::Headers {
410                    self.header_block.clear();
411                    self.header_stream = header.stream;
412                    self.header_end_stream = header.has(flags::END_STREAM);
413                    let body = frame::strip_padding(payload, header.flags)?;
414                    // A priority block sits between the padding and the header
415                    // block, and feeding it to HPACK fails on a valid frame.
416                    if header.has(flags::PRIORITY) {
417                        body.get(5..).ok_or(Error::Protocol)?
418                    } else {
419                        body
420                    }
421                } else {
422                    payload
423                };
424
425                self.header_block
426                    .extend_from_slice(block)
427                    .map_err(|_| Error::BufferTooSmall)?;
428
429                if !header.has(flags::END_HEADERS) {
430                    // More CONTINUATION frames to come. Decoding a partial block
431                    // would corrupt the HPACK state for the whole connection.
432                    return Ok((Event::Nothing, 0));
433                }
434
435                let stream = self.header_stream;
436                let end_stream = self.header_end_stream;
437                let active = self.streams.iter().position(|entry| entry.id == stream);
438                if let Some(index) = active {
439                    if self.streams[index].response_started {
440                        // Trailers remain outside this client's supported surface.
441                        return Err(Error::Protocol);
442                    }
443                    // Validate the whole block before any fields escape to the caller.
444                    // The fixed-size clone keeps the real HPACK table at its starting
445                    // state for the delivery pass; each insertion commits only once.
446                    let mut preflight = self.decoder.clone();
447                    let mut fields = headers::ResponseHeaders::default();
448                    let mut valid = Ok(());
449                    preflight.decode(&self.header_block, |name, value| {
450                        if valid.is_ok() {
451                            valid = fields.field(name, value);
452                        }
453                    })?;
454                    valid?;
455                    let status = fields.status.ok_or(Error::Protocol)?;
456                    if status < 200 {
457                        if end_stream || fields.content_length.is_some() {
458                            return Err(Error::Protocol);
459                        }
460                    } else {
461                        let entry = &mut self.streams[index];
462                        let tunnel = entry.connect_request && (200..300).contains(&status);
463                        // RFC 9110 section 9.3.6 requires clients to ignore a
464                        // successful CONNECT's Content-Length. Ordinary 205
465                        // has zero-length content, unlike the HEAD/304 metadata
466                        // exceptions in sections 6.4.1 and 8.6.
467                        if !tunnel
468                            && ((status == 204 && fields.content_length.is_some())
469                                || (status == 205
470                                    && fields.content_length.is_some_and(|length| length != 0)))
471                        {
472                            return Err(Error::Protocol);
473                        }
474                        let no_body =
475                            !tunnel && (entry.head_request || matches!(status, 204 | 205 | 304));
476                        let expected = if no_body || tunnel {
477                            None
478                        } else {
479                            fields.content_length
480                        };
481                        if end_stream && expected.is_some_and(|length| length != 0) {
482                            return Err(Error::Protocol);
483                        }
484                        entry.response_started = true;
485                        entry.no_body = no_body;
486                        entry.content_length = expected;
487                    }
488                    self.decoder.decode(&self.header_block, &mut on_header)?;
489                } else {
490                    // Late fields on closed streams still affect connection-wide
491                    // HPACK state, but must not become application headers.
492                    self.decoder.decode(&self.header_block, |_, _| {})?;
493                }
494                self.header_stream = 0;
495                self.header_block.clear();
496                if active.is_none() {
497                    return Ok((Event::Nothing, 0));
498                }
499                if end_stream {
500                    self.close(stream);
501                }
502                Ok((Event::Headers { stream, end_stream }, 0))
503            }
504
505            FrameType::Data => {
506                let data = frame::strip_padding(payload, header.flags)?;
507                let end_stream = header.has(flags::END_STREAM);
508                let active = self
509                    .streams
510                    .iter()
511                    .position(|entry| entry.id == header.stream);
512                let received = active
513                    .map(|index| self.data_length(index, data.len(), end_stream))
514                    .transpose()?;
515                // The window is consumed by the whole payload, padding included,
516                // not just the bytes we hand back.
517                written = self.credit(header.stream, header.length as u32, out)?;
518                let Some(index) = active else {
519                    return Ok((Event::Nothing, written));
520                };
521                self.streams[index].received = received.unwrap();
522                if end_stream {
523                    self.close(header.stream);
524                }
525                Ok((
526                    Event::Data {
527                        stream: header.stream,
528                        data,
529                        end_stream,
530                    },
531                    written,
532                ))
533            }
534
535            FrameType::RstStream => {
536                let code = read_u32(payload).ok_or(Error::Protocol)?;
537                if !self.streams.iter().any(|entry| entry.id == header.stream) {
538                    return Ok((Event::Nothing, 0));
539                }
540                self.close(header.stream);
541                Ok((
542                    Event::Reset {
543                        stream: header.stream,
544                        code,
545                    },
546                    0,
547                ))
548            }
549
550            FrameType::GoAway => {
551                // last-stream-id, then the error code.
552                let code = payload
553                    .get(4..8)
554                    .and_then(read_u32)
555                    .ok_or(Error::Protocol)?;
556                self.goaway = true;
557                Ok((Event::GoAway { code }, 0))
558            }
559
560            FrameType::WindowUpdate => {
561                let increment = read_u32(payload).ok_or(Error::Protocol)? & 0x7fff_ffff;
562                if header.stream == 0 {
563                    self.send_window = self
564                        .send_window
565                        .checked_add(increment)
566                        .filter(|value| *value <= 0x7fff_ffff)
567                        .ok_or(Error::Protocol)?;
568                } else if let Some(stream) = self.streams.iter_mut().find(|s| s.id == header.stream)
569                {
570                    let window = stream.send_window + increment as i64;
571                    if window > 0x7fff_ffff {
572                        return Err(Error::Protocol);
573                    }
574                    stream.send_window = window;
575                }
576                Ok((Event::Nothing, 0))
577            }
578            FrameType::Priority => Ok((Event::Nothing, 0)),
579
580            // Push was refused in our SETTINGS, so a promise is a protocol
581            // violation rather than something to ignore.
582            FrameType::PushPromise => Err(Error::Protocol),
583
584            // RFC 7540 requires unknown types to be discarded.
585            FrameType::Unknown(_) => Ok((Event::Nothing, 0)),
586        }
587    }
588
589    /// Complete a DATA frame whose payload was consumed incrementally by the
590    /// transport adapter.
591    ///
592    /// DATA does not affect HPACK state, so constrained adapters can stream a
593    /// large payload without retaining the complete frame. Flow-control credit
594    /// and stream closure still happen exactly once, after every payload byte
595    /// has been authenticated and consumed. For padded frames, use
596    /// [`Self::finish_data_with_length`] to supply the unpadded content length.
597    pub fn finish_data(&mut self, header: FrameHeader, out: &mut [u8]) -> Result<usize, Error> {
598        if header.has(flags::PADDED) {
599            return Err(Error::Protocol);
600        }
601        self.finish_data_with_length(header, header.length, out)
602    }
603
604    /// Complete a streamed DATA frame, supplying the unpadded content length.
605    /// The adapter must validate the padding byte and consume the full payload
606    /// before calling this method. Credit includes padding; Content-Length does not.
607    pub fn finish_data_with_length(
608        &mut self,
609        header: FrameHeader,
610        data_length: usize,
611        out: &mut [u8],
612    ) -> Result<usize, Error> {
613        if header.kind != FrameType::Data || header.stream == 0 || self.header_stream != 0 {
614            return Err(Error::Protocol);
615        }
616        if header.length > frame::DEFAULT_MAX_FRAME {
617            return Err(Error::FrameTooLarge);
618        }
619        let padding = header
620            .length
621            .checked_sub(data_length)
622            .ok_or(Error::Protocol)?;
623        if (header.has(flags::PADDED) && !(1..=256).contains(&padding))
624            || (!header.has(flags::PADDED) && padding != 0)
625        {
626            return Err(Error::Protocol);
627        }
628        self.known_stream(header.stream)?;
629        let active = self
630            .streams
631            .iter()
632            .position(|entry| entry.id == header.stream);
633        let received = active
634            .map(|index| self.data_length(index, data_length, header.has(flags::END_STREAM)))
635            .transpose()?;
636        let written = self.credit(header.stream, header.length as u32, out)?;
637        if let Some(index) = active {
638            self.streams[index].received = received.unwrap();
639        }
640        if header.has(flags::END_STREAM) {
641            self.close(header.stream);
642        }
643        Ok(written)
644    }
645
646    fn known_stream(&self, stream: u32) -> Result<(), Error> {
647        // Push is disabled; only odd streams actually opened by us can exist.
648        if stream & 1 == 0 || stream >= self.next_stream {
649            return Err(Error::Protocol);
650        }
651        Ok(())
652    }
653
654    fn data_length(&self, index: usize, length: usize, end_stream: bool) -> Result<u64, Error> {
655        let entry = &self.streams[index];
656        if !entry.response_started || (entry.no_body && length != 0) {
657            return Err(Error::Protocol);
658        }
659        let received = entry
660            .received
661            .checked_add(length as u64)
662            .ok_or(Error::Protocol)?;
663        if entry
664            .content_length
665            .is_some_and(|expected| received > expected || (end_stream && received != expected))
666        {
667            return Err(Error::Protocol);
668        }
669        Ok(received)
670    }
671
672    /// Return flow-control credit for `length` bytes consumed on `stream`.
673    fn credit(&mut self, stream: u32, length: u32, out: &mut [u8]) -> Result<usize, Error> {
674        if length == 0 {
675            return Ok(0);
676        }
677        const THRESHOLD: u32 = RECEIVE_WINDOW / 2;
678        let consumed = self.consumed.checked_add(length).ok_or(Error::Protocol)?;
679        let stream_consumed = self
680            .streams
681            .iter()
682            .find(|s| s.id == stream)
683            .map(|entry| entry.consumed.checked_add(length).ok_or(Error::Protocol))
684            .transpose()?;
685        let connection_update = consumed >= THRESHOLD;
686        let stream_update = stream_consumed.is_some_and(|value| value >= THRESHOLD);
687        let required =
688            (connection_update as usize + stream_update as usize) * (frame::HEADER_LEN + 4);
689        if out.len() < required {
690            return Err(Error::BufferTooSmall);
691        }
692        let mut written = 0;
693        if connection_update {
694            written +=
695                frame::write_frame(FrameType::WindowUpdate, 0, 0, &consumed.to_be_bytes(), out)?;
696        }
697        if stream_update {
698            written += frame::write_frame(
699                FrameType::WindowUpdate,
700                0,
701                stream,
702                &stream_consumed.unwrap().to_be_bytes(),
703                &mut out[written..],
704            )?;
705        }
706        self.consumed = if connection_update { 0 } else { consumed };
707        if let Some(entry) = self.streams.iter_mut().find(|s| s.id == stream) {
708            entry.consumed = if stream_update {
709                0
710            } else {
711                stream_consumed.unwrap()
712            };
713        }
714        Ok(written)
715    }
716
717    fn apply_settings(&mut self, payload: &[u8]) -> Result<(), Error> {
718        if !payload.len().is_multiple_of(6) {
719            return Err(Error::Protocol);
720        }
721        for entry in payload.as_chunks::<6>().0 {
722            let identifier = u16::from_be_bytes([entry[0], entry[1]]);
723            let value = u32::from_be_bytes([entry[2], entry[3], entry[4], entry[5]]);
724            match identifier {
725                settings::MAX_FRAME_SIZE => {
726                    if !(16_384..=16_777_215).contains(&value) {
727                        return Err(Error::Protocol);
728                    }
729                    self.peer_max_frame = value as usize;
730                }
731                settings::INITIAL_WINDOW_SIZE => {
732                    if value > 0x7fff_ffff {
733                        return Err(Error::Protocol);
734                    }
735                    let delta = value as i64 - self.peer_initial_window as i64;
736                    if self
737                        .streams
738                        .iter()
739                        .any(|stream| stream.send_window + delta > 0x7fff_ffff)
740                    {
741                        return Err(Error::Protocol);
742                    }
743                    for stream in &mut self.streams {
744                        stream.send_window += delta;
745                    }
746                    self.peer_initial_window = value;
747                }
748                settings::MAX_CONCURRENT_STREAMS => self.peer_max_streams = value,
749                settings::ENABLE_PUSH => return Err(Error::Protocol),
750                // This limits our encoder, which deliberately has no dynamic
751                // table. The receive decoder is governed by the 256-byte limit
752                // we advertised and must retain its connection-wide state.
753                settings::HEADER_TABLE_SIZE => {}
754                // Unknown settings are ignored as required by HTTP/2.
755                _ => {}
756            }
757        }
758        Ok(())
759    }
760
761    fn close(&mut self, stream: u32) {
762        if let Some(entry) = self.streams.iter_mut().find(|s| s.id == stream) {
763            entry.open = false;
764        }
765        self.streams.retain(|s| s.open);
766    }
767
768    pub fn open_streams(&self) -> usize {
769        self.streams.len()
770    }
771}
772
773fn write_setting(out: &mut [u8], identifier: u16, value: u32) {
774    out[0..2].copy_from_slice(&identifier.to_be_bytes());
775    out[2..6].copy_from_slice(&value.to_be_bytes());
776}
777
778fn read_u32(bytes: &[u8]) -> Option<u32> {
779    Some(u32::from_be_bytes(bytes.get(..4)?.try_into().ok()?))
780}
781
782#[cfg(test)]
783mod tests {
784    use super::*;
785
786    /// Build a frame the way a server would, for feeding to `recv`.
787    fn frame_bytes(
788        kind: FrameType,
789        flags: u8,
790        stream: u32,
791        payload: &[u8],
792    ) -> heapless::Vec<u8, 512> {
793        let mut out = heapless::Vec::<u8, 512>::new();
794        out.resize_default(512).unwrap();
795        let len = frame::write_frame(kind, flags, stream, payload, &mut out).unwrap();
796        out.truncate(len);
797        out
798    }
799
800    fn open_connection() -> Connection {
801        let mut connection = Connection::new();
802        connection
803            .request("GET", "/", "h", "https", &[], b"", &mut [0; 256])
804            .unwrap();
805        connection
806    }
807
808    fn response_connection() -> Connection {
809        let mut connection = open_connection();
810        let headers = frame_bytes(FrameType::Headers, flags::END_HEADERS, 1, &[0x88]);
811        connection.recv(&headers, |_, _| {}, &mut [0; 64]).unwrap();
812        connection
813    }
814
815    #[test]
816    fn the_preface_is_exactly_what_the_rfc_requires() {
817        let mut connection = Connection::new();
818        let mut out = [0u8; 128];
819        let len = connection.start(&mut out).unwrap();
820        assert!(out[..len].starts_with(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"));
821
822        // Then SETTINGS, then the connection-level window update. Without the
823        // last one a streaming response stops dead at 65535 bytes.
824        let after = &out[frame::CLIENT_PREFACE.len()..len];
825        let settings = FrameHeader::parse(after).unwrap();
826        assert_eq!(settings.kind, FrameType::Settings);
827        assert_eq!(settings.stream, 0);
828
829        let update = &after[frame::HEADER_LEN + settings.length..];
830        let update_header = FrameHeader::parse(update).unwrap();
831        assert_eq!(update_header.kind, FrameType::WindowUpdate);
832        assert_eq!(update_header.stream, 0);
833        let increment = read_u32(&update[frame::HEADER_LEN..]).unwrap();
834        assert_eq!(increment, RECEIVE_WINDOW - DEFAULT_WINDOW);
835    }
836
837    #[test]
838    fn a_request_opens_an_odd_numbered_stream_and_ends_it() {
839        let mut connection = Connection::new();
840        let mut out = [0u8; 512];
841        let (stream, len) = connection
842            .request(
843                "POST",
844                "/machine/register",
845                "127.0.0.1:8080",
846                "http",
847                &[("content-type", "application/json")],
848                b"{}",
849                &mut out,
850            )
851            .unwrap();
852
853        // Client streams are odd, and the first is 1.
854        assert_eq!(stream, 1);
855        let headers = FrameHeader::parse(&out).unwrap();
856        assert_eq!(headers.kind, FrameType::Headers);
857        assert!(headers.has(flags::END_HEADERS));
858        assert!(!headers.has(flags::END_STREAM), "a body follows");
859
860        let data = FrameHeader::parse(&out[frame::HEADER_LEN + headers.length..]).unwrap();
861        assert_eq!(data.kind, FrameType::Data);
862        assert!(data.has(flags::END_STREAM));
863        assert_eq!(len, frame::HEADER_LEN * 2 + headers.length + data.length);
864
865        // The next request must not reuse the identifier.
866        let (second, _) = connection
867            .request("GET", "/", "h", "http", &[], b"", &mut out)
868            .unwrap();
869        assert_eq!(second, 3);
870    }
871
872    #[test]
873    fn settings_are_acknowledged_and_an_ack_is_not() {
874        let mut connection = Connection::new();
875        let mut out = [0u8; 128];
876
877        let settings = frame_bytes(FrameType::Settings, 0, 0, &[0, 5, 0, 0, 0x40, 0]);
878        let (event, written) = connection.recv(&settings, |_, _| {}, &mut out).unwrap();
879        assert_eq!(event, Event::Nothing);
880        let ack = FrameHeader::parse(&out[..written]).unwrap();
881        assert_eq!(ack.kind, FrameType::Settings);
882        assert!(ack.has(flags::ACK));
883        assert_eq!(ack.length, 0);
884
885        // Acknowledging an acknowledgement would loop forever.
886        let their_ack = frame_bytes(FrameType::Settings, flags::ACK, 0, &[]);
887        let (_, written) = connection.recv(&their_ack, |_, _| {}, &mut out).unwrap();
888        assert_eq!(written, 0);
889    }
890
891    #[test]
892    fn peer_encoder_settings_do_not_reset_receive_hpack_state() {
893        let mut connection = Connection::new();
894        connection
895            .decoder
896            .decode(&[0x40, 0x01, b'x', 0x01, b'y'], |_, _| {})
897            .unwrap();
898        assert_eq!(connection.decoder.table().len(), 1);
899
900        let settings = frame_bytes(FrameType::Settings, 0, 0, &[0, 1, 0, 0, 0x10, 0]);
901        let mut out = [0u8; 64];
902        connection.recv(&settings, |_, _| {}, &mut out).unwrap();
903        assert_eq!(connection.decoder.table().len(), 1);
904        assert_eq!(
905            connection.decoder.table().capacity(),
906            hpack::DEFAULT_TABLE_SIZE
907        );
908    }
909
910    #[test]
911    fn a_ping_is_echoed_exactly() {
912        let mut connection = Connection::new();
913        let mut out = [0u8; 128];
914        let payload = [1, 2, 3, 4, 5, 6, 7, 8];
915        let ping = frame_bytes(FrameType::Ping, 0, 0, &payload);
916        let (_, written) = connection.recv(&ping, |_, _| {}, &mut out).unwrap();
917        let header = FrameHeader::parse(&out[..written]).unwrap();
918        assert!(header.has(flags::ACK));
919        assert_eq!(&out[frame::HEADER_LEN..written], &payload);
920    }
921
922    #[test]
923    fn a_header_block_split_across_continuation_frames_is_reassembled() {
924        // Decoding either half alone would corrupt the HPACK table for the rest
925        // of the connection, so the partial frame must produce no event at all.
926        let mut connection = open_connection();
927        let mut out = [0u8; 256];
928
929        // A status followed by an ordinary field, split between frames.
930        let first = frame_bytes(FrameType::Headers, 0, 1, &[0x88]);
931        let (event, _) = connection.recv(&first, |_, _| {}, &mut out).unwrap();
932        assert_eq!(
933            event,
934            Event::Nothing,
935            "an unterminated block yields nothing"
936        );
937
938        let mut seen = heapless::Vec::<(heapless::String<32>, heapless::String<32>), 4>::new();
939        let second = frame_bytes(
940            FrameType::Continuation,
941            flags::END_HEADERS,
942            1,
943            &[0, 1, b'x', 1, b'y'],
944        );
945        let (event, _) = connection
946            .recv(
947                &second,
948                |name, value| {
949                    let mut n = heapless::String::new();
950                    let mut v = heapless::String::new();
951                    n.push_str(name).unwrap();
952                    v.push_str(value).unwrap();
953                    seen.push((n, v)).unwrap();
954                },
955                &mut out,
956            )
957            .unwrap();
958
959        assert!(matches!(event, Event::Headers { stream: 1, .. }));
960        assert_eq!(seen.len(), 2);
961        assert_eq!(seen[0].0.as_str(), ":status");
962        assert_eq!(seen[0].1.as_str(), "200");
963        assert_eq!(seen[1].0.as_str(), "x");
964    }
965
966    #[test]
967    fn data_returns_the_payload_and_eventually_a_window_update() {
968        let mut connection = response_connection();
969        let mut out = [0u8; 256];
970
971        let data = frame_bytes(FrameType::Data, 0, 1, b"hello");
972        let (event, written) = connection.recv(&data, |_, _| {}, &mut out).unwrap();
973        assert_eq!(
974            event,
975            Event::Data {
976                stream: 1,
977                data: b"hello",
978                end_stream: false
979            }
980        );
981        // Below the threshold, so no update yet: one update per small frame
982        // would be pure overhead.
983        assert_eq!(written, 0);
984    }
985
986    #[test]
987    fn a_long_response_gets_its_window_topped_up_before_it_can_stall() {
988        // The regression test for the failure mode that looks like a server
989        // fault: without this the peer stops after 65535 bytes.
990        let mut connection = response_connection();
991        let mut out = [0u8; 256];
992        let payload = [0u8; 400];
993
994        let mut updates = 0;
995        let mut delivered = 0usize;
996        // Well past both the 65535-byte default window and our own top-up
997        // threshold, so a connection that never replenishes would have stalled
998        // long before the end of this loop.
999        let frames = 2_000;
1000        for _ in 0..frames {
1001            let data = frame_bytes(FrameType::Data, 0, 1, &payload);
1002            let (event, written) = connection.recv(&data, |_, _| {}, &mut out).unwrap();
1003            if let Event::Data { data, .. } = event {
1004                delivered += data.len();
1005            }
1006            if written > 0 {
1007                updates += 1;
1008                let header = FrameHeader::parse(&out[..written]).unwrap();
1009                assert_eq!(header.kind, FrameType::WindowUpdate);
1010            }
1011        }
1012        assert_eq!(delivered, frames * 400);
1013        assert!(
1014            updates > 0,
1015            "the connection window must be replenished, or the server stops at 65535 bytes"
1016        );
1017    }
1018
1019    #[test]
1020    fn padding_and_priority_are_stripped_before_hpack_sees_the_block() {
1021        let mut connection = open_connection();
1022        let mut out = [0u8; 256];
1023        // pad length 2, priority (5 bytes), the block, then the padding.
1024        let payload = [2, 0, 0, 0, 0, 0, 0x88, 0xaa, 0xbb];
1025        let headers = frame_bytes(
1026            FrameType::Headers,
1027            flags::END_HEADERS | flags::PADDED | flags::PRIORITY,
1028            1,
1029            &payload,
1030        );
1031        let mut status = heapless::String::<8>::new();
1032        connection
1033            .recv(
1034                &headers,
1035                |name, value| {
1036                    if name == ":status" {
1037                        status.push_str(value).unwrap();
1038                    }
1039                },
1040                &mut out,
1041            )
1042            .unwrap();
1043        assert_eq!(status.as_str(), "200");
1044    }
1045
1046    #[test]
1047    fn goaway_and_reset_are_reported_rather_than_hidden() {
1048        let mut connection = open_connection();
1049        let mut out = [0u8; 128];
1050
1051        let reset = frame_bytes(FrameType::RstStream, 0, 1, &[0, 0, 0, 8]);
1052        let (event, _) = connection.recv(&reset, |_, _| {}, &mut out).unwrap();
1053        assert_eq!(event, Event::Reset { stream: 1, code: 8 });
1054
1055        let goaway = frame_bytes(FrameType::GoAway, 0, 0, &[0, 0, 0, 1, 0, 0, 0, 2]);
1056        let (event, _) = connection.recv(&goaway, |_, _| {}, &mut out).unwrap();
1057        assert_eq!(event, Event::GoAway { code: 2 });
1058    }
1059
1060    #[test]
1061    fn a_promised_push_is_a_protocol_error_because_we_refused_push() {
1062        let mut connection = Connection::new();
1063        let mut out = [0u8; 128];
1064        let promise = frame_bytes(FrameType::PushPromise, flags::END_HEADERS, 1, &[0, 0, 0, 2]);
1065        assert_eq!(
1066            connection.recv(&promise, |_, _| {}, &mut out).err(),
1067            Some(Error::Protocol)
1068        );
1069    }
1070
1071    #[test]
1072    fn an_unknown_frame_type_is_discarded_not_fatal() {
1073        let mut connection = Connection::new();
1074        let mut out = [0u8; 128];
1075        let unknown = frame_bytes(FrameType::Unknown(0x63), 0, 0, b"whatever");
1076        let (event, written) = connection.recv(&unknown, |_, _| {}, &mut out).unwrap();
1077        assert_eq!(event, Event::Nothing);
1078        assert_eq!(written, 0);
1079    }
1080    #[test]
1081    fn failed_requests_do_not_consume_stream_slots_or_identifiers() {
1082        let mut connection = Connection::new();
1083        for _ in 0..MAX_STREAMS + 1 {
1084            assert_eq!(
1085                connection.request("GET", "/", "h", "https", &[], b"", &mut []),
1086                Err(Error::BufferTooSmall)
1087            );
1088            assert_eq!(connection.open_streams(), 0);
1089        }
1090        let mut out = [0u8; 128];
1091        assert_eq!(
1092            connection
1093                .request("GET", "/", "h", "https", &[], b"", &mut out)
1094                .unwrap()
1095                .0,
1096            1
1097        );
1098    }
1099
1100    #[test]
1101    fn continuation_must_follow_headers_on_the_same_stream() {
1102        for (kind, stream) in [
1103            (FrameType::Continuation, 3),
1104            (FrameType::Ping, 0),
1105            (FrameType::Headers, 3),
1106        ] {
1107            let mut connection = open_connection();
1108            let mut out = [0u8; 128];
1109            let first = frame_bytes(FrameType::Headers, 0, 1, &[0x88]);
1110            connection.recv(&first, |_, _| {}, &mut out).unwrap();
1111            let next = frame_bytes(kind, flags::END_HEADERS, stream, &[0x82]);
1112            assert_eq!(
1113                connection.recv(&next, |_, _| {}, &mut out).err(),
1114                Some(Error::Protocol)
1115            );
1116        }
1117        let mut connection = open_connection();
1118        let orphan = frame_bytes(FrameType::Continuation, flags::END_HEADERS, 1, &[0x88]);
1119        assert_eq!(
1120            connection.recv(&orphan, |_, _| {}, &mut [0u8; 128]).err(),
1121            Some(Error::Protocol)
1122        );
1123    }
1124    #[test]
1125    fn request_body_waits_for_connection_and_stream_credit() {
1126        let mut connection = Connection::new();
1127        let mut out = [0u8; 256];
1128        let zero = frame_bytes(FrameType::Settings, 0, 0, &[0, 4, 0, 0, 0, 0]);
1129        connection.recv(&zero, |_, _| {}, &mut out).unwrap();
1130        assert_eq!(
1131            connection.request("POST", "/", "h", "https", &[], b"a", &mut out),
1132            Err(Error::FlowControl)
1133        );
1134        assert_eq!(connection.open_streams(), 0);
1135        let one = frame_bytes(FrameType::Settings, 0, 0, &[0, 4, 0, 0, 0, 1]);
1136        connection.recv(&one, |_, _| {}, &mut out).unwrap();
1137        connection.send_window = 0;
1138        assert_eq!(
1139            connection.request("POST", "/", "h", "https", &[], b"a", &mut out),
1140            Err(Error::FlowControl)
1141        );
1142        let update = frame_bytes(FrameType::WindowUpdate, 0, 0, &[0, 0, 0, 1]);
1143        connection.recv(&update, |_, _| {}, &mut out).unwrap();
1144        assert_eq!(
1145            connection
1146                .request("POST", "/", "h", "https", &[], b"a", &mut out)
1147                .unwrap()
1148                .0,
1149            1
1150        );
1151        assert_eq!(connection.send_window, 0);
1152    }
1153
1154    #[test]
1155    fn exhausted_connection_credit_blocks_later_small_requests() {
1156        let mut connection = Connection::new();
1157        let mut out = [0u8; 17_000];
1158        for _ in 0..4 {
1159            let (id, _) = connection
1160                .request("POST", "/", "h", "https", &[], &[0; 16_000], &mut out)
1161                .unwrap();
1162            connection.close(id);
1163        }
1164        assert_eq!(
1165            connection.request("POST", "/", "h", "https", &[], &[0; 2_000], &mut out),
1166            Err(Error::FlowControl)
1167        );
1168    }
1169
1170    #[test]
1171    fn failed_credit_output_can_be_retried_without_double_counting() {
1172        let mut connection = Connection::new();
1173        let mut out = [0u8; 128];
1174        let (id, _) = connection
1175            .request("GET", "/", "h", "https", &[], b"", &mut out)
1176            .unwrap();
1177        let headers = frame_bytes(FrameType::Headers, flags::END_HEADERS, id, &[0x88]);
1178        connection.recv(&headers, |_, _| {}, &mut out).unwrap();
1179        connection.consumed = RECEIVE_WINDOW / 2 - 1;
1180        connection.streams[0].consumed = RECEIVE_WINDOW / 2 - 1;
1181        let data = frame_bytes(FrameType::Data, 0, id, b"a");
1182        assert_eq!(
1183            connection.recv(&data, |_, _| {}, &mut out[..13]).err(),
1184            Some(Error::BufferTooSmall)
1185        );
1186        assert_eq!(connection.consumed, RECEIVE_WINDOW / 2 - 1);
1187        let (_, written) = connection.recv(&data, |_, _| {}, &mut out).unwrap();
1188        assert_eq!(written, 26);
1189        assert_eq!(read_u32(&out[9..13]), Some(RECEIVE_WINDOW / 2));
1190        assert_eq!(read_u32(&out[22..26]), Some(RECEIVE_WINDOW / 2));
1191    }
1192
1193    #[test]
1194    fn settings_stream_limits_and_goaway_stop_new_requests() {
1195        let mut connection = Connection::new();
1196        let mut out = [0u8; 128];
1197        let zero = frame_bytes(FrameType::Settings, 0, 0, &[0, 3, 0, 0, 0, 0]);
1198        connection.recv(&zero, |_, _| {}, &mut out).unwrap();
1199        assert_eq!(
1200            connection.request("GET", "/", "h", "https", &[], b"", &mut out),
1201            Err(Error::TooManyStreams)
1202        );
1203        let goaway = frame_bytes(FrameType::GoAway, 0, 0, &[0; 8]);
1204        connection.recv(&goaway, |_, _| {}, &mut out).unwrap();
1205        assert_eq!(
1206            connection.request("GET", "/", "h", "https", &[], b"", &mut out),
1207            Err(Error::GoAway)
1208        );
1209    }
1210
1211    #[test]
1212    fn malformed_control_frames_are_rejected() {
1213        for (kind, flags, stream, payload) in [
1214            (FrameType::Settings, flags::ACK, 0, &[0u8; 6][..]),
1215            (FrameType::Settings, 0, 1, &[][..]),
1216            (FrameType::Ping, 0, 0, &[0; 7][..]),
1217            (FrameType::RstStream, 0, 0, &[0; 4][..]),
1218            (FrameType::WindowUpdate, 0, 0, &[0; 4][..]),
1219            (FrameType::Headers, flags::END_HEADERS, 0, &[0x88][..]),
1220        ] {
1221            let bytes = frame_bytes(kind, flags, stream, payload);
1222            assert_eq!(
1223                Connection::new()
1224                    .recv(&bytes, |_, _| {}, &mut [0u8; 128])
1225                    .err(),
1226                Some(Error::Protocol)
1227            );
1228        }
1229    }
1230}