15 const http_header static_table_entries[] = {
21 {
":path",
"/index.html"},
31 {
"accept-charset",
""},
32 {
"accept-encoding",
"gzip, deflate"},
33 {
"accept-language",
""},
34 {
"accept-ranges",
""},
36 {
"access-control-allow-origin",
""},
39 {
"authorization",
""},
40 {
"cache-control",
""},
41 {
"content-disposition",
""},
42 {
"content-encoding",
""},
43 {
"content-language",
""},
44 {
"content-length",
""},
45 {
"content-location",
""},
46 {
"content-range",
""},
56 {
"if-modified-since",
""},
57 {
"if-none-match",
""},
59 {
"if-unmodified-since",
""},
60 {
"last-modified",
""},
64 {
"proxy-authenticate",
""},
65 {
"proxy-authorization",
""},
72 {
"strict-transport-security",
""},
73 {
"transfer-encoding",
""},
77 {
"www-authenticate",
""}
80 constexpr size_t static_table_size = 61;
86 if (index == 0 || index > static_table_size)
90 return static_table_entries[index];
96 for (
size_t i = 1; i <= static_table_size; ++i)
98 const auto& entry = static_table_entries[i];
99 if (entry.name == name)
101 if (value.empty() || entry.value == value)
112 : max_size_(max_size)
118 http_header header{std::string(name), std::string(value)};
119 size_t entry_size = header.
size();
122 evict_to_size(max_size_ - entry_size);
125 entries_.push_front(std::move(header));
126 current_size_ += entry_size;
131 if (index >= entries_.size())
135 return entries_[index];
139 -> std::optional<size_t>
141 for (
size_t i = 0; i < entries_.size(); ++i)
143 const auto& entry = entries_[i];
144 if (entry.name == name)
146 if (value.empty() || entry.value == value)
158 evict_to_size(max_size_);
184 while (current_size_ > target_size && !entries_.empty())
186 current_size_ -= entries_.back().size();
193 : table_(max_table_size)
198 -> std::vector<uint8_t>
200 std::vector<uint8_t> result;
202 for (
const auto& header :
headers)
206 if (static_index > 0)
209 auto encoded = encode_indexed(static_index);
210 result.insert(result.end(), encoded.begin(), encoded.end());
215 auto dynamic_index = table_.find(header.name, header.value);
216 if (dynamic_index.has_value())
220 auto encoded = encode_indexed(index);
221 result.insert(result.end(), encoded.begin(), encoded.end());
230 auto encoded = encode_literal_with_indexing(name_index, header.value);
231 result.insert(result.end(), encoded.begin(), encoded.end());
232 table_.insert(header.name, header.value);
237 auto dynamic_name_index = table_.find(header.name,
"");
238 if (dynamic_name_index.has_value())
241 auto encoded = encode_literal_with_indexing(index, header.value);
242 result.insert(result.end(), encoded.begin(), encoded.end());
243 table_.insert(header.name, header.value);
248 auto encoded = encode_literal_with_indexing(header.name, header.value);
249 result.insert(result.end(), encoded.begin(), encoded.end());
250 table_.insert(header.name, header.value);
260 table_.set_max_size(size);
269 -> std::vector<uint8_t>
271 std::vector<uint8_t> result;
272 uint8_t max_prefix = (1 << prefix_bits) - 1;
274 if (value < max_prefix)
276 result.push_back(
static_cast<uint8_t
>(value));
280 result.push_back(max_prefix);
285 result.push_back(
static_cast<uint8_t
>((value % 128) + 128));
288 result.push_back(
static_cast<uint8_t
>(value));
295 -> std::vector<uint8_t>
297 std::vector<uint8_t> result;
304 if (encoded.size() < str.size())
306 auto length_bytes = encode_integer(encoded.size(), 7);
307 length_bytes[0] |= 0x80;
308 result.insert(result.end(), length_bytes.begin(), length_bytes.end());
309 result.insert(result.end(), encoded.begin(), encoded.end());
315 auto length_bytes = encode_integer(str.size(), 7);
316 result.insert(result.end(), length_bytes.begin(), length_bytes.end());
317 result.insert(result.end(), str.begin(), str.end());
324 auto result = encode_integer(index, 7);
330 std::string_view value)
331 -> std::vector<uint8_t>
333 std::vector<uint8_t> result;
336 result.push_back(0x40);
339 auto name_bytes = encode_string(name);
340 result.insert(result.end(), name_bytes.begin(), name_bytes.end());
343 auto value_bytes = encode_string(value);
344 result.insert(result.end(), value_bytes.begin(), value_bytes.end());
350 std::string_view value)
351 -> std::vector<uint8_t>
353 std::vector<uint8_t> result;
356 auto index_bytes = encode_integer(name_index, 6);
357 index_bytes[0] |= 0x40;
358 result.insert(result.end(), index_bytes.begin(), index_bytes.end());
361 auto value_bytes = encode_string(value);
362 result.insert(result.end(), value_bytes.begin(), value_bytes.end());
368 std::string_view value)
369 -> std::vector<uint8_t>
371 std::vector<uint8_t> result;
374 result.push_back(0x00);
377 auto name_bytes = encode_string(name);
378 result.insert(result.end(), name_bytes.begin(), name_bytes.end());
381 auto value_bytes = encode_string(value);
382 result.insert(result.end(), value_bytes.begin(), value_bytes.end());
388 std::string_view value)
389 -> std::vector<uint8_t>
391 std::vector<uint8_t> result;
394 auto index_bytes = encode_integer(name_index, 4);
396 result.insert(result.end(), index_bytes.begin(), index_bytes.end());
399 auto value_bytes = encode_string(value);
400 result.insert(result.end(), value_bytes.begin(), value_bytes.end());
407 : table_(max_table_size)
414 std::vector<http_header>
headers;
415 auto remaining =
data;
417 while (!remaining.empty())
419 uint8_t first_byte = remaining[0];
421 if (first_byte & 0x80)
424 auto index_result = decode_integer(remaining, 7);
425 if (index_result.is_err())
427 return index_result.error();
429 size_t index = index_result.value();
431 auto header_result = get_indexed_header(index);
432 if (header_result.is_err())
434 return header_result.error();
437 headers.push_back(header_result.value());
439 else if (first_byte & 0x40)
442 auto name_index_result = decode_integer(remaining, 6);
443 if (name_index_result.is_err())
445 return name_index_result.error();
447 size_t name_index = name_index_result.value();
453 auto name_result = decode_string(remaining);
454 if (name_result.is_err())
456 return name_result.error();
458 name = name_result.value();
463 auto header_result = get_indexed_header(name_index);
464 if (header_result.is_err())
466 return header_result.error();
468 name = header_result.value().name;
472 auto value_result = decode_string(remaining);
473 if (value_result.is_err())
475 return value_result.error();
477 std::string value = value_result.value();
479 headers.emplace_back(name, value);
480 table_.insert(name, value);
485 uint8_t prefix_bits = (first_byte & 0x10) ? 4 : 4;
487 auto name_index_result = decode_integer(remaining, prefix_bits);
488 if (name_index_result.is_err())
490 return name_index_result.error();
492 size_t name_index = name_index_result.value();
498 auto name_result = decode_string(remaining);
499 if (name_result.is_err())
501 return name_result.error();
503 name = name_result.value();
508 auto header_result = get_indexed_header(name_index);
509 if (header_result.is_err())
511 return header_result.error();
513 name = header_result.value().name;
517 auto value_result = decode_string(remaining);
518 if (value_result.is_err())
520 return value_result.error();
523 headers.emplace_back(name, value_result.value());
532 table_.set_max_size(size);
546 return error_info(100,
"Insufficient data for integer",
"hpack");
549 uint8_t prefix_mask = (1 << prefix_bits) - 1;
550 uint64_t value =
data[0] & prefix_mask;
553 if (value < prefix_mask)
564 return error_info(101,
"Incomplete integer encoding",
"hpack");
567 uint8_t
byte =
data[0];
570 const uint64_t payload =
byte & 0x7F;
571 if (m >= 64 || payload > ((std::numeric_limits<uint64_t>::max() - value) >> m))
573 return error_info(102,
"Integer overflow",
"hpack");
575 value += payload << m;
577 if ((
byte & 0x80) == 0)
592 return error_info(103,
"Insufficient data for string",
"hpack");
597 auto length_result = decode_integer(
data, 7);
598 if (length_result.is_err())
600 return length_result.error();
603 size_t length = length_result.value();
605 if (
data.size() < length)
607 return error_info(104,
"Insufficient data for string value",
"hpack");
614 if (decoded.is_err())
616 return decoded.error();
618 result = std::move(decoded.value());
622 result.assign(
data.begin(),
data.begin() + length);
634 return error_info(105,
"Invalid index 0",
"hpack");
641 if (header.has_value())
643 return std::move(header.value());
645 return error_info(106,
"Invalid static table index",
"hpack");
650 auto header = table_.get(dynamic_index);
651 if (header.has_value())
653 return std::move(header.value());
656 return error_info(107,
"Invalid dynamic table index",
"hpack");
666 struct huffman_symbol
672 constexpr huffman_symbol kHuffmanTable[257] = {
673 {0x1ff8u, 13}, {0x7fffd8u, 23}, {0xfffffe2u, 28}, {0xfffffe3u, 28},
674 {0xfffffe4u, 28}, {0xfffffe5u, 28}, {0xfffffe6u, 28}, {0xfffffe7u, 28},
675 {0xfffffe8u, 28}, {0xffffeau, 24}, {0x3ffffffcu, 30},{0xfffffe9u, 28},
676 {0xfffffeau, 28}, {0x3ffffffdu, 30},{0xfffffebu, 28}, {0xfffffecu, 28},
677 {0xfffffedu, 28}, {0xfffffeeu, 28}, {0xfffffefu, 28}, {0xffffff0u, 28},
678 {0xffffff1u, 28}, {0xffffff2u, 28}, {0x3ffffffeu, 30},{0xffffff3u, 28},
679 {0xffffff4u, 28}, {0xffffff5u, 28}, {0xffffff6u, 28}, {0xffffff7u, 28},
680 {0xffffff8u, 28}, {0xffffff9u, 28}, {0xffffffau, 28}, {0xffffffbu, 28},
681 {0x14u, 6}, {0x3f8u, 10}, {0x3f9u, 10}, {0xffau, 12},
682 {0x1ff9u, 13}, {0x15u, 6}, {0xf8u, 8}, {0x7fau, 11},
683 {0x3fau, 10}, {0x3fbu, 10}, {0xf9u, 8}, {0x7fbu, 11},
684 {0xfau, 8}, {0x16u, 6}, {0x17u, 6}, {0x18u, 6},
685 {0x0u, 5}, {0x1u, 5}, {0x2u, 5}, {0x19u, 6},
686 {0x1au, 6}, {0x1bu, 6}, {0x1cu, 6}, {0x1du, 6},
687 {0x1eu, 6}, {0x1fu, 6}, {0x5cu, 7}, {0xfbu, 8},
688 {0x7ffcu, 15}, {0x20u, 6}, {0xffbu, 12}, {0x3fcu, 10},
689 {0x1ffau, 13}, {0x21u, 6}, {0x5du, 7}, {0x5eu, 7},
690 {0x5fu, 7}, {0x60u, 7}, {0x61u, 7}, {0x62u, 7},
691 {0x63u, 7}, {0x64u, 7}, {0x65u, 7}, {0x66u, 7},
692 {0x67u, 7}, {0x68u, 7}, {0x69u, 7}, {0x6au, 7},
693 {0x6bu, 7}, {0x6cu, 7}, {0x6du, 7}, {0x6eu, 7},
694 {0x6fu, 7}, {0x70u, 7}, {0x71u, 7}, {0x72u, 7},
695 {0xfcu, 8}, {0x73u, 7}, {0xfdu, 8}, {0x1ffbu, 13},
696 {0x7fff0u, 19}, {0x1ffcu, 13}, {0x3ffcu, 14}, {0x22u, 6},
697 {0x7ffdu, 15}, {0x3u, 5}, {0x23u, 6}, {0x4u, 5},
698 {0x24u, 6}, {0x5u, 5}, {0x25u, 6}, {0x26u, 6},
699 {0x27u, 6}, {0x6u, 5}, {0x74u, 7}, {0x75u, 7},
700 {0x28u, 6}, {0x29u, 6}, {0x2au, 6}, {0x7u, 5},
701 {0x2bu, 6}, {0x76u, 7}, {0x2cu, 6}, {0x8u, 5},
702 {0x9u, 5}, {0x2du, 6}, {0x77u, 7}, {0x78u, 7},
703 {0x79u, 7}, {0x7au, 7}, {0x7bu, 7}, {0x7ffeu, 15},
704 {0x7fcu, 11}, {0x3ffdu, 14}, {0x1ffdu, 13}, {0xffffffcu, 28},
705 {0xfffe6u, 20}, {0x3fffd2u, 22}, {0xfffe7u, 20}, {0xfffe8u, 20},
706 {0x3fffd3u, 22}, {0x3fffd4u, 22}, {0x3fffd5u, 22}, {0x7fffd9u, 23},
707 {0x3fffd6u, 22}, {0x7fffdau, 23}, {0x7fffdbu, 23}, {0x7fffdcu, 23},
708 {0x7fffddu, 23}, {0x7fffdeu, 23}, {0xffffebu, 24}, {0x7fffdfu, 23},
709 {0xffffecu, 24}, {0xffffedu, 24}, {0x3fffd7u, 22}, {0x7fffe0u, 23},
710 {0xffffeeu, 24}, {0x7fffe1u, 23}, {0x7fffe2u, 23}, {0x7fffe3u, 23},
711 {0x7fffe4u, 23}, {0x1fffdcu, 21}, {0x3fffd8u, 22}, {0x7fffe5u, 23},
712 {0x3fffd9u, 22}, {0x7fffe6u, 23}, {0x7fffe7u, 23}, {0xffffefu, 24},
713 {0x3fffdau, 22}, {0x1fffddu, 21}, {0xfffe9u, 20}, {0x3fffdbu, 22},
714 {0x3fffdcu, 22}, {0x7fffe8u, 23}, {0x7fffe9u, 23}, {0x1fffdeu, 21},
715 {0x7fffeau, 23}, {0x3fffddu, 22}, {0x3fffdeu, 22}, {0xfffff0u, 24},
716 {0x1fffdfu, 21}, {0x3fffdfu, 22}, {0x7fffebu, 23}, {0x7fffecu, 23},
717 {0x1fffe0u, 21}, {0x1fffe1u, 21}, {0x3fffe0u, 22}, {0x1fffe2u, 21},
718 {0x7fffedu, 23}, {0x3fffe1u, 22}, {0x7fffeeu, 23}, {0x7fffefu, 23},
719 {0xfffeau, 20}, {0x3fffe2u, 22}, {0x3fffe3u, 22}, {0x3fffe4u, 22},
720 {0x7ffff0u, 23}, {0x3fffe5u, 22}, {0x3fffe6u, 22}, {0x7ffff1u, 23},
721 {0x3ffffe0u, 26}, {0x3ffffe1u, 26}, {0xfffebu, 20}, {0x7fff1u, 19},
722 {0x3fffe7u, 22}, {0x7ffff2u, 23}, {0x3fffe8u, 22}, {0x1ffffecu, 25},
723 {0x3ffffe2u, 26}, {0x3ffffe3u, 26}, {0x3ffffe4u, 26}, {0x7ffffdeu, 27},
724 {0x7ffffdfu, 27}, {0x3ffffe5u, 26}, {0xfffff1u, 24}, {0x1ffffedu, 25},
725 {0x7fff2u, 19}, {0x1fffe3u, 21}, {0x3ffffe6u, 26}, {0x7ffffe0u, 27},
726 {0x7ffffe1u, 27}, {0x3ffffe7u, 26}, {0x7ffffe2u, 27}, {0xfffff2u, 24},
727 {0x1fffe4u, 21}, {0x1fffe5u, 21}, {0x3ffffe8u, 26}, {0x3ffffe9u, 26},
728 {0xffffffdu, 28}, {0x7ffffe3u, 27}, {0x7ffffe4u, 27}, {0x7ffffe5u, 27},
729 {0xfffecu, 20}, {0xfffff3u, 24}, {0xfffedu, 20}, {0x1fffe6u, 21},
730 {0x3fffe9u, 22}, {0x1fffe7u, 21}, {0x1fffe8u, 21}, {0x7ffff3u, 23},
731 {0x3fffeau, 22}, {0x3fffebu, 22}, {0x1ffffeeu, 25}, {0x1ffffefu, 25},
732 {0xfffff4u, 24}, {0xfffff5u, 24}, {0x3ffffeau, 26}, {0x7ffff4u, 23},
733 {0x3ffffebu, 26}, {0x7ffffe6u, 27}, {0x3ffffecu, 26}, {0x3ffffedu, 26},
734 {0x7ffffe7u, 27}, {0x7ffffe8u, 27}, {0x7ffffe9u, 27}, {0x7ffffeau, 27},
735 {0x7ffffebu, 27}, {0xffffffeu, 28}, {0x7ffffecu, 27}, {0x7ffffedu, 27},
736 {0x7ffffeeu, 27}, {0x7ffffefu, 27}, {0x7fffff0u, 27}, {0x3ffffeeu, 26},
740 constexpr int kEosSymbol = 256;
750 auto build_decode_tree() -> std::vector<decode_node>
752 std::vector<decode_node> nodes(1);
753 for (
int sym = 0; sym < 257; ++sym)
755 const auto& entry = kHuffmanTable[sym];
757 for (
int b =
static_cast<int>(entry.bits) - 1; b >= 0; --b)
759 int bit =
static_cast<int>((entry.code >> b) & 1u);
760 int next = nodes[cur].children[bit];
763 nodes.push_back(decode_node{});
764 next =
static_cast<int>(nodes.size()) - 1;
765 nodes[cur].children[bit] = next;
769 nodes[cur].symbol = sym;
775 auto encode(std::string_view input) -> std::vector<uint8_t>
777 std::vector<uint8_t> out;
778 out.reserve(input.size());
783 for (
unsigned char ch : input)
785 const auto& entry = kHuffmanTable[ch];
786 buffer = (buffer << entry.bits) | entry.code;
787 buffer_bits += entry.bits;
789 while (buffer_bits >= 8)
792 out.push_back(
static_cast<uint8_t
>(buffer >> buffer_bits));
795 buffer &= (1ull << buffer_bits) - 1;
801 int pad = 8 - buffer_bits;
802 out.push_back(
static_cast<uint8_t
>((buffer << pad) | ((1u << pad) - 1)));
810 static const std::vector<decode_node> tree = build_decode_tree();
814 int partial_bits = 0;
815 bool partial_all_ones =
true;
817 for (uint8_t
byte :
data)
819 for (
int i = 7; i >= 0; --i)
821 int bit = (
byte >> i) & 1;
822 node = tree[node].children[bit];
825 return error_info(108,
"Invalid Huffman code",
"hpack");
830 partial_all_ones =
false;
833 if (tree[node].
symbol >= 0)
835 if (tree[node].
symbol == kEosSymbol)
838 return error_info(108,
"EOS symbol in Huffman-encoded data",
841 out.push_back(
static_cast<char>(tree[node].
symbol));
844 partial_all_ones =
true;
851 if (node != 0 && (partial_bits > 7 || !partial_all_ones))
853 return error_info(108,
"Invalid Huffman padding",
"hpack");
862 for (
unsigned char ch : input)
864 bits += kHuffmanTable[ch].bits;
866 return (
bits + 7) / 8;
auto entry_count() const -> size_t
Get number of entries.
auto max_size() const -> size_t
Get maximum table size.
auto clear() -> void
Clear all entries.
std::deque< http_header > entries_
auto set_max_size(size_t size) -> void
Set maximum table size.
auto evict_to_size(size_t target_size) -> void
auto get(size_t index) const -> std::optional< http_header >
Get header by dynamic table index.
auto find(std::string_view name, std::string_view value="") const -> std::optional< size_t >
Find header in dynamic table.
auto insert(std::string_view name, std::string_view value) -> void
Insert header at beginning of table.
auto current_size() const -> size_t
Get current table size.
dynamic_table(size_t max_size=4096)
Construct dynamic table with max size.
auto decode(std::span< const uint8_t > data) -> Result< std::vector< http_header > >
Decode HPACK binary to headers.
auto decode_string(std::span< const uint8_t > &data) -> Result< std::string >
auto set_max_table_size(size_t size) -> void
Set maximum dynamic table size.
auto table_size() const -> size_t
Get current dynamic table size.
auto decode_integer(std::span< const uint8_t > &data, uint8_t prefix_bits) -> Result< uint64_t >
auto get_indexed_header(size_t index) const -> Result< http_header >
hpack_decoder(size_t max_table_size=4096)
Construct decoder with max table size.
auto encode_literal_with_indexing(std::string_view name, std::string_view value) -> std::vector< uint8_t >
auto set_max_table_size(size_t size) -> void
Set maximum dynamic table size.
auto encode(const std::vector< http_header > &headers) -> std::vector< uint8_t >
Encode headers to HPACK binary format.
hpack_encoder(size_t max_table_size=4096)
Construct encoder with max table size.
auto table_size() const -> size_t
Get current dynamic table size.
auto encode_indexed(size_t index) -> std::vector< uint8_t >
auto encode_literal_without_indexing(std::string_view name, std::string_view value) -> std::vector< uint8_t >
auto encode_integer(uint64_t value, uint8_t prefix_bits) -> std::vector< uint8_t >
auto encode_string(std::string_view str, bool huffman=false) -> std::vector< uint8_t >
static constexpr auto size() -> size_t
Get static table size.
static auto get(size_t index) -> std::optional< http_header >
Get static table entry by index.
static auto find(std::string_view name, std::string_view value="") -> size_t
Find index of header in static table.
Huffman coding for HPACK string compression.
auto decode(std::span< const uint8_t > data) -> Result< std::string >
Decode Huffman encoded string.
auto encode(std::string_view input) -> std::vector< uint8_t >
Encode string using Huffman coding.
auto encoded_size(std::string_view input) -> size_t
Get encoded size for string.