1 /**
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0.
4 */
5
6 #include <aws/http/private/h2_stream.h>
7
8 #include <aws/http/private/h2_connection.h>
9 #include <aws/http/private/strutil.h>
10 #include <aws/http/status_code.h>
11 #include <aws/io/channel.h>
12 #include <aws/io/logging.h>
13
14 /* Apple toolchains such as xcode and swiftpm define the DEBUG symbol. undef it here so we can actually use the token */
15 #undef DEBUG
16
17 static void s_stream_destroy(struct aws_http_stream *stream_base);
18 static void s_stream_update_window(struct aws_http_stream *stream_base, size_t increment_size);
19 static int s_stream_reset_stream(struct aws_http_stream *stream_base, uint32_t http2_error);
20 static int s_stream_get_received_error_code(struct aws_http_stream *stream_base, uint32_t *out_http2_error);
21 static int s_stream_get_sent_error_code(struct aws_http_stream *stream_base, uint32_t *out_http2_error);
22
23 static void s_stream_cross_thread_work_task(struct aws_channel_task *task, void *arg, enum aws_task_status status);
24 static struct aws_h2err s_send_rst_and_close_stream(struct aws_h2_stream *stream, struct aws_h2err stream_error);
25 static int s_stream_reset_stream_internal(struct aws_http_stream *stream_base, struct aws_h2err stream_error);
26
27 struct aws_http_stream_vtable s_h2_stream_vtable = {
28 .destroy = s_stream_destroy,
29 .update_window = s_stream_update_window,
30 .activate = aws_h2_stream_activate,
31 .http1_write_chunk = NULL,
32 .http2_reset_stream = s_stream_reset_stream,
33 .http2_get_received_error_code = s_stream_get_received_error_code,
34 .http2_get_sent_error_code = s_stream_get_sent_error_code,
35 };
36
aws_h2_stream_state_to_str(enum aws_h2_stream_state state)37 const char *aws_h2_stream_state_to_str(enum aws_h2_stream_state state) {
38 switch (state) {
39 case AWS_H2_STREAM_STATE_IDLE:
40 return "IDLE";
41 case AWS_H2_STREAM_STATE_RESERVED_LOCAL:
42 return "RESERVED_LOCAL";
43 case AWS_H2_STREAM_STATE_RESERVED_REMOTE:
44 return "RESERVED_REMOTE";
45 case AWS_H2_STREAM_STATE_OPEN:
46 return "OPEN";
47 case AWS_H2_STREAM_STATE_HALF_CLOSED_LOCAL:
48 return "HALF_CLOSED_LOCAL";
49 case AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE:
50 return "HALF_CLOSED_REMOTE";
51 case AWS_H2_STREAM_STATE_CLOSED:
52 return "CLOSED";
53 default:
54 /* unreachable */
55 AWS_ASSERT(0);
56 return "*** UNKNOWN ***";
57 }
58 }
59
s_get_h2_connection(const struct aws_h2_stream * stream)60 static struct aws_h2_connection *s_get_h2_connection(const struct aws_h2_stream *stream) {
61 return AWS_CONTAINER_OF(stream->base.owning_connection, struct aws_h2_connection, base);
62 }
63
s_lock_synced_data(struct aws_h2_stream * stream)64 static void s_lock_synced_data(struct aws_h2_stream *stream) {
65 int err = aws_mutex_lock(&stream->synced_data.lock);
66 AWS_ASSERT(!err && "lock failed");
67 (void)err;
68 }
69
s_unlock_synced_data(struct aws_h2_stream * stream)70 static void s_unlock_synced_data(struct aws_h2_stream *stream) {
71 int err = aws_mutex_unlock(&stream->synced_data.lock);
72 AWS_ASSERT(!err && "unlock failed");
73 (void)err;
74 }
75
76 #define AWS_PRECONDITION_ON_CHANNEL_THREAD(STREAM) \
77 AWS_PRECONDITION(aws_channel_thread_is_callers_thread(s_get_h2_connection(STREAM)->base.channel_slot->channel))
78
79 static bool s_client_state_allows_frame_type[AWS_H2_STREAM_STATE_COUNT][AWS_H2_FRAME_TYPE_COUNT] = {
80 /* State before anything is sent or received */
81 [AWS_H2_STREAM_STATE_IDLE] = {0},
82 /* Client streams are never in reserved (local) state */
83 [AWS_H2_STREAM_STATE_RESERVED_LOCAL] = {0},
84 /* Client received push-request via PUSH_PROMISE on another stream.
85 * Waiting for push-response to start arriving on this server-initiated stream. */
86 [AWS_H2_STREAM_STATE_RESERVED_REMOTE] =
87 {
88 [AWS_H2_FRAME_T_HEADERS] = true,
89 [AWS_H2_FRAME_T_RST_STREAM] = true,
90 },
91 /* Client is sending request and has not received full response yet. */
92 [AWS_H2_STREAM_STATE_OPEN] =
93 {
94 [AWS_H2_FRAME_T_DATA] = true,
95 [AWS_H2_FRAME_T_HEADERS] = true,
96 [AWS_H2_FRAME_T_RST_STREAM] = true,
97 [AWS_H2_FRAME_T_PUSH_PROMISE] = true,
98 [AWS_H2_FRAME_T_WINDOW_UPDATE] = true,
99 },
100 /* Client has sent full request (END_STREAM), but has not received full response yet. */
101 [AWS_H2_STREAM_STATE_HALF_CLOSED_LOCAL] =
102 {
103 [AWS_H2_FRAME_T_DATA] = true,
104 [AWS_H2_FRAME_T_HEADERS] = true,
105 [AWS_H2_FRAME_T_RST_STREAM] = true,
106 [AWS_H2_FRAME_T_PUSH_PROMISE] = true,
107 [AWS_H2_FRAME_T_WINDOW_UPDATE] = true,
108 },
109 /* Client has received full response (END_STREAM), but is still sending request (uncommon). */
110 [AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE] =
111 {
112 [AWS_H2_FRAME_T_RST_STREAM] = true,
113 [AWS_H2_FRAME_T_WINDOW_UPDATE] = true,
114 },
115 /* Full request sent (END_STREAM) and full response received (END_STREAM).
116 * OR sent RST_STREAM. OR received RST_STREAM. */
117 [AWS_H2_STREAM_STATE_CLOSED] = {0},
118 };
119
120 static bool s_server_state_allows_frame_type[AWS_H2_STREAM_STATE_COUNT][AWS_H2_FRAME_TYPE_COUNT] = {
121 /* State before anything is sent or received, waiting for request headers to arrives and start things off */
122 [AWS_H2_STREAM_STATE_IDLE] =
123 {
124 [AWS_H2_FRAME_T_HEADERS] = true,
125 },
126 /* Server sent push-request via PUSH_PROMISE on a client-initiated stream,
127 * but hasn't started sending the push-response on this server-initiated stream yet. */
128 [AWS_H2_STREAM_STATE_RESERVED_LOCAL] =
129 {
130 [AWS_H2_FRAME_T_RST_STREAM] = true,
131 [AWS_H2_FRAME_T_WINDOW_UPDATE] = true,
132 },
133 /* Server streams are never in reserved (remote) state */
134 [AWS_H2_STREAM_STATE_RESERVED_REMOTE] = {0},
135 /* Server is receiving request, and has sent full response yet. */
136 [AWS_H2_STREAM_STATE_OPEN] =
137 {
138 [AWS_H2_FRAME_T_HEADERS] = true,
139 [AWS_H2_FRAME_T_DATA] = true,
140 [AWS_H2_FRAME_T_RST_STREAM] = true,
141 [AWS_H2_FRAME_T_WINDOW_UPDATE] = true,
142 },
143 /* Server has sent full response (END_STREAM), but has not received full response yet (uncommon). */
144 [AWS_H2_STREAM_STATE_HALF_CLOSED_LOCAL] =
145 {
146 [AWS_H2_FRAME_T_HEADERS] = true,
147 [AWS_H2_FRAME_T_DATA] = true,
148 [AWS_H2_FRAME_T_RST_STREAM] = true,
149 [AWS_H2_FRAME_T_WINDOW_UPDATE] = true,
150 },
151 /* Server has received full request (END_STREAM), and is still sending response. */
152 [AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE] =
153 {
154 [AWS_H2_FRAME_T_RST_STREAM] = true,
155 [AWS_H2_FRAME_T_WINDOW_UPDATE] = true,
156 },
157 /* Full request received (END_STREAM) and full response sent (END_STREAM).
158 * OR sent RST_STREAM. OR received RST_STREAM. */
159 [AWS_H2_STREAM_STATE_CLOSED] = {0},
160 };
161
162 /* Returns the appropriate Stream Error if given frame not allowed in current state */
s_check_state_allows_frame_type(const struct aws_h2_stream * stream,enum aws_h2_frame_type frame_type)163 static struct aws_h2err s_check_state_allows_frame_type(
164 const struct aws_h2_stream *stream,
165 enum aws_h2_frame_type frame_type) {
166
167 AWS_PRECONDITION(frame_type < AWS_H2_FRAME_T_UNKNOWN); /* Decoder won't invoke callbacks for unknown frame types */
168 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
169
170 const enum aws_h2_stream_state state = stream->thread_data.state;
171
172 bool allowed;
173 if (stream->base.server_data) {
174 allowed = s_server_state_allows_frame_type[state][frame_type];
175 } else {
176 allowed = s_client_state_allows_frame_type[state][frame_type];
177 }
178
179 if (allowed) {
180 return AWS_H2ERR_SUCCESS;
181 }
182
183 /* Determine specific error code */
184 enum aws_http2_error_code h2_error_code = AWS_HTTP2_ERR_PROTOCOL_ERROR;
185
186 /* If peer knows the state is closed, then it's a STREAM_CLOSED error */
187 if (state == AWS_H2_STREAM_STATE_CLOSED || state == AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE) {
188 h2_error_code = AWS_HTTP2_ERR_STREAM_CLOSED;
189 }
190
191 AWS_H2_STREAM_LOGF(
192 ERROR,
193 stream,
194 "Malformed message, cannot receive %s frame in %s state",
195 aws_h2_frame_type_to_str(frame_type),
196 aws_h2_stream_state_to_str(state));
197
198 return aws_h2err_from_h2_code(h2_error_code);
199 }
200
s_stream_send_update_window_frame(struct aws_h2_stream * stream,size_t increment_size)201 static int s_stream_send_update_window_frame(struct aws_h2_stream *stream, size_t increment_size) {
202 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
203 AWS_PRECONDITION(increment_size <= AWS_H2_WINDOW_UPDATE_MAX);
204
205 struct aws_h2_connection *connection = s_get_h2_connection(stream);
206 struct aws_h2_frame *stream_window_update_frame =
207 aws_h2_frame_new_window_update(stream->base.alloc, stream->base.id, (uint32_t)increment_size);
208
209 if (!stream_window_update_frame) {
210 AWS_H2_STREAM_LOGF(
211 ERROR,
212 stream,
213 "Failed to create WINDOW_UPDATE frame on connection, error %s",
214 aws_error_name(aws_last_error()));
215 return AWS_OP_ERR;
216 }
217 aws_h2_connection_enqueue_outgoing_frame(connection, stream_window_update_frame);
218
219 return AWS_OP_SUCCESS;
220 }
221
aws_h2_stream_new_request(struct aws_http_connection * client_connection,const struct aws_http_make_request_options * options)222 struct aws_h2_stream *aws_h2_stream_new_request(
223 struct aws_http_connection *client_connection,
224 const struct aws_http_make_request_options *options) {
225 AWS_PRECONDITION(client_connection);
226 AWS_PRECONDITION(options);
227
228 struct aws_h2_stream *stream = aws_mem_calloc(client_connection->alloc, 1, sizeof(struct aws_h2_stream));
229 if (!stream) {
230 return NULL;
231 }
232
233 /* Initialize base stream */
234 stream->base.vtable = &s_h2_stream_vtable;
235 stream->base.alloc = client_connection->alloc;
236 stream->base.owning_connection = client_connection;
237 stream->base.user_data = options->user_data;
238 stream->base.on_incoming_headers = options->on_response_headers;
239 stream->base.on_incoming_header_block_done = options->on_response_header_block_done;
240 stream->base.on_incoming_body = options->on_response_body;
241 stream->base.on_complete = options->on_complete;
242 stream->base.client_data = &stream->base.client_or_server_data.client;
243 stream->base.client_data->response_status = AWS_HTTP_STATUS_CODE_UNKNOWN;
244
245 /* Stream refcount starts at 1, and gets incremented again for the connection upon a call to activate() */
246 aws_atomic_init_int(&stream->base.refcount, 1);
247
248 /* Init H2 specific stuff */
249 stream->thread_data.state = AWS_H2_STREAM_STATE_IDLE;
250 stream->thread_data.outgoing_message = options->request;
251
252 stream->sent_reset_error_code = -1;
253 stream->received_reset_error_code = -1;
254
255 stream->synced_data.reset_error.h2_code = AWS_HTTP2_ERR_COUNT;
256 stream->synced_data.api_state = AWS_H2_STREAM_API_STATE_INIT;
257 if (aws_mutex_init(&stream->synced_data.lock)) {
258 AWS_H2_STREAM_LOGF(
259 ERROR, stream, "Mutex init error %d (%s).", aws_last_error(), aws_error_name(aws_last_error()));
260 aws_mem_release(stream->base.alloc, stream);
261 return NULL;
262 }
263 aws_http_message_acquire(stream->thread_data.outgoing_message);
264 aws_channel_task_init(
265 &stream->cross_thread_work_task, s_stream_cross_thread_work_task, stream, "HTTP/2 stream cross-thread work");
266 return stream;
267 }
268
s_stream_cross_thread_work_task(struct aws_channel_task * task,void * arg,enum aws_task_status status)269 static void s_stream_cross_thread_work_task(struct aws_channel_task *task, void *arg, enum aws_task_status status) {
270 (void)task;
271
272 struct aws_h2_stream *stream = arg;
273 if (status != AWS_TASK_STATUS_RUN_READY) {
274 goto end;
275 }
276
277 struct aws_h2_connection *connection = s_get_h2_connection(stream);
278
279 if (aws_h2_stream_get_state(stream) == AWS_H2_STREAM_STATE_CLOSED) {
280 /* stream is closed, silently ignoring the requests from user */
281 AWS_H2_STREAM_LOG(
282 TRACE, stream, "Stream closed before cross thread work task runs, ignoring everything was sent by user.");
283 goto end;
284 }
285
286 /* Not sending window update at half closed remote state */
287 bool ignore_window_update = (aws_h2_stream_get_state(stream) == AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE);
288 bool reset_called;
289 size_t window_update_size;
290 struct aws_h2err reset_error;
291
292 { /* BEGIN CRITICAL SECTION */
293 s_lock_synced_data(stream);
294 stream->synced_data.is_cross_thread_work_task_scheduled = false;
295
296 /* window_update_size is ensured to be not greater than AWS_H2_WINDOW_UPDATE_MAX */
297 window_update_size = stream->synced_data.window_update_size;
298 stream->synced_data.window_update_size = 0;
299 reset_called = stream->synced_data.reset_called;
300 reset_error = stream->synced_data.reset_error;
301
302 s_unlock_synced_data(stream);
303 } /* END CRITICAL SECTION */
304
305 if (window_update_size > 0 && !ignore_window_update) {
306 if (s_stream_send_update_window_frame(stream, window_update_size)) {
307 /* Treat this as a connection error */
308 aws_h2_connection_shutdown_due_to_write_err(connection, aws_last_error());
309 }
310 }
311
312 /* The largest legal value will be 2 * max window size, which is way less than INT64_MAX, so if the window_size_self
313 * overflows, remote peer will find it out. So just apply the change and ignore the possible overflow.*/
314 stream->thread_data.window_size_self += window_update_size;
315
316 if (reset_called) {
317 struct aws_h2err returned_h2err = s_send_rst_and_close_stream(stream, reset_error);
318 if (aws_h2err_failed(returned_h2err)) {
319 aws_h2_connection_shutdown_due_to_write_err(connection, returned_h2err.aws_code);
320 }
321 }
322
323 /* It's likely that frames were queued while processing cross-thread work.
324 * If so, try writing them now */
325 aws_h2_try_write_outgoing_frames(connection);
326
327 end:
328 aws_http_stream_release(&stream->base);
329 }
330
s_stream_destroy(struct aws_http_stream * stream_base)331 static void s_stream_destroy(struct aws_http_stream *stream_base) {
332 AWS_PRECONDITION(stream_base);
333 struct aws_h2_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h2_stream, base);
334
335 AWS_H2_STREAM_LOG(DEBUG, stream, "Destroying stream");
336 aws_mutex_clean_up(&stream->synced_data.lock);
337 aws_http_message_release(stream->thread_data.outgoing_message);
338
339 aws_mem_release(stream->base.alloc, stream);
340 }
341
s_stream_update_window(struct aws_http_stream * stream_base,size_t increment_size)342 static void s_stream_update_window(struct aws_http_stream *stream_base, size_t increment_size) {
343 AWS_PRECONDITION(stream_base);
344 struct aws_h2_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h2_stream, base);
345 struct aws_h2_connection *connection = s_get_h2_connection(stream);
346 if (!increment_size) {
347 return;
348 }
349 if (!connection->base.manual_window_management) {
350 /* auto-mode, manual update window is not supported */
351 AWS_H2_STREAM_LOG(
352 DEBUG, stream, "Manual window management is off, update window operations are not supported.");
353 return;
354 }
355
356 int err = 0;
357 bool stream_is_init;
358 bool cross_thread_work_should_schedule = false;
359 size_t sum_size;
360 { /* BEGIN CRITICAL SECTION */
361 s_lock_synced_data(stream);
362
363 err |= aws_add_size_checked(stream->synced_data.window_update_size, increment_size, &sum_size);
364 err |= sum_size > AWS_H2_WINDOW_UPDATE_MAX;
365 stream_is_init = stream->synced_data.api_state == AWS_H2_STREAM_API_STATE_INIT;
366
367 if (!err && !stream_is_init) {
368 cross_thread_work_should_schedule = !stream->synced_data.is_cross_thread_work_task_scheduled;
369 stream->synced_data.is_cross_thread_work_task_scheduled = true;
370 stream->synced_data.window_update_size = sum_size;
371 }
372 s_unlock_synced_data(stream);
373 } /* END CRITICAL SECTION */
374
375 if (cross_thread_work_should_schedule) {
376 AWS_H2_STREAM_LOG(TRACE, stream, "Scheduling stream cross-thread work task");
377 /* increment the refcount of stream to keep it alive until the task runs */
378 aws_atomic_fetch_add(&stream->base.refcount, 1);
379 aws_channel_schedule_task_now(connection->base.channel_slot->channel, &stream->cross_thread_work_task);
380 return;
381 }
382
383 if (stream_is_init) {
384 AWS_H2_STREAM_LOG(
385 ERROR,
386 stream,
387 "Stream update window failed. Stream is in initialized state, please activate the stream first.");
388 aws_raise_error(AWS_ERROR_INVALID_STATE);
389 return;
390 }
391
392 if (err) {
393 /* The increment_size is still not 100% safe, since we cannot control the incoming data frame. So just
394 * ruled out the value that is obviously wrong values */
395 AWS_H2_STREAM_LOG(
396 ERROR,
397 stream,
398 "The stream's flow-control window has been incremented beyond 2**31 -1, the max for HTTP/2. The stream "
399 "will close.");
400 aws_raise_error(AWS_ERROR_OVERFLOW_DETECTED);
401 struct aws_h2err stream_error = {
402 .aws_code = AWS_ERROR_OVERFLOW_DETECTED,
403 .h2_code = AWS_HTTP2_ERR_INTERNAL_ERROR,
404 };
405 /* Only when stream is not initialized reset will fail. So, we can assert it to be succeed. */
406 AWS_FATAL_ASSERT(s_stream_reset_stream_internal(stream_base, stream_error) == AWS_OP_SUCCESS);
407 }
408 return;
409 }
410
s_stream_reset_stream_internal(struct aws_http_stream * stream_base,struct aws_h2err stream_error)411 static int s_stream_reset_stream_internal(struct aws_http_stream *stream_base, struct aws_h2err stream_error) {
412
413 struct aws_h2_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h2_stream, base);
414 struct aws_h2_connection *connection = s_get_h2_connection(stream);
415 bool reset_called;
416 bool stream_is_init;
417 bool cross_thread_work_should_schedule = false;
418
419 { /* BEGIN CRITICAL SECTION */
420 s_lock_synced_data(stream);
421
422 reset_called = stream->synced_data.reset_called;
423 stream_is_init = stream->synced_data.api_state == AWS_H2_STREAM_API_STATE_INIT;
424 if (!reset_called && !stream_is_init) {
425 cross_thread_work_should_schedule = !stream->synced_data.is_cross_thread_work_task_scheduled;
426 stream->synced_data.reset_called = true;
427 stream->synced_data.reset_error = stream_error;
428 }
429 s_unlock_synced_data(stream);
430 } /* END CRITICAL SECTION */
431
432 if (stream_is_init) {
433 AWS_H2_STREAM_LOG(
434 ERROR, stream, "Reset stream failed. Stream is in initialized state, please activate the stream first.");
435 return aws_raise_error(AWS_ERROR_INVALID_STATE);
436 }
437 if (cross_thread_work_should_schedule) {
438 AWS_H2_STREAM_LOG(TRACE, stream, "Scheduling stream cross-thread work task");
439 /* increment the refcount of stream to keep it alive until the task runs */
440 aws_atomic_fetch_add(&stream->base.refcount, 1);
441 aws_channel_schedule_task_now(connection->base.channel_slot->channel, &stream->cross_thread_work_task);
442 return AWS_OP_SUCCESS;
443 }
444 if (reset_called) {
445 AWS_H2_STREAM_LOG(DEBUG, stream, "Reset stream ignored. Reset stream has been called already.");
446 }
447
448 return AWS_OP_SUCCESS;
449 }
450
s_stream_reset_stream(struct aws_http_stream * stream_base,uint32_t http2_error)451 static int s_stream_reset_stream(struct aws_http_stream *stream_base, uint32_t http2_error) {
452 struct aws_h2err stream_error = {
453 .aws_code = AWS_ERROR_HTTP_RST_STREAM_SENT,
454 .h2_code = http2_error,
455 };
456
457 return s_stream_reset_stream_internal(stream_base, stream_error);
458 }
459
s_stream_get_received_error_code(struct aws_http_stream * stream_base,uint32_t * out_http2_error)460 static int s_stream_get_received_error_code(struct aws_http_stream *stream_base, uint32_t *out_http2_error) {
461 struct aws_h2_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h2_stream, base);
462 if (stream->received_reset_error_code == -1) {
463 return aws_raise_error(AWS_ERROR_HTTP_DATA_NOT_AVAILABLE);
464 }
465 *out_http2_error = (uint32_t)stream->received_reset_error_code;
466 return AWS_OP_SUCCESS;
467 }
468
s_stream_get_sent_error_code(struct aws_http_stream * stream_base,uint32_t * out_http2_error)469 static int s_stream_get_sent_error_code(struct aws_http_stream *stream_base, uint32_t *out_http2_error) {
470 struct aws_h2_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h2_stream, base);
471 if (stream->sent_reset_error_code == -1) {
472 return aws_raise_error(AWS_ERROR_HTTP_DATA_NOT_AVAILABLE);
473 }
474 *out_http2_error = (uint32_t)stream->sent_reset_error_code;
475 return AWS_OP_SUCCESS;
476 }
477
aws_h2_stream_get_state(const struct aws_h2_stream * stream)478 enum aws_h2_stream_state aws_h2_stream_get_state(const struct aws_h2_stream *stream) {
479 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
480 return stream->thread_data.state;
481 }
482
483 /* Given a Stream Error, send RST_STREAM frame and close stream.
484 * A Connection Error is returned if something goes catastrophically wrong */
s_send_rst_and_close_stream(struct aws_h2_stream * stream,struct aws_h2err stream_error)485 static struct aws_h2err s_send_rst_and_close_stream(struct aws_h2_stream *stream, struct aws_h2err stream_error) {
486 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
487 AWS_PRECONDITION(stream->thread_data.state != AWS_H2_STREAM_STATE_CLOSED);
488
489 struct aws_h2_connection *connection = s_get_h2_connection(stream);
490
491 stream->thread_data.state = AWS_H2_STREAM_STATE_CLOSED;
492 { /* BEGIN CRITICAL SECTION */
493 s_lock_synced_data(stream);
494 stream->synced_data.api_state = AWS_H2_STREAM_API_STATE_COMPLETE;
495 s_unlock_synced_data(stream);
496 } /* END CRITICAL SECTION */
497 AWS_H2_STREAM_LOGF(
498 DEBUG,
499 stream,
500 "Sending RST_STREAM with error code %s (0x%x). State -> CLOSED",
501 aws_http2_error_code_to_str(stream_error.h2_code),
502 stream_error.h2_code);
503
504 /* Send RST_STREAM */
505 struct aws_h2_frame *rst_stream_frame =
506 aws_h2_frame_new_rst_stream(stream->base.alloc, stream->base.id, stream_error.h2_code);
507 if (!rst_stream_frame) {
508 AWS_H2_STREAM_LOGF(ERROR, stream, "Error creating RST_STREAM frame, %s", aws_error_name(aws_last_error()));
509 return aws_h2err_from_last_error();
510 }
511 aws_h2_connection_enqueue_outgoing_frame(connection, rst_stream_frame); /* connection takes ownership of frame */
512 stream->sent_reset_error_code = stream_error.h2_code;
513
514 /* Tell connection that stream is now closed */
515 if (aws_h2_connection_on_stream_closed(
516 connection, stream, AWS_H2_STREAM_CLOSED_WHEN_RST_STREAM_SENT, stream_error.aws_code)) {
517 return aws_h2err_from_last_error();
518 }
519
520 return AWS_H2ERR_SUCCESS;
521 }
522
aws_h2_stream_window_size_change(struct aws_h2_stream * stream,int32_t size_changed,bool self)523 struct aws_h2err aws_h2_stream_window_size_change(struct aws_h2_stream *stream, int32_t size_changed, bool self) {
524 if (self) {
525 if (stream->thread_data.window_size_self + size_changed > AWS_H2_WINDOW_UPDATE_MAX) {
526 return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FLOW_CONTROL_ERROR);
527 }
528 stream->thread_data.window_size_self += size_changed;
529 } else {
530 if ((int64_t)stream->thread_data.window_size_peer + size_changed > AWS_H2_WINDOW_UPDATE_MAX) {
531 return aws_h2err_from_h2_code(AWS_HTTP2_ERR_FLOW_CONTROL_ERROR);
532 }
533 stream->thread_data.window_size_peer += size_changed;
534 }
535 return AWS_H2ERR_SUCCESS;
536 }
537
aws_h2_stream_on_activated(struct aws_h2_stream * stream,bool * out_has_outgoing_data)538 int aws_h2_stream_on_activated(struct aws_h2_stream *stream, bool *out_has_outgoing_data) {
539 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
540
541 struct aws_h2_connection *connection = s_get_h2_connection(stream);
542
543 /* Create HEADERS frame */
544 struct aws_http_message *msg = stream->thread_data.outgoing_message;
545 bool has_body_stream = aws_http_message_get_body_stream(msg) != NULL;
546 struct aws_http_headers *h2_headers = aws_h2_create_headers_from_request(msg, stream->base.alloc);
547 if (!h2_headers) {
548 AWS_H2_STREAM_LOGF(
549 ERROR, stream, "Failed to create HTTP/2 style headers from request %s", aws_error_name(aws_last_error()));
550 goto error;
551 }
552 struct aws_h2_frame *headers_frame = aws_h2_frame_new_headers(
553 stream->base.alloc,
554 stream->base.id,
555 h2_headers,
556 !has_body_stream /* end_stream */,
557 0 /* padding - not currently configurable via public API */,
558 NULL /* priority - not currently configurable via public API */);
559
560 /* Release refcount of h2_headers here, let frame take the full ownership of it */
561 aws_http_headers_release(h2_headers);
562 if (!headers_frame) {
563 AWS_H2_STREAM_LOGF(ERROR, stream, "Failed to create HEADERS frame: %s", aws_error_name(aws_last_error()));
564 goto error;
565 }
566
567 /* Initialize the flow-control window size */
568 stream->thread_data.window_size_peer =
569 connection->thread_data.settings_peer[AWS_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
570 stream->thread_data.window_size_self =
571 connection->thread_data.settings_self[AWS_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
572
573 if (has_body_stream) {
574 /* If stream has DATA to send, put it in the outgoing_streams_list, and we'll send data later */
575 stream->thread_data.state = AWS_H2_STREAM_STATE_OPEN;
576 AWS_H2_STREAM_LOG(TRACE, stream, "Sending HEADERS. State -> OPEN");
577 } else {
578 /* If stream has no body, then HEADERS frame marks the end of outgoing data */
579 stream->thread_data.state = AWS_H2_STREAM_STATE_HALF_CLOSED_LOCAL;
580 AWS_H2_STREAM_LOG(TRACE, stream, "Sending HEADERS with END_STREAM. State -> HALF_CLOSED_LOCAL");
581 }
582
583 *out_has_outgoing_data = has_body_stream;
584 aws_h2_connection_enqueue_outgoing_frame(connection, headers_frame);
585 return AWS_OP_SUCCESS;
586
587 error:
588 return AWS_OP_ERR;
589 }
590
aws_h2_stream_encode_data_frame(struct aws_h2_stream * stream,struct aws_h2_frame_encoder * encoder,struct aws_byte_buf * output,int * data_encode_status)591 int aws_h2_stream_encode_data_frame(
592 struct aws_h2_stream *stream,
593 struct aws_h2_frame_encoder *encoder,
594 struct aws_byte_buf *output,
595 int *data_encode_status) {
596
597 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
598 AWS_PRECONDITION(
599 stream->thread_data.state == AWS_H2_STREAM_STATE_OPEN ||
600 stream->thread_data.state == AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE);
601 struct aws_h2_connection *connection = s_get_h2_connection(stream);
602 AWS_PRECONDITION(connection->thread_data.window_size_peer > AWS_H2_MIN_WINDOW_SIZE);
603
604 if (stream->thread_data.window_size_peer <= AWS_H2_MIN_WINDOW_SIZE) {
605 /* The stream is stalled now */
606 *data_encode_status = AWS_H2_DATA_ENCODE_ONGOING_WINDOW_STALLED;
607 return AWS_OP_SUCCESS;
608 }
609
610 *data_encode_status = AWS_H2_DATA_ENCODE_COMPLETE;
611 struct aws_input_stream *body = aws_http_message_get_body_stream(stream->thread_data.outgoing_message);
612 AWS_ASSERT(body);
613
614 bool body_complete;
615 bool body_stalled;
616 if (aws_h2_encode_data_frame(
617 encoder,
618 stream->base.id,
619 body,
620 true /*body_ends_stream*/,
621 0 /*pad_length*/,
622 &stream->thread_data.window_size_peer,
623 &connection->thread_data.window_size_peer,
624 output,
625 &body_complete,
626 &body_stalled)) {
627
628 /* Failed to write DATA, treat it as a Stream Error */
629 AWS_H2_STREAM_LOGF(ERROR, stream, "Error encoding stream DATA, %s", aws_error_name(aws_last_error()));
630 struct aws_h2err returned_h2err = s_send_rst_and_close_stream(stream, aws_h2err_from_last_error());
631 if (aws_h2err_failed(returned_h2err)) {
632 aws_h2_connection_shutdown_due_to_write_err(connection, returned_h2err.aws_code);
633 }
634 return AWS_OP_SUCCESS;
635 }
636
637 if (body_complete) {
638 if (stream->thread_data.state == AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE) {
639 /* Both sides have sent END_STREAM */
640 stream->thread_data.state = AWS_H2_STREAM_STATE_CLOSED;
641 AWS_H2_STREAM_LOG(TRACE, stream, "Sent END_STREAM. State -> CLOSED");
642 { /* BEGIN CRITICAL SECTION */
643 s_lock_synced_data(stream);
644 stream->synced_data.api_state = AWS_H2_STREAM_API_STATE_COMPLETE;
645 s_unlock_synced_data(stream);
646 } /* END CRITICAL SECTION */
647 /* Tell connection that stream is now closed */
648 if (aws_h2_connection_on_stream_closed(
649 connection, stream, AWS_H2_STREAM_CLOSED_WHEN_BOTH_SIDES_END_STREAM, AWS_ERROR_SUCCESS)) {
650 return AWS_OP_ERR;
651 }
652 } else {
653 /* Else can't close until we receive END_STREAM */
654 stream->thread_data.state = AWS_H2_STREAM_STATE_HALF_CLOSED_LOCAL;
655 AWS_H2_STREAM_LOG(TRACE, stream, "Sent END_STREAM. State -> HALF_CLOSED_LOCAL");
656 }
657 } else {
658 /* Body not complete */
659 *data_encode_status = AWS_H2_DATA_ENCODE_ONGOING;
660 if (body_stalled) {
661 *data_encode_status = AWS_H2_DATA_ENCODE_ONGOING_BODY_STALLED;
662 }
663 if (stream->thread_data.window_size_peer <= AWS_H2_MIN_WINDOW_SIZE) {
664 /* if body and window both stalled, we take the window stalled status, which will take the stream out from
665 * outgoing list */
666 *data_encode_status = AWS_H2_DATA_ENCODE_ONGOING_WINDOW_STALLED;
667 }
668 }
669
670 return AWS_OP_SUCCESS;
671 }
672
aws_h2_stream_on_decoder_headers_begin(struct aws_h2_stream * stream)673 struct aws_h2err aws_h2_stream_on_decoder_headers_begin(struct aws_h2_stream *stream) {
674 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
675
676 struct aws_h2err stream_err = s_check_state_allows_frame_type(stream, AWS_H2_FRAME_T_HEADERS);
677 if (aws_h2err_failed(stream_err)) {
678 return s_send_rst_and_close_stream(stream, stream_err);
679 }
680
681 return AWS_H2ERR_SUCCESS;
682 }
683
aws_h2_stream_on_decoder_headers_i(struct aws_h2_stream * stream,const struct aws_http_header * header,enum aws_http_header_name name_enum,enum aws_http_header_block block_type)684 struct aws_h2err aws_h2_stream_on_decoder_headers_i(
685 struct aws_h2_stream *stream,
686 const struct aws_http_header *header,
687 enum aws_http_header_name name_enum,
688 enum aws_http_header_block block_type) {
689
690 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
691
692 /* Not calling s_check_state_allows_frame_type() here because we already checked
693 * at start of HEADERS frame in aws_h2_stream_on_decoder_headers_begin() */
694
695 bool is_server = stream->base.server_data;
696
697 /* RFC-7540 8.1 - Message consists of:
698 * - 0+ Informational 1xx headers (response-only, decoder validates that this only occurs in responses)
699 * - 1 main headers with normal request or response.
700 * - 0 or 1 trailing headers with no pseudo-headers */
701 switch (block_type) {
702 case AWS_HTTP_HEADER_BLOCK_INFORMATIONAL:
703 if (stream->thread_data.received_main_headers) {
704 AWS_H2_STREAM_LOG(
705 ERROR, stream, "Malformed message, received informational (1xx) response after main response");
706 goto malformed;
707 }
708 break;
709 case AWS_HTTP_HEADER_BLOCK_MAIN:
710 if (stream->thread_data.received_main_headers) {
711 AWS_H2_STREAM_LOG(ERROR, stream, "Malformed message, received second set of headers");
712 goto malformed;
713 }
714 break;
715 case AWS_HTTP_HEADER_BLOCK_TRAILING:
716 if (!stream->thread_data.received_main_headers) {
717 /* A HEADERS frame without any pseudo-headers looks like trailing headers to the decoder */
718 AWS_H2_STREAM_LOG(ERROR, stream, "Malformed headers lack required pseudo-header fields.");
719 goto malformed;
720 }
721 break;
722 default:
723 AWS_ASSERT(0);
724 }
725
726 if (is_server) {
727 return aws_h2err_from_aws_code(AWS_ERROR_UNIMPLEMENTED);
728
729 } else {
730 /* Client */
731 if (name_enum == AWS_HTTP_HEADER_STATUS) {
732 uint64_t status_code;
733 int err = aws_strutil_read_unsigned_num(header->value, &status_code);
734 AWS_ASSERT(!err && "Invalid :status value. Decoder should have already validated this");
735 (void)err;
736
737 stream->base.client_data->response_status = (int)status_code;
738 }
739 }
740
741 if (stream->base.on_incoming_headers) {
742 if (stream->base.on_incoming_headers(&stream->base, block_type, header, 1, stream->base.user_data)) {
743 /* #TODO: callback errors should be Stream Errors, not Connection Errors */
744 AWS_H2_STREAM_LOGF(
745 ERROR, stream, "Incoming header callback raised error, %s", aws_error_name(aws_last_error()));
746 return aws_h2err_from_last_error();
747 }
748 }
749
750 return AWS_H2ERR_SUCCESS;
751
752 malformed:
753 return s_send_rst_and_close_stream(stream, aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR));
754 }
755
aws_h2_stream_on_decoder_headers_end(struct aws_h2_stream * stream,bool malformed,enum aws_http_header_block block_type)756 struct aws_h2err aws_h2_stream_on_decoder_headers_end(
757 struct aws_h2_stream *stream,
758 bool malformed,
759 enum aws_http_header_block block_type) {
760
761 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
762
763 /* Not calling s_check_state_allows_frame_type() here because we already checked
764 * at start of HEADERS frame in aws_h2_stream_on_decoder_headers_begin() */
765
766 if (malformed) {
767 AWS_H2_STREAM_LOG(ERROR, stream, "Headers are malformed");
768 return s_send_rst_and_close_stream(stream, aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR));
769 }
770
771 switch (block_type) {
772 case AWS_HTTP_HEADER_BLOCK_INFORMATIONAL:
773 AWS_H2_STREAM_LOG(TRACE, stream, "Informational 1xx header-block done.");
774 break;
775 case AWS_HTTP_HEADER_BLOCK_MAIN:
776 AWS_H2_STREAM_LOG(TRACE, stream, "Main header-block done.");
777 stream->thread_data.received_main_headers = true;
778 break;
779 case AWS_HTTP_HEADER_BLOCK_TRAILING:
780 AWS_H2_STREAM_LOG(TRACE, stream, "Trailing 1xx header-block done.");
781 break;
782 default:
783 AWS_ASSERT(0);
784 }
785
786 if (stream->base.on_incoming_header_block_done) {
787 if (stream->base.on_incoming_header_block_done(&stream->base, block_type, stream->base.user_data)) {
788 AWS_H2_STREAM_LOGF(
789 ERROR,
790 stream,
791 "Incoming-header-block-done callback raised error, %s",
792 aws_error_name(aws_last_error()));
793 return aws_h2err_from_last_error();
794 }
795 }
796
797 return AWS_H2ERR_SUCCESS;
798 }
799
aws_h2_stream_on_decoder_push_promise(struct aws_h2_stream * stream,uint32_t promised_stream_id)800 struct aws_h2err aws_h2_stream_on_decoder_push_promise(struct aws_h2_stream *stream, uint32_t promised_stream_id) {
801 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
802
803 struct aws_h2err stream_err = s_check_state_allows_frame_type(stream, AWS_H2_FRAME_T_PUSH_PROMISE);
804 if (aws_h2err_failed(stream_err)) {
805 return s_send_rst_and_close_stream(stream, stream_err);
806 }
807
808 /* Note: Until we have a need for it, PUSH_PROMISE is not a fully supported feature.
809 * Promised streams are automatically rejected in a manner compliant with RFC-7540. */
810 AWS_H2_STREAM_LOG(DEBUG, stream, "Automatically rejecting promised stream, PUSH_PROMISE is not fully supported");
811 if (aws_h2_connection_send_rst_and_close_reserved_stream(
812 s_get_h2_connection(stream), promised_stream_id, AWS_HTTP2_ERR_REFUSED_STREAM)) {
813 return aws_h2err_from_last_error();
814 }
815
816 return AWS_H2ERR_SUCCESS;
817 }
818
aws_h2_stream_on_decoder_data_begin(struct aws_h2_stream * stream,uint32_t payload_len,bool end_stream)819 struct aws_h2err aws_h2_stream_on_decoder_data_begin(
820 struct aws_h2_stream *stream,
821 uint32_t payload_len,
822 bool end_stream) {
823
824 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
825
826 struct aws_h2err stream_err = s_check_state_allows_frame_type(stream, AWS_H2_FRAME_T_DATA);
827 if (aws_h2err_failed(stream_err)) {
828 return s_send_rst_and_close_stream(stream, stream_err);
829 }
830
831 if (!stream->thread_data.received_main_headers) {
832 AWS_H2_STREAM_LOG(ERROR, stream, "Malformed message, received DATA before main HEADERS");
833 return s_send_rst_and_close_stream(stream, aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR));
834 }
835
836 /* RFC-7540 6.9.1:
837 * The sender MUST NOT send a flow-controlled frame with a length that exceeds
838 * the space available in either of the flow-control windows advertised by the receiver.
839 * Frames with zero length with the END_STREAM flag set (that is, an empty DATA frame)
840 * MAY be sent if there is no available space in either flow-control window. */
841 if ((int32_t)payload_len > stream->thread_data.window_size_self && payload_len != 0) {
842 AWS_H2_STREAM_LOGF(
843 ERROR,
844 stream,
845 "DATA length=%" PRIu32 " exceeds flow-control window=%" PRIi64,
846 payload_len,
847 stream->thread_data.window_size_self);
848 return s_send_rst_and_close_stream(stream, aws_h2err_from_h2_code(AWS_HTTP2_ERR_FLOW_CONTROL_ERROR));
849 }
850 stream->thread_data.window_size_self -= payload_len;
851
852 /* send a stream window_update frame to automatically maintain the stream self window size, if
853 * manual_window_management is not set */
854 if (payload_len != 0 && !end_stream && !stream->base.owning_connection->manual_window_management) {
855 struct aws_h2_frame *stream_window_update_frame =
856 aws_h2_frame_new_window_update(stream->base.alloc, stream->base.id, payload_len);
857 if (!stream_window_update_frame) {
858 AWS_H2_STREAM_LOGF(
859 ERROR,
860 stream,
861 "WINDOW_UPDATE frame on stream failed to be sent, error %s",
862 aws_error_name(aws_last_error()));
863 return aws_h2err_from_last_error();
864 }
865
866 aws_h2_connection_enqueue_outgoing_frame(s_get_h2_connection(stream), stream_window_update_frame);
867 stream->thread_data.window_size_self += payload_len;
868 }
869
870 return AWS_H2ERR_SUCCESS;
871 }
872
aws_h2_stream_on_decoder_data_i(struct aws_h2_stream * stream,struct aws_byte_cursor data)873 struct aws_h2err aws_h2_stream_on_decoder_data_i(struct aws_h2_stream *stream, struct aws_byte_cursor data) {
874 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
875
876 /* Not calling s_check_state_allows_frame_type() here because we already checked at start of DATA frame in
877 * aws_h2_stream_on_decoder_data_begin() */
878
879 if (stream->base.on_incoming_body) {
880 if (stream->base.on_incoming_body(&stream->base, &data, stream->base.user_data)) {
881 AWS_H2_STREAM_LOGF(
882 ERROR, stream, "Incoming body callback raised error, %s", aws_error_name(aws_last_error()));
883 return aws_h2err_from_last_error();
884 }
885 }
886
887 return AWS_H2ERR_SUCCESS;
888 }
889
aws_h2_stream_on_decoder_window_update(struct aws_h2_stream * stream,uint32_t window_size_increment,bool * window_resume)890 struct aws_h2err aws_h2_stream_on_decoder_window_update(
891 struct aws_h2_stream *stream,
892 uint32_t window_size_increment,
893 bool *window_resume) {
894 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
895
896 *window_resume = false;
897
898 struct aws_h2err stream_err = s_check_state_allows_frame_type(stream, AWS_H2_FRAME_T_WINDOW_UPDATE);
899 if (aws_h2err_failed(stream_err)) {
900 return s_send_rst_and_close_stream(stream, stream_err);
901 }
902 if (window_size_increment == 0) {
903 /* flow-control window increment of 0 MUST be treated as error (RFC7540 6.9.1) */
904 AWS_H2_STREAM_LOG(ERROR, stream, "Window update frame with 0 increment size");
905 return s_send_rst_and_close_stream(stream, aws_h2err_from_h2_code(AWS_HTTP2_ERR_PROTOCOL_ERROR));
906 }
907 int32_t old_window_size = stream->thread_data.window_size_peer;
908 stream_err = (aws_h2_stream_window_size_change(stream, window_size_increment, false /*self*/));
909 if (aws_h2err_failed(stream_err)) {
910 /* We MUST NOT allow a flow-control window to exceed the max */
911 AWS_H2_STREAM_LOG(
912 ERROR, stream, "Window update frame causes the stream flow-control window to exceed the maximum size");
913 return s_send_rst_and_close_stream(stream, stream_err);
914 }
915 if (stream->thread_data.window_size_peer > AWS_H2_MIN_WINDOW_SIZE && old_window_size <= AWS_H2_MIN_WINDOW_SIZE) {
916 *window_resume = true;
917 }
918 return AWS_H2ERR_SUCCESS;
919 }
920
aws_h2_stream_on_decoder_end_stream(struct aws_h2_stream * stream)921 struct aws_h2err aws_h2_stream_on_decoder_end_stream(struct aws_h2_stream *stream) {
922 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
923
924 /* Not calling s_check_state_allows_frame_type() here because END_STREAM isn't
925 * an actual frame type. It's a flag on DATA or HEADERS frames, and we
926 * already checked the legality of those frames in their respective callbacks. */
927
928 if (stream->thread_data.state == AWS_H2_STREAM_STATE_HALF_CLOSED_LOCAL) {
929 /* Both sides have sent END_STREAM */
930 stream->thread_data.state = AWS_H2_STREAM_STATE_CLOSED;
931 AWS_H2_STREAM_LOG(TRACE, stream, "Received END_STREAM. State -> CLOSED");
932 { /* BEGIN CRITICAL SECTION */
933 s_lock_synced_data(stream);
934 stream->synced_data.api_state = AWS_H2_STREAM_API_STATE_COMPLETE;
935 s_unlock_synced_data(stream);
936 } /* END CRITICAL SECTION */
937 /* Tell connection that stream is now closed */
938 if (aws_h2_connection_on_stream_closed(
939 s_get_h2_connection(stream),
940 stream,
941 AWS_H2_STREAM_CLOSED_WHEN_BOTH_SIDES_END_STREAM,
942 AWS_ERROR_SUCCESS)) {
943 return aws_h2err_from_last_error();
944 }
945
946 } else {
947 /* Else can't close until our side sends END_STREAM */
948 stream->thread_data.state = AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE;
949 AWS_H2_STREAM_LOG(TRACE, stream, "Received END_STREAM. State -> HALF_CLOSED_REMOTE");
950 }
951
952 return AWS_H2ERR_SUCCESS;
953 }
954
aws_h2_stream_on_decoder_rst_stream(struct aws_h2_stream * stream,uint32_t h2_error_code)955 struct aws_h2err aws_h2_stream_on_decoder_rst_stream(struct aws_h2_stream *stream, uint32_t h2_error_code) {
956 AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
957
958 /* Check that this state allows RST_STREAM. */
959 struct aws_h2err err = s_check_state_allows_frame_type(stream, AWS_H2_FRAME_T_RST_STREAM);
960 if (aws_h2err_failed(err)) {
961 /* Usually we send a RST_STREAM when the state doesn't allow a frame type, but RFC-7540 5.4.2 says:
962 * "To avoid looping, an endpoint MUST NOT send a RST_STREAM in response to a RST_STREAM frame." */
963 return err;
964 }
965
966 /* RFC-7540 8.1 - a server MAY request that the client abort transmission of a request without error by sending a
967 * RST_STREAM with an error code of NO_ERROR after sending a complete response (i.e., a frame with the END_STREAM
968 * flag). Clients MUST NOT discard responses as a result of receiving such a RST_STREAM */
969 int aws_error_code;
970 if (stream->base.client_data && (h2_error_code == AWS_HTTP2_ERR_NO_ERROR) &&
971 (stream->thread_data.state == AWS_H2_STREAM_STATE_HALF_CLOSED_REMOTE)) {
972
973 aws_error_code = AWS_ERROR_SUCCESS;
974
975 } else {
976 aws_error_code = AWS_ERROR_HTTP_RST_STREAM_RECEIVED;
977 AWS_H2_STREAM_LOGF(
978 ERROR,
979 stream,
980 "Peer terminated stream with HTTP/2 RST_STREAM frame, error-code=0x%x(%s)",
981 h2_error_code,
982 aws_http2_error_code_to_str(h2_error_code));
983 }
984
985 stream->thread_data.state = AWS_H2_STREAM_STATE_CLOSED;
986 { /* BEGIN CRITICAL SECTION */
987 s_lock_synced_data(stream);
988 stream->synced_data.api_state = AWS_H2_STREAM_API_STATE_COMPLETE;
989 s_unlock_synced_data(stream);
990 } /* END CRITICAL SECTION */
991 stream->received_reset_error_code = h2_error_code;
992
993 AWS_H2_STREAM_LOGF(
994 TRACE,
995 stream,
996 "Received RST_STREAM code=0x%x(%s). State -> CLOSED",
997 h2_error_code,
998 aws_http2_error_code_to_str(h2_error_code));
999
1000 if (aws_h2_connection_on_stream_closed(
1001 s_get_h2_connection(stream), stream, AWS_H2_STREAM_CLOSED_WHEN_RST_STREAM_RECEIVED, aws_error_code)) {
1002 return aws_h2err_from_last_error();
1003 }
1004
1005 return AWS_H2ERR_SUCCESS;
1006 }
1007