in source/h1_decoder.c [382:580]
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;
}