static struct aws_h2err s_process_header_field()

in source/h2_decoder.c [1234:1365]


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(&current_block->cookies, &delimiter)) {
                        return aws_h2err_from_last_error();
                    }
                }
                if (aws_byte_buf_append_dynamic(&current_block->cookies, &header_field->value)) {
                    return aws_h2err_from_last_error();
                }
                break;
            /* TODO: Validate connection-specific header field (RFC7540 8.1.2.2) */
            default:
                /* 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);
                }
                break;
        }
    }

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