Skip to main content

micro_h2/
lib.rs

1//! A minimal HTTP/2 client: `no_std`, allocation-free, sans-io.
2//!
3//! # Scope
4//!
5//! Enough HTTP/2 to be a client and no more. No server, no push, no priority,
6//! no trailers, and a small fixed number of concurrent streams. That is not
7//! laziness — HTTP/2's full surface is large, and every feature carried is
8//! another piece of state that has to be right. What this omits, it omits
9//! explicitly, and refuses rather than half-implements.
10//!
11//! # What is genuinely hard here
12//!
13//! Two things, and both fail silently rather than loudly:
14//!
15//! **HPACK's dynamic table is connection state built from the peer's stream.**
16//! Skipping an insertion does not lose a header; it shifts every subsequent
17//! index, so later headers decode as *different headers*. See [`hpack::dynamic`].
18//!
19//! **Flow control is not optional.** A receiver starts with a 65535-byte window
20//! per stream and per connection, and a sender that has exhausted it simply
21//! stops. A long-poll streaming a netmap will deliver exactly 65535 bytes and
22//! then hang, which looks like a server fault and is not. See [`conn`].
23//!
24//! # Sans-io
25//!
26//! [`conn::Connection`] never touches a socket. It is fed the bytes that arrived
27//! and writes the bytes to send, so the same code runs over TCP, over a ts2021
28//! Noise channel, and over a captured session in a test.
29//!
30//! ```
31//! let mut connection = micro_h2::Connection::new();
32//! let mut out = [0u8; 256];
33//! let written = connection.start(&mut out).unwrap();
34//! assert!(written > 24); // client preface plus SETTINGS and WINDOW_UPDATE
35//! ```
36
37#![no_std]
38#![forbid(unsafe_code)]
39
40pub mod conn;
41pub mod frame;
42pub mod hpack;
43
44pub use conn::{Connection, Event};
45pub use frame::{FrameHeader, FrameType};
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum Error {
49    /// More bytes are needed before this can be decoded.
50    Incomplete,
51    /// The peer sent something that is not valid HTTP/2.
52    Protocol,
53    /// A header block could not be decoded. Fatal for the connection: HPACK
54    /// state cannot be resynchronised.
55    Hpack,
56    /// A fixed buffer was too small.
57    BufferTooSmall,
58    /// A frame larger than the negotiated maximum.
59    FrameTooLarge,
60    /// The peer closed the connection with GOAWAY.
61    GoAway,
62    /// The peer reset the stream.
63    StreamReset,
64    /// More concurrent streams than this client supports.
65    TooManyStreams,
66}
67
68impl core::fmt::Display for Error {
69    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70        f.write_str(match self {
71            Self::Incomplete => "incomplete",
72            Self::Protocol => "http/2 protocol error",
73            Self::Hpack => "header decoding failed",
74            Self::BufferTooSmall => "buffer too small",
75            Self::FrameTooLarge => "frame too large",
76            Self::GoAway => "the server sent GOAWAY",
77            Self::StreamReset => "the stream was reset",
78            Self::TooManyStreams => "too many concurrent streams",
79        })
80    }
81}
82
83impl core::error::Error for Error {}