Table of Contents
- src/n_http2.c
- Structures
- Functions
- Attributes
- Detailed Description
- Functions Documentation
- function n_http2_settings_parse
- function n_http2_hpack_set_max_size
- function n_http2_hpack_new
- function n_http2_hpack_headers_free
- function n_http2_hpack_free
- function n_http2_hpack_encode
- function n_http2_hpack_decode
- function n_http2_frame_parse
- function n_http2_frame_build_header
- function hpack_str_encode
- function hpack_str_decode
- function hpack_lookup
- function hpack_int_encode
- function hpack_int_decode
- function hpack_huff_lookup
- function hpack_huff_decode
- function hpack_evict
- function hpack_dyn_add
- Attributes Documentation
- variable hpack_static_value
- variable hpack_static_name
- variable hpack_huff_len
- variable hpack_huff_code
- Source code
src/n_http2.c
HTTP/2 (RFC 7540) wire framing and HPACK (RFC 7541) header compression. More...
Structures
| Name | |
|---|---|
| struct | N_H2_HPACK HPACK codec context: the dynamic table (index 0 = most recently added). |
| struct | H2_DYN_ENTRY one HPACK dynamic table entry (owned strings and its accounted size) |
Functions
| Name | |
|---|---|
| int | n_http2_settings_parse(const unsigned char * payload, size_t len, N_H2_SETTING * out, size_t max, size_t * count) parse a SETTINGS frame payload (6-byte id/value entries) into out. |
| void | n_http2_hpack_set_max_size(N_H2_HPACK * ctx, size_t max_table_size) change the dynamic table size limit (a SETTINGS_HEADER_TABLE_SIZE change), evicting entries as needed so the table fits. |
| N_H2_HPACK * | n_http2_hpack_new(size_t max_table_size) create an HPACK codec context with the given dynamic table size limit (0 uses N_H2_HPACK_DEFAULT_TABLE_SIZE). |
| void | n_http2_hpack_headers_free(N_H2_HEADER * headers, size_t n) free the owned name/value strings of a decoded header array (does not free the array itself) |
| void | n_http2_hpack_free(N_H2_HPACK ** pctx) free an HPACK codec context and NULL the caller's pointer |
| size_t | n_http2_hpack_encode(const N_H2_HEADER * headers, size_t n, unsigned char * out, size_t out_cap) encode a header list into a block using literal representations without indexing and without Huffman (a valid, interoperable, self-contained encoding that needs no dynamic-table state). |
| int | n_http2_hpack_decode(N_H2_HPACK * ctx, const unsigned char * block, size_t len, N_H2_HEADER * out, size_t max_out, size_t * count) decode an HPACK header block into out[] (owned name/value strings). |
| int | n_http2_frame_parse(const unsigned char * buf, size_t len, N_H2_FRAME * frame, size_t * consumed) parse one HTTP/2 frame from a byte buffer (stateless). |
| size_t | n_http2_frame_build_header(unsigned char * out, size_t out_cap, uint32_t length, int type, int flags, uint32_t stream_id) write a 9-byte HTTP/2 frame header into out. |
| size_t | hpack_str_encode(unsigned char * out, size_t cap, const char * s) encode a literal (non-Huffman) string with its 7-bit length prefix |
| int | hpack_str_decode(const unsigned char * buf, size_t len, size_t * pos, char ** out) decode an HPACK string (Huffman or literal); allocates *out |
| int | hpack_lookup(const N_H2_HPACK * ctx, uint64_t idx, const char ** name, const char ** value) resolve a table index to its name/value (const, not owned). |
| size_t | hpack_int_encode(unsigned char * out, size_t cap, int prefix_bits, unsigned char flags, uint64_t value) encode an HPACK integer with an N-bit prefix and high-bit flags |
| int | hpack_int_decode(const unsigned char * buf, size_t len, size_t * pos, int prefix_bits, uint64_t * out) decode an HPACK integer with an N-bit prefix; advances *pos |
| int | hpack_huff_lookup(uint32_t code, int nbits) find the Huffman symbol for an accumulated code of nbits bits, or -1 |
| int | hpack_huff_decode(const unsigned char * s, size_t len, char ** out) decode a Huffman-coded string of len bytes into a fresh C string |
| void | hpack_evict(N_H2_HPACK * ctx) drop the oldest dynamic entries until the table fits max_size |
| int | hpack_dyn_add(N_H2_HPACK * ctx, const char * name, const char * value) insert (name, value) at the front of the dynamic table, evicting as needed; both strings are copied. |
Attributes
| Name | |
|---|---|
| const char *const[61] | hpack_static_value HPACK static table values (empty where the entry has no value). |
| const char *const[61] | hpack_static_name HPACK static table names (RFC 7541 Appendix A, index 1..61). |
| const uint8_t[256] | hpack_huff_len HPACK Huffman code lengths in bits (RFC 7541 Appendix B). |
| const uint32_t[256] | hpack_huff_code HPACK Huffman codes (RFC 7541 Appendix B), index = byte value 0..255. |
Detailed Description
HTTP/2 (RFC 7540) wire framing and HPACK (RFC 7541) header compression.
Author: Castagnier Mickael
Version: 1.0
Functions Documentation
function n_http2_settings_parse
int n_http2_settings_parse(
const unsigned char * payload,
size_t len,
N_H2_SETTING * out,
size_t max,
size_t * count
)
parse a SETTINGS frame payload (6-byte id/value entries) into out.
Returns 0 on success (*count set, <= max), -1 when the payload length is not a multiple of 6.
function n_http2_hpack_set_max_size
void n_http2_hpack_set_max_size(
N_H2_HPACK * ctx,
size_t max_table_size
)
change the dynamic table size limit (a SETTINGS_HEADER_TABLE_SIZE change), evicting entries as needed so the table fits.
function n_http2_hpack_new
N_H2_HPACK * n_http2_hpack_new(
size_t max_table_size
)
create an HPACK codec context with the given dynamic table size limit (0 uses N_H2_HPACK_DEFAULT_TABLE_SIZE).
Returns NULL on allocation failure.
function n_http2_hpack_headers_free
void n_http2_hpack_headers_free(
N_H2_HEADER * headers,
size_t n
)
free the owned name/value strings of a decoded header array (does not free the array itself)
function n_http2_hpack_free
void n_http2_hpack_free(
N_H2_HPACK ** pctx
)
free an HPACK codec context and NULL the caller's pointer
function n_http2_hpack_encode
size_t n_http2_hpack_encode(
const N_H2_HEADER * headers,
size_t n,
unsigned char * out,
size_t out_cap
)
encode a header list into a block using literal representations without indexing and without Huffman (a valid, interoperable, self-contained encoding that needs no dynamic-table state).
Parameters:
- headers the header fields to encode (name/value C strings)
- n number of headers
- out destination buffer
- out_cap capacity of out
Return: bytes written, or 0 on error
Returns the number of bytes written, or 0 when out_cap is too small or an argument is invalid.
function n_http2_hpack_decode
int n_http2_hpack_decode(
N_H2_HPACK * ctx,
const unsigned char * block,
size_t len,
N_H2_HEADER * out,
size_t max_out,
size_t * count
)
decode an HPACK header block into out[] (owned name/value strings).
Parameters:
- ctx codec context (its dynamic table is read and updated)
- block the header block fragment bytes
- len length of block
- out array receiving the decoded headers
- max_out capacity of out
- count receives the number of headers decoded
Return: 0 on success, -1 on error
Handles indexed fields (static and dynamic table), the three literal forms (with/without/never indexing), Huffman-coded and literal strings, and dynamic table size updates. On success *count holds the number of headers written (<= max_out) and the dynamic table is updated. Returns 0 on success, -1 on a malformed block or when more than max_out headers are produced. Free the decoded headers with n_http2_hpack_headers_free.
function n_http2_frame_parse
int n_http2_frame_parse(
const unsigned char * buf,
size_t len,
N_H2_FRAME * frame,
size_t * consumed
)
parse one HTTP/2 frame from a byte buffer (stateless).
Returns 1 and fills frame + *consumed (header + payload) on a complete frame, 0 when more bytes are needed, -1 when the payload exceeds N_H2_MAX_FRAME_PAYLOAD.
function n_http2_frame_build_header
size_t n_http2_frame_build_header(
unsigned char * out,
size_t out_cap,
uint32_t length,
int type,
int flags,
uint32_t stream_id
)
write a 9-byte HTTP/2 frame header into out.
Returns N_H2_FRAME_HEADER_LEN, or 0 when out_cap is too small or length exceeds 24 bits.
function hpack_str_encode
static size_t hpack_str_encode(
unsigned char * out,
size_t cap,
const char * s
)
encode a literal (non-Huffman) string with its 7-bit length prefix
function hpack_str_decode
static int hpack_str_decode(
const unsigned char * buf,
size_t len,
size_t * pos,
char ** out
)
decode an HPACK string (Huffman or literal); allocates *out
function hpack_lookup
static int hpack_lookup(
const N_H2_HPACK * ctx,
uint64_t idx,
const char ** name,
const char ** value
)
resolve a table index to its name/value (const, not owned).
Returns 0 on success, -1 when the index is out of range.
function hpack_int_encode
static size_t hpack_int_encode(
unsigned char * out,
size_t cap,
int prefix_bits,
unsigned char flags,
uint64_t value
)
encode an HPACK integer with an N-bit prefix and high-bit flags
function hpack_int_decode
static int hpack_int_decode(
const unsigned char * buf,
size_t len,
size_t * pos,
int prefix_bits,
uint64_t * out
)
decode an HPACK integer with an N-bit prefix; advances *pos
function hpack_huff_lookup
static int hpack_huff_lookup(
uint32_t code,
int nbits
)
find the Huffman symbol for an accumulated code of nbits bits, or -1
function hpack_huff_decode
static int hpack_huff_decode(
const unsigned char * s,
size_t len,
char ** out
)
decode a Huffman-coded string of len bytes into a fresh C string
function hpack_evict
static void hpack_evict(
N_H2_HPACK * ctx
)
drop the oldest dynamic entries until the table fits max_size
function hpack_dyn_add
static int hpack_dyn_add(
N_H2_HPACK * ctx,
const char * name,
const char * value
)
insert (name, value) at the front of the dynamic table, evicting as needed; both strings are copied.
An entry larger than max_size empties the table without being stored (RFC 7541 4.4).
Attributes Documentation
variable hpack_static_value
static const char *const[61] hpack_static_value = {
"", "GET", "POST", "/", "/index.html", "http", "https",
"200", "204", "206", "304", "400", "404", "500",
"", "gzip, deflate", "", "", "",
"", "", "", "", "",
"", "", "", "",
"", "", "", "", "", "",
"", "", "", "", "", "", "",
"", "", "", "", "", "",
"", "", "", "", "", "",
"", "", "", "", "",
"", "", ""};
HPACK static table values (empty where the entry has no value).
variable hpack_static_name
static const char *const[61] hpack_static_name = {
":authority", ":method", ":method", ":path", ":path", ":scheme", ":scheme",
":status", ":status", ":status", ":status", ":status", ":status", ":status",
"accept-charset", "accept-encoding", "accept-language", "accept-ranges", "accept",
"access-control-allow-origin", "age", "allow", "authorization", "cache-control",
"content-disposition", "content-encoding", "content-language", "content-length",
"content-location", "content-range", "content-type", "cookie", "date", "etag",
"expect", "expires", "from", "host", "if-match", "if-modified-since", "if-none-match",
"if-range", "if-unmodified-since", "last-modified", "link", "location", "max-forwards",
"proxy-authenticate", "proxy-authorization", "range", "referer", "refresh", "retry-after",
"server", "set-cookie", "strict-transport-security", "transfer-encoding", "user-agent",
"vary", "via", "www-authenticate"};
HPACK static table names (RFC 7541 Appendix A, index 1..61).
variable hpack_huff_len
static const uint8_t[256] hpack_huff_len = {
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26};
HPACK Huffman code lengths in bits (RFC 7541 Appendix B).
variable hpack_huff_code
static const uint32_t[256] hpack_huff_code = {
0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7,
0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec,
0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3,
0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb,
0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa,
0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18,
0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc,
0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22,
0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26,
0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7,
0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78,
0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc,
0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9,
0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf,
0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3,
0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef,
0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde,
0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec,
0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef,
0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1,
0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec,
0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed,
0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2,
0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5,
0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3,
0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4,
0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea,
0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee};
HPACK Huffman codes (RFC 7541 Appendix B), index = byte value 0..255.
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nilorea/n_http2.h"
#include <stdlib.h>
#include <string.h>
int n_http2_frame_parse(const unsigned char* buf, size_t len, N_H2_FRAME* frame, size_t* consumed) {
uint32_t plen;
if (!buf || !frame || !consumed)
return -1;
if (len < N_H2_FRAME_HEADER_LEN)
return 0;
plen = ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | (uint32_t)buf[2];
if (plen > N_H2_MAX_FRAME_PAYLOAD)
return -1;
if (len < (size_t)N_H2_FRAME_HEADER_LEN + plen)
return 0;
frame->length = plen;
frame->type = buf[3];
frame->flags = buf[4];
frame->stream_id = (((uint32_t)buf[5] & 0x7F) << 24) | ((uint32_t)buf[6] << 16) | ((uint32_t)buf[7] << 8) | (uint32_t)buf[8];
frame->payload = (plen > 0) ? (buf + N_H2_FRAME_HEADER_LEN) : NULL;
*consumed = (size_t)N_H2_FRAME_HEADER_LEN + plen;
return 1;
}
size_t n_http2_frame_build_header(unsigned char* out, size_t out_cap, uint32_t length, int type, int flags, uint32_t stream_id) {
if (!out || out_cap < N_H2_FRAME_HEADER_LEN)
return 0;
if (length > 0x00FFFFFFu)
return 0;
out[0] = (unsigned char)((length >> 16) & 0xFF);
out[1] = (unsigned char)((length >> 8) & 0xFF);
out[2] = (unsigned char)(length & 0xFF);
out[3] = (unsigned char)(type & 0xFF);
out[4] = (unsigned char)(flags & 0xFF);
/* the reserved top bit of the stream id is always sent as 0 */
out[5] = (unsigned char)((stream_id >> 24) & 0x7F);
out[6] = (unsigned char)((stream_id >> 16) & 0xFF);
out[7] = (unsigned char)((stream_id >> 8) & 0xFF);
out[8] = (unsigned char)(stream_id & 0xFF);
return N_H2_FRAME_HEADER_LEN;
}
int n_http2_settings_parse(const unsigned char* payload, size_t len, N_H2_SETTING* out, size_t max, size_t* count) {
size_t i, n = 0;
if (count)
*count = 0;
if (len % 6 != 0)
return -1;
if (!payload && len > 0)
return -1;
for (i = 0; i + 6 <= len && n < max; i += 6) {
if (out) {
out[n].id = (uint16_t)(((uint16_t)payload[i] << 8) | (uint16_t)payload[i + 1]);
out[n].value = ((uint32_t)payload[i + 2] << 24) | ((uint32_t)payload[i + 3] << 16) | ((uint32_t)payload[i + 4] << 8) | (uint32_t)payload[i + 5];
}
n++;
}
if (count)
*count = n;
return 0;
}
/* ---- HPACK header compression (RFC 7541) ---- */
static const char* const hpack_static_name[61] = {
":authority", ":method", ":method", ":path", ":path", ":scheme", ":scheme",
":status", ":status", ":status", ":status", ":status", ":status", ":status",
"accept-charset", "accept-encoding", "accept-language", "accept-ranges", "accept",
"access-control-allow-origin", "age", "allow", "authorization", "cache-control",
"content-disposition", "content-encoding", "content-language", "content-length",
"content-location", "content-range", "content-type", "cookie", "date", "etag",
"expect", "expires", "from", "host", "if-match", "if-modified-since", "if-none-match",
"if-range", "if-unmodified-since", "last-modified", "link", "location", "max-forwards",
"proxy-authenticate", "proxy-authorization", "range", "referer", "refresh", "retry-after",
"server", "set-cookie", "strict-transport-security", "transfer-encoding", "user-agent",
"vary", "via", "www-authenticate"};
static const char* const hpack_static_value[61] = {
"", "GET", "POST", "/", "/index.html", "http", "https",
"200", "204", "206", "304", "400", "404", "500",
"", "gzip, deflate", "", "", "",
"", "", "", "", "",
"", "", "", "",
"", "", "", "", "", "",
"", "", "", "", "", "", "",
"", "", "", "", "", "",
"", "", "", "", "", "",
"", "", "", "", "",
"", "", ""};
static const uint32_t hpack_huff_code[256] = {
0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7,
0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec,
0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3,
0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb,
0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa,
0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18,
0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc,
0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22,
0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26,
0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7,
0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78,
0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc,
0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9,
0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf,
0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3,
0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef,
0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde,
0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec,
0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef,
0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1,
0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec,
0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed,
0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2,
0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5,
0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3,
0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4,
0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea,
0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee};
static const uint8_t hpack_huff_len[256] = {
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26};
typedef struct H2_DYN_ENTRY {
char* name;
char* value;
size_t size; /* name_len + value_len + 32 (RFC 7541 4.1) */
} H2_DYN_ENTRY;
struct N_H2_HPACK {
H2_DYN_ENTRY* ents; /* dynamic array, position 0 = newest = table index 62 */
size_t nents;
size_t cap;
size_t cur_size; /* sum of entry sizes */
size_t max_size; /* current effective size cap (<= limit) */
size_t limit; /* the advertised SETTINGS_HEADER_TABLE_SIZE upper bound */
};
static int hpack_huff_lookup(uint32_t code, int nbits) {
int i;
for (i = 0; i < 256; i++) {
if ((int)hpack_huff_len[i] == nbits && hpack_huff_code[i] == code)
return i;
}
return -1;
}
static int hpack_huff_decode(const unsigned char* s, size_t len, char** out) {
size_t cap = len * 2 + 16;
char* r = malloc(cap);
size_t rlen = 0;
uint32_t code = 0;
int nbits = 0;
size_t i;
if (!r)
return -1;
for (i = 0; i < len; i++) {
int bit;
for (bit = 7; bit >= 0; bit--) {
int sym;
code = (code << 1) | (uint32_t)((s[i] >> bit) & 1);
nbits++;
if (nbits > 30) {
free(r);
return -1;
}
sym = hpack_huff_lookup(code, nbits);
if (sym >= 0) {
if (rlen + 1 >= cap) {
char* nr;
cap = cap * 2 + 16;
nr = realloc(r, cap);
if (!nr) {
free(r);
return -1;
}
r = nr;
}
r[rlen++] = (char)sym;
code = 0;
nbits = 0;
}
}
}
/* the trailing padding must be fewer than 8 bits and all 1s (an EOS prefix) */
if (nbits > 7) {
free(r);
return -1;
}
if (nbits > 0) {
uint32_t mask = (uint32_t)((1u << nbits) - 1u);
if ((code & mask) != mask) {
free(r);
return -1;
}
}
r[rlen] = '\0';
*out = r;
return 0;
}
static int hpack_int_decode(const unsigned char* buf, size_t len, size_t* pos, int prefix_bits, uint64_t* out) {
uint64_t max_prefix = (uint64_t)((1u << prefix_bits) - 1u);
uint64_t val;
unsigned m = 0;
if (*pos >= len)
return -1;
val = (uint64_t)buf[*pos] & max_prefix;
(*pos)++;
if (val < max_prefix) {
*out = val;
return 0;
}
for (;;) {
unsigned char b;
if (*pos >= len)
return -1;
b = buf[*pos];
(*pos)++;
val += (uint64_t)(b & 0x7f) << m;
m += 7;
if (m > 63)
return -1; /* overflow guard */
if (!(b & 0x80))
break;
}
*out = val;
return 0;
}
static int hpack_str_decode(const unsigned char* buf, size_t len, size_t* pos, char** out) {
int huff;
uint64_t slen;
const unsigned char* s;
if (*pos >= len)
return -1;
huff = (buf[*pos] & 0x80) != 0;
if (hpack_int_decode(buf, len, pos, 7, &slen) != 0)
return -1;
if (slen > len || *pos + (size_t)slen > len)
return -1;
s = buf + *pos;
*pos += (size_t)slen;
if (huff)
return hpack_huff_decode(s, (size_t)slen, out);
{
char* r = malloc((size_t)slen + 1);
if (!r)
return -1;
if (slen > 0)
memcpy(r, s, (size_t)slen);
r[slen] = '\0';
*out = r;
return 0;
}
}
static size_t hpack_int_encode(unsigned char* out, size_t cap, int prefix_bits, unsigned char flags, uint64_t value) {
unsigned char max_prefix = (unsigned char)((1u << prefix_bits) - 1u);
size_t n = 0;
if (value < max_prefix) {
if (cap < 1)
return 0;
out[0] = (unsigned char)(flags | (unsigned char)value);
return 1;
}
if (cap < 1)
return 0;
out[n++] = (unsigned char)(flags | max_prefix);
value -= max_prefix;
while (value >= 128) {
if (n >= cap)
return 0;
out[n++] = (unsigned char)((value & 0x7f) | 0x80);
value >>= 7;
}
if (n >= cap)
return 0;
out[n++] = (unsigned char)value;
return n;
}
static size_t hpack_str_encode(unsigned char* out, size_t cap, const char* s) {
size_t slen = s ? strlen(s) : 0;
size_t n = hpack_int_encode(out, cap, 7, 0x00, (uint64_t)slen);
if (n == 0)
return 0;
if (n + slen > cap)
return 0;
if (slen > 0)
memcpy(out + n, s, slen);
return n + slen;
}
N_H2_HPACK* n_http2_hpack_new(size_t max_table_size) {
N_H2_HPACK* ctx = calloc(1, sizeof(*ctx));
if (!ctx)
return NULL;
if (max_table_size == 0)
max_table_size = N_H2_HPACK_DEFAULT_TABLE_SIZE;
ctx->max_size = max_table_size;
ctx->limit = max_table_size;
return ctx;
}
static void hpack_evict(N_H2_HPACK* ctx) {
while (ctx->cur_size > ctx->max_size && ctx->nents > 0) {
H2_DYN_ENTRY* e = &ctx->ents[ctx->nents - 1];
ctx->cur_size -= e->size;
free(e->name);
free(e->value);
ctx->nents--;
}
}
void n_http2_hpack_free(N_H2_HPACK** pctx) {
N_H2_HPACK* ctx;
if (!pctx || !*pctx)
return;
ctx = *pctx;
while (ctx->nents > 0) {
ctx->nents--;
free(ctx->ents[ctx->nents].name);
free(ctx->ents[ctx->nents].value);
}
free(ctx->ents);
free(ctx);
*pctx = NULL;
}
void n_http2_hpack_set_max_size(N_H2_HPACK* ctx, size_t max_table_size) {
if (!ctx)
return;
ctx->limit = max_table_size;
if (ctx->max_size > max_table_size)
ctx->max_size = max_table_size;
hpack_evict(ctx);
}
static int hpack_dyn_add(N_H2_HPACK* ctx, const char* name, const char* value) {
size_t esize = strlen(name) + strlen(value) + 32;
H2_DYN_ENTRY e;
if (esize > ctx->max_size) {
/* evict everything; the entry is not added */
while (ctx->nents > 0) {
ctx->nents--;
free(ctx->ents[ctx->nents].name);
free(ctx->ents[ctx->nents].value);
}
ctx->cur_size = 0;
return 0;
}
ctx->cur_size += esize;
hpack_evict(ctx);
if (ctx->nents + 1 > ctx->cap) {
size_t nc = ctx->cap ? ctx->cap * 2 : 8;
H2_DYN_ENTRY* ne = realloc(ctx->ents, nc * sizeof(*ne));
if (!ne) {
ctx->cur_size -= esize;
return -1;
}
ctx->ents = ne;
ctx->cap = nc;
}
e.name = strdup(name);
e.value = strdup(value);
e.size = esize;
if (!e.name || !e.value) {
free(e.name);
free(e.value);
ctx->cur_size -= esize;
return -1;
}
memmove(&ctx->ents[1], &ctx->ents[0], ctx->nents * sizeof(ctx->ents[0]));
ctx->ents[0] = e;
ctx->nents++;
return 0;
}
static int hpack_lookup(const N_H2_HPACK* ctx, uint64_t idx, const char** name, const char** value) {
if (idx == 0)
return -1;
if (idx <= 61) {
*name = hpack_static_name[idx - 1];
*value = hpack_static_value[idx - 1];
return 0;
}
idx -= 62;
if (idx >= ctx->nents)
return -1;
*name = ctx->ents[idx].name;
*value = ctx->ents[idx].value;
return 0;
}
void n_http2_hpack_headers_free(N_H2_HEADER* headers, size_t n) {
size_t i;
if (!headers)
return;
for (i = 0; i < n; i++) {
free(headers[i].name);
free(headers[i].value);
headers[i].name = NULL;
headers[i].value = NULL;
}
}
int n_http2_hpack_decode(N_H2_HPACK* ctx, const unsigned char* block, size_t len, N_H2_HEADER* out, size_t max_out, size_t* count) {
size_t pos = 0, n = 0;
if (count)
*count = 0;
if (!ctx || !out || (!block && len > 0))
return -1;
while (pos < len) {
unsigned char b = block[pos];
char* name = NULL;
char* value = NULL;
if (b & 0x80) {
/* indexed header field (6.1) */
uint64_t idx;
const char *cn, *cv;
if (hpack_int_decode(block, len, &pos, 7, &idx) != 0)
goto fail;
if (hpack_lookup(ctx, idx, &cn, &cv) != 0)
goto fail;
name = strdup(cn);
value = strdup(cv);
} else if (b & 0x40) {
/* literal with incremental indexing (6.2.1) */
uint64_t idx;
if (hpack_int_decode(block, len, &pos, 6, &idx) != 0)
goto fail;
if (idx != 0) {
const char *cn, *cv;
if (hpack_lookup(ctx, idx, &cn, &cv) != 0)
goto fail;
name = strdup(cn);
} else if (hpack_str_decode(block, len, &pos, &name) != 0) {
goto fail;
}
if (hpack_str_decode(block, len, &pos, &value) != 0)
goto fail;
if (name && value)
hpack_dyn_add(ctx, name, value);
} else if (b & 0x20) {
/* dynamic table size update (6.3) */
uint64_t newsize;
if (hpack_int_decode(block, len, &pos, 5, &newsize) != 0)
goto fail;
if (newsize > ctx->limit)
goto fail;
ctx->max_size = (size_t)newsize;
hpack_evict(ctx);
continue; /* no header field produced */
} else {
/* literal without indexing (6.2.2) or never indexed (6.2.3), 4-bit prefix */
uint64_t idx;
if (hpack_int_decode(block, len, &pos, 4, &idx) != 0)
goto fail;
if (idx != 0) {
const char *cn, *cv;
if (hpack_lookup(ctx, idx, &cn, &cv) != 0)
goto fail;
name = strdup(cn);
} else if (hpack_str_decode(block, len, &pos, &name) != 0) {
goto fail;
}
if (hpack_str_decode(block, len, &pos, &value) != 0)
goto fail;
}
if (!name || !value)
goto fail;
if (n >= max_out)
goto fail; /* fail: frees the current name/value */
out[n].name = name;
out[n].value = value;
n++;
continue;
fail:
free(name);
free(value);
n_http2_hpack_headers_free(out, n);
return -1;
}
if (count)
*count = n;
return 0;
}
size_t n_http2_hpack_encode(const N_H2_HEADER* headers, size_t n, unsigned char* out, size_t out_cap) {
size_t pos = 0, i;
if (!headers || !out)
return 0;
for (i = 0; i < n; i++) {
size_t w;
if (!headers[i].name)
return 0;
if (pos + 1 > out_cap)
return 0;
/* literal without indexing, new name: first byte 0x00 (4-bit index = 0) */
out[pos++] = 0x00;
w = hpack_str_encode(out + pos, out_cap - pos, headers[i].name);
if (w == 0)
return 0;
pos += w;
w = hpack_str_encode(out + pos, out_cap - pos, headers[i].value ? headers[i].value : "");
if (w == 0)
return 0;
pos += w;
}
return pos;
}
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB