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