struct aws_h2err s_get_active_stream_for_incoming_frame()

in source/h2_connection.c [901:1037]


struct aws_h2err s_get_active_stream_for_incoming_frame(
    struct aws_h2_connection *connection,
    uint32_t stream_id,
    enum aws_h2_frame_type frame_type,
    struct aws_h2_stream **out_stream) {

    *out_stream = NULL;

    /* Check active streams */
    struct aws_hash_element *found = NULL;
    const void *stream_id_key = (void *)(size_t)stream_id;
    aws_hash_table_find(&connection->thread_data.active_streams_map, stream_id_key, &found);
    if (found) {
        /* Found it! return */
        *out_stream = found->value;
        return AWS_H2ERR_SUCCESS;
    }

    bool client_initiated = (stream_id % 2) == 1;
    bool self_initiated_stream = client_initiated && (connection->base.client_data != NULL);
    bool peer_initiated_stream = !self_initiated_stream;

    if ((self_initiated_stream && stream_id >= connection->base.next_stream_id) ||
        (peer_initiated_stream && stream_id > connection->thread_data.latest_peer_initiated_stream_id)) {
        /* Illegal to receive frames for a stream in the idle state (stream doesn't exist yet)
         * (except server receiving HEADERS to start a stream, but that's handled elsewhere) */
        CONNECTION_LOGF(
            ERROR,
            connection,
            "Illegal to receive %s frame on stream id=%" PRIu32 " state=IDLE",
            aws_h2_frame_type_to_str(frame_type),
            stream_id);
        return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
    }

    if (peer_initiated_stream && stream_id > connection->thread_data.goaway_sent_last_stream_id) {
        /* Once GOAWAY sent, ignore frames for peer-initiated streams whose id > last-stream-id */
        CONNECTION_LOGF(
            TRACE,
            connection,
            "Ignoring %s frame on stream id=%" PRIu32 " because GOAWAY sent with last-stream-id=%" PRIu32,
            aws_h2_frame_type_to_str(frame_type),
            stream_id,
            connection->thread_data.goaway_sent_last_stream_id);

        return AWS_H2ERR_SUCCESS;
    }

    void *cached_value = NULL;
    /* Stream is closed, check whether it's legal for a few more frames to trickle in */
    if (aws_cache_find(connection->thread_data.closed_streams, stream_id_key, &cached_value)) {
        return aws_h2err_from_last_error();
    }
    if (cached_value) {
        if (frame_type == AWS_H2_FRAME_T_PRIORITY) {
            /* If we support PRIORITY, do something here. Right now just ignore it */
            return AWS_H2ERR_SUCCESS;
        }
        enum aws_h2_stream_closed_when closed_when = (enum aws_h2_stream_closed_when)(size_t)cached_value;
        switch (closed_when) {
            case AWS_H2_STREAM_CLOSED_WHEN_BOTH_SIDES_END_STREAM:
                /* WINDOW_UPDATE or RST_STREAM frames can be received ... for a short period after
                 * a DATA or HEADERS frame containing an END_STREAM flag is sent.
                 * Endpoints MUST ignore WINDOW_UPDATE or RST_STREAM frames received in this state */
                if (frame_type == AWS_H2_FRAME_T_WINDOW_UPDATE || frame_type == AWS_H2_FRAME_T_RST_STREAM) {
                    CONNECTION_LOGF(
                        TRACE,
                        connection,
                        "Ignoring %s frame on stream id=%" PRIu32 " because END_STREAM flag was recently sent.",
                        aws_h2_frame_type_to_str(frame_type),
                        stream_id);

                    return AWS_H2ERR_SUCCESS;
                } else {
                    CONNECTION_LOGF(
                        ERROR,
                        connection,
                        "Illegal to receive %s frame on stream id=%" PRIu32 " after END_STREAM has been received.",
                        aws_h2_frame_type_to_str(frame_type),
                        stream_id);

                    return aws_h2err_from_h2_code(AWS_HTTP2_ERR_STREAM_CLOSED);
                }
                break;
            case AWS_H2_STREAM_CLOSED_WHEN_RST_STREAM_RECEIVED:
                /* An endpoint that receives any frame other than PRIORITY after receiving a RST_STREAM
                 * MUST treat that as a stream error (Section 5.4.2) of type STREAM_CLOSED */
                CONNECTION_LOGF(
                    ERROR,
                    connection,
                    "Illegal to receive %s frame on stream id=%" PRIu32 " after RST_STREAM has been received",
                    aws_h2_frame_type_to_str(frame_type),
                    stream_id);
                struct aws_h2_frame *rst_stream =
                    aws_h2_frame_new_rst_stream(connection->base.alloc, stream_id, AWS_HTTP2_ERR_STREAM_CLOSED);
                if (!rst_stream) {
                    CONNECTION_LOGF(
                        ERROR, connection, "Error creating RST_STREAM frame, %s", aws_error_name(aws_last_error()));
                    return aws_h2err_from_last_error();
                }
                aws_h2_connection_enqueue_outgoing_frame(connection, rst_stream);
                return AWS_H2ERR_SUCCESS;
            case AWS_H2_STREAM_CLOSED_WHEN_RST_STREAM_SENT:
                /* An endpoint MUST ignore frames that it receives on closed streams after it has sent a RST_STREAM
                 * frame */
                CONNECTION_LOGF(
                    TRACE,
                    connection,
                    "Ignoring %s frame on stream id=%" PRIu32 " because RST_STREAM was recently sent.",
                    aws_h2_frame_type_to_str(frame_type),
                    stream_id);

                return AWS_H2ERR_SUCCESS;
                break;
            default:
                CONNECTION_LOGF(
                    ERROR, connection, "Invalid state fo cached closed stream, stream id=%" PRIu32, stream_id);
                return aws_h2err_from_h2_code(AWS_HTTP2_ERR_INTERNAL_ERROR);
                break;
        }
    }
    if (frame_type == AWS_H2_FRAME_T_PRIORITY) {
        /* ignored if the stream has been removed from the dependency tree */
        return AWS_H2ERR_SUCCESS;
    }

    /* Stream closed (purged from closed_streams, or implicitly closed when its ID was skipped) */
    CONNECTION_LOGF(
        ERROR,
        connection,
        "Illegal to receive %s frame on stream id=%" PRIu32
        ", no memory of closed stream (ID skipped, or removed from cache)",
        aws_h2_frame_type_to_str(frame_type),
        stream_id);

    return aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR);
}