123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761 |
- /**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
- #include <aws/common/string.h>
- #include <aws/http/private/h1_decoder.h>
- #include <aws/http/private/strutil.h>
- #include <aws/http/status_code.h>
- #include <aws/io/logging.h>
- AWS_STATIC_STRING_FROM_LITERAL(s_transfer_coding_chunked, "chunked");
- AWS_STATIC_STRING_FROM_LITERAL(s_transfer_coding_compress, "compress");
- AWS_STATIC_STRING_FROM_LITERAL(s_transfer_coding_x_compress, "x-compress");
- AWS_STATIC_STRING_FROM_LITERAL(s_transfer_coding_deflate, "deflate");
- AWS_STATIC_STRING_FROM_LITERAL(s_transfer_coding_gzip, "gzip");
- AWS_STATIC_STRING_FROM_LITERAL(s_transfer_coding_x_gzip, "x-gzip");
- /* Decoder runs a state machine.
- * Each state consumes data until it sets the next state.
- * A common state is the "line state", which handles consuming one line ending in CRLF
- * and feeding the line to a linestate_fn, which should process data and set the next state.
- */
- typedef int(state_fn)(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input);
- typedef int(linestate_fn)(struct aws_h1_decoder *decoder, struct aws_byte_cursor input);
- struct aws_h1_decoder {
- /* Implementation data. */
- struct aws_allocator *alloc;
- struct aws_byte_buf scratch_space;
- state_fn *run_state;
- linestate_fn *process_line;
- int transfer_encoding;
- uint64_t content_processed;
- uint64_t content_length;
- uint64_t chunk_processed;
- uint64_t chunk_size;
- bool doing_trailers;
- bool is_done;
- bool body_headers_ignored;
- bool body_headers_forbidden;
- enum aws_http_header_block header_block;
- const void *logging_id;
- /* User callbacks and settings. */
- struct aws_h1_decoder_vtable vtable;
- bool is_decoding_requests;
- void *user_data;
- };
- static int s_linestate_request(struct aws_h1_decoder *decoder, struct aws_byte_cursor input);
- static int s_linestate_response(struct aws_h1_decoder *decoder, struct aws_byte_cursor input);
- static int s_linestate_header(struct aws_h1_decoder *decoder, struct aws_byte_cursor input);
- static int s_linestate_chunk_size(struct aws_h1_decoder *decoder, struct aws_byte_cursor input);
- static bool s_scan_for_crlf(struct aws_h1_decoder *decoder, struct aws_byte_cursor input, size_t *bytes_processed) {
- AWS_ASSERT(input.len > 0);
- /* In a loop, scan for "\n", then look one char back for "\r" */
- uint8_t *ptr = input.ptr;
- uint8_t *end = input.ptr + input.len;
- while (ptr != end) {
- uint8_t *newline = (uint8_t *)memchr(ptr, '\n', end - ptr);
- if (!newline) {
- break;
- }
- uint8_t prev_char;
- if (newline == input.ptr) {
- /* If "\n" is first character check scratch_space for previous character */
- if (decoder->scratch_space.len > 0) {
- prev_char = decoder->scratch_space.buffer[decoder->scratch_space.len - 1];
- } else {
- prev_char = 0;
- }
- } else {
- prev_char = *(newline - 1);
- }
- if (prev_char == '\r') {
- *bytes_processed = 1 + (newline - input.ptr);
- return true;
- }
- ptr = newline + 1;
- }
- *bytes_processed = input.len;
- return false;
- }
- /* This state consumes an entire line, then calls a linestate_fn to process the line. */
- static int s_state_getline(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input) {
- /* If preceding runs of this state failed to find CRLF, their data is stored in the scratch_space
- * and new data needs to be combined with the old data for processing. */
- bool has_prev_data = decoder->scratch_space.len;
- size_t line_length = 0;
- bool found_crlf = s_scan_for_crlf(decoder, *input, &line_length);
- /* Found end of line! Run the line processor on it */
- struct aws_byte_cursor line = aws_byte_cursor_advance(input, line_length);
- bool use_scratch = !found_crlf | has_prev_data;
- if (AWS_UNLIKELY(use_scratch)) {
- if (aws_byte_buf_append_dynamic(&decoder->scratch_space, &line)) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Internal buffer write failed with error code %d (%s)",
- decoder->logging_id,
- aws_last_error(),
- aws_error_name(aws_last_error()));
- return AWS_OP_ERR;
- }
- /* Line is actually the entire scratch buffer now */
- line = aws_byte_cursor_from_buf(&decoder->scratch_space);
- }
- if (AWS_LIKELY(found_crlf)) {
- /* Backup so "\r\n" is not included. */
- /* RFC-7230 section 3 Message Format */
- AWS_ASSERT(line.len >= 2);
- line.len -= 2;
- return decoder->process_line(decoder, line);
- }
- /* Didn't find crlf, we'll continue scanning when more data comes in */
- return AWS_OP_SUCCESS;
- }
- static int s_cursor_split_impl(
- struct aws_byte_cursor input,
- char split_on,
- struct aws_byte_cursor *cursor_array,
- size_t num_cursors,
- bool error_if_more_splits_possible) {
- struct aws_byte_cursor split;
- AWS_ZERO_STRUCT(split);
- for (size_t i = 0; i < num_cursors; ++i) {
- if (!aws_byte_cursor_next_split(&input, split_on, &split)) {
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- cursor_array[i] = split;
- }
- if (error_if_more_splits_possible) {
- if (aws_byte_cursor_next_split(&input, split_on, &split)) {
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- } else {
- /* Otherwise, the last cursor will contain the remainder of the string */
- struct aws_byte_cursor *last_cursor = &cursor_array[num_cursors - 1];
- last_cursor->len = (input.ptr + input.len) - last_cursor->ptr;
- }
- return AWS_OP_SUCCESS;
- }
- /* Final cursor contains remainder of input. */
- static int s_cursor_split_first_n_times(
- struct aws_byte_cursor input,
- char split_on,
- struct aws_byte_cursor *cursor_array,
- size_t num_cursors) {
- return s_cursor_split_impl(input, split_on, cursor_array, num_cursors, false);
- }
- /* Error if input could have been split more times */
- static int s_cursor_split_exactly_n_times(
- struct aws_byte_cursor input,
- char split_on,
- struct aws_byte_cursor *cursor_array,
- size_t num_cursors) {
- return s_cursor_split_impl(input, split_on, cursor_array, num_cursors, true);
- }
- static void s_set_state(struct aws_h1_decoder *decoder, state_fn *state) {
- decoder->scratch_space.len = 0;
- decoder->run_state = state;
- decoder->process_line = NULL;
- }
- /* Set next state to capture a full line, then call the specified linestate_fn on it */
- static void s_set_line_state(struct aws_h1_decoder *decoder, linestate_fn *line_processor) {
- s_set_state(decoder, s_state_getline);
- decoder->process_line = line_processor;
- }
- static int s_mark_done(struct aws_h1_decoder *decoder) {
- decoder->is_done = true;
- return decoder->vtable.on_done(decoder->user_data);
- }
- /* Reset state, in preparation for processing a new message */
- static void s_reset_state(struct aws_h1_decoder *decoder) {
- if (decoder->is_decoding_requests) {
- s_set_line_state(decoder, s_linestate_request);
- } else {
- s_set_line_state(decoder, s_linestate_response);
- }
- decoder->transfer_encoding = 0;
- decoder->content_processed = 0;
- decoder->content_length = 0;
- decoder->chunk_processed = 0;
- decoder->chunk_size = 0;
- decoder->doing_trailers = false;
- decoder->is_done = false;
- decoder->body_headers_ignored = false;
- decoder->body_headers_forbidden = false;
- /* set to normal by default */
- decoder->header_block = AWS_HTTP_HEADER_BLOCK_MAIN;
- }
- static int s_state_unchunked_body(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input) {
- size_t processed_bytes = 0;
- AWS_FATAL_ASSERT(decoder->content_processed < decoder->content_length); /* shouldn't be possible */
- if (input->len > (decoder->content_length - decoder->content_processed)) {
- processed_bytes = (size_t)(decoder->content_length - decoder->content_processed);
- } else {
- processed_bytes = input->len;
- }
- decoder->content_processed += processed_bytes;
- bool finished = decoder->content_processed == decoder->content_length;
- struct aws_byte_cursor body = aws_byte_cursor_advance(input, processed_bytes);
- int err = decoder->vtable.on_body(&body, finished, decoder->user_data);
- if (err) {
- return AWS_OP_ERR;
- }
- if (AWS_LIKELY(finished)) {
- err = s_mark_done(decoder);
- if (err) {
- return AWS_OP_ERR;
- }
- }
- return AWS_OP_SUCCESS;
- }
- static int s_linestate_chunk_terminator(struct aws_h1_decoder *decoder, struct aws_byte_cursor input) {
- /* Expecting CRLF at end of each chunk */
- /* RFC-7230 section 4.1 Chunked Transfer Encoding */
- if (AWS_UNLIKELY(input.len != 0)) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM, "id=%p: Incoming chunk is invalid, does not end with CRLF.", decoder->logging_id);
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- s_set_line_state(decoder, s_linestate_chunk_size);
- return AWS_OP_SUCCESS;
- }
- static int s_state_chunk(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input) {
- size_t processed_bytes = 0;
- AWS_ASSERT(decoder->chunk_processed < decoder->chunk_size);
- if (input->len > (decoder->chunk_size - decoder->chunk_processed)) {
- processed_bytes = (size_t)(decoder->chunk_size - decoder->chunk_processed);
- } else {
- processed_bytes = input->len;
- }
- decoder->chunk_processed += processed_bytes;
- bool finished = decoder->chunk_processed == decoder->chunk_size;
- struct aws_byte_cursor body = aws_byte_cursor_advance(input, processed_bytes);
- int err = decoder->vtable.on_body(&body, false, decoder->user_data);
- if (err) {
- return AWS_OP_ERR;
- }
- if (AWS_LIKELY(finished)) {
- s_set_line_state(decoder, s_linestate_chunk_terminator);
- }
- return AWS_OP_SUCCESS;
- }
- static int s_linestate_chunk_size(struct aws_h1_decoder *decoder, struct aws_byte_cursor input) {
- struct aws_byte_cursor size;
- AWS_ZERO_STRUCT(size);
- if (!aws_byte_cursor_next_split(&input, ';', &size)) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM, "id=%p: Incoming chunk is invalid, first line is malformed.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad chunk line is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(input));
- return AWS_OP_ERR;
- }
- int err = aws_byte_cursor_utf8_parse_u64_hex(size, &decoder->chunk_size);
- if (err) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Failed to parse size of incoming chunk.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad chunk size is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(size));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- decoder->chunk_processed = 0;
- /* Empty chunk signifies all chunks have been read. */
- if (AWS_UNLIKELY(decoder->chunk_size == 0)) {
- struct aws_byte_cursor cursor;
- cursor.ptr = NULL;
- cursor.len = 0;
- err = decoder->vtable.on_body(&cursor, true, decoder->user_data);
- if (err) {
- return AWS_OP_ERR;
- }
- /* Expected empty newline and end of message. */
- decoder->doing_trailers = true;
- s_set_line_state(decoder, s_linestate_header);
- return AWS_OP_SUCCESS;
- }
- /* Skip all chunk extensions, as they are optional. */
- /* RFC-7230 section 4.1.1 Chunk Extensions */
- s_set_state(decoder, s_state_chunk);
- return AWS_OP_SUCCESS;
- }
- static int s_linestate_header(struct aws_h1_decoder *decoder, struct aws_byte_cursor input) {
- int err;
- /* The \r\n was just processed by `s_state_getline`. */
- /* Empty line signifies end of headers, and beginning of body or end of trailers. */
- /* RFC-7230 section 3 Message Format */
- if (input.len == 0) {
- if (AWS_LIKELY(!decoder->doing_trailers)) {
- if (decoder->body_headers_ignored) {
- err = s_mark_done(decoder);
- if (err) {
- return AWS_OP_ERR;
- }
- } else if (decoder->transfer_encoding & AWS_HTTP_TRANSFER_ENCODING_CHUNKED) {
- s_set_line_state(decoder, s_linestate_chunk_size);
- } else if (decoder->content_length > 0) {
- s_set_state(decoder, s_state_unchunked_body);
- } else {
- err = s_mark_done(decoder);
- if (err) {
- return AWS_OP_ERR;
- }
- }
- } else {
- /* Empty line means end of message. */
- err = s_mark_done(decoder);
- if (err) {
- return AWS_OP_ERR;
- }
- }
- return AWS_OP_SUCCESS;
- }
- /* Each header field consists of a case-insensitive field name followed by a colon (":"),
- * optional leading whitespace, the field value, and optional trailing whitespace.
- * RFC-7230 3.2 */
- struct aws_byte_cursor splits[2];
- err = s_cursor_split_first_n_times(input, ':', splits, 2); /* value may contain more colons */
- if (err) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Invalid incoming header, missing colon.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM, "id=%p: Bad header is: '" PRInSTR "'", decoder->logging_id, AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- struct aws_byte_cursor name = splits[0];
- if (!aws_strutil_is_http_token(name)) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Invalid incoming header, bad name.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM, "id=%p: Bad header is: '" PRInSTR "'", decoder->logging_id, AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- struct aws_byte_cursor value = aws_strutil_trim_http_whitespace(splits[1]);
- if (!aws_strutil_is_http_field_value(value)) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Invalid incoming header, bad value.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM, "id=%p: Bad header is: '" PRInSTR "'", decoder->logging_id, AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- struct aws_h1_decoded_header header;
- header.name = aws_http_str_to_header_name(name);
- header.name_data = name;
- header.value_data = value;
- header.data = input;
- switch (header.name) {
- case AWS_HTTP_HEADER_CONTENT_LENGTH:
- if (decoder->transfer_encoding) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Incoming headers for both content-length and transfer-encoding received. This is illegal.",
- decoder->logging_id);
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- if (aws_byte_cursor_utf8_parse_u64(header.value_data, &decoder->content_length)) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Incoming content-length header has invalid value.",
- decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad content-length value is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(header.value_data));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- if (decoder->body_headers_forbidden && decoder->content_length != 0) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Incoming headers for content-length received, but it is illegal for this message to have a "
- "body",
- decoder->logging_id);
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- break;
- case AWS_HTTP_HEADER_TRANSFER_ENCODING: {
- if (decoder->content_length) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Incoming headers for both content-length and transfer-encoding received. This is illegal.",
- decoder->logging_id);
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- if (decoder->body_headers_forbidden) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Incoming headers for transfer-encoding received, but it is illegal for this message to "
- "have a body",
- decoder->logging_id);
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- /* RFC-7230 section 3.3.1 Transfer-Encoding */
- /* RFC-7230 section 4.2 Compression Codings */
- /* Note that it's possible for multiple Transfer-Encoding headers to exist, in which case the values
- * should be appended with those from any previously encountered Transfer-Encoding headers. */
- struct aws_byte_cursor split;
- AWS_ZERO_STRUCT(split);
- while (aws_byte_cursor_next_split(&header.value_data, ',', &split)) {
- struct aws_byte_cursor coding = aws_strutil_trim_http_whitespace(split);
- int prev_flags = decoder->transfer_encoding;
- if (aws_string_eq_byte_cursor_ignore_case(s_transfer_coding_chunked, &coding)) {
- decoder->transfer_encoding |= AWS_HTTP_TRANSFER_ENCODING_CHUNKED;
- } else if (
- aws_string_eq_byte_cursor_ignore_case(s_transfer_coding_compress, &coding) ||
- aws_string_eq_byte_cursor_ignore_case(s_transfer_coding_x_compress, &coding)) {
- /* A recipient SHOULD consider "x-compress" to be equivalent to "compress". RFC-7230 4.2.1 */
- decoder->transfer_encoding |= AWS_HTTP_TRANSFER_ENCODING_DEPRECATED_COMPRESS;
- } else if (aws_string_eq_byte_cursor_ignore_case(s_transfer_coding_deflate, &coding)) {
- decoder->transfer_encoding |= AWS_HTTP_TRANSFER_ENCODING_DEFLATE;
- } else if (
- aws_string_eq_byte_cursor_ignore_case(s_transfer_coding_gzip, &coding) ||
- aws_string_eq_byte_cursor_ignore_case(s_transfer_coding_x_gzip, &coding)) {
- /* A recipient SHOULD consider "x-gzip" to be equivalent to "gzip". RFC-7230 4.2.3 */
- decoder->transfer_encoding |= AWS_HTTP_TRANSFER_ENCODING_GZIP;
- } else if (coding.len > 0) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Incoming transfer-encoding header lists unrecognized coding.",
- decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Unrecognized coding is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(coding));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- /* If any transfer coding other than chunked is applied to a request payload body, the sender MUST
- * apply chunked as the final transfer coding to ensure that the message is properly framed.
- * RFC-7230 3.3.1 */
- if ((prev_flags & AWS_HTTP_TRANSFER_ENCODING_CHUNKED) && (decoder->transfer_encoding != prev_flags)) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM,
- "id=%p: Incoming transfer-encoding header lists a coding after 'chunked', this is illegal.",
- decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Misplaced coding is '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(coding));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- }
- /* TODO: deal with body of indeterminate length, marking it as successful when connection is closed:
- *
- * A response that has neither chunked transfer coding nor Content-Length is terminated by closure of
- * the connection and, thus, is considered complete regardless of the number of message body octets
- * received, provided that the header section was received intact.
- * RFC-7230 3.4 */
- } break;
- default:
- break;
- }
- err = decoder->vtable.on_header(&header, decoder->user_data);
- if (err) {
- return AWS_OP_ERR;
- }
- s_set_line_state(decoder, s_linestate_header);
- return AWS_OP_SUCCESS;
- }
- static int s_linestate_request(struct aws_h1_decoder *decoder, struct aws_byte_cursor input) {
- struct aws_byte_cursor cursors[3];
- int err = s_cursor_split_exactly_n_times(input, ' ', cursors, 3); /* extra spaces not allowed */
- if (err) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM, "id=%p: Incoming request line has wrong number of spaces.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad request line is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- for (size_t i = 0; i < AWS_ARRAY_SIZE(cursors); ++i) {
- if (cursors[i].len == 0) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Incoming request line has empty values.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad request line is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- }
- struct aws_byte_cursor method = cursors[0];
- struct aws_byte_cursor uri = cursors[1];
- struct aws_byte_cursor version = cursors[2];
- if (!aws_strutil_is_http_token(method)) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Incoming request has invalid method.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad request line is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- if (!aws_strutil_is_http_request_target(uri)) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Incoming request has invalid path.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad request line is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- struct aws_byte_cursor version_expected = aws_http_version_to_str(AWS_HTTP_VERSION_1_1);
- if (!aws_byte_cursor_eq(&version, &version_expected)) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM, "id=%p: Incoming request uses unsupported HTTP version.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Unsupported version is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(version));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- err = decoder->vtable.on_request(aws_http_str_to_method(method), &method, &uri, decoder->user_data);
- if (err) {
- return AWS_OP_ERR;
- }
- s_set_line_state(decoder, s_linestate_header);
- return AWS_OP_SUCCESS;
- }
- static bool s_check_info_response_status_code(int code_val) {
- return code_val >= 100 && code_val < 200;
- }
- static int s_linestate_response(struct aws_h1_decoder *decoder, struct aws_byte_cursor input) {
- struct aws_byte_cursor cursors[3];
- int err = s_cursor_split_first_n_times(input, ' ', cursors, 3); /* phrase may contain spaces */
- if (err) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Incoming response status line is invalid.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad status line is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(input));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- struct aws_byte_cursor version = cursors[0];
- struct aws_byte_cursor code = cursors[1];
- struct aws_byte_cursor phrase = cursors[2];
- struct aws_byte_cursor version_1_1_expected = aws_http_version_to_str(AWS_HTTP_VERSION_1_1);
- struct aws_byte_cursor version_1_0_expected = aws_http_version_to_str(AWS_HTTP_VERSION_1_0);
- if (!aws_byte_cursor_eq(&version, &version_1_1_expected) && !aws_byte_cursor_eq(&version, &version_1_0_expected)) {
- AWS_LOGF_ERROR(
- AWS_LS_HTTP_STREAM, "id=%p: Incoming response uses unsupported HTTP version.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Unsupported version is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(version));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- /* Validate phrase */
- if (!aws_strutil_is_http_reason_phrase(phrase)) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Incoming response has invalid reason phrase.", decoder->logging_id);
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- /* Status-code is a 3-digit integer. RFC7230 section 3.1.2 */
- uint64_t code_val_u64;
- err = aws_byte_cursor_utf8_parse_u64(code, &code_val_u64);
- if (err || code.len != 3 || code_val_u64 > 999) {
- AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Incoming response has invalid status code.", decoder->logging_id);
- AWS_LOGF_DEBUG(
- AWS_LS_HTTP_STREAM,
- "id=%p: Bad status code is: '" PRInSTR "'",
- decoder->logging_id,
- AWS_BYTE_CURSOR_PRI(code));
- return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
- }
- int code_val = (int)code_val_u64;
- /* RFC-7230 section 3.3 Message Body */
- decoder->body_headers_ignored |= code_val == AWS_HTTP_STATUS_CODE_304_NOT_MODIFIED;
- decoder->body_headers_forbidden = code_val == AWS_HTTP_STATUS_CODE_204_NO_CONTENT || code_val / 100 == 1;
- if (s_check_info_response_status_code(code_val)) {
- decoder->header_block = AWS_HTTP_HEADER_BLOCK_INFORMATIONAL;
- }
- err = decoder->vtable.on_response(code_val, decoder->user_data);
- if (err) {
- return AWS_OP_ERR;
- }
- s_set_line_state(decoder, s_linestate_header);
- return AWS_OP_SUCCESS;
- }
- struct aws_h1_decoder *aws_h1_decoder_new(struct aws_h1_decoder_params *params) {
- AWS_ASSERT(params);
- struct aws_h1_decoder *decoder = aws_mem_acquire(params->alloc, sizeof(struct aws_h1_decoder));
- if (!decoder) {
- return NULL;
- }
- AWS_ZERO_STRUCT(*decoder);
- decoder->alloc = params->alloc;
- decoder->user_data = params->user_data;
- decoder->vtable = params->vtable;
- decoder->is_decoding_requests = params->is_decoding_requests;
- aws_byte_buf_init(&decoder->scratch_space, params->alloc, params->scratch_space_initial_size);
- s_reset_state(decoder);
- return decoder;
- }
- void aws_h1_decoder_destroy(struct aws_h1_decoder *decoder) {
- if (!decoder) {
- return;
- }
- aws_byte_buf_clean_up(&decoder->scratch_space);
- aws_mem_release(decoder->alloc, decoder);
- }
- int aws_h1_decode(struct aws_h1_decoder *decoder, struct aws_byte_cursor *data) {
- AWS_ASSERT(decoder);
- AWS_ASSERT(data);
- struct aws_byte_cursor backup = *data;
- while (data->len && !decoder->is_done) {
- int err = decoder->run_state(decoder, data);
- if (err) {
- /* Reset the data param to how we found it */
- *data = backup;
- return AWS_OP_ERR;
- }
- }
- if (decoder->is_done) {
- s_reset_state(decoder);
- }
- return AWS_OP_SUCCESS;
- }
- int aws_h1_decoder_get_encoding_flags(const struct aws_h1_decoder *decoder) {
- return decoder->transfer_encoding;
- }
- uint64_t aws_h1_decoder_get_content_length(const struct aws_h1_decoder *decoder) {
- return decoder->content_length;
- }
- bool aws_h1_decoder_get_body_headers_ignored(const struct aws_h1_decoder *decoder) {
- return decoder->body_headers_ignored;
- }
- enum aws_http_header_block aws_h1_decoder_get_header_block(const struct aws_h1_decoder *decoder) {
- return decoder->header_block;
- }
- void aws_h1_decoder_set_logging_id(struct aws_h1_decoder *decoder, const void *id) {
- decoder->logging_id = id;
- }
- void aws_h1_decoder_set_body_headers_ignored(struct aws_h1_decoder *decoder, bool body_headers_ignored) {
- decoder->body_headers_ignored = body_headers_ignored;
- }
|