static struct aws_h2err s_flush_pseudoheaders()

in source/h2_decoder.c [1128:1229]


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;
            }
        } else {
            current_block->block_type = AWS_HTTP_HEADER_BLOCK_MAIN;
        }

    } 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;
}