hpack_encoder.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/http/private/hpack.h>
  6. #define HPACK_LOGF(level, encoder, text, ...) \
  7. AWS_LOGF_##level(AWS_LS_HTTP_ENCODER, "id=%p [HPACK]: " text, (encoder)->log_id, __VA_ARGS__)
  8. #define HPACK_LOG(level, encoder, text) HPACK_LOGF(level, encoder, "%s", text)
  9. struct aws_huffman_symbol_coder *hpack_get_coder(void);
  10. void aws_hpack_encoder_init(struct aws_hpack_encoder *encoder, struct aws_allocator *allocator, const void *log_id) {
  11. AWS_ZERO_STRUCT(*encoder);
  12. encoder->log_id = log_id;
  13. aws_huffman_encoder_init(&encoder->huffman_encoder, hpack_get_coder());
  14. aws_hpack_context_init(&encoder->context, allocator, AWS_LS_HTTP_ENCODER, log_id);
  15. encoder->dynamic_table_size_update.pending = false;
  16. encoder->dynamic_table_size_update.latest_value = SIZE_MAX;
  17. encoder->dynamic_table_size_update.smallest_value = SIZE_MAX;
  18. }
  19. void aws_hpack_encoder_clean_up(struct aws_hpack_encoder *encoder) {
  20. aws_hpack_context_clean_up(&encoder->context);
  21. AWS_ZERO_STRUCT(*encoder);
  22. }
  23. void aws_hpack_encoder_set_huffman_mode(struct aws_hpack_encoder *encoder, enum aws_hpack_huffman_mode mode) {
  24. encoder->huffman_mode = mode;
  25. }
  26. void aws_hpack_encoder_update_max_table_size(struct aws_hpack_encoder *encoder, uint32_t new_max_size) {
  27. if (!encoder->dynamic_table_size_update.pending) {
  28. encoder->dynamic_table_size_update.pending = true;
  29. }
  30. encoder->dynamic_table_size_update.smallest_value =
  31. aws_min_size(new_max_size, encoder->dynamic_table_size_update.smallest_value);
  32. /* TODO: don't necessarily go as high as possible. The peer said the encoder's
  33. * dynamic table COULD get this big, but it's not required to.
  34. * It's probably not a good idea to let the peer decide how much memory we allocate.
  35. * Not sure how to cap it though... Use a hardcoded number?
  36. * Match whatever SETTINGS_HEADER_TABLE_SIZE this side sends? */
  37. encoder->dynamic_table_size_update.latest_value = new_max_size;
  38. }
  39. /* Return a byte with the N right-most bits masked.
  40. * Ex: 2 -> 00000011 */
  41. static uint8_t s_masked_right_bits_u8(uint8_t num_masked_bits) {
  42. AWS_ASSERT(num_masked_bits <= 8);
  43. const uint8_t cut_bits = 8 - num_masked_bits;
  44. return UINT8_MAX >> cut_bits;
  45. }
  46. /* If buffer isn't big enough, grow it intelligently */
  47. static int s_ensure_space(struct aws_byte_buf *output, size_t required_space) {
  48. size_t available_space = output->capacity - output->len;
  49. if (required_space <= available_space) {
  50. return AWS_OP_SUCCESS;
  51. }
  52. /* Capacity must grow to at least this size */
  53. size_t required_capacity;
  54. if (aws_add_size_checked(output->len, required_space, &required_capacity)) {
  55. return AWS_OP_ERR;
  56. }
  57. /* Prefer to double capacity, but if that's not enough grow to exactly required_capacity */
  58. size_t double_capacity = aws_add_size_saturating(output->capacity, output->capacity);
  59. size_t reserve = aws_max_size(required_capacity, double_capacity);
  60. return aws_byte_buf_reserve(output, reserve);
  61. }
  62. int aws_hpack_encode_integer(
  63. uint64_t integer,
  64. uint8_t starting_bits,
  65. uint8_t prefix_size,
  66. struct aws_byte_buf *output) {
  67. AWS_ASSERT(prefix_size <= 8);
  68. const uint8_t prefix_mask = s_masked_right_bits_u8(prefix_size);
  69. AWS_ASSERT((starting_bits & prefix_mask) == 0);
  70. const size_t original_len = output->len;
  71. if (integer < prefix_mask) {
  72. /* If the integer fits inside the specified number of bits but won't be all 1's, just write it */
  73. /* Just write out the bits we care about */
  74. uint8_t first_byte = starting_bits | (uint8_t)integer;
  75. if (aws_byte_buf_append_byte_dynamic(output, first_byte)) {
  76. goto error;
  77. }
  78. } else {
  79. /* Set all of the bits in the first octet to 1 */
  80. uint8_t first_byte = starting_bits | prefix_mask;
  81. if (aws_byte_buf_append_byte_dynamic(output, first_byte)) {
  82. goto error;
  83. }
  84. integer -= prefix_mask;
  85. const uint64_t hi_57bit_mask = UINT64_MAX - (UINT8_MAX >> 1);
  86. do {
  87. /* Take top 7 bits from the integer */
  88. uint8_t this_octet = integer % 128;
  89. if (integer & hi_57bit_mask) {
  90. /* If there's more after this octet, set the hi bit */
  91. this_octet += 128;
  92. }
  93. if (aws_byte_buf_append_byte_dynamic(output, this_octet)) {
  94. goto error;
  95. }
  96. /* Remove the written bits */
  97. integer >>= 7;
  98. } while (integer);
  99. }
  100. return AWS_OP_SUCCESS;
  101. error:
  102. output->len = original_len;
  103. return AWS_OP_ERR;
  104. }
  105. int aws_hpack_encode_string(
  106. struct aws_hpack_encoder *encoder,
  107. struct aws_byte_cursor to_encode,
  108. struct aws_byte_buf *output) {
  109. AWS_PRECONDITION(encoder);
  110. AWS_PRECONDITION(aws_byte_cursor_is_valid(&to_encode));
  111. AWS_PRECONDITION(output);
  112. const size_t original_len = output->len;
  113. /* Determine length of encoded string (and whether or not to use huffman) */
  114. uint8_t use_huffman;
  115. size_t str_length;
  116. switch (encoder->huffman_mode) {
  117. case AWS_HPACK_HUFFMAN_NEVER:
  118. use_huffman = 0;
  119. str_length = to_encode.len;
  120. break;
  121. case AWS_HPACK_HUFFMAN_ALWAYS:
  122. use_huffman = 1;
  123. str_length = aws_huffman_get_encoded_length(&encoder->huffman_encoder, to_encode);
  124. break;
  125. case AWS_HPACK_HUFFMAN_SMALLEST:
  126. str_length = aws_huffman_get_encoded_length(&encoder->huffman_encoder, to_encode);
  127. if (str_length < to_encode.len) {
  128. use_huffman = 1;
  129. } else {
  130. str_length = to_encode.len;
  131. use_huffman = 0;
  132. }
  133. break;
  134. default:
  135. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  136. goto error;
  137. }
  138. /*
  139. * String literals are encoded like so (RFC-7541 5.2):
  140. * H is whether or not data is huffman-encoded.
  141. *
  142. * 0 1 2 3 4 5 6 7
  143. * +---+---+---+---+---+---+---+---+
  144. * | H | String Length (7+) |
  145. * +---+---------------------------+
  146. * | String Data (Length octets) |
  147. * +-------------------------------+
  148. */
  149. /* Encode string length */
  150. uint8_t starting_bits = use_huffman << 7;
  151. if (aws_hpack_encode_integer(str_length, starting_bits, 7, output)) {
  152. HPACK_LOGF(ERROR, encoder, "Error encoding HPACK integer: %s", aws_error_name(aws_last_error()));
  153. goto error;
  154. }
  155. /* Encode string data */
  156. if (str_length > 0) {
  157. if (use_huffman) {
  158. /* Huffman encoder doesn't grow buffer, so we ensure it's big enough here */
  159. if (s_ensure_space(output, str_length)) {
  160. goto error;
  161. }
  162. if (aws_huffman_encode(&encoder->huffman_encoder, &to_encode, output)) {
  163. HPACK_LOGF(ERROR, encoder, "Error from Huffman encoder: %s", aws_error_name(aws_last_error()));
  164. goto error;
  165. }
  166. } else {
  167. if (aws_byte_buf_append_dynamic(output, &to_encode)) {
  168. goto error;
  169. }
  170. }
  171. }
  172. return AWS_OP_SUCCESS;
  173. error:
  174. output->len = original_len;
  175. aws_huffman_encoder_reset(&encoder->huffman_encoder);
  176. return AWS_OP_ERR;
  177. }
  178. /* All types that HPACK might encode/decode (RFC-7541 6 - Binary Format) */
  179. enum aws_hpack_entry_type {
  180. AWS_HPACK_ENTRY_INDEXED_HEADER_FIELD, /* RFC-7541 6.1 */
  181. AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITH_INCREMENTAL_INDEXING, /* RFC-7541 6.2.1 */
  182. AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITHOUT_INDEXING, /* RFC-7541 6.2.2 */
  183. AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_NEVER_INDEXED, /* RFC-7541 6.2.3 */
  184. AWS_HPACK_ENTRY_DYNAMIC_TABLE_RESIZE, /* RFC-7541 6.3 */
  185. AWS_HPACK_ENTRY_TYPE_COUNT,
  186. };
  187. /**
  188. * First byte each entry type looks like this (RFC-7541 6):
  189. * The "xxxxx" part is the "N-bit prefix" of the entry's first encoded integer.
  190. *
  191. * 1xxxxxxx: Indexed Header Field Representation
  192. * 01xxxxxx: Literal Header Field with Incremental Indexing
  193. * 001xxxxx: Dynamic Table Size Update
  194. * 0001xxxx: Literal Header Field Never Indexed
  195. * 0000xxxx: Literal Header Field without Indexing
  196. */
  197. static const uint8_t s_hpack_entry_starting_bit_pattern[AWS_HPACK_ENTRY_TYPE_COUNT] = {
  198. [AWS_HPACK_ENTRY_INDEXED_HEADER_FIELD] = 1 << 7,
  199. [AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITH_INCREMENTAL_INDEXING] = 1 << 6,
  200. [AWS_HPACK_ENTRY_DYNAMIC_TABLE_RESIZE] = 1 << 5,
  201. [AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_NEVER_INDEXED] = 1 << 4,
  202. [AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITHOUT_INDEXING] = 0 << 4,
  203. };
  204. static const uint8_t s_hpack_entry_num_prefix_bits[AWS_HPACK_ENTRY_TYPE_COUNT] = {
  205. [AWS_HPACK_ENTRY_INDEXED_HEADER_FIELD] = 7,
  206. [AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITH_INCREMENTAL_INDEXING] = 6,
  207. [AWS_HPACK_ENTRY_DYNAMIC_TABLE_RESIZE] = 5,
  208. [AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_NEVER_INDEXED] = 4,
  209. [AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITHOUT_INDEXING] = 4,
  210. };
  211. static int s_convert_http_compression_to_literal_entry_type(
  212. enum aws_http_header_compression compression,
  213. enum aws_hpack_entry_type *out_entry_type) {
  214. switch (compression) {
  215. case AWS_HTTP_HEADER_COMPRESSION_USE_CACHE:
  216. *out_entry_type = AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITH_INCREMENTAL_INDEXING;
  217. return AWS_OP_SUCCESS;
  218. case AWS_HTTP_HEADER_COMPRESSION_NO_CACHE:
  219. *out_entry_type = AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITHOUT_INDEXING;
  220. return AWS_OP_SUCCESS;
  221. case AWS_HTTP_HEADER_COMPRESSION_NO_FORWARD_CACHE:
  222. *out_entry_type = AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_NEVER_INDEXED;
  223. return AWS_OP_SUCCESS;
  224. }
  225. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  226. }
  227. static int s_encode_header_field(
  228. struct aws_hpack_encoder *encoder,
  229. const struct aws_http_header *header,
  230. struct aws_byte_buf *output) {
  231. AWS_PRECONDITION(encoder);
  232. AWS_PRECONDITION(header);
  233. AWS_PRECONDITION(output);
  234. size_t original_len = output->len;
  235. /* Search for header-field in tables */
  236. bool found_indexed_value;
  237. size_t header_index = aws_hpack_find_index(&encoder->context, header, true, &found_indexed_value);
  238. if (header->compression != AWS_HTTP_HEADER_COMPRESSION_USE_CACHE) {
  239. /* If user doesn't want to use indexed value, then don't use it */
  240. found_indexed_value = false;
  241. }
  242. if (header_index && found_indexed_value) {
  243. /* Indexed header field */
  244. const enum aws_hpack_entry_type entry_type = AWS_HPACK_ENTRY_INDEXED_HEADER_FIELD;
  245. /* encode the one index (along with the entry type), and we're done! */
  246. uint8_t starting_bit_pattern = s_hpack_entry_starting_bit_pattern[entry_type];
  247. uint8_t num_prefix_bits = s_hpack_entry_num_prefix_bits[entry_type];
  248. if (aws_hpack_encode_integer(header_index, starting_bit_pattern, num_prefix_bits, output)) {
  249. goto error;
  250. }
  251. return AWS_OP_SUCCESS;
  252. }
  253. /* Else, Literal header field... */
  254. /* determine exactly which type of literal header-field to encode. */
  255. enum aws_hpack_entry_type literal_entry_type = AWS_HPACK_ENTRY_TYPE_COUNT;
  256. if (s_convert_http_compression_to_literal_entry_type(header->compression, &literal_entry_type)) {
  257. goto error;
  258. }
  259. /* the entry type makes up the first few bits of the next integer we encode */
  260. uint8_t starting_bit_pattern = s_hpack_entry_starting_bit_pattern[literal_entry_type];
  261. uint8_t num_prefix_bits = s_hpack_entry_num_prefix_bits[literal_entry_type];
  262. if (header_index) {
  263. /* Literal header field, indexed name */
  264. /* first encode the index of name */
  265. if (aws_hpack_encode_integer(header_index, starting_bit_pattern, num_prefix_bits, output)) {
  266. goto error;
  267. }
  268. } else {
  269. /* Literal header field, new name */
  270. /* first encode index of 0 to indicate that header-name is not indexed */
  271. if (aws_hpack_encode_integer(0, starting_bit_pattern, num_prefix_bits, output)) {
  272. goto error;
  273. }
  274. /* next encode header-name string */
  275. if (aws_hpack_encode_string(encoder, header->name, output)) {
  276. goto error;
  277. }
  278. }
  279. /* then encode header-value string, and we're done encoding! */
  280. if (aws_hpack_encode_string(encoder, header->value, output)) {
  281. goto error;
  282. }
  283. /* if "incremental indexing" type, insert header into the dynamic table. */
  284. if (AWS_HPACK_ENTRY_LITERAL_HEADER_FIELD_WITH_INCREMENTAL_INDEXING == literal_entry_type) {
  285. if (aws_hpack_insert_header(&encoder->context, header)) {
  286. goto error;
  287. }
  288. }
  289. return AWS_OP_SUCCESS;
  290. error:
  291. output->len = original_len;
  292. return AWS_OP_ERR;
  293. }
  294. int aws_hpack_encode_header_block(
  295. struct aws_hpack_encoder *encoder,
  296. const struct aws_http_headers *headers,
  297. struct aws_byte_buf *output) {
  298. /* Encode a dynamic table size update at the beginning of the first header-block
  299. * following the change to the dynamic table size RFC-7541 4.2 */
  300. if (encoder->dynamic_table_size_update.pending) {
  301. if (encoder->dynamic_table_size_update.smallest_value != encoder->dynamic_table_size_update.latest_value) {
  302. size_t smallest_update_value = encoder->dynamic_table_size_update.smallest_value;
  303. HPACK_LOGF(
  304. TRACE, encoder, "Encoding smallest dynamic table size update entry size: %zu", smallest_update_value);
  305. if (aws_hpack_resize_dynamic_table(&encoder->context, smallest_update_value)) {
  306. HPACK_LOGF(ERROR, encoder, "Dynamic table resize failed, size: %zu", smallest_update_value);
  307. return AWS_OP_ERR;
  308. }
  309. uint8_t starting_bit_pattern = s_hpack_entry_starting_bit_pattern[AWS_HPACK_ENTRY_DYNAMIC_TABLE_RESIZE];
  310. uint8_t num_prefix_bits = s_hpack_entry_num_prefix_bits[AWS_HPACK_ENTRY_DYNAMIC_TABLE_RESIZE];
  311. if (aws_hpack_encode_integer(smallest_update_value, starting_bit_pattern, num_prefix_bits, output)) {
  312. HPACK_LOGF(
  313. ERROR,
  314. encoder,
  315. "Integer encoding failed for table size update entry, integer: %zu",
  316. smallest_update_value);
  317. return AWS_OP_ERR;
  318. }
  319. }
  320. size_t last_update_value = encoder->dynamic_table_size_update.latest_value;
  321. HPACK_LOGF(TRACE, encoder, "Encoding last dynamic table size update entry size: %zu", last_update_value);
  322. if (aws_hpack_resize_dynamic_table(&encoder->context, last_update_value)) {
  323. HPACK_LOGF(ERROR, encoder, "Dynamic table resize failed, size: %zu", last_update_value);
  324. return AWS_OP_ERR;
  325. }
  326. uint8_t starting_bit_pattern = s_hpack_entry_starting_bit_pattern[AWS_HPACK_ENTRY_DYNAMIC_TABLE_RESIZE];
  327. uint8_t num_prefix_bits = s_hpack_entry_num_prefix_bits[AWS_HPACK_ENTRY_DYNAMIC_TABLE_RESIZE];
  328. if (aws_hpack_encode_integer(last_update_value, starting_bit_pattern, num_prefix_bits, output)) {
  329. HPACK_LOGF(
  330. ERROR, encoder, "Integer encoding failed for table size update entry, integer: %zu", last_update_value);
  331. return AWS_OP_ERR;
  332. }
  333. encoder->dynamic_table_size_update.pending = false;
  334. encoder->dynamic_table_size_update.latest_value = SIZE_MAX;
  335. encoder->dynamic_table_size_update.smallest_value = SIZE_MAX;
  336. }
  337. const size_t num_headers = aws_http_headers_count(headers);
  338. for (size_t i = 0; i < num_headers; ++i) {
  339. struct aws_http_header header;
  340. aws_http_headers_get_index(headers, i, &header);
  341. if (s_encode_header_field(encoder, &header, output)) {
  342. return AWS_OP_ERR;
  343. }
  344. }
  345. return AWS_OP_SUCCESS;
  346. }