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, 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}
58
59/// What arrived.
60#[derive(Debug, PartialEq, Eq)]
61pub enum Event<'a> {
62    /// Nothing the caller needs to act on: a settings exchange, a ping, an
63    /// unknown frame type. Anything that needed a reply is already in `out`.
64    Nothing,
65    /// A complete header block. The fields were passed to the callback.
66    Headers { stream: u32, end_stream: bool },
67    /// Response body bytes. Borrowed from the caller's own frame buffer, so
68    /// nothing is copied.
69    Data {
70        stream: u32,
71        data: &'a [u8],
72        end_stream: bool,
73    },
74    /// The peer reset one stream. The connection is still usable.
75    Reset { stream: u32, code: u32 },
76    /// The peer is shutting the connection down.
77    GoAway { code: u32 },
78}
79
80pub struct Connection {
81    decoder: Decoder,
82    streams: heapless::Vec<Stream, MAX_STREAMS>,
83    next_stream: u32,
84    /// Reassembly buffer for a header block split across `CONTINUATION` frames.
85    header_block: heapless::Vec<u8, MAX_HEADER_BLOCK>,
86    /// Which stream the block being reassembled belongs to.
87    header_stream: u32,
88    header_end_stream: bool,
89    /// Bytes received on the connection since the last `WINDOW_UPDATE`.
90    consumed: u32,
91    /// What the peer said it will accept in one frame.
92    peer_max_frame: usize,
93    send_window: u32,
94    peer_initial_window: u32,
95    peer_max_streams: u32,
96    goaway: bool,
97}
98
99impl Default for Connection {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl Connection {
106    pub fn new() -> Self {
107        Self {
108            decoder: Decoder::new(hpack::DEFAULT_TABLE_SIZE),
109            streams: heapless::Vec::new(),
110            // Client-initiated streams are odd-numbered.
111            next_stream: 1,
112            header_block: heapless::Vec::new(),
113            header_stream: 0,
114            header_end_stream: false,
115            consumed: 0,
116            peer_max_frame: frame::DEFAULT_MAX_FRAME,
117            send_window: DEFAULT_WINDOW,
118            peer_initial_window: DEFAULT_WINDOW,
119            peer_max_streams: u32::MAX,
120            goaway: false,
121        }
122    }
123
124    /// Write the client preface, our settings, and the connection-level window
125    /// update that stops a large response stalling at 65535 bytes.
126    pub fn start(&mut self, out: &mut [u8]) -> Result<usize, Error> {
127        let preface = frame::CLIENT_PREFACE;
128        out.get_mut(..preface.len())
129            .ok_or(Error::BufferTooSmall)?
130            .copy_from_slice(preface);
131        let mut len = preface.len();
132
133        // Push is refused outright rather than handled: a client that never
134        // accepts a promise needs no state for one.
135        let mut payload = [0u8; 24];
136        write_setting(
137            &mut payload[0..6],
138            settings::HEADER_TABLE_SIZE,
139            hpack::DEFAULT_TABLE_SIZE as u32,
140        );
141        write_setting(&mut payload[6..12], settings::ENABLE_PUSH, 0);
142        write_setting(
143            &mut payload[12..18],
144            settings::INITIAL_WINDOW_SIZE,
145            RECEIVE_WINDOW,
146        );
147        write_setting(
148            &mut payload[18..24],
149            settings::MAX_CONCURRENT_STREAMS,
150            MAX_STREAMS as u32,
151        );
152        len += frame::write_frame(FrameType::Settings, 0, 0, &payload, &mut out[len..])?;
153
154        // SETTINGS_INITIAL_WINDOW_SIZE applies to streams only; the connection
155        // window can be raised only by an explicit update.
156        let increment = (RECEIVE_WINDOW - DEFAULT_WINDOW).to_be_bytes();
157        len += frame::write_frame(FrameType::WindowUpdate, 0, 0, &increment, &mut out[len..])?;
158        Ok(len)
159    }
160
161    /// Open a stream and send a request, headers and body together.
162    ///
163    /// Returns the stream identifier and how many bytes were written.
164    #[allow(clippy::too_many_arguments)]
165    pub fn request(
166        &mut self,
167        method: &str,
168        path: &str,
169        authority: &str,
170        scheme: &str,
171        extra: &[(&str, &str)],
172        body: &[u8],
173        out: &mut [u8],
174    ) -> Result<(u32, usize), Error> {
175        if self.goaway {
176            return Err(Error::GoAway);
177        }
178        let id = self.next_stream;
179        if self.streams.len() as u32 >= self.peer_max_streams {
180            return Err(Error::TooManyStreams);
181        }
182        if self.streams.is_full() || id > 0x7fff_ffff {
183            return Err(Error::TooManyStreams);
184        }
185
186        // Pseudo-headers must come first and in this order; a server is entitled
187        // to reject a block that interleaves them with ordinary fields.
188        let mut block = [0u8; MAX_HEADER_BLOCK];
189        let mut block_len = 0;
190        block_len = encode::encode_header(":method", method, &mut block, block_len)?;
191        block_len = encode::encode_header(":path", path, &mut block, block_len)?;
192        block_len = encode::encode_header(":scheme", scheme, &mut block, block_len)?;
193        block_len = encode::encode_header(":authority", authority, &mut block, block_len)?;
194        for (name, value) in extra {
195            block_len = encode::encode_header(name, value, &mut block, block_len)?;
196        }
197
198        // No CONTINUATION is emitted: a header block that does not fit one frame
199        // would need one, and these two request shapes never come close.
200        if block_len > self.peer_max_frame {
201            return Err(Error::FrameTooLarge);
202        }
203
204        if body.len() > self.peer_max_frame {
205            return Err(Error::FrameTooLarge);
206        }
207        if body.len() > self.send_window as usize || body.len() > self.peer_initial_window as usize
208        {
209            return Err(Error::FlowControl);
210        }
211        let required = frame::HEADER_LEN
212            + block_len
213            + if body.is_empty() {
214                0
215            } else {
216                frame::HEADER_LEN + body.len()
217            };
218        if out.len() < required {
219            return Err(Error::BufferTooSmall);
220        }
221
222        let end_stream = if body.is_empty() {
223            flags::END_STREAM
224        } else {
225            0
226        };
227        let mut len = frame::write_frame(
228            FrameType::Headers,
229            flags::END_HEADERS | end_stream,
230            id,
231            &block[..block_len],
232            out,
233        )?;
234
235        if !body.is_empty() {
236            len += frame::write_frame(
237                FrameType::Data,
238                flags::END_STREAM,
239                id,
240                body,
241                &mut out[len..],
242            )?;
243        }
244        self.streams
245            .push(Stream {
246                id,
247                consumed: 0,
248                open: true,
249                send_window: self.peer_initial_window as i64 - body.len() as i64,
250            })
251            .map_err(|_| Error::TooManyStreams)?;
252        self.next_stream += 2;
253        self.send_window -= body.len() as u32;
254        Ok((id, len))
255    }
256
257    /// Consume one whole frame.
258    ///
259    /// `frame` is the nine-byte header followed by exactly its payload. Replies
260    /// this connection owes — settings and ping acknowledgements, window updates
261    /// — are written to `out`, and the caller must send them.
262    pub fn recv<'a>(
263        &mut self,
264        bytes: &'a [u8],
265        mut on_header: impl FnMut(&str, &str),
266        out: &mut [u8],
267    ) -> Result<(Event<'a>, usize), Error> {
268        let header = FrameHeader::parse(bytes)?;
269        let payload = bytes
270            .get(frame::HEADER_LEN..frame::HEADER_LEN + header.length)
271            .ok_or(Error::Incomplete)?;
272        if header.length > frame::DEFAULT_MAX_FRAME {
273            return Err(Error::FrameTooLarge);
274        }
275        if self.header_stream != 0 {
276            if header.kind != FrameType::Continuation || header.stream != self.header_stream {
277                return Err(Error::Protocol);
278            }
279        } else if header.kind == FrameType::Continuation {
280            return Err(Error::Protocol);
281        }
282        match header.kind {
283            FrameType::Settings
284                if header.stream != 0 || (header.has(flags::ACK) && !payload.is_empty()) =>
285            {
286                return Err(Error::Protocol);
287            }
288            FrameType::Ping if header.stream != 0 || payload.len() != 8 => {
289                return Err(Error::Protocol);
290            }
291            FrameType::GoAway if header.stream != 0 || payload.len() < 8 => {
292                return Err(Error::Protocol);
293            }
294            FrameType::Headers | FrameType::Data | FrameType::Continuation
295                if header.stream == 0 =>
296            {
297                return Err(Error::Protocol);
298            }
299            FrameType::RstStream if header.stream == 0 || payload.len() != 4 => {
300                return Err(Error::Protocol);
301            }
302            FrameType::Priority if header.stream == 0 || payload.len() != 5 => {
303                return Err(Error::Protocol);
304            }
305            FrameType::WindowUpdate
306                if payload.len() != 4 || read_u32(payload).unwrap() & 0x7fff_ffff == 0 =>
307            {
308                return Err(Error::Protocol);
309            }
310            _ => {}
311        }
312        let mut written = 0;
313
314        match header.kind {
315            FrameType::Settings => {
316                if !header.has(flags::ACK) {
317                    self.apply_settings(payload)?;
318                    // An unacknowledged SETTINGS blocks the peer indefinitely.
319                    written = frame::write_frame(FrameType::Settings, flags::ACK, 0, &[], out)?;
320                }
321                Ok((Event::Nothing, written))
322            }
323
324            FrameType::Ping => {
325                if !header.has(flags::ACK) {
326                    // The payload must be echoed exactly.
327                    written = frame::write_frame(FrameType::Ping, flags::ACK, 0, payload, out)?;
328                }
329                Ok((Event::Nothing, written))
330            }
331
332            FrameType::Headers | FrameType::Continuation => {
333                let block = if header.kind == FrameType::Headers {
334                    self.header_block.clear();
335                    self.header_stream = header.stream;
336                    self.header_end_stream = header.has(flags::END_STREAM);
337                    let body = frame::strip_padding(payload, header.flags)?;
338                    // A priority block sits between the padding and the header
339                    // block, and feeding it to HPACK fails on a valid frame.
340                    if header.has(flags::PRIORITY) {
341                        body.get(5..).ok_or(Error::Protocol)?
342                    } else {
343                        body
344                    }
345                } else {
346                    payload
347                };
348
349                self.header_block
350                    .extend_from_slice(block)
351                    .map_err(|_| Error::BufferTooSmall)?;
352
353                if !header.has(flags::END_HEADERS) {
354                    // More CONTINUATION frames to come. Decoding a partial block
355                    // would corrupt the HPACK state for the whole connection.
356                    return Ok((Event::Nothing, 0));
357                }
358
359                let stream = self.header_stream;
360                let end_stream = self.header_end_stream;
361                self.decoder.decode(&self.header_block, &mut on_header)?;
362                self.header_stream = 0;
363                self.header_block.clear();
364                if end_stream {
365                    self.close(stream);
366                }
367                Ok((Event::Headers { stream, end_stream }, 0))
368            }
369
370            FrameType::Data => {
371                let data = frame::strip_padding(payload, header.flags)?;
372                // The window is consumed by the whole payload, padding included,
373                // not just the bytes we hand back.
374                written = self.credit(header.stream, header.length as u32, out)?;
375                let end_stream = header.has(flags::END_STREAM);
376                if end_stream {
377                    self.close(header.stream);
378                }
379                Ok((
380                    Event::Data {
381                        stream: header.stream,
382                        data,
383                        end_stream,
384                    },
385                    written,
386                ))
387            }
388
389            FrameType::RstStream => {
390                let code = read_u32(payload).ok_or(Error::Protocol)?;
391                self.close(header.stream);
392                Ok((
393                    Event::Reset {
394                        stream: header.stream,
395                        code,
396                    },
397                    0,
398                ))
399            }
400
401            FrameType::GoAway => {
402                // last-stream-id, then the error code.
403                let code = payload
404                    .get(4..8)
405                    .and_then(read_u32)
406                    .ok_or(Error::Protocol)?;
407                self.goaway = true;
408                Ok((Event::GoAway { code }, 0))
409            }
410
411            FrameType::WindowUpdate => {
412                let increment = read_u32(payload).ok_or(Error::Protocol)? & 0x7fff_ffff;
413                if header.stream == 0 {
414                    self.send_window = self
415                        .send_window
416                        .checked_add(increment)
417                        .filter(|value| *value <= 0x7fff_ffff)
418                        .ok_or(Error::Protocol)?;
419                } else if let Some(stream) = self.streams.iter_mut().find(|s| s.id == header.stream)
420                {
421                    let window = stream.send_window + increment as i64;
422                    if window > 0x7fff_ffff {
423                        return Err(Error::Protocol);
424                    }
425                    stream.send_window = window;
426                }
427                Ok((Event::Nothing, 0))
428            }
429            FrameType::Priority => Ok((Event::Nothing, 0)),
430
431            // Push was refused in our SETTINGS, so a promise is a protocol
432            // violation rather than something to ignore.
433            FrameType::PushPromise => Err(Error::Protocol),
434
435            // RFC 7540 requires unknown types to be discarded.
436            FrameType::Unknown(_) => Ok((Event::Nothing, 0)),
437        }
438    }
439
440    /// Complete a DATA frame whose payload was consumed incrementally by the
441    /// transport adapter.
442    ///
443    /// DATA does not affect HPACK state, so constrained adapters can stream a
444    /// large payload without retaining the complete frame. Flow-control credit
445    /// and stream closure still happen exactly once, after every payload byte
446    /// (including padding) has been authenticated and consumed.
447    pub fn finish_data(&mut self, header: FrameHeader, out: &mut [u8]) -> Result<usize, Error> {
448        if header.kind != FrameType::Data || header.stream == 0 || self.header_stream != 0 {
449            return Err(Error::Protocol);
450        }
451        if header.length > frame::DEFAULT_MAX_FRAME {
452            return Err(Error::FrameTooLarge);
453        }
454        let written = self.credit(header.stream, header.length as u32, out)?;
455        if header.has(flags::END_STREAM) {
456            self.close(header.stream);
457        }
458        Ok(written)
459    }
460
461    /// Return flow-control credit for `length` bytes consumed on `stream`.
462    fn credit(&mut self, stream: u32, length: u32, out: &mut [u8]) -> Result<usize, Error> {
463        if length == 0 {
464            return Ok(0);
465        }
466        const THRESHOLD: u32 = RECEIVE_WINDOW / 2;
467        let consumed = self.consumed.checked_add(length).ok_or(Error::Protocol)?;
468        let stream_consumed = self
469            .streams
470            .iter()
471            .find(|s| s.id == stream)
472            .map(|entry| entry.consumed.checked_add(length).ok_or(Error::Protocol))
473            .transpose()?;
474        let connection_update = consumed >= THRESHOLD;
475        let stream_update = stream_consumed.is_some_and(|value| value >= THRESHOLD);
476        let required =
477            (connection_update as usize + stream_update as usize) * (frame::HEADER_LEN + 4);
478        if out.len() < required {
479            return Err(Error::BufferTooSmall);
480        }
481        let mut written = 0;
482        if connection_update {
483            written +=
484                frame::write_frame(FrameType::WindowUpdate, 0, 0, &consumed.to_be_bytes(), out)?;
485        }
486        if stream_update {
487            written += frame::write_frame(
488                FrameType::WindowUpdate,
489                0,
490                stream,
491                &stream_consumed.unwrap().to_be_bytes(),
492                &mut out[written..],
493            )?;
494        }
495        self.consumed = if connection_update { 0 } else { consumed };
496        if let Some(entry) = self.streams.iter_mut().find(|s| s.id == stream) {
497            entry.consumed = if stream_update {
498                0
499            } else {
500                stream_consumed.unwrap()
501            };
502        }
503        Ok(written)
504    }
505
506    fn apply_settings(&mut self, payload: &[u8]) -> Result<(), Error> {
507        if !payload.len().is_multiple_of(6) {
508            return Err(Error::Protocol);
509        }
510        for entry in payload.as_chunks::<6>().0 {
511            let identifier = u16::from_be_bytes([entry[0], entry[1]]);
512            let value = u32::from_be_bytes([entry[2], entry[3], entry[4], entry[5]]);
513            match identifier {
514                settings::MAX_FRAME_SIZE => {
515                    if !(16_384..=16_777_215).contains(&value) {
516                        return Err(Error::Protocol);
517                    }
518                    self.peer_max_frame = value as usize;
519                }
520                settings::INITIAL_WINDOW_SIZE => {
521                    if value > 0x7fff_ffff {
522                        return Err(Error::Protocol);
523                    }
524                    let delta = value as i64 - self.peer_initial_window as i64;
525                    if self
526                        .streams
527                        .iter()
528                        .any(|stream| stream.send_window + delta > 0x7fff_ffff)
529                    {
530                        return Err(Error::Protocol);
531                    }
532                    for stream in &mut self.streams {
533                        stream.send_window += delta;
534                    }
535                    self.peer_initial_window = value;
536                }
537                settings::MAX_CONCURRENT_STREAMS => self.peer_max_streams = value,
538                settings::ENABLE_PUSH => return Err(Error::Protocol),
539                // This limits our encoder, which deliberately has no dynamic
540                // table. The receive decoder is governed by the 256-byte limit
541                // we advertised and must retain its connection-wide state.
542                settings::HEADER_TABLE_SIZE => {}
543                // Unknown settings are ignored as required by HTTP/2.
544                _ => {}
545            }
546        }
547        Ok(())
548    }
549
550    fn close(&mut self, stream: u32) {
551        if let Some(entry) = self.streams.iter_mut().find(|s| s.id == stream) {
552            entry.open = false;
553        }
554        self.streams.retain(|s| s.open);
555    }
556
557    pub fn open_streams(&self) -> usize {
558        self.streams.len()
559    }
560}
561
562fn write_setting(out: &mut [u8], identifier: u16, value: u32) {
563    out[0..2].copy_from_slice(&identifier.to_be_bytes());
564    out[2..6].copy_from_slice(&value.to_be_bytes());
565}
566
567fn read_u32(bytes: &[u8]) -> Option<u32> {
568    Some(u32::from_be_bytes(bytes.get(..4)?.try_into().ok()?))
569}
570
571#[cfg(test)]
572mod tests {
573    use super::*;
574
575    /// Build a frame the way a server would, for feeding to `recv`.
576    fn frame_bytes(
577        kind: FrameType,
578        flags: u8,
579        stream: u32,
580        payload: &[u8],
581    ) -> heapless::Vec<u8, 512> {
582        let mut out = heapless::Vec::<u8, 512>::new();
583        out.resize_default(512).unwrap();
584        let len = frame::write_frame(kind, flags, stream, payload, &mut out).unwrap();
585        out.truncate(len);
586        out
587    }
588
589    #[test]
590    fn the_preface_is_exactly_what_the_rfc_requires() {
591        let mut connection = Connection::new();
592        let mut out = [0u8; 128];
593        let len = connection.start(&mut out).unwrap();
594        assert!(out[..len].starts_with(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"));
595
596        // Then SETTINGS, then the connection-level window update. Without the
597        // last one a streaming response stops dead at 65535 bytes.
598        let after = &out[frame::CLIENT_PREFACE.len()..len];
599        let settings = FrameHeader::parse(after).unwrap();
600        assert_eq!(settings.kind, FrameType::Settings);
601        assert_eq!(settings.stream, 0);
602
603        let update = &after[frame::HEADER_LEN + settings.length..];
604        let update_header = FrameHeader::parse(update).unwrap();
605        assert_eq!(update_header.kind, FrameType::WindowUpdate);
606        assert_eq!(update_header.stream, 0);
607        let increment = read_u32(&update[frame::HEADER_LEN..]).unwrap();
608        assert_eq!(increment, RECEIVE_WINDOW - DEFAULT_WINDOW);
609    }
610
611    #[test]
612    fn a_request_opens_an_odd_numbered_stream_and_ends_it() {
613        let mut connection = Connection::new();
614        let mut out = [0u8; 512];
615        let (stream, len) = connection
616            .request(
617                "POST",
618                "/machine/register",
619                "127.0.0.1:8080",
620                "http",
621                &[("content-type", "application/json")],
622                b"{}",
623                &mut out,
624            )
625            .unwrap();
626
627        // Client streams are odd, and the first is 1.
628        assert_eq!(stream, 1);
629        let headers = FrameHeader::parse(&out).unwrap();
630        assert_eq!(headers.kind, FrameType::Headers);
631        assert!(headers.has(flags::END_HEADERS));
632        assert!(!headers.has(flags::END_STREAM), "a body follows");
633
634        let data = FrameHeader::parse(&out[frame::HEADER_LEN + headers.length..]).unwrap();
635        assert_eq!(data.kind, FrameType::Data);
636        assert!(data.has(flags::END_STREAM));
637        assert_eq!(len, frame::HEADER_LEN * 2 + headers.length + data.length);
638
639        // The next request must not reuse the identifier.
640        let (second, _) = connection
641            .request("GET", "/", "h", "http", &[], b"", &mut out)
642            .unwrap();
643        assert_eq!(second, 3);
644    }
645
646    #[test]
647    fn settings_are_acknowledged_and_an_ack_is_not() {
648        let mut connection = Connection::new();
649        let mut out = [0u8; 128];
650
651        let settings = frame_bytes(FrameType::Settings, 0, 0, &[0, 5, 0, 0, 0x40, 0]);
652        let (event, written) = connection.recv(&settings, |_, _| {}, &mut out).unwrap();
653        assert_eq!(event, Event::Nothing);
654        let ack = FrameHeader::parse(&out[..written]).unwrap();
655        assert_eq!(ack.kind, FrameType::Settings);
656        assert!(ack.has(flags::ACK));
657        assert_eq!(ack.length, 0);
658
659        // Acknowledging an acknowledgement would loop forever.
660        let their_ack = frame_bytes(FrameType::Settings, flags::ACK, 0, &[]);
661        let (_, written) = connection.recv(&their_ack, |_, _| {}, &mut out).unwrap();
662        assert_eq!(written, 0);
663    }
664
665    #[test]
666    fn peer_encoder_settings_do_not_reset_receive_hpack_state() {
667        let mut connection = Connection::new();
668        connection
669            .decoder
670            .decode(&[0x40, 0x01, b'x', 0x01, b'y'], |_, _| {})
671            .unwrap();
672        assert_eq!(connection.decoder.table().len(), 1);
673
674        let settings = frame_bytes(FrameType::Settings, 0, 0, &[0, 1, 0, 0, 0x10, 0]);
675        let mut out = [0u8; 64];
676        connection.recv(&settings, |_, _| {}, &mut out).unwrap();
677        assert_eq!(connection.decoder.table().len(), 1);
678        assert_eq!(
679            connection.decoder.table().capacity(),
680            hpack::DEFAULT_TABLE_SIZE
681        );
682    }
683
684    #[test]
685    fn a_ping_is_echoed_exactly() {
686        let mut connection = Connection::new();
687        let mut out = [0u8; 128];
688        let payload = [1, 2, 3, 4, 5, 6, 7, 8];
689        let ping = frame_bytes(FrameType::Ping, 0, 0, &payload);
690        let (_, written) = connection.recv(&ping, |_, _| {}, &mut out).unwrap();
691        let header = FrameHeader::parse(&out[..written]).unwrap();
692        assert!(header.has(flags::ACK));
693        assert_eq!(&out[frame::HEADER_LEN..written], &payload);
694    }
695
696    #[test]
697    fn a_header_block_split_across_continuation_frames_is_reassembled() {
698        // Decoding either half alone would corrupt the HPACK table for the rest
699        // of the connection, so the partial frame must produce no event at all.
700        let mut connection = Connection::new();
701        let mut out = [0u8; 256];
702
703        // ":status: 200" then ":method: GET", as two indexed fields.
704        let first = frame_bytes(FrameType::Headers, 0, 1, &[0x88]);
705        let (event, _) = connection.recv(&first, |_, _| {}, &mut out).unwrap();
706        assert_eq!(
707            event,
708            Event::Nothing,
709            "an unterminated block yields nothing"
710        );
711
712        let mut seen = heapless::Vec::<(heapless::String<32>, heapless::String<32>), 4>::new();
713        let second = frame_bytes(FrameType::Continuation, flags::END_HEADERS, 1, &[0x82]);
714        let (event, _) = connection
715            .recv(
716                &second,
717                |name, value| {
718                    let mut n = heapless::String::new();
719                    let mut v = heapless::String::new();
720                    n.push_str(name).unwrap();
721                    v.push_str(value).unwrap();
722                    seen.push((n, v)).unwrap();
723                },
724                &mut out,
725            )
726            .unwrap();
727
728        assert!(matches!(event, Event::Headers { stream: 1, .. }));
729        assert_eq!(seen.len(), 2);
730        assert_eq!(seen[0].0.as_str(), ":status");
731        assert_eq!(seen[0].1.as_str(), "200");
732        assert_eq!(seen[1].0.as_str(), ":method");
733    }
734
735    #[test]
736    fn data_returns_the_payload_and_eventually_a_window_update() {
737        let mut connection = Connection::new();
738        let mut out = [0u8; 256];
739
740        let data = frame_bytes(FrameType::Data, 0, 1, b"hello");
741        let (event, written) = connection.recv(&data, |_, _| {}, &mut out).unwrap();
742        assert_eq!(
743            event,
744            Event::Data {
745                stream: 1,
746                data: b"hello",
747                end_stream: false
748            }
749        );
750        // Below the threshold, so no update yet: one update per small frame
751        // would be pure overhead.
752        assert_eq!(written, 0);
753    }
754
755    #[test]
756    fn a_long_response_gets_its_window_topped_up_before_it_can_stall() {
757        // The regression test for the failure mode that looks like a server
758        // fault: without this the peer stops after 65535 bytes.
759        let mut connection = Connection::new();
760        let mut out = [0u8; 256];
761        let payload = [0u8; 400];
762
763        let mut updates = 0;
764        let mut delivered = 0usize;
765        // Well past both the 65535-byte default window and our own top-up
766        // threshold, so a connection that never replenishes would have stalled
767        // long before the end of this loop.
768        let frames = 2_000;
769        for _ in 0..frames {
770            let data = frame_bytes(FrameType::Data, 0, 1, &payload);
771            let (event, written) = connection.recv(&data, |_, _| {}, &mut out).unwrap();
772            if let Event::Data { data, .. } = event {
773                delivered += data.len();
774            }
775            if written > 0 {
776                updates += 1;
777                let header = FrameHeader::parse(&out[..written]).unwrap();
778                assert_eq!(header.kind, FrameType::WindowUpdate);
779            }
780        }
781        assert_eq!(delivered, frames * 400);
782        assert!(
783            updates > 0,
784            "the connection window must be replenished, or the server stops at 65535 bytes"
785        );
786    }
787
788    #[test]
789    fn padding_and_priority_are_stripped_before_hpack_sees_the_block() {
790        let mut connection = Connection::new();
791        let mut out = [0u8; 256];
792        // pad length 2, priority (5 bytes), the block, then the padding.
793        let payload = [2, 0, 0, 0, 0, 0, 0x88, 0xaa, 0xbb];
794        let headers = frame_bytes(
795            FrameType::Headers,
796            flags::END_HEADERS | flags::PADDED | flags::PRIORITY,
797            1,
798            &payload,
799        );
800        let mut status = heapless::String::<8>::new();
801        connection
802            .recv(
803                &headers,
804                |name, value| {
805                    if name == ":status" {
806                        status.push_str(value).unwrap();
807                    }
808                },
809                &mut out,
810            )
811            .unwrap();
812        assert_eq!(status.as_str(), "200");
813    }
814
815    #[test]
816    fn goaway_and_reset_are_reported_rather_than_hidden() {
817        let mut connection = Connection::new();
818        let mut out = [0u8; 128];
819
820        let reset = frame_bytes(FrameType::RstStream, 0, 1, &[0, 0, 0, 8]);
821        let (event, _) = connection.recv(&reset, |_, _| {}, &mut out).unwrap();
822        assert_eq!(event, Event::Reset { stream: 1, code: 8 });
823
824        let goaway = frame_bytes(FrameType::GoAway, 0, 0, &[0, 0, 0, 1, 0, 0, 0, 2]);
825        let (event, _) = connection.recv(&goaway, |_, _| {}, &mut out).unwrap();
826        assert_eq!(event, Event::GoAway { code: 2 });
827    }
828
829    #[test]
830    fn a_promised_push_is_a_protocol_error_because_we_refused_push() {
831        let mut connection = Connection::new();
832        let mut out = [0u8; 128];
833        let promise = frame_bytes(FrameType::PushPromise, flags::END_HEADERS, 1, &[0, 0, 0, 2]);
834        assert_eq!(
835            connection.recv(&promise, |_, _| {}, &mut out).err(),
836            Some(Error::Protocol)
837        );
838    }
839
840    #[test]
841    fn an_unknown_frame_type_is_discarded_not_fatal() {
842        let mut connection = Connection::new();
843        let mut out = [0u8; 128];
844        let unknown = frame_bytes(FrameType::Unknown(0x63), 0, 0, b"whatever");
845        let (event, written) = connection.recv(&unknown, |_, _| {}, &mut out).unwrap();
846        assert_eq!(event, Event::Nothing);
847        assert_eq!(written, 0);
848    }
849    #[test]
850    fn failed_requests_do_not_consume_stream_slots_or_identifiers() {
851        let mut connection = Connection::new();
852        for _ in 0..MAX_STREAMS + 1 {
853            assert_eq!(
854                connection.request("GET", "/", "h", "https", &[], b"", &mut []),
855                Err(Error::BufferTooSmall)
856            );
857            assert_eq!(connection.open_streams(), 0);
858        }
859        let mut out = [0u8; 128];
860        assert_eq!(
861            connection
862                .request("GET", "/", "h", "https", &[], b"", &mut out)
863                .unwrap()
864                .0,
865            1
866        );
867    }
868
869    #[test]
870    fn continuation_must_follow_headers_on_the_same_stream() {
871        for (kind, stream) in [
872            (FrameType::Continuation, 3),
873            (FrameType::Ping, 0),
874            (FrameType::Headers, 3),
875        ] {
876            let mut connection = Connection::new();
877            let mut out = [0u8; 128];
878            let first = frame_bytes(FrameType::Headers, 0, 1, &[0x88]);
879            connection.recv(&first, |_, _| {}, &mut out).unwrap();
880            let next = frame_bytes(kind, flags::END_HEADERS, stream, &[0x82]);
881            assert_eq!(
882                connection.recv(&next, |_, _| {}, &mut out).err(),
883                Some(Error::Protocol)
884            );
885        }
886        let mut connection = Connection::new();
887        let orphan = frame_bytes(FrameType::Continuation, flags::END_HEADERS, 1, &[0x88]);
888        assert_eq!(
889            connection.recv(&orphan, |_, _| {}, &mut [0u8; 128]).err(),
890            Some(Error::Protocol)
891        );
892    }
893    #[test]
894    fn request_body_waits_for_connection_and_stream_credit() {
895        let mut connection = Connection::new();
896        let mut out = [0u8; 256];
897        let zero = frame_bytes(FrameType::Settings, 0, 0, &[0, 4, 0, 0, 0, 0]);
898        connection.recv(&zero, |_, _| {}, &mut out).unwrap();
899        assert_eq!(
900            connection.request("POST", "/", "h", "https", &[], b"a", &mut out),
901            Err(Error::FlowControl)
902        );
903        assert_eq!(connection.open_streams(), 0);
904        let one = frame_bytes(FrameType::Settings, 0, 0, &[0, 4, 0, 0, 0, 1]);
905        connection.recv(&one, |_, _| {}, &mut out).unwrap();
906        connection.send_window = 0;
907        assert_eq!(
908            connection.request("POST", "/", "h", "https", &[], b"a", &mut out),
909            Err(Error::FlowControl)
910        );
911        let update = frame_bytes(FrameType::WindowUpdate, 0, 0, &[0, 0, 0, 1]);
912        connection.recv(&update, |_, _| {}, &mut out).unwrap();
913        assert_eq!(
914            connection
915                .request("POST", "/", "h", "https", &[], b"a", &mut out)
916                .unwrap()
917                .0,
918            1
919        );
920        assert_eq!(connection.send_window, 0);
921    }
922
923    #[test]
924    fn exhausted_connection_credit_blocks_later_small_requests() {
925        let mut connection = Connection::new();
926        let mut out = [0u8; 17_000];
927        for _ in 0..4 {
928            let (id, _) = connection
929                .request("POST", "/", "h", "https", &[], &[0; 16_000], &mut out)
930                .unwrap();
931            connection.close(id);
932        }
933        assert_eq!(
934            connection.request("POST", "/", "h", "https", &[], &[0; 2_000], &mut out),
935            Err(Error::FlowControl)
936        );
937    }
938
939    #[test]
940    fn failed_credit_output_can_be_retried_without_double_counting() {
941        let mut connection = Connection::new();
942        let mut out = [0u8; 128];
943        let (id, _) = connection
944            .request("GET", "/", "h", "https", &[], b"", &mut out)
945            .unwrap();
946        connection.consumed = RECEIVE_WINDOW / 2 - 1;
947        connection.streams[0].consumed = RECEIVE_WINDOW / 2 - 1;
948        let data = frame_bytes(FrameType::Data, 0, id, b"a");
949        assert_eq!(
950            connection.recv(&data, |_, _| {}, &mut out[..13]).err(),
951            Some(Error::BufferTooSmall)
952        );
953        assert_eq!(connection.consumed, RECEIVE_WINDOW / 2 - 1);
954        let (_, written) = connection.recv(&data, |_, _| {}, &mut out).unwrap();
955        assert_eq!(written, 26);
956        assert_eq!(read_u32(&out[9..13]), Some(RECEIVE_WINDOW / 2));
957        assert_eq!(read_u32(&out[22..26]), Some(RECEIVE_WINDOW / 2));
958    }
959
960    #[test]
961    fn settings_stream_limits_and_goaway_stop_new_requests() {
962        let mut connection = Connection::new();
963        let mut out = [0u8; 128];
964        let zero = frame_bytes(FrameType::Settings, 0, 0, &[0, 3, 0, 0, 0, 0]);
965        connection.recv(&zero, |_, _| {}, &mut out).unwrap();
966        assert_eq!(
967            connection.request("GET", "/", "h", "https", &[], b"", &mut out),
968            Err(Error::TooManyStreams)
969        );
970        let goaway = frame_bytes(FrameType::GoAway, 0, 0, &[0; 8]);
971        connection.recv(&goaway, |_, _| {}, &mut out).unwrap();
972        assert_eq!(
973            connection.request("GET", "/", "h", "https", &[], b"", &mut out),
974            Err(Error::GoAway)
975        );
976    }
977
978    #[test]
979    fn malformed_control_frames_are_rejected() {
980        for (kind, flags, stream, payload) in [
981            (FrameType::Settings, flags::ACK, 0, &[0u8; 6][..]),
982            (FrameType::Settings, 0, 1, &[][..]),
983            (FrameType::Ping, 0, 0, &[0; 7][..]),
984            (FrameType::RstStream, 0, 0, &[0; 4][..]),
985            (FrameType::WindowUpdate, 0, 0, &[0; 4][..]),
986            (FrameType::Headers, flags::END_HEADERS, 0, &[0x88][..]),
987        ] {
988            let bytes = frame_bytes(kind, flags, stream, payload);
989            assert_eq!(
990                Connection::new()
991                    .recv(&bytes, |_, _| {}, &mut [0u8; 128])
992                    .err(),
993                Some(Error::Protocol)
994            );
995        }
996    }
997}