HTTP/2 framing, HPACK and connection state, I/O-free
1(*---------------------------------------------------------------------------
2 Copyright (c) 2019 Antonio Nuno Monteiro. Copyright (c) 2025 Anil Madhavapeddy
3 <anil@recoil.org>.
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12
13 2. Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation
15 and/or other materials provided with the distribution.
16
17 3. Neither the name of the copyright holder nor the names of its contributors
18 may be used to endorse or promote products derived from this software without
19 specific prior written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 SPDX-License-Identifier: BSD-3-Clause
32 ---------------------------------------------------------------------------*)
33
34(** HPACK Header Compression per RFC 7541.
35
36 This module implements header compression for HTTP/2 as specified in
37 {{:https://datatracker.ietf.org/doc/html/rfc7541}RFC 7541}.
38
39 {2 Overview}
40
41 HPACK compresses HTTP headers by:
42 - Using static and dynamic tables to index frequently used headers
43 - Huffman encoding string literals
44 - Variable-length integer encoding
45
46 {2 Usage}
47
48 Create encoder/decoder contexts for each HTTP/2 connection:
49 {[
50 open H2
51
52 let demo () =
53 let encoder = Hpack.Encoder.v 4096 in
54 let decoder = Hpack.Decoder.v 4096 in
55 let headers =
56 [
57 { Hpack.name = ":method"; value = "GET"; sensitive = false };
58 { Hpack.name = ":path"; value = "/"; sensitive = false };
59 ]
60 in
61 let buf = Bytes.create 1024 in
62 let len = Hpack.Encoder.encode_headers encoder buf headers in
63 let block = Bytes.sub buf 0 len in
64 match Hpack.Decoder.decode decoder block with
65 | Ok _hs -> ()
66 | Error Hpack.Decoding_error -> ()
67 ]} *)
68
69(** {1 Types} *)
70
71type header = {
72 name : string;
73 (** Header field name (lowercase, or pseudo-header with colon prefix). *)
74 value : string; (** Header field value. *)
75 sensitive : bool;
76 (** If true, this header should never be indexed. Used for sensitive
77 values like cookies and authorization. *)
78}
79(** A header field with optional sensitivity flag. *)
80
81type error = Decoding_error (** HPACK decoding error. *)
82
83val pp_error : Format.formatter -> error -> unit
84(** Pretty printer for errors. *)
85
86(** {1 Decoder}
87
88 HPACK decoder with dynamic table context. *)
89module Decoder : sig
90 type t
91 (** Decoder context maintaining dynamic table state. *)
92
93 val v : ?max_header_list_size:int -> int -> t
94 (** [v ?max_header_list_size max_capacity] creates a decoder with the given
95 maximum dynamic table capacity in bytes (default 4096).
96
97 [max_header_list_size] bounds the total decoded header-list size: the sum
98 over decoded fields of [String.length name + String.length value + 32],
99 the SETTINGS_MAX_HEADER_LIST_SIZE metric (RFC 9113 Section 6.5.2).
100 {!decode} returns [Error Decoding_error] as soon as a block exceeds it, so
101 a compressed block that expands without limit -- an HPACK decompression
102 bomb (RFC 7541 Section 7.2) -- is rejected in bounded work rather than
103 allocated. Defaults to no limit. *)
104
105 val set_capacity : t -> int -> (unit, error) result
106 (** [set_capacity t capacity] updates the dynamic table capacity. Returns
107 [Error Decoding_error] if capacity exceeds max_capacity. *)
108
109 val decode : t -> bytes -> (header list, error) result
110 (** [decode t buf] decodes a header block fragment. Returns headers in
111 transmission order.
112
113 Per RFC 7541, decoding updates the dynamic table state, so the same
114 decoder must be used for all header blocks on a connection. *)
115end
116
117(** {1 Encoder}
118
119 HPACK encoder with dynamic table context. *)
120module Encoder : sig
121 type t
122 (** Encoder context maintaining dynamic table state. *)
123
124 val v : int -> t
125 (** [v capacity] creates an encoder with the given dynamic table capacity in
126 bytes (default 4096). *)
127
128 val set_capacity : t -> int -> unit
129 (** [set_capacity t capacity] updates the dynamic table capacity. This may
130 evict entries if the new capacity is smaller. *)
131
132 val encode_headers : t -> bytes -> header list -> int
133 (** [encode_headers t buf headers] encodes headers into [buf]. Returns the
134 number of bytes written.
135
136 The buffer must be large enough for the encoded output. A safe estimate is
137 16KB for typical header blocks.
138
139 Per RFC 7541, encoding updates the dynamic table state, so the same
140 encoder must be used for all header blocks on a connection. *)
141end
142
143(** {1 Huffman Encoding}
144
145 Low-level Huffman encoding/decoding per RFC 7541 Appendix B. *)
146module Huffman : sig
147 val encoded_length : string -> int
148 (** [encoded_length s] returns the Huffman-encoded length in bytes. *)
149
150 val encode : bytes -> int -> string -> int
151 (** [encode buf off s] encodes [s] using Huffman coding. Returns the number of
152 bytes written. *)
153
154 val decode : string -> (string, error) result
155 (** [decode s] decodes a Huffman-encoded string. *)
156end
157
158(** {1 Constants} *)
159
160val default_table_size : int
161(** Default dynamic table size: 4096 bytes. *)