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 {}