12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592 |
- /**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
- #include <aws/http/private/h2_decoder.h>
- #include <aws/http/private/hpack.h>
- #include <aws/http/private/strutil.h>
- #include <aws/common/string.h>
- #include <aws/http/status_code.h>
- #include <aws/io/logging.h>
- #include <inttypes.h>
- #ifdef _MSC_VER
- # pragma warning(disable : 4204) /* Declared initializers */
- #endif
- /***********************************************************************************************************************
- * Constants
- **********************************************************************************************************************/
- /* The scratch buffers data for states with bytes_required > 0. Must be big enough for largest state */
- static const size_t s_scratch_space_size = 9;
- /* Stream ids & dependencies should only write the bottom 31 bits */
- static const uint32_t s_31_bit_mask = UINT32_MAX >> 1;
- /* initial size for cookie buffer, buffer will grow if needed */
- static const size_t s_decoder_cookie_buffer_initial_size = 512;
- #define DECODER_LOGF(level, decoder, text, ...) \
- AWS_LOGF_##level(AWS_LS_HTTP_DECODER, "id=%p " text, (decoder)->logging_id, __VA_ARGS__)
- #define DECODER_LOG(level, decoder, text) DECODER_LOGF(level, decoder, "%s", text)
- #define DECODER_CALL_VTABLE(decoder, fn) \
- do { \
- if ((decoder)->vtable->fn) { \
- DECODER_LOG(TRACE, decoder, "Invoking callback " #fn); \
- struct aws_h2err vtable_err = (decoder)->vtable->fn((decoder)->userdata); \
- if (aws_h2err_failed(vtable_err)) { \
- DECODER_LOGF( \
- ERROR, \
- decoder, \
- "Error from callback " #fn ", %s->%s", \
- aws_http2_error_code_to_str(vtable_err.h2_code), \
- aws_error_name(vtable_err.aws_code)); \
- return vtable_err; \
- } \
- } \
- } while (false)
- #define DECODER_CALL_VTABLE_ARGS(decoder, fn, ...) \
- do { \
- if ((decoder)->vtable->fn) { \
- DECODER_LOG(TRACE, decoder, "Invoking callback " #fn); \
- struct aws_h2err vtable_err = (decoder)->vtable->fn(__VA_ARGS__, (decoder)->userdata); \
- if (aws_h2err_failed(vtable_err)) { \
- DECODER_LOGF( \
- ERROR, \
- decoder, \
- "Error from callback " #fn ", %s->%s", \
- aws_http2_error_code_to_str(vtable_err.h2_code), \
- aws_error_name(vtable_err.aws_code)); \
- return vtable_err; \
- } \
- } \
- } while (false)
- #define DECODER_CALL_VTABLE_STREAM(decoder, fn) \
- DECODER_CALL_VTABLE_ARGS(decoder, fn, (decoder)->frame_in_progress.stream_id)
- #define DECODER_CALL_VTABLE_STREAM_ARGS(decoder, fn, ...) \
- DECODER_CALL_VTABLE_ARGS(decoder, fn, (decoder)->frame_in_progress.stream_id, __VA_ARGS__)
- /* for storing things in array without worrying about the specific values of the other AWS_HTTP_HEADER_XYZ enums */
- enum pseudoheader_name {
- PSEUDOHEADER_UNKNOWN = -1, /* Unrecognized value */
- /* Request pseudo-headers */
- PSEUDOHEADER_METHOD,
- PSEUDOHEADER_SCHEME,
- PSEUDOHEADER_AUTHORITY,
- PSEUDOHEADER_PATH,
- /* Response pseudo-headers */
- PSEUDOHEADER_STATUS,
- PSEUDOHEADER_COUNT, /* Number of valid enums */
- };
- static const struct aws_byte_cursor *s_pseudoheader_name_to_cursor[PSEUDOHEADER_COUNT] = {
- [PSEUDOHEADER_METHOD] = &aws_http_header_method,
- [PSEUDOHEADER_SCHEME] = &aws_http_header_scheme,
- [PSEUDOHEADER_AUTHORITY] = &aws_http_header_authority,
- [PSEUDOHEADER_PATH] = &aws_http_header_path,
- [PSEUDOHEADER_STATUS] = &aws_http_header_status,
- };
- static const enum aws_http_header_name s_pseudoheader_to_header_name[PSEUDOHEADER_COUNT] = {
- [PSEUDOHEADER_METHOD] = AWS_HTTP_HEADER_METHOD,
- [PSEUDOHEADER_SCHEME] = AWS_HTTP_HEADER_SCHEME,
- [PSEUDOHEADER_AUTHORITY] = AWS_HTTP_HEADER_AUTHORITY,
- [PSEUDOHEADER_PATH] = AWS_HTTP_HEADER_PATH,
- [PSEUDOHEADER_STATUS] = AWS_HTTP_HEADER_STATUS,
- };
- static enum pseudoheader_name s_header_to_pseudoheader_name(enum aws_http_header_name name) {
- /* The compiled switch statement is actually faster than array lookup with bounds-checking.
- * (the lookup arrays above don't need to do bounds-checking) */
- switch (name) {
- case AWS_HTTP_HEADER_METHOD:
- return PSEUDOHEADER_METHOD;
- case AWS_HTTP_HEADER_SCHEME:
- return PSEUDOHEADER_SCHEME;
- case AWS_HTTP_HEADER_AUTHORITY:
- return PSEUDOHEADER_AUTHORITY;
- case AWS_HTTP_HEADER_PATH:
- return PSEUDOHEADER_PATH;
- case AWS_HTTP_HEADER_STATUS:
- return PSEUDOHEADER_STATUS;
- default:
- return PSEUDOHEADER_UNKNOWN;
- }
- }
- /***********************************************************************************************************************
- * State Machine
- **********************************************************************************************************************/
- typedef struct aws_h2err(state_fn)(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input);
- struct h2_decoder_state {
- state_fn *fn;
- uint32_t bytes_required;
- const char *name;
- };
- #define DEFINE_STATE(_name, _bytes_required) \
- static state_fn s_state_fn_##_name; \
- enum { s_state_##_name##_requires_##_bytes_required##_bytes = _bytes_required }; \
- static const struct h2_decoder_state s_state_##_name = { \
- .fn = s_state_fn_##_name, \
- .bytes_required = s_state_##_name##_requires_##_bytes_required##_bytes, \
- .name = #_name, \
- }
- /* Common states */
- DEFINE_STATE(prefix, 9);
- DEFINE_STATE(padding_len, 1);
- DEFINE_STATE(padding, 0);
- DEFINE_STATE(priority_block, 5);
- DEFINE_STATE(header_block_loop, 0);
- DEFINE_STATE(header_block_entry, 1); /* requires 1 byte, but may consume more */
- /* Frame-specific states */
- DEFINE_STATE(frame_data, 0);
- DEFINE_STATE(frame_headers, 0);
- DEFINE_STATE(frame_priority, 0);
- DEFINE_STATE(frame_rst_stream, 4);
- DEFINE_STATE(frame_settings_begin, 0);
- DEFINE_STATE(frame_settings_loop, 0);
- DEFINE_STATE(frame_settings_i, 6);
- DEFINE_STATE(frame_push_promise, 4);
- DEFINE_STATE(frame_ping, 8);
- DEFINE_STATE(frame_goaway, 8);
- DEFINE_STATE(frame_goaway_debug_data, 0);
- DEFINE_STATE(frame_window_update, 4);
- DEFINE_STATE(frame_continuation, 0);
- DEFINE_STATE(frame_unknown, 0);
- /* States that have nothing to do with frames */
- DEFINE_STATE(connection_preface_string, 1); /* requires 1 byte but may consume more */
- /* Helper for states that need to transition to frame-type states */
- static const struct h2_decoder_state *s_state_frames[AWS_H2_FRAME_TYPE_COUNT] = {
- [AWS_H2_FRAME_T_DATA] = &s_state_frame_data,
- [AWS_H2_FRAME_T_HEADERS] = &s_state_frame_headers,
- [AWS_H2_FRAME_T_PRIORITY] = &s_state_frame_priority,
- [AWS_H2_FRAME_T_RST_STREAM] = &s_state_frame_rst_stream,
- [AWS_H2_FRAME_T_SETTINGS] = &s_state_frame_settings_begin,
- [AWS_H2_FRAME_T_PUSH_PROMISE] = &s_state_frame_push_promise,
- [AWS_H2_FRAME_T_PING] = &s_state_frame_ping,
- [AWS_H2_FRAME_T_GOAWAY] = &s_state_frame_goaway,
- [AWS_H2_FRAME_T_WINDOW_UPDATE] = &s_state_frame_window_update,
- [AWS_H2_FRAME_T_CONTINUATION] = &s_state_frame_continuation,
- [AWS_H2_FRAME_T_UNKNOWN] = &s_state_frame_unknown,
- };
- /***********************************************************************************************************************
- * Struct
- **********************************************************************************************************************/
- struct aws_h2_decoder {
- /* Implementation data. */
- struct aws_allocator *alloc;
- const void *logging_id;
- struct aws_hpack_decoder hpack;
- bool is_server;
- struct aws_byte_buf scratch;
- const struct h2_decoder_state *state;
- bool state_changed;
- /* HTTP/2 connection preface must be first thing received (RFC-7540 3.5):
- * Server must receive (client must send): magic string, then SETTINGS frame.
- * Client must receive (server must send): SETTINGS frame. */
- bool connection_preface_complete;
- /* Cursor over the canonical client connection preface string */
- struct aws_byte_cursor connection_preface_cursor;
- /* Frame-in-progress */
- struct aws_frame_in_progress {
- enum aws_h2_frame_type type;
- uint32_t stream_id;
- uint32_t payload_len;
- uint8_t padding_len;
- struct {
- bool ack;
- bool end_stream;
- bool end_headers;
- bool priority;
- } flags;
- } frame_in_progress;
- /* GOAWAY buffer */
- struct aws_goaway_in_progress {
- uint32_t last_stream;
- uint32_t error_code;
- /* Buffer of the received debug data in the latest goaway frame */
- struct aws_byte_buf debug_data;
- } goaway_in_progress;
- /* A header-block starts with a HEADERS or PUSH_PROMISE frame, followed by 0 or more CONTINUATION frames.
- * It's an error for any other frame-type or stream ID to arrive while a header-block is in progress.
- * The header-block ends when a frame has the END_HEADERS flag set. (RFC-7540 4.3) */
- struct aws_header_block_in_progress {
- /* If 0, then no header-block in progress */
- uint32_t stream_id;
- /* Whether these are informational (1xx), normal, or trailing headers */
- enum aws_http_header_block block_type;
- /* Buffer up pseudo-headers and deliver them once they're all validated */
- struct aws_string *pseudoheader_values[PSEUDOHEADER_COUNT];
- enum aws_http_header_compression pseudoheader_compression[PSEUDOHEADER_COUNT];
- /* All pseudo-header fields MUST appear in the header block before regular header fields. */
- bool pseudoheaders_done;
- /* T: PUSH_PROMISE header-block
- * F: HEADERS header-block */
- bool is_push_promise;
- /* If frame that starts header-block has END_STREAM flag,
- * then frame that ends header-block also ends the stream. */
- bool ends_stream;
- /* True if something occurs that makes the header-block malformed (ex: invalid header name).
- * A malformed header-block is not a connection error, it's a Stream Error (RFC-7540 5.4.2).
- * We continue decoding and report that it's malformed in on_headers_end(). */
- bool malformed;
- bool body_headers_forbidden;
- /* Buffer up cookie header fields to concatenate separate ones */
- struct aws_byte_buf cookies;
- /* If separate cookie fields have different compression types, the concatenated cookie uses the strictest type.
- */
- enum aws_http_header_compression cookie_header_compression_type;
- } header_block_in_progress;
- /* Settings for decoder, which is based on the settings sent to the peer and ACKed by peer */
- struct {
- /* enable/disable server push */
- uint32_t enable_push;
- /* the size of the largest frame payload */
- uint32_t max_frame_size;
- } settings;
- struct aws_array_list settings_buffer_list;
- /* User callbacks and settings. */
- const struct aws_h2_decoder_vtable *vtable;
- void *userdata;
- /* If this is set to true, decode may no longer be called */
- bool has_errored;
- };
- /***********************************************************************************************************************/
- struct aws_h2_decoder *aws_h2_decoder_new(struct aws_h2_decoder_params *params) {
- AWS_PRECONDITION(params);
- AWS_PRECONDITION(params->alloc);
- AWS_PRECONDITION(params->vtable);
- struct aws_h2_decoder *decoder = NULL;
- void *scratch_buf = NULL;
- void *allocation = aws_mem_acquire_many(
- params->alloc, 2, &decoder, sizeof(struct aws_h2_decoder), &scratch_buf, s_scratch_space_size);
- if (!allocation) {
- goto error;
- }
- AWS_ZERO_STRUCT(*decoder);
- decoder->alloc = params->alloc;
- decoder->vtable = params->vtable;
- decoder->userdata = params->userdata;
- decoder->logging_id = params->logging_id;
- decoder->is_server = params->is_server;
- decoder->connection_preface_complete = params->skip_connection_preface;
- decoder->scratch = aws_byte_buf_from_empty_array(scratch_buf, s_scratch_space_size);
- aws_hpack_decoder_init(&decoder->hpack, params->alloc, decoder);
- if (decoder->is_server && !params->skip_connection_preface) {
- decoder->state = &s_state_connection_preface_string;
- decoder->connection_preface_cursor = aws_h2_connection_preface_client_string;
- } else {
- decoder->state = &s_state_prefix;
- }
- decoder->settings.enable_push = aws_h2_settings_initial[AWS_HTTP2_SETTINGS_ENABLE_PUSH];
- decoder->settings.max_frame_size = aws_h2_settings_initial[AWS_HTTP2_SETTINGS_MAX_FRAME_SIZE];
- if (aws_array_list_init_dynamic(
- &decoder->settings_buffer_list, decoder->alloc, 0, sizeof(struct aws_http2_setting))) {
- goto error;
- }
- if (aws_byte_buf_init(
- &decoder->header_block_in_progress.cookies, decoder->alloc, s_decoder_cookie_buffer_initial_size)) {
- goto error;
- }
- return decoder;
- error:
- if (decoder) {
- aws_hpack_decoder_clean_up(&decoder->hpack);
- aws_array_list_clean_up(&decoder->settings_buffer_list);
- aws_byte_buf_clean_up(&decoder->header_block_in_progress.cookies);
- }
- aws_mem_release(params->alloc, allocation);
- return NULL;
- }
- static void s_reset_header_block_in_progress(struct aws_h2_decoder *decoder) {
- for (size_t i = 0; i < PSEUDOHEADER_COUNT; ++i) {
- aws_string_destroy(decoder->header_block_in_progress.pseudoheader_values[i]);
- }
- struct aws_byte_buf cookie_backup = decoder->header_block_in_progress.cookies;
- AWS_ZERO_STRUCT(decoder->header_block_in_progress);
- decoder->header_block_in_progress.cookies = cookie_backup;
- aws_byte_buf_reset(&decoder->header_block_in_progress.cookies, false);
- }
- void aws_h2_decoder_destroy(struct aws_h2_decoder *decoder) {
- if (!decoder) {
- return;
- }
- aws_array_list_clean_up(&decoder->settings_buffer_list);
- aws_hpack_decoder_clean_up(&decoder->hpack);
- s_reset_header_block_in_progress(decoder);
- aws_byte_buf_clean_up(&decoder->header_block_in_progress.cookies);
- aws_byte_buf_clean_up(&decoder->goaway_in_progress.debug_data);
- aws_mem_release(decoder->alloc, decoder);
- }
- struct aws_h2err aws_h2_decode(struct aws_h2_decoder *decoder, struct aws_byte_cursor *data) {
- AWS_PRECONDITION(decoder);
- AWS_PRECONDITION(data);
- AWS_FATAL_ASSERT(!decoder->has_errored);
- struct aws_h2err err = AWS_H2ERR_SUCCESS;
- /* Run decoder state machine until we're no longer changing states.
- * We don't simply loop `while(data->len)` because some states consume no data,
- * and these states should run even when there is no data left. */
- do {
- decoder->state_changed = false;
- const uint32_t bytes_required = decoder->state->bytes_required;
- AWS_ASSERT(bytes_required <= decoder->scratch.capacity);
- const char *current_state_name = decoder->state->name;
- const size_t prev_data_len = data->len;
- (void)prev_data_len;
- if (!decoder->scratch.len && data->len >= bytes_required) {
- /* Easy case, there is no scratch and we have enough data, so just send it to the state */
- DECODER_LOGF(TRACE, decoder, "Running state '%s' with %zu bytes available", current_state_name, data->len);
- err = decoder->state->fn(decoder, data);
- if (aws_h2err_failed(err)) {
- goto handle_error;
- }
- AWS_ASSERT(prev_data_len - data->len >= bytes_required && "Decoder state requested more data than it used");
- } else {
- /* Otherwise, state requires a minimum amount of data and we have to use the scratch */
- size_t bytes_to_read = bytes_required - decoder->scratch.len;
- bool will_finish_state = true;
- if (bytes_to_read > data->len) {
- /* Not enough in this cursor, need to read as much as possible and then come back */
- bytes_to_read = data->len;
- will_finish_state = false;
- }
- if (AWS_LIKELY(bytes_to_read)) {
- /* Read the appropriate number of bytes into scratch */
- struct aws_byte_cursor to_read = aws_byte_cursor_advance(data, bytes_to_read);
- bool succ = aws_byte_buf_write_from_whole_cursor(&decoder->scratch, to_read);
- AWS_ASSERT(succ);
- (void)succ;
- }
- /* If we have the correct number of bytes, call the state */
- if (will_finish_state) {
- DECODER_LOGF(TRACE, decoder, "Running state '%s' (using scratch)", current_state_name);
- struct aws_byte_cursor state_data = aws_byte_cursor_from_buf(&decoder->scratch);
- err = decoder->state->fn(decoder, &state_data);
- if (aws_h2err_failed(err)) {
- goto handle_error;
- }
- AWS_ASSERT(state_data.len == 0 && "Decoder state requested more data than it used");
- } else {
- DECODER_LOGF(
- TRACE,
- decoder,
- "State '%s' requires %" PRIu32 " bytes, but only %zu available, trying again later",
- current_state_name,
- bytes_required,
- decoder->scratch.len);
- }
- }
- } while (decoder->state_changed);
- return AWS_H2ERR_SUCCESS;
- handle_error:
- decoder->has_errored = true;
- return err;
- }
- /***********************************************************************************************************************
- * State functions
- **********************************************************************************************************************/
- static struct aws_h2err s_decoder_switch_state(struct aws_h2_decoder *decoder, const struct h2_decoder_state *state) {
- /* Ensure payload is big enough to enter next state.
- * If this fails, then the payload length we received is too small for this frame type.
- * (ex: a RST_STREAM frame with < 4 bytes) */
- if (decoder->frame_in_progress.payload_len < state->bytes_required) {
- DECODER_LOGF(
- ERROR, decoder, "%s payload is too small", aws_h2_frame_type_to_str(decoder->frame_in_progress.type));
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FRAME_SIZE_ERROR);
- }
- DECODER_LOGF(TRACE, decoder, "Moving from state '%s' to '%s'", decoder->state->name, state->name);
- decoder->scratch.len = 0;
- decoder->state = state;
- decoder->state_changed = true;
- return AWS_H2ERR_SUCCESS;
- }
- static struct aws_h2err s_decoder_switch_to_frame_state(struct aws_h2_decoder *decoder) {
- AWS_ASSERT(decoder->frame_in_progress.type < AWS_H2_FRAME_TYPE_COUNT);
- return s_decoder_switch_state(decoder, s_state_frames[decoder->frame_in_progress.type]);
- }
- static struct aws_h2err s_decoder_reset_state(struct aws_h2_decoder *decoder) {
- /* Ensure we've consumed all payload (and padding) when state machine finishes this frame.
- * If this fails, the payload length we received is too large for this frame type.
- * (ex: a RST_STREAM frame with > 4 bytes) */
- if (decoder->frame_in_progress.payload_len > 0 || decoder->frame_in_progress.padding_len > 0) {
- DECODER_LOGF(
- ERROR, decoder, "%s frame payload is too large", aws_h2_frame_type_to_str(decoder->frame_in_progress.type));
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FRAME_SIZE_ERROR);
- }
- DECODER_LOGF(TRACE, decoder, "%s frame complete", aws_h2_frame_type_to_str(decoder->frame_in_progress.type));
- decoder->scratch.len = 0;
- decoder->state = &s_state_prefix;
- decoder->state_changed = true;
- AWS_ZERO_STRUCT(decoder->frame_in_progress);
- return AWS_H2ERR_SUCCESS;
- }
- /* Returns as much of the current frame's payload as possible, and updates payload_len */
- static struct aws_byte_cursor s_decoder_get_payload(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- struct aws_byte_cursor result;
- const uint32_t remaining_length = decoder->frame_in_progress.payload_len;
- if (input->len < remaining_length) {
- AWS_ASSERT(input->len <= UINT32_MAX);
- result = aws_byte_cursor_advance(input, input->len);
- } else {
- result = aws_byte_cursor_advance(input, remaining_length);
- }
- decoder->frame_in_progress.payload_len -= (uint32_t)result.len;
- return result;
- }
- /* clang-format off */
- /* Mask of flags supported by each frame type.
- * Frames not listed have mask of 0, which means all flags will be ignored. */
- static const uint8_t s_acceptable_flags_for_frame[AWS_H2_FRAME_TYPE_COUNT] = {
- [AWS_H2_FRAME_T_DATA] = AWS_H2_FRAME_F_END_STREAM | AWS_H2_FRAME_F_PADDED,
- [AWS_H2_FRAME_T_HEADERS] = AWS_H2_FRAME_F_END_STREAM | AWS_H2_FRAME_F_END_HEADERS |
- AWS_H2_FRAME_F_PADDED | AWS_H2_FRAME_F_PRIORITY,
- [AWS_H2_FRAME_T_PRIORITY] = 0,
- [AWS_H2_FRAME_T_RST_STREAM] = 0,
- [AWS_H2_FRAME_T_SETTINGS] = AWS_H2_FRAME_F_ACK,
- [AWS_H2_FRAME_T_PUSH_PROMISE] = AWS_H2_FRAME_F_END_HEADERS | AWS_H2_FRAME_F_PADDED,
- [AWS_H2_FRAME_T_PING] = AWS_H2_FRAME_F_ACK,
- [AWS_H2_FRAME_T_GOAWAY] = 0,
- [AWS_H2_FRAME_T_WINDOW_UPDATE] = 0,
- [AWS_H2_FRAME_T_CONTINUATION] = AWS_H2_FRAME_F_END_HEADERS,
- [AWS_H2_FRAME_T_UNKNOWN] = 0,
- };
- enum stream_id_rules {
- STREAM_ID_REQUIRED,
- STREAM_ID_FORBIDDEN,
- STREAM_ID_EITHER_WAY,
- };
- /* Frame-types generally either require a stream-id, or require that it be zero. */
- static const enum stream_id_rules s_stream_id_rules_for_frame[AWS_H2_FRAME_TYPE_COUNT] = {
- [AWS_H2_FRAME_T_DATA] = STREAM_ID_REQUIRED,
- [AWS_H2_FRAME_T_HEADERS] = STREAM_ID_REQUIRED,
- [AWS_H2_FRAME_T_PRIORITY] = STREAM_ID_REQUIRED,
- [AWS_H2_FRAME_T_RST_STREAM] = STREAM_ID_REQUIRED,
- [AWS_H2_FRAME_T_SETTINGS] = STREAM_ID_FORBIDDEN,
- [AWS_H2_FRAME_T_PUSH_PROMISE] = STREAM_ID_REQUIRED,
- [AWS_H2_FRAME_T_PING] = STREAM_ID_FORBIDDEN,
- [AWS_H2_FRAME_T_GOAWAY] = STREAM_ID_FORBIDDEN,
- [AWS_H2_FRAME_T_WINDOW_UPDATE] = STREAM_ID_EITHER_WAY, /* WINDOW_UPDATE is special and can do either */
- [AWS_H2_FRAME_T_CONTINUATION] = STREAM_ID_REQUIRED,
- [AWS_H2_FRAME_T_UNKNOWN] = STREAM_ID_EITHER_WAY, /* Everything in an UNKNOWN frame type is ignored */
- };
- /* clang-format on */
- /* All frames begin with a fixed 9-octet header followed by a variable-length payload. (RFC-7540 4.1)
- * This function processes everything preceding Frame Payload in the following diagram:
- * +-----------------------------------------------+
- * | Length (24) |
- * +---------------+---------------+---------------+
- * | Type (8) | Flags (8) |
- * +-+-------------+---------------+-------------------------------+
- * |R| Stream Identifier (31) |
- * +=+=============================================================+
- * | Frame Payload (0...) ...
- * +---------------------------------------------------------------+
- */
- static struct aws_h2err s_state_fn_prefix(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_prefix_requires_9_bytes);
- struct aws_frame_in_progress *frame = &decoder->frame_in_progress;
- uint8_t raw_type = 0;
- uint8_t raw_flags = 0;
- /* Read the raw values from the first 9 bytes */
- bool all_read = true;
- all_read &= aws_byte_cursor_read_be24(input, &frame->payload_len);
- all_read &= aws_byte_cursor_read_u8(input, &raw_type);
- all_read &= aws_byte_cursor_read_u8(input, &raw_flags);
- all_read &= aws_byte_cursor_read_be32(input, &frame->stream_id);
- AWS_ASSERT(all_read);
- (void)all_read;
- /* Validate frame type */
- frame->type = raw_type < AWS_H2_FRAME_T_UNKNOWN ? raw_type : AWS_H2_FRAME_T_UNKNOWN;
- /* Validate the frame's flags
- * Flags that have no defined semantics for a particular frame type MUST be ignored (RFC-7540 4.1) */
- const uint8_t flags = raw_flags & s_acceptable_flags_for_frame[decoder->frame_in_progress.type];
- bool is_padded = flags & AWS_H2_FRAME_F_PADDED;
- decoder->frame_in_progress.flags.ack = flags & AWS_H2_FRAME_F_ACK;
- decoder->frame_in_progress.flags.end_stream = flags & AWS_H2_FRAME_F_END_STREAM;
- decoder->frame_in_progress.flags.end_headers = flags & AWS_H2_FRAME_F_END_HEADERS;
- decoder->frame_in_progress.flags.priority =
- flags & AWS_H2_FRAME_F_PRIORITY || decoder->frame_in_progress.type == AWS_H2_FRAME_T_PRIORITY;
- /* Connection preface requires that SETTINGS be sent first (RFC-7540 3.5).
- * This should be the first error we check for, so that a connection sending
- * total garbage data is likely to trigger this PROTOCOL_ERROR */
- if (!decoder->connection_preface_complete) {
- if (frame->type == AWS_H2_FRAME_T_SETTINGS && !frame->flags.ack) {
- DECODER_LOG(TRACE, decoder, "Connection preface satisfied.");
- decoder->connection_preface_complete = true;
- } else {
- DECODER_LOG(ERROR, decoder, "First frame must be SETTINGS");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- }
- /* Validate the frame's stream ID. */
- /* Reserved bit (1st bit) MUST be ignored when receiving (RFC-7540 4.1) */
- frame->stream_id &= s_31_bit_mask;
- /* Some frame types require a stream ID, some frame types require that stream ID be zero. */
- const enum stream_id_rules stream_id_rules = s_stream_id_rules_for_frame[frame->type];
- if (frame->stream_id) {
- if (stream_id_rules == STREAM_ID_FORBIDDEN) {
- DECODER_LOGF(ERROR, decoder, "Stream ID for %s frame must be 0.", aws_h2_frame_type_to_str(frame->type));
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- } else {
- if (stream_id_rules == STREAM_ID_REQUIRED) {
- DECODER_LOGF(ERROR, decoder, "Stream ID for %s frame cannot be 0.", aws_h2_frame_type_to_str(frame->type));
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- }
- /* A header-block starts with a HEADERS or PUSH_PROMISE frame, followed by 0 or more CONTINUATION frames.
- * It's an error for any other frame-type or stream ID to arrive while a header-block is in progress.
- * (RFC-7540 4.3) */
- if (frame->type == AWS_H2_FRAME_T_CONTINUATION) {
- if (decoder->header_block_in_progress.stream_id != frame->stream_id) {
- DECODER_LOG(ERROR, decoder, "Unexpected CONTINUATION frame.");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- } else {
- if (decoder->header_block_in_progress.stream_id) {
- DECODER_LOG(ERROR, decoder, "Expected CONTINUATION frame.");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- }
- /* Validate payload length. */
- uint32_t max_frame_size = decoder->settings.max_frame_size;
- if (frame->payload_len > max_frame_size) {
- DECODER_LOGF(
- ERROR,
- decoder,
- "Decoder's max frame size is %" PRIu32 ", but frame of size %" PRIu32 " was received.",
- max_frame_size,
- frame->payload_len);
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FRAME_SIZE_ERROR);
- }
- DECODER_LOGF(
- TRACE,
- decoder,
- "Done decoding frame prefix (type=%s stream-id=%" PRIu32 " payload-len=%" PRIu32 "), moving on to payload",
- aws_h2_frame_type_to_str(frame->type),
- frame->stream_id,
- frame->payload_len);
- if (is_padded) {
- /* Read padding length if necessary */
- return s_decoder_switch_state(decoder, &s_state_padding_len);
- }
- if (decoder->frame_in_progress.type == AWS_H2_FRAME_T_DATA) {
- /* We invoke the on_data_begin here to report the whole payload size */
- DECODER_CALL_VTABLE_STREAM_ARGS(
- decoder, on_data_begin, frame->payload_len, 0 /*padding_len*/, frame->flags.end_stream);
- }
- if (decoder->frame_in_progress.flags.priority) {
- /* Read the stream dependency and weight if PRIORITY is set */
- return s_decoder_switch_state(decoder, &s_state_priority_block);
- }
- /* Set the state to the appropriate frame's state */
- return s_decoder_switch_to_frame_state(decoder);
- }
- /* Frames that support padding, and have the PADDED flag set, begin with a 1-byte Pad Length.
- * (Actual padding comes later at the very end of the frame)
- * +---------------+
- * |Pad Length? (8)|
- * +---------------+
- */
- static struct aws_h2err s_state_fn_padding_len(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_padding_len_requires_1_bytes);
- struct aws_frame_in_progress *frame = &decoder->frame_in_progress;
- /* Read the padding length */
- bool succ = aws_byte_cursor_read_u8(input, &frame->padding_len);
- AWS_ASSERT(succ);
- (void)succ;
- /* Adjust payload size so it doesn't include padding (or the 1-byte padding length) */
- uint32_t reduce_payload = s_state_padding_len_requires_1_bytes + frame->padding_len;
- if (reduce_payload > decoder->frame_in_progress.payload_len) {
- DECODER_LOG(ERROR, decoder, "Padding length exceeds payload length");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- if (frame->type == AWS_H2_FRAME_T_DATA) {
- /* We invoke the on_data_begin here to report the whole payload size and the padding size */
- DECODER_CALL_VTABLE_STREAM_ARGS(
- decoder, on_data_begin, frame->payload_len, frame->padding_len + 1, frame->flags.end_stream);
- }
- frame->payload_len -= reduce_payload;
- DECODER_LOGF(TRACE, decoder, "Padding length of frame: %" PRIu32, frame->padding_len);
- if (frame->flags.priority) {
- /* Read the stream dependency and weight if PRIORITY is set */
- return s_decoder_switch_state(decoder, &s_state_priority_block);
- }
- /* Set the state to the appropriate frame's state */
- return s_decoder_switch_to_frame_state(decoder);
- }
- static struct aws_h2err s_state_fn_padding(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- const uint8_t remaining_len = decoder->frame_in_progress.padding_len;
- const uint8_t consuming_len = input->len < remaining_len ? (uint8_t)input->len : remaining_len;
- aws_byte_cursor_advance(input, consuming_len);
- decoder->frame_in_progress.padding_len -= consuming_len;
- if (remaining_len == consuming_len) {
- /* Done with the frame! */
- return s_decoder_reset_state(decoder);
- }
- return AWS_H2ERR_SUCCESS;
- }
- /* Shared code for:
- * PRIORITY frame (RFC-7540 6.3)
- * Start of HEADERS frame IF the priority flag is set (RFC-7540 6.2)
- * +-+-------------+-----------------------------------------------+
- * |E| Stream Dependency (31) |
- * +-+-------------+-----------------------------------------------+
- * | Weight (8) |
- * +-+-------------+-----------------------------------------------+
- */
- static struct aws_h2err s_state_fn_priority_block(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_priority_block_requires_5_bytes);
- /* #NOTE: throw priority data on the GROUND. They make us hecka vulnerable to DDoS and stuff.
- * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9513
- */
- aws_byte_cursor_advance(input, s_state_priority_block_requires_5_bytes);
- decoder->frame_in_progress.payload_len -= s_state_priority_block_requires_5_bytes;
- return s_decoder_switch_to_frame_state(decoder);
- }
- static struct aws_h2err s_state_fn_frame_data(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- const struct aws_byte_cursor body_data = s_decoder_get_payload(decoder, input);
- if (body_data.len) {
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_data_i, body_data);
- }
- if (decoder->frame_in_progress.payload_len == 0) {
- DECODER_CALL_VTABLE_STREAM(decoder, on_data_end);
- /* If frame had END_STREAM flag, alert user now */
- if (decoder->frame_in_progress.flags.end_stream) {
- DECODER_CALL_VTABLE_STREAM(decoder, on_end_stream);
- }
- /* Process padding if necessary, otherwise we're done! */
- return s_decoder_switch_state(decoder, &s_state_padding);
- }
- return AWS_H2ERR_SUCCESS;
- }
- static struct aws_h2err s_state_fn_frame_headers(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- (void)input;
- /* Start header-block and alert the user */
- decoder->header_block_in_progress.stream_id = decoder->frame_in_progress.stream_id;
- decoder->header_block_in_progress.is_push_promise = false;
- decoder->header_block_in_progress.ends_stream = decoder->frame_in_progress.flags.end_stream;
- DECODER_CALL_VTABLE_STREAM(decoder, on_headers_begin);
- /* Read the header-block fragment */
- return s_decoder_switch_state(decoder, &s_state_header_block_loop);
- }
- static struct aws_h2err s_state_fn_frame_priority(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- (void)input;
- /* We already processed this data in the shared priority_block state, so we're done! */
- return s_decoder_reset_state(decoder);
- }
- /* RST_STREAM is just a 4-byte error code.
- * +---------------------------------------------------------------+
- * | Error Code (32) |
- * +---------------------------------------------------------------+
- */
- static struct aws_h2err s_state_fn_frame_rst_stream(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_frame_rst_stream_requires_4_bytes);
- uint32_t error_code = 0;
- bool succ = aws_byte_cursor_read_be32(input, &error_code);
- AWS_ASSERT(succ);
- (void)succ;
- decoder->frame_in_progress.payload_len -= s_state_frame_rst_stream_requires_4_bytes;
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_rst_stream, error_code);
- return s_decoder_reset_state(decoder);
- }
- /* A SETTINGS frame may contain any number of 6-byte entries.
- * This state consumes no data, but sends us into the appropriate next state */
- static struct aws_h2err s_state_fn_frame_settings_begin(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- (void)input;
- /* If ack is set, report and we're done */
- if (decoder->frame_in_progress.flags.ack) {
- /* Receipt of a SETTINGS frame with the ACK flag set and a length field value other
- * than 0 MUST be treated as a connection error of type FRAME_SIZE_ERROR */
- if (decoder->frame_in_progress.payload_len) {
- DECODER_LOGF(
- ERROR,
- decoder,
- "SETTINGS ACK frame received, but it has non-0 payload length %" PRIu32,
- decoder->frame_in_progress.payload_len);
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FRAME_SIZE_ERROR);
- }
- DECODER_CALL_VTABLE(decoder, on_settings_ack);
- return s_decoder_reset_state(decoder);
- }
- if (decoder->frame_in_progress.payload_len % s_state_frame_settings_i_requires_6_bytes != 0) {
- /* A SETTINGS frame with a length other than a multiple of 6 octets MUST be
- * treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR */
- DECODER_LOGF(
- ERROR,
- decoder,
- "Settings frame payload length is %" PRIu32 ", but it must be divisible by %" PRIu32,
- decoder->frame_in_progress.payload_len,
- s_state_frame_settings_i_requires_6_bytes);
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FRAME_SIZE_ERROR);
- }
- /* Enter looping states until all entries are consumed. */
- return s_decoder_switch_state(decoder, &s_state_frame_settings_loop);
- }
- /* Check if we're done consuming settings */
- static struct aws_h2err s_state_fn_frame_settings_loop(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- (void)input;
- if (decoder->frame_in_progress.payload_len == 0) {
- /* Huzzah, done with the frame, fire the callback */
- struct aws_array_list *buffer = &decoder->settings_buffer_list;
- DECODER_CALL_VTABLE_ARGS(
- decoder, on_settings, buffer->data, aws_array_list_length(&decoder->settings_buffer_list));
- /* clean up the buffer */
- aws_array_list_clear(&decoder->settings_buffer_list);
- return s_decoder_reset_state(decoder);
- }
- return s_decoder_switch_state(decoder, &s_state_frame_settings_i);
- }
- /* Each run through this state consumes one 6-byte setting.
- * There may be multiple settings in a SETTINGS frame.
- * +-------------------------------+
- * | Identifier (16) |
- * +-------------------------------+-------------------------------+
- * | Value (32) |
- * +---------------------------------------------------------------+
- */
- static struct aws_h2err s_state_fn_frame_settings_i(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_frame_settings_i_requires_6_bytes);
- uint16_t id = 0;
- uint32_t value = 0;
- bool succ = aws_byte_cursor_read_be16(input, &id);
- AWS_ASSERT(succ);
- (void)succ;
- succ = aws_byte_cursor_read_be32(input, &value);
- AWS_ASSERT(succ);
- (void)succ;
- /* An endpoint that receives a SETTINGS frame with any unknown or unsupported identifier MUST ignore that setting.
- * RFC-7540 6.5.2 */
- if (id >= AWS_HTTP2_SETTINGS_BEGIN_RANGE && id < AWS_HTTP2_SETTINGS_END_RANGE) {
- /* check the value meets the settings bounds */
- if (value < aws_h2_settings_bounds[id][0] || value > aws_h2_settings_bounds[id][1]) {
- DECODER_LOGF(
- ERROR, decoder, "A value of SETTING frame is invalid, id: %" PRIu16 ", value: %" PRIu32, id, value);
- if (id == AWS_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) {
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FLOW_CONTROL_ERROR);
- } else {
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- }
- struct aws_http2_setting setting;
- setting.id = id;
- setting.value = value;
- /* array_list will keep a copy of setting, it is fine to be a local variable */
- if (aws_array_list_push_back(&decoder->settings_buffer_list, &setting)) {
- DECODER_LOGF(ERROR, decoder, "Writing setting to buffer failed, %s", aws_error_name(aws_last_error()));
- return aws_h2err_from_last_error();
- }
- }
- /* Update payload len */
- decoder->frame_in_progress.payload_len -= s_state_frame_settings_i_requires_6_bytes;
- return s_decoder_switch_state(decoder, &s_state_frame_settings_loop);
- }
- /* Read 4-byte Promised Stream ID
- * The rest of the frame is just like HEADERS, so move on to shared states...
- * +-+-------------------------------------------------------------+
- * |R| Promised Stream ID (31) |
- * +-+-----------------------------+-------------------------------+
- */
- static struct aws_h2err s_state_fn_frame_push_promise(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- if (decoder->settings.enable_push == 0) {
- /* treat the receipt of a PUSH_PROMISE frame as a connection error of type PROTOCOL_ERROR.(RFC-7540 6.5.2) */
- DECODER_LOG(ERROR, decoder, "PUSH_PROMISE is invalid, the seting for enable push is 0");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- AWS_ASSERT(input->len >= s_state_frame_push_promise_requires_4_bytes);
- uint32_t promised_stream_id = 0;
- bool succ = aws_byte_cursor_read_be32(input, &promised_stream_id);
- AWS_ASSERT(succ);
- (void)succ;
- decoder->frame_in_progress.payload_len -= s_state_frame_push_promise_requires_4_bytes;
- /* Reserved bit (top bit) must be ignored when receiving (RFC-7540 4.1) */
- promised_stream_id &= s_31_bit_mask;
- /* Promised stream ID must not be 0 (RFC-7540 6.6).
- * Promised stream ID (server-initiated) must be even-numbered (RFC-7540 5.1.1). */
- if ((promised_stream_id == 0) || (promised_stream_id % 2) != 0) {
- DECODER_LOGF(ERROR, decoder, "PUSH_PROMISE is promising invalid stream ID %" PRIu32, promised_stream_id);
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- /* Server cannot receive PUSH_PROMISE frames */
- if (decoder->is_server) {
- DECODER_LOG(ERROR, decoder, "Server cannot receive PUSH_PROMISE frames");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- /* Start header-block and alert the user. */
- decoder->header_block_in_progress.stream_id = decoder->frame_in_progress.stream_id;
- decoder->header_block_in_progress.is_push_promise = true;
- decoder->header_block_in_progress.ends_stream = false;
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_push_promise_begin, promised_stream_id);
- /* Read the header-block fragment */
- return s_decoder_switch_state(decoder, &s_state_header_block_loop);
- }
- /* PING frame is just 8-bytes of opaque data.
- * +---------------------------------------------------------------+
- * | |
- * | Opaque Data (64) |
- * | |
- * +---------------------------------------------------------------+
- */
- static struct aws_h2err s_state_fn_frame_ping(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_frame_ping_requires_8_bytes);
- uint8_t opaque_data[AWS_HTTP2_PING_DATA_SIZE] = {0};
- bool succ = aws_byte_cursor_read(input, &opaque_data, AWS_HTTP2_PING_DATA_SIZE);
- AWS_ASSERT(succ);
- (void)succ;
- decoder->frame_in_progress.payload_len -= s_state_frame_ping_requires_8_bytes;
- if (decoder->frame_in_progress.flags.ack) {
- DECODER_CALL_VTABLE_ARGS(decoder, on_ping_ack, opaque_data);
- } else {
- DECODER_CALL_VTABLE_ARGS(decoder, on_ping, opaque_data);
- }
- return s_decoder_reset_state(decoder);
- }
- /* Read first 8 bytes of GOAWAY.
- * This may be followed by N bytes of debug data.
- * +-+-------------------------------------------------------------+
- * |R| Last-Stream-ID (31) |
- * +-+-------------------------------------------------------------+
- * | Error Code (32) |
- * +---------------------------------------------------------------+
- */
- static struct aws_h2err s_state_fn_frame_goaway(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_frame_goaway_requires_8_bytes);
- uint32_t last_stream = 0;
- uint32_t error_code = AWS_HTTP2_ERR_NO_ERROR;
- bool succ = aws_byte_cursor_read_be32(input, &last_stream);
- AWS_ASSERT(succ);
- (void)succ;
- last_stream &= s_31_bit_mask;
- succ = aws_byte_cursor_read_be32(input, &error_code);
- AWS_ASSERT(succ);
- (void)succ;
- decoder->frame_in_progress.payload_len -= s_state_frame_goaway_requires_8_bytes;
- uint32_t debug_data_length = decoder->frame_in_progress.payload_len;
- /* Received new GOAWAY, clean up the previous one. Buffer it up and invoke the callback once the debug data decoded
- * fully. */
- decoder->goaway_in_progress.error_code = error_code;
- decoder->goaway_in_progress.last_stream = last_stream;
- int init_result = aws_byte_buf_init(&decoder->goaway_in_progress.debug_data, decoder->alloc, debug_data_length);
- AWS_ASSERT(init_result == 0);
- (void)init_result;
- return s_decoder_switch_state(decoder, &s_state_frame_goaway_debug_data);
- }
- /* Optional remainder of GOAWAY frame.
- * +---------------------------------------------------------------+
- * | Additional Debug Data (*) |
- * +---------------------------------------------------------------+
- */
- static struct aws_h2err s_state_fn_frame_goaway_debug_data(
- struct aws_h2_decoder *decoder,
- struct aws_byte_cursor *input) {
- struct aws_byte_cursor debug_data = s_decoder_get_payload(decoder, input);
- if (debug_data.len > 0) {
- /* As we initialized the buffer to the size of debug data, we can safely append here */
- aws_byte_buf_append(&decoder->goaway_in_progress.debug_data, &debug_data);
- }
- /* If this is the last data in the frame, reset decoder */
- if (decoder->frame_in_progress.payload_len == 0) {
- struct aws_byte_cursor debug_cursor = aws_byte_cursor_from_buf(&decoder->goaway_in_progress.debug_data);
- DECODER_CALL_VTABLE_ARGS(
- decoder,
- on_goaway,
- decoder->goaway_in_progress.last_stream,
- decoder->goaway_in_progress.error_code,
- debug_cursor);
- aws_byte_buf_clean_up(&decoder->goaway_in_progress.debug_data);
- return s_decoder_reset_state(decoder);
- }
- return AWS_H2ERR_SUCCESS;
- }
- /* WINDOW_UPDATE frame.
- * +-+-------------------------------------------------------------+
- * |R| Window Size Increment (31) |
- * +-+-------------------------------------------------------------+
- */
- static struct aws_h2err s_state_fn_frame_window_update(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- AWS_ASSERT(input->len >= s_state_frame_window_update_requires_4_bytes);
- uint32_t window_increment = 0;
- bool succ = aws_byte_cursor_read_be32(input, &window_increment);
- AWS_ASSERT(succ);
- (void)succ;
- decoder->frame_in_progress.payload_len -= s_state_frame_window_update_requires_4_bytes;
- window_increment &= s_31_bit_mask;
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_window_update, window_increment);
- return s_decoder_reset_state(decoder);
- }
- /* CONTINUATION is a lot like HEADERS, so it uses shared states. */
- static struct aws_h2err s_state_fn_frame_continuation(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- (void)input;
- /* Read the header-block fragment */
- return s_decoder_switch_state(decoder, &s_state_header_block_loop);
- }
- /* Implementations MUST ignore and discard any frame that has a type that is unknown. */
- static struct aws_h2err s_state_fn_frame_unknown(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- /* Read all data possible, and throw it on the floor */
- s_decoder_get_payload(decoder, input);
- /* If there's no more data expected, end the frame */
- if (decoder->frame_in_progress.payload_len == 0) {
- return s_decoder_reset_state(decoder);
- }
- return AWS_H2ERR_SUCCESS;
- }
- /* Perform analysis that can't be done until all pseudo-headers are received.
- * Then deliver buffered pseudoheaders via callback */
- static struct aws_h2err s_flush_pseudoheaders(struct aws_h2_decoder *decoder) {
- struct aws_header_block_in_progress *current_block = &decoder->header_block_in_progress;
- if (current_block->malformed) {
- goto already_malformed;
- }
- if (current_block->pseudoheaders_done) {
- return AWS_H2ERR_SUCCESS;
- }
- current_block->pseudoheaders_done = true;
- /* s_process_header_field() already checked that we're not mixing request & response pseudoheaders */
- bool has_request_pseudoheaders = false;
- for (int i = PSEUDOHEADER_METHOD; i <= PSEUDOHEADER_PATH; ++i) {
- if (current_block->pseudoheader_values[i] != NULL) {
- has_request_pseudoheaders = true;
- break;
- }
- }
- bool has_response_pseudoheaders = current_block->pseudoheader_values[PSEUDOHEADER_STATUS] != NULL;
- if (current_block->is_push_promise && !has_request_pseudoheaders) {
- DECODER_LOG(ERROR, decoder, "PUSH_PROMISE is missing :method");
- goto malformed;
- }
- if (has_request_pseudoheaders) {
- /* Request header-block. */
- current_block->block_type = AWS_HTTP_HEADER_BLOCK_MAIN;
- } else if (has_response_pseudoheaders) {
- /* Response header block. */
- /* Determine whether this is an Informational (1xx) response */
- struct aws_byte_cursor status_value =
- aws_byte_cursor_from_string(current_block->pseudoheader_values[PSEUDOHEADER_STATUS]);
- uint64_t status_code;
- if (status_value.len != 3 || aws_byte_cursor_utf8_parse_u64(status_value, &status_code)) {
- DECODER_LOG(ERROR, decoder, ":status header has invalid value");
- DECODER_LOGF(DEBUG, decoder, "Bad :status value is '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(status_value));
- goto malformed;
- }
- if (status_code / 100 == 1) {
- current_block->block_type = AWS_HTTP_HEADER_BLOCK_INFORMATIONAL;
- if (current_block->ends_stream) {
- /* Informational headers do not constitute a full response (RFC-7540 8.1) */
- DECODER_LOG(ERROR, decoder, "Informational (1xx) response cannot END_STREAM");
- goto malformed;
- }
- current_block->body_headers_forbidden = true;
- } else {
- current_block->block_type = AWS_HTTP_HEADER_BLOCK_MAIN;
- }
- /**
- * RFC-9110 8.6.
- * A server MUST NOT send a Content-Length header field in any response with a status code of 1xx
- * (Informational) or 204 (No Content).
- */
- current_block->body_headers_forbidden |= status_code == AWS_HTTP_STATUS_CODE_204_NO_CONTENT;
- } else {
- /* Trailing header block. */
- if (!current_block->ends_stream) {
- DECODER_LOG(ERROR, decoder, "HEADERS appear to be trailer, but lack END_STREAM");
- goto malformed;
- }
- current_block->block_type = AWS_HTTP_HEADER_BLOCK_TRAILING;
- }
- /* #TODO RFC-7540 8.1.2.3 & 8.3 Validate request has correct pseudoheaders. Note different rules for CONNECT */
- /* #TODO validate pseudoheader values. each one has its own special rules */
- /* Finally, deliver header-fields via callback */
- for (size_t i = 0; i < PSEUDOHEADER_COUNT; ++i) {
- const struct aws_string *value_string = current_block->pseudoheader_values[i];
- if (value_string) {
- struct aws_http_header header_field = {
- .name = *s_pseudoheader_name_to_cursor[i],
- .value = aws_byte_cursor_from_string(value_string),
- .compression = current_block->pseudoheader_compression[i],
- };
- enum aws_http_header_name name_enum = s_pseudoheader_to_header_name[i];
- if (current_block->is_push_promise) {
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_push_promise_i, &header_field, name_enum);
- } else {
- DECODER_CALL_VTABLE_STREAM_ARGS(
- decoder, on_headers_i, &header_field, name_enum, current_block->block_type);
- }
- }
- }
- return AWS_H2ERR_SUCCESS;
- malformed:
- /* A malformed header-block is not a connection error, it's a Stream Error (RFC-7540 5.4.2).
- * We continue decoding and report that it's malformed in on_headers_end(). */
- current_block->malformed = true;
- return AWS_H2ERR_SUCCESS;
- already_malformed:
- return AWS_H2ERR_SUCCESS;
- }
- /* Process single header-field.
- * If it's invalid, mark the header-block as malformed.
- * If it's valid, and header-block is not malformed, deliver via callback. */
- static struct aws_h2err s_process_header_field(
- struct aws_h2_decoder *decoder,
- const struct aws_http_header *header_field) {
- struct aws_header_block_in_progress *current_block = &decoder->header_block_in_progress;
- if (current_block->malformed) {
- goto already_malformed;
- }
- const struct aws_byte_cursor name = header_field->name;
- if (name.len == 0) {
- DECODER_LOG(ERROR, decoder, "Header name is blank");
- goto malformed;
- }
- enum aws_http_header_name name_enum = aws_http_lowercase_str_to_header_name(name);
- bool is_pseudoheader = name.ptr[0] == ':';
- if (is_pseudoheader) {
- if (current_block->pseudoheaders_done) {
- /* Note: being careful not to leak possibly sensitive data except at DEBUG level and lower */
- DECODER_LOG(ERROR, decoder, "Pseudo-headers must appear before regular fields.");
- DECODER_LOGF(DEBUG, decoder, "Misplaced pseudo-header is '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(name));
- goto malformed;
- }
- enum pseudoheader_name pseudoheader_enum = s_header_to_pseudoheader_name(name_enum);
- if (pseudoheader_enum == PSEUDOHEADER_UNKNOWN) {
- DECODER_LOG(ERROR, decoder, "Unrecognized pseudo-header");
- DECODER_LOGF(DEBUG, decoder, "Unrecognized pseudo-header is '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(name));
- goto malformed;
- }
- /* Ensure request pseudo-headers vs response pseudoheaders were sent appropriately.
- * This also ensures that request and response pseudoheaders aren't being mixed. */
- bool expect_request_pseudoheader = decoder->is_server || current_block->is_push_promise;
- bool is_request_pseudoheader = pseudoheader_enum != PSEUDOHEADER_STATUS;
- if (expect_request_pseudoheader != is_request_pseudoheader) {
- DECODER_LOGF(
- ERROR, /* ok to log name of recognized pseudo-header at ERROR level */
- decoder,
- "'" PRInSTR "' pseudo-header cannot be in %s header-block to %s",
- AWS_BYTE_CURSOR_PRI(name),
- current_block->is_push_promise ? "PUSH_PROMISE" : "HEADERS",
- decoder->is_server ? "server" : "client");
- goto malformed;
- }
- /* Protect against duplicates. */
- if (current_block->pseudoheader_values[pseudoheader_enum] != NULL) {
- /* ok to log name of recognized pseudo-header at ERROR level */
- DECODER_LOGF(
- ERROR, decoder, "'" PRInSTR "' pseudo-header occurred multiple times", AWS_BYTE_CURSOR_PRI(name));
- goto malformed;
- }
- /* Buffer up pseudo-headers, we'll deliver them later once they're all validated. */
- current_block->pseudoheader_compression[pseudoheader_enum] = header_field->compression;
- current_block->pseudoheader_values[pseudoheader_enum] =
- aws_string_new_from_cursor(decoder->alloc, &header_field->value);
- if (!current_block->pseudoheader_values[pseudoheader_enum]) {
- return aws_h2err_from_last_error();
- }
- } else { /* Else regular header-field. */
- /* Regular header-fields come after pseudo-headers, so make sure pseudo-headers are flushed */
- if (!current_block->pseudoheaders_done) {
- struct aws_h2err err = s_flush_pseudoheaders(decoder);
- if (aws_h2err_failed(err)) {
- return err;
- }
- /* might have realized that header-block is malformed during flush */
- if (current_block->malformed) {
- goto already_malformed;
- }
- }
- /* Validate header name (not necessary if string already matched against a known enum) */
- if (name_enum == AWS_HTTP_HEADER_UNKNOWN) {
- if (!aws_strutil_is_lowercase_http_token(name)) {
- DECODER_LOG(ERROR, decoder, "Header name contains invalid characters");
- DECODER_LOGF(DEBUG, decoder, "Bad header name is '" PRInSTR "'", AWS_BYTE_CURSOR_PRI(name));
- goto malformed;
- }
- }
- /* #TODO Validate characters used in header_field->value */
- switch (name_enum) {
- case AWS_HTTP_HEADER_COOKIE:
- /* for a header cookie, we will not fire callback until we concatenate them all, let's store it at the
- * buffer */
- if (header_field->compression > current_block->cookie_header_compression_type) {
- current_block->cookie_header_compression_type = header_field->compression;
- }
- if (current_block->cookies.len) {
- /* add a delimiter */
- struct aws_byte_cursor delimiter = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("; ");
- if (aws_byte_buf_append_dynamic(¤t_block->cookies, &delimiter)) {
- return aws_h2err_from_last_error();
- }
- }
- if (aws_byte_buf_append_dynamic(¤t_block->cookies, &header_field->value)) {
- return aws_h2err_from_last_error();
- }
- /* Early return */
- return AWS_H2ERR_SUCCESS;
- case AWS_HTTP_HEADER_TRANSFER_ENCODING:
- case AWS_HTTP_HEADER_UPGRADE:
- case AWS_HTTP_HEADER_KEEP_ALIVE:
- case AWS_HTTP_HEADER_PROXY_CONNECTION: {
- /* connection-specific header field are treated as malformed (RFC9113 8.2.2) */
- DECODER_LOGF(
- ERROR,
- decoder,
- "Connection-specific header ('" PRInSTR "') found, not allowed in HTTP/2",
- AWS_BYTE_CURSOR_PRI(name));
- goto malformed;
- } break;
- case AWS_HTTP_HEADER_CONTENT_LENGTH:
- if (current_block->body_headers_forbidden) {
- /* The content-length are forbidden */
- DECODER_LOG(ERROR, decoder, "Unexpected Content-Length header found");
- goto malformed;
- }
- break;
- default:
- break;
- }
- /* Deliver header-field via callback */
- if (current_block->is_push_promise) {
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_push_promise_i, header_field, name_enum);
- } else {
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_headers_i, header_field, name_enum, current_block->block_type);
- }
- }
- return AWS_H2ERR_SUCCESS;
- malformed:
- /* A malformed header-block is not a connection error, it's a Stream Error (RFC-7540 5.4.2).
- * We continue decoding and report that it's malformed in on_headers_end(). */
- current_block->malformed = true;
- return AWS_H2ERR_SUCCESS;
- already_malformed:
- return AWS_H2ERR_SUCCESS;
- }
- static struct aws_h2err s_flush_cookie_header(struct aws_h2_decoder *decoder) {
- struct aws_header_block_in_progress *current_block = &decoder->header_block_in_progress;
- if (current_block->malformed) {
- return AWS_H2ERR_SUCCESS;
- }
- if (current_block->cookies.len == 0) {
- /* Nothing to flush */
- return AWS_H2ERR_SUCCESS;
- }
- struct aws_http_header concatenated_cookie;
- struct aws_byte_cursor header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("cookie");
- concatenated_cookie.name = header_name;
- concatenated_cookie.value = aws_byte_cursor_from_buf(¤t_block->cookies);
- concatenated_cookie.compression = current_block->cookie_header_compression_type;
- if (current_block->is_push_promise) {
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_push_promise_i, &concatenated_cookie, AWS_HTTP_HEADER_COOKIE);
- } else {
- DECODER_CALL_VTABLE_STREAM_ARGS(
- decoder, on_headers_i, &concatenated_cookie, AWS_HTTP_HEADER_COOKIE, current_block->block_type);
- }
- return AWS_H2ERR_SUCCESS;
- }
- /* This state checks whether we've consumed the current frame's entire header-block fragment.
- * We revisit this state after each entry is decoded.
- * This state consumes no data. */
- static struct aws_h2err s_state_fn_header_block_loop(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- (void)input;
- /* If we're out of payload data, handle frame complete */
- if (decoder->frame_in_progress.payload_len == 0) {
- /* If this is the end of the header-block, invoke callback and clear header_block_in_progress */
- if (decoder->frame_in_progress.flags.end_headers) {
- /* Ensure pseudo-headers have been flushed */
- struct aws_h2err err = s_flush_pseudoheaders(decoder);
- if (aws_h2err_failed(err)) {
- return err;
- }
- /* flush the concatenated cookie header */
- err = s_flush_cookie_header(decoder);
- if (aws_h2err_failed(err)) {
- return err;
- }
- bool malformed = decoder->header_block_in_progress.malformed;
- DECODER_LOGF(TRACE, decoder, "Done decoding header-block, malformed=%d", malformed);
- if (decoder->header_block_in_progress.is_push_promise) {
- DECODER_CALL_VTABLE_STREAM_ARGS(decoder, on_push_promise_end, malformed);
- } else {
- DECODER_CALL_VTABLE_STREAM_ARGS(
- decoder, on_headers_end, malformed, decoder->header_block_in_progress.block_type);
- }
- /* If header-block began with END_STREAM flag, alert user now */
- if (decoder->header_block_in_progress.ends_stream) {
- DECODER_CALL_VTABLE_STREAM(decoder, on_end_stream);
- }
- s_reset_header_block_in_progress(decoder);
- } else {
- DECODER_LOG(TRACE, decoder, "Done decoding header-block fragment, expecting CONTINUATION frames");
- }
- /* Finish this frame */
- return s_decoder_switch_state(decoder, &s_state_padding);
- }
- DECODER_LOGF(
- TRACE,
- decoder,
- "Decoding header-block entry, %" PRIu32 " bytes remaining in payload",
- decoder->frame_in_progress.payload_len);
- return s_decoder_switch_state(decoder, &s_state_header_block_entry);
- }
- /* We stay in this state until a single "entry" is decoded from the header-block fragment.
- * Then we return to the header_block_loop state */
- static struct aws_h2err s_state_fn_header_block_entry(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {
- /* This state requires at least 1 byte, but will likely consume more */
- AWS_ASSERT(input->len >= s_state_header_block_entry_requires_1_bytes);
- /* Feed header-block fragment to HPACK decoder.
- * Don't let decoder consume anything beyond payload_len. */
- struct aws_byte_cursor fragment = *input;
- if (fragment.len > decoder->frame_in_progress.payload_len) {
- fragment.len = decoder->frame_in_progress.payload_len;
- }
- const size_t prev_fragment_len = fragment.len;
- struct aws_hpack_decode_result result;
- if (aws_hpack_decode(&decoder->hpack, &fragment, &result)) {
- DECODER_LOGF(ERROR, decoder, "Error decoding header-block fragment: %s", aws_error_name(aws_last_error()));
- /* Any possible error from HPACK decoder (except OOM) is treated as a COMPRESSION error. */
- if (aws_last_error() == AWS_ERROR_OOM) {
- return aws_h2err_from_last_error();
- } else {
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_COMPRESSION_ERROR);
- }
- }
- /* HPACK decoder returns when it reaches the end of an entry, or when it's consumed the whole fragment.
- * Update input & payload_len to reflect the number of bytes consumed. */
- const size_t bytes_consumed = prev_fragment_len - fragment.len;
- aws_byte_cursor_advance(input, bytes_consumed);
- decoder->frame_in_progress.payload_len -= (uint32_t)bytes_consumed;
- if (result.type == AWS_HPACK_DECODE_T_ONGOING) {
- /* HPACK decoder hasn't finished entry */
- if (decoder->frame_in_progress.payload_len > 0) {
- /* More payload is coming. Remain in state until it arrives */
- DECODER_LOG(TRACE, decoder, "Header-block entry partially decoded, waiting for more data.");
- return AWS_H2ERR_SUCCESS;
- }
- if (decoder->frame_in_progress.flags.end_headers) {
- /* Reached end of the frame's payload, and this frame ends the header-block.
- * Error if we ended up with a partially decoded entry. */
- DECODER_LOG(ERROR, decoder, "Compression error: incomplete entry at end of header-block");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_COMPRESSION_ERROR);
- }
- /* Reached end of this frame's payload, but CONTINUATION frames are expected to arrive.
- * We'll resume decoding this entry when we get them. */
- DECODER_LOG(TRACE, decoder, "Header-block entry partially decoded, resumes in CONTINUATION frame");
- return s_decoder_switch_state(decoder, &s_state_header_block_loop);
- }
- /* Finished decoding HPACK entry! */
- /* #TODO Enforces dynamic table resize rules from RFC-7541 4.2
- * If dynamic table size changed via SETTINGS frame, next header-block must start with DYNAMIC_TABLE_RESIZE entry.
- * Is it illegal to receive a resize entry at other times? */
- /* #TODO The TE header field ... MUST NOT contain any value other than "trailers" */
- if (result.type == AWS_HPACK_DECODE_T_HEADER_FIELD) {
- const struct aws_http_header *header_field = &result.data.header_field;
- DECODER_LOGF(
- TRACE,
- decoder,
- "Decoded header field: \"" PRInSTR ": " PRInSTR "\"",
- AWS_BYTE_CURSOR_PRI(header_field->name),
- AWS_BYTE_CURSOR_PRI(header_field->value));
- struct aws_h2err err = s_process_header_field(decoder, header_field);
- if (aws_h2err_failed(err)) {
- return err;
- }
- }
- return s_decoder_switch_state(decoder, &s_state_header_block_loop);
- }
- /* The first thing a client sends on a connection is a 24 byte magic string (RFC-7540 3.5).
- * Note that this state doesn't "require" the full 24 bytes, it runs as data arrives.
- * This avoids hanging if < 24 bytes rolled in. */
- static struct aws_h2err s_state_fn_connection_preface_string(
- struct aws_h2_decoder *decoder,
- struct aws_byte_cursor *input) {
- size_t remaining_len = decoder->connection_preface_cursor.len;
- size_t consuming_len = input->len < remaining_len ? input->len : remaining_len;
- struct aws_byte_cursor expected = aws_byte_cursor_advance(&decoder->connection_preface_cursor, consuming_len);
- struct aws_byte_cursor received = aws_byte_cursor_advance(input, consuming_len);
- if (!aws_byte_cursor_eq(&expected, &received)) {
- DECODER_LOG(ERROR, decoder, "Client connection preface is invalid");
- return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
- }
- if (decoder->connection_preface_cursor.len == 0) {
- /* Done receiving connection preface string, proceed to decoding normal frames. */
- return s_decoder_reset_state(decoder);
- }
- /* Remain in state until more data arrives */
- return AWS_H2ERR_SUCCESS;
- }
- void aws_h2_decoder_set_setting_header_table_size(struct aws_h2_decoder *decoder, uint32_t data) {
- /* Set the protocol_max_size_setting for hpack. */
- aws_hpack_decoder_update_max_table_size(&decoder->hpack, data);
- }
- void aws_h2_decoder_set_setting_enable_push(struct aws_h2_decoder *decoder, uint32_t data) {
- decoder->settings.enable_push = data;
- }
- void aws_h2_decoder_set_setting_max_frame_size(struct aws_h2_decoder *decoder, uint32_t data) {
- decoder->settings.max_frame_size = data;
- }
|