1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
|
#ifndef AWS_HTTP_HPACK_H
#define AWS_HTTP_HPACK_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/http/request_response.h>
#include <aws/common/hash_table.h>
#include <aws/compression/huffman.h>
/**
* Result of aws_hpack_decode() call.
* If a complete entry has not been decoded yet, type is ONGOING.
* Otherwise, type informs which data to look at.
*/
struct aws_hpack_decode_result {
enum aws_hpack_decode_type {
AWS_HPACK_DECODE_T_ONGOING,
AWS_HPACK_DECODE_T_HEADER_FIELD,
AWS_HPACK_DECODE_T_DYNAMIC_TABLE_RESIZE,
} type;
union {
/* If type is AWS_HPACK_DECODE_T_HEADER_FIELD */
struct aws_http_header header_field;
/* If type is AWS_HPACK_DECODE_T_DYNAMIC_TABLE_RESIZE */
size_t dynamic_table_resize;
} data;
};
/**
* Controls whether non-indexed strings will use Huffman encoding.
* In SMALLEST mode, strings will only be sent with Huffman encoding if it makes them smaller.
*
* Note: This does not control compression via "indexing",
* for that, see `aws_http_header_compression`.
* This only controls how string values are encoded when they're not already in a table.
*/
enum aws_hpack_huffman_mode {
AWS_HPACK_HUFFMAN_SMALLEST,
AWS_HPACK_HUFFMAN_NEVER,
AWS_HPACK_HUFFMAN_ALWAYS,
};
/**
* Maintains the dynamic table.
* Insertion is backwards, indexing is forwards
*/
struct aws_hpack_context {
struct aws_allocator *allocator;
enum aws_http_log_subject log_subject;
const void *log_id;
struct {
/* Array of headers, pointers to memory we alloced, which needs to be cleaned up whenever we move an entry out
*/
struct aws_http_header *buffer;
size_t buffer_capacity; /* Number of http_headers that can fit in buffer */
size_t num_elements;
size_t index_0;
/* Size in bytes, according to [4.1] */
size_t size;
size_t max_size;
/* aws_http_header * -> size_t */
struct aws_hash_table reverse_lookup;
/* aws_byte_cursor * -> size_t */
struct aws_hash_table reverse_lookup_name_only;
} dynamic_table;
};
/**
* Encodes outgoing headers.
*/
struct aws_hpack_encoder {
const void *log_id;
struct aws_huffman_encoder huffman_encoder;
enum aws_hpack_huffman_mode huffman_mode;
struct aws_hpack_context context;
struct {
size_t latest_value;
size_t smallest_value;
bool pending;
} dynamic_table_size_update;
};
/**
* Decodes incoming headers
*/
struct aws_hpack_decoder {
const void *log_id;
struct aws_huffman_decoder huffman_decoder;
struct aws_hpack_context context;
/* TODO: check the new (RFC 9113 - 4.3.1) to make sure we did it right */
/* SETTINGS_HEADER_TABLE_SIZE from http2 */
size_t dynamic_table_protocol_max_size_setting;
/* PRO TIP: Don't union progress_integer and progress_string together, since string_decode calls integer_decode */
struct hpack_progress_integer {
enum {
HPACK_INTEGER_STATE_INIT,
HPACK_INTEGER_STATE_VALUE,
} state;
uint8_t bit_count;
} progress_integer;
struct hpack_progress_string {
enum {
HPACK_STRING_STATE_INIT,
HPACK_STRING_STATE_LENGTH,
HPACK_STRING_STATE_VALUE,
} state;
bool use_huffman;
uint64_t length;
} progress_string;
struct hpack_progress_entry {
enum {
HPACK_ENTRY_STATE_INIT,
/* Indexed header field: just 1 state. read index, find name and value at index */
HPACK_ENTRY_STATE_INDEXED,
/* Literal header field: name may be indexed OR literal, value is always literal */
HPACK_ENTRY_STATE_LITERAL_BEGIN,
HPACK_ENTRY_STATE_LITERAL_NAME_STRING,
HPACK_ENTRY_STATE_LITERAL_VALUE_STRING,
/* Dynamic table resize: just 1 state. read new size */
HPACK_ENTRY_STATE_DYNAMIC_TABLE_RESIZE,
/* Done */
HPACK_ENTRY_STATE_COMPLETE,
} state;
union {
struct {
uint64_t index;
} indexed;
struct hpack_progress_literal {
uint8_t prefix_size;
enum aws_http_header_compression compression;
uint64_t name_index;
size_t name_length;
} literal;
struct {
uint64_t size;
} dynamic_table_resize;
} u;
enum aws_hpack_decode_type type;
/* Scratch holds header name and value while decoding */
struct aws_byte_buf scratch;
} progress_entry;
};
AWS_EXTERN_C_BEGIN
/* Library-level init and shutdown */
void aws_hpack_static_table_init(struct aws_allocator *allocator);
void aws_hpack_static_table_clean_up(void);
AWS_HTTP_API
void aws_hpack_context_init(
struct aws_hpack_context *aws_hpack_context,
struct aws_allocator *allocator,
enum aws_http_log_subject log_subject,
const void *log_id);
AWS_HTTP_API
void aws_hpack_context_clean_up(struct aws_hpack_context *context);
/* Returns the hpack size of a header (name.len + value.len + 32) [4.1] */
AWS_HTTP_API
size_t aws_hpack_get_header_size(const struct aws_http_header *header);
/* Returns the number of elements in dynamic table now */
AWS_HTTP_API
size_t aws_hpack_get_dynamic_table_num_elements(const struct aws_hpack_context *context);
size_t aws_hpack_get_dynamic_table_max_size(const struct aws_hpack_context *context);
AWS_HTTP_API
const struct aws_http_header *aws_hpack_get_header(const struct aws_hpack_context *context, size_t index);
/* A return value of 0 indicates that the header wasn't found */
AWS_HTTP_API
size_t aws_hpack_find_index(
const struct aws_hpack_context *context,
const struct aws_http_header *header,
bool search_value,
bool *found_value);
AWS_HTTP_API
int aws_hpack_insert_header(struct aws_hpack_context *context, const struct aws_http_header *header);
/**
* Set the max size of the dynamic table (in octets). The size of each header is name.len + value.len + 32 [4.1].
*/
AWS_HTTP_API
int aws_hpack_resize_dynamic_table(struct aws_hpack_context *context, size_t new_max_size);
AWS_HTTP_API
void aws_hpack_encoder_init(struct aws_hpack_encoder *encoder, struct aws_allocator *allocator, const void *log_id);
AWS_HTTP_API
void aws_hpack_encoder_clean_up(struct aws_hpack_encoder *encoder);
/* Call this after receiving SETTINGS_HEADER_TABLE_SIZE from peer and sending the ACK.
* The hpack-encoder remembers all size updates, and makes sure to encode the proper
* number of Dynamic Table Size Updates the next time a header block is sent. */
AWS_HTTP_API
void aws_hpack_encoder_update_max_table_size(struct aws_hpack_encoder *encoder, uint32_t new_max_size);
AWS_HTTP_API
void aws_hpack_encoder_set_huffman_mode(struct aws_hpack_encoder *encoder, enum aws_hpack_huffman_mode mode);
/**
* Encode header-block into the output.
* This function will mutate hpack, so an error means hpack can no longer be used.
* Note that output will be dynamically resized if it's too short.
*/
AWS_HTTP_API
int aws_hpack_encode_header_block(
struct aws_hpack_encoder *encoder,
const struct aws_http_headers *headers,
struct aws_byte_buf *output);
AWS_HTTP_API
void aws_hpack_decoder_init(struct aws_hpack_decoder *decoder, struct aws_allocator *allocator, const void *log_id);
AWS_HTTP_API
void aws_hpack_decoder_clean_up(struct aws_hpack_decoder *decoder);
/* Call this after sending SETTINGS_HEADER_TABLE_SIZE and receiving ACK from the peer.
* The hpack-decoder remembers all size updates, and makes sure that the peer
* sends the appropriate Dynamic Table Size Updates in the next header block we receive. */
AWS_HTTP_API
void aws_hpack_decoder_update_max_table_size(struct aws_hpack_decoder *decoder, uint32_t new_max_size);
/**
* Decode the next entry in the header-block-fragment.
* If result->type is ONGOING, then call decode() again with more data to resume decoding.
* Otherwise, type is either a HEADER_FIELD or a DYNAMIC_TABLE_RESIZE.
*
* If an error occurs, the decoder is broken and decode() must not be called again.
*/
AWS_HTTP_API
int aws_hpack_decode(
struct aws_hpack_decoder *decoder,
struct aws_byte_cursor *to_decode,
struct aws_hpack_decode_result *result);
/*******************************************************************************
* Private functions for encoder/decoder, but public for testing purposes
******************************************************************************/
/* Output will be dynamically resized if it's too short */
AWS_HTTP_API
int aws_hpack_encode_integer(uint64_t integer, uint8_t starting_bits, uint8_t prefix_size, struct aws_byte_buf *output);
/* Output will be dynamically resized if it's too short */
AWS_HTTP_API
int aws_hpack_encode_string(
struct aws_hpack_encoder *encoder,
struct aws_byte_cursor to_encode,
struct aws_byte_buf *output);
AWS_HTTP_API
int aws_hpack_decode_integer(
struct aws_hpack_decoder *decoder,
struct aws_byte_cursor *to_decode,
uint8_t prefix_size,
uint64_t *integer,
bool *complete);
AWS_HTTP_API
int aws_hpack_decode_string(
struct aws_hpack_decoder *decoder,
struct aws_byte_cursor *to_decode,
struct aws_byte_buf *output,
bool *complete);
AWS_EXTERN_C_END
#endif /* AWS_HTTP_HPACK_H */
|