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_frames.h>
7 #include <aws/http/private/hpack.h>
8
9 #include <aws/compression/huffman.h>
10
11 #include <aws/common/logging.h>
12
13 #include <aws/io/stream.h>
14
15 #include <inttypes.h>
16
17 #if _MSC_VER
18 # pragma warning(disable : 4204) /* non-constant aggregate initializer */
19 #endif
20
21 #define ENCODER_LOGF(level, encoder, text, ...) \
22 AWS_LOGF_##level(AWS_LS_HTTP_ENCODER, "id=%p " text, (encoder)->logging_id, __VA_ARGS__)
23
24 #define ENCODER_LOG(level, encoder, text) ENCODER_LOGF(level, encoder, "%s", text)
25
26 const struct aws_byte_cursor aws_h2_connection_preface_client_string =
27 AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
28
29 /* Initial values and bounds are from RFC-7540 6.5.2 */
30 const uint32_t aws_h2_settings_initial[AWS_HTTP2_SETTINGS_END_RANGE] = {
31 [AWS_HTTP2_SETTINGS_HEADER_TABLE_SIZE] = 4096,
32 [AWS_HTTP2_SETTINGS_ENABLE_PUSH] = 1,
33 [AWS_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = UINT32_MAX, /* "Initially there is no limit to this value" */
34 [AWS_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = 65535,
35 [AWS_HTTP2_SETTINGS_MAX_FRAME_SIZE] = 16384,
36 [AWS_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE] = UINT32_MAX, /* "The initial value of this setting is unlimited" */
37 };
38
39 const uint32_t aws_h2_settings_bounds[AWS_HTTP2_SETTINGS_END_RANGE][2] = {
40 [AWS_HTTP2_SETTINGS_HEADER_TABLE_SIZE][0] = 0,
41 [AWS_HTTP2_SETTINGS_HEADER_TABLE_SIZE][1] = UINT32_MAX,
42
43 [AWS_HTTP2_SETTINGS_ENABLE_PUSH][0] = 0,
44 [AWS_HTTP2_SETTINGS_ENABLE_PUSH][1] = 1,
45
46 [AWS_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS][0] = 0,
47 [AWS_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS][1] = UINT32_MAX,
48
49 [AWS_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE][0] = 0,
50 [AWS_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE][1] = AWS_H2_WINDOW_UPDATE_MAX,
51
52 [AWS_HTTP2_SETTINGS_MAX_FRAME_SIZE][0] = 16384,
53 [AWS_HTTP2_SETTINGS_MAX_FRAME_SIZE][1] = AWS_H2_PAYLOAD_MAX,
54
55 [AWS_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE][0] = 0,
56 [AWS_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE][1] = UINT32_MAX,
57 };
58
59 /* Stream ids & dependencies should only write the bottom 31 bits */
60 static const uint32_t s_u32_top_bit_mask = UINT32_MAX << 31;
61
62 /* Bytes to initially reserve for encoding of an entire header block. Buffer will grow if necessary. */
63 static const size_t s_encoded_header_block_reserve = 128; /* Value pulled from thin air */
64
65 #define DEFINE_FRAME_VTABLE(NAME) \
66 static aws_h2_frame_destroy_fn s_frame_##NAME##_destroy; \
67 static aws_h2_frame_encode_fn s_frame_##NAME##_encode; \
68 static const struct aws_h2_frame_vtable s_frame_##NAME##_vtable = { \
69 .destroy = s_frame_##NAME##_destroy, \
70 .encode = s_frame_##NAME##_encode, \
71 }
72
aws_h2_frame_type_to_str(enum aws_h2_frame_type type)73 const char *aws_h2_frame_type_to_str(enum aws_h2_frame_type type) {
74 switch (type) {
75 case AWS_H2_FRAME_T_DATA:
76 return "DATA";
77 case AWS_H2_FRAME_T_HEADERS:
78 return "HEADERS";
79 case AWS_H2_FRAME_T_PRIORITY:
80 return "PRIORITY";
81 case AWS_H2_FRAME_T_RST_STREAM:
82 return "RST_STREAM";
83 case AWS_H2_FRAME_T_SETTINGS:
84 return "SETTINGS";
85 case AWS_H2_FRAME_T_PUSH_PROMISE:
86 return "PUSH_PROMISE";
87 case AWS_H2_FRAME_T_PING:
88 return "PING";
89 case AWS_H2_FRAME_T_GOAWAY:
90 return "GOAWAY";
91 case AWS_H2_FRAME_T_WINDOW_UPDATE:
92 return "WINDOW_UPDATE";
93 case AWS_H2_FRAME_T_CONTINUATION:
94 return "CONTINUATION";
95 default:
96 return "**UNKNOWN**";
97 }
98 }
99
aws_http2_error_code_to_str(enum aws_http2_error_code h2_error_code)100 const char *aws_http2_error_code_to_str(enum aws_http2_error_code h2_error_code) {
101 switch (h2_error_code) {
102 case AWS_HTTP2_ERR_NO_ERROR:
103 return "NO_ERROR";
104 case AWS_HTTP2_ERR_PROTOCOL_ERROR:
105 return "PROTOCOL_ERROR";
106 case AWS_HTTP2_ERR_INTERNAL_ERROR:
107 return "INTERNAL_ERROR";
108 case AWS_HTTP2_ERR_FLOW_CONTROL_ERROR:
109 return "FLOW_CONTROL_ERROR";
110 case AWS_HTTP2_ERR_SETTINGS_TIMEOUT:
111 return "SETTINGS_TIMEOUT";
112 case AWS_HTTP2_ERR_STREAM_CLOSED:
113 return "STREAM_CLOSED";
114 case AWS_HTTP2_ERR_FRAME_SIZE_ERROR:
115 return "FRAME_SIZE_ERROR";
116 case AWS_HTTP2_ERR_REFUSED_STREAM:
117 return "REFUSED_STREAM";
118 case AWS_HTTP2_ERR_CANCEL:
119 return "CANCEL";
120 case AWS_HTTP2_ERR_COMPRESSION_ERROR:
121 return "COMPRESSION_ERROR";
122 case AWS_HTTP2_ERR_CONNECT_ERROR:
123 return "CONNECT_ERROR";
124 case AWS_HTTP2_ERR_ENHANCE_YOUR_CALM:
125 return "ENHANCE_YOUR_CALM";
126 case AWS_HTTP2_ERR_INADEQUATE_SECURITY:
127 return "INADEQUATE_SECURITY";
128 case AWS_HTTP2_ERR_HTTP_1_1_REQUIRED:
129 return "HTTP_1_1_REQUIRED";
130 default:
131 return "UNKNOWN_ERROR";
132 }
133 }
134
aws_h2err_from_h2_code(enum aws_http2_error_code h2_error_code)135 struct aws_h2err aws_h2err_from_h2_code(enum aws_http2_error_code h2_error_code) {
136 AWS_PRECONDITION(h2_error_code > AWS_HTTP2_ERR_NO_ERROR && h2_error_code < AWS_HTTP2_ERR_COUNT);
137
138 return (struct aws_h2err){
139 .h2_code = h2_error_code,
140 .aws_code = AWS_ERROR_HTTP_PROTOCOL_ERROR,
141 };
142 }
143
aws_h2err_from_aws_code(int aws_error_code)144 struct aws_h2err aws_h2err_from_aws_code(int aws_error_code) {
145 AWS_PRECONDITION(aws_error_code != 0);
146
147 return (struct aws_h2err){
148 .h2_code = AWS_HTTP2_ERR_INTERNAL_ERROR,
149 .aws_code = aws_error_code,
150 };
151 }
152
aws_h2err_from_last_error(void)153 struct aws_h2err aws_h2err_from_last_error(void) {
154 return aws_h2err_from_aws_code(aws_last_error());
155 }
156
aws_h2err_success(struct aws_h2err err)157 bool aws_h2err_success(struct aws_h2err err) {
158 return err.h2_code == 0 && err.aws_code == 0;
159 }
160
aws_h2err_failed(struct aws_h2err err)161 bool aws_h2err_failed(struct aws_h2err err) {
162 return err.h2_code != 0 || err.aws_code != 0;
163 }
164
aws_h2_validate_stream_id(uint32_t stream_id)165 int aws_h2_validate_stream_id(uint32_t stream_id) {
166 if (stream_id == 0 || stream_id > AWS_H2_STREAM_ID_MAX) {
167 return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
168 }
169 return AWS_OP_SUCCESS;
170 }
171
172 /**
173 * Determine max frame payload length that will:
174 * 1) fit in output's available space
175 * 2) obey encoders current MAX_FRAME_SIZE
176 *
177 * Assumes no part of the frame has been written yet to output.
178 * The total length of the frame would be: returned-payload-len + AWS_H2_FRAME_PREFIX_SIZE
179 *
180 * Raises error if there is not enough space available for even a frame prefix.
181 */
s_get_max_contiguous_payload_length(const struct aws_h2_frame_encoder * encoder,const struct aws_byte_buf * output,size_t * max_payload_length)182 static int s_get_max_contiguous_payload_length(
183 const struct aws_h2_frame_encoder *encoder,
184 const struct aws_byte_buf *output,
185 size_t *max_payload_length) {
186
187 const size_t space_available = output->capacity - output->len;
188
189 size_t max_payload_given_space_available;
190 if (aws_sub_size_checked(space_available, AWS_H2_FRAME_PREFIX_SIZE, &max_payload_given_space_available)) {
191 return aws_raise_error(AWS_ERROR_SHORT_BUFFER);
192 }
193
194 size_t max_payload_given_settings = encoder->settings.max_frame_size;
195
196 *max_payload_length = aws_min_size(max_payload_given_space_available, max_payload_given_settings);
197 return AWS_OP_SUCCESS;
198 }
199
200 /***********************************************************************************************************************
201 * Priority
202 **********************************************************************************************************************/
203 static size_t s_frame_priority_settings_size = 5;
204
s_frame_priority_settings_encode(const struct aws_h2_frame_priority_settings * priority,struct aws_byte_buf * output)205 static void s_frame_priority_settings_encode(
206 const struct aws_h2_frame_priority_settings *priority,
207 struct aws_byte_buf *output) {
208 AWS_PRECONDITION(priority);
209 AWS_PRECONDITION(output);
210 AWS_PRECONDITION((priority->stream_dependency & s_u32_top_bit_mask) == 0);
211 (void)s_u32_top_bit_mask;
212
213 /* PRIORITY is encoded as (RFC-7540 6.3):
214 * +-+-------------------------------------------------------------+
215 * |E| Stream Dependency (31) |
216 * +-+-------------+-----------------------------------------------+
217 * | Weight (8) |
218 * +-+-------------+
219 */
220 bool writes_ok = true;
221
222 /* Write the top 4 bytes */
223 uint32_t top_bytes = priority->stream_dependency | ((uint32_t)priority->stream_dependency_exclusive << 31);
224 writes_ok &= aws_byte_buf_write_be32(output, top_bytes);
225
226 /* Write the priority weight */
227 writes_ok &= aws_byte_buf_write_u8(output, priority->weight);
228
229 AWS_ASSERT(writes_ok);
230 }
231
232 /***********************************************************************************************************************
233 * Common Frame Prefix
234 **********************************************************************************************************************/
s_init_frame_base(struct aws_h2_frame * frame_base,struct aws_allocator * alloc,enum aws_h2_frame_type type,const struct aws_h2_frame_vtable * vtable,uint32_t stream_id)235 static void s_init_frame_base(
236 struct aws_h2_frame *frame_base,
237 struct aws_allocator *alloc,
238 enum aws_h2_frame_type type,
239 const struct aws_h2_frame_vtable *vtable,
240 uint32_t stream_id) {
241
242 frame_base->vtable = vtable;
243 frame_base->alloc = alloc;
244 frame_base->type = type;
245 frame_base->stream_id = stream_id;
246 }
247
s_frame_prefix_encode(enum aws_h2_frame_type type,uint32_t stream_id,size_t length,uint8_t flags,struct aws_byte_buf * output)248 static void s_frame_prefix_encode(
249 enum aws_h2_frame_type type,
250 uint32_t stream_id,
251 size_t length,
252 uint8_t flags,
253 struct aws_byte_buf *output) {
254 AWS_PRECONDITION(output);
255 AWS_PRECONDITION(!(stream_id & s_u32_top_bit_mask), "Invalid stream ID");
256 AWS_PRECONDITION(length <= AWS_H2_PAYLOAD_MAX);
257
258 /* Frame prefix is encoded like this (RFC-7540 4.1):
259 * +-----------------------------------------------+
260 * | Length (24) |
261 * +---------------+---------------+---------------+
262 * | Type (8) | Flags (8) |
263 * +-+-------------+---------------+-------------------------------+
264 * |R| Stream Identifier (31) |
265 * +=+=============================================================+
266 */
267 bool writes_ok = true;
268
269 /* Write length */
270 writes_ok &= aws_byte_buf_write_be24(output, (uint32_t)length);
271
272 /* Write type */
273 writes_ok &= aws_byte_buf_write_u8(output, type);
274
275 /* Write flags */
276 writes_ok &= aws_byte_buf_write_u8(output, flags);
277
278 /* Write stream id (with reserved first bit) */
279 writes_ok &= aws_byte_buf_write_be32(output, stream_id);
280
281 AWS_ASSERT(writes_ok);
282 }
283
284 /***********************************************************************************************************************
285 * Encoder
286 **********************************************************************************************************************/
aws_h2_frame_encoder_init(struct aws_h2_frame_encoder * encoder,struct aws_allocator * allocator,const void * logging_id)287 int aws_h2_frame_encoder_init(
288 struct aws_h2_frame_encoder *encoder,
289 struct aws_allocator *allocator,
290 const void *logging_id) {
291
292 AWS_PRECONDITION(encoder);
293 AWS_PRECONDITION(allocator);
294
295 AWS_ZERO_STRUCT(*encoder);
296 encoder->allocator = allocator;
297 encoder->logging_id = logging_id;
298
299 encoder->hpack = aws_hpack_context_new(allocator, AWS_LS_HTTP_ENCODER, logging_id);
300 if (!encoder->hpack) {
301 return AWS_OP_ERR;
302 }
303
304 encoder->settings.max_frame_size = aws_h2_settings_initial[AWS_HTTP2_SETTINGS_MAX_FRAME_SIZE];
305 return AWS_OP_SUCCESS;
306 }
aws_h2_frame_encoder_clean_up(struct aws_h2_frame_encoder * encoder)307 void aws_h2_frame_encoder_clean_up(struct aws_h2_frame_encoder *encoder) {
308 AWS_PRECONDITION(encoder);
309
310 aws_hpack_context_destroy(encoder->hpack);
311 }
312
313 /***********************************************************************************************************************
314 * DATA
315 **********************************************************************************************************************/
aws_h2_encode_data_frame(struct aws_h2_frame_encoder * encoder,uint32_t stream_id,struct aws_input_stream * body_stream,bool body_ends_stream,uint8_t pad_length,int32_t * stream_window_size_peer,size_t * connection_window_size_peer,struct aws_byte_buf * output,bool * body_complete,bool * body_stalled)316 int aws_h2_encode_data_frame(
317 struct aws_h2_frame_encoder *encoder,
318 uint32_t stream_id,
319 struct aws_input_stream *body_stream,
320 bool body_ends_stream,
321 uint8_t pad_length,
322 int32_t *stream_window_size_peer,
323 size_t *connection_window_size_peer,
324 struct aws_byte_buf *output,
325 bool *body_complete,
326 bool *body_stalled) {
327
328 AWS_PRECONDITION(encoder);
329 AWS_PRECONDITION(body_stream);
330 AWS_PRECONDITION(output);
331 AWS_PRECONDITION(body_complete);
332 AWS_PRECONDITION(body_stalled);
333 AWS_PRECONDITION(*stream_window_size_peer > 0);
334
335 if (aws_h2_validate_stream_id(stream_id)) {
336 return AWS_OP_ERR;
337 }
338
339 *body_complete = false;
340 *body_stalled = false;
341 uint8_t flags = 0;
342
343 /*
344 * Payload-length is the first thing encoded in a frame, but we don't know how
345 * much data we'll get from the body-stream until we actually read it.
346 * Therefore, we determine the exact location that the body data should go,
347 * then stream the body directly into that part of the output buffer.
348 * Then we will go and write the other parts of the frame in around it.
349 */
350
351 size_t bytes_preceding_body = AWS_H2_FRAME_PREFIX_SIZE;
352 size_t payload_overhead = 0; /* Amount of "payload" that will not contain body (padding) */
353 if (pad_length > 0) {
354 flags |= AWS_H2_FRAME_F_PADDED;
355
356 /* Padding len is 1st byte of payload (padding itself goes at end of payload) */
357 bytes_preceding_body += 1;
358 payload_overhead = 1 + pad_length;
359 }
360
361 /* Max amount allowed by stream and connection flow-control window */
362 size_t min_window_size = aws_min_size(*stream_window_size_peer, *connection_window_size_peer);
363
364 /* Max amount of payload we can do right now */
365 size_t max_payload;
366 if (s_get_max_contiguous_payload_length(encoder, output, &max_payload)) {
367 goto handle_waiting_for_more_space;
368 }
369 /* The flow-control window will limit the size for max_payload of a flow-controlled frame */
370 max_payload = aws_min_size(max_payload, min_window_size);
371 /* Max amount of body we can fit in the payload*/
372 size_t max_body;
373 if (aws_sub_size_checked(max_payload, payload_overhead, &max_body) || max_body == 0) {
374 goto handle_waiting_for_more_space;
375 }
376
377 /* Use a sub-buffer to limit where body can go */
378 struct aws_byte_buf body_sub_buf =
379 aws_byte_buf_from_empty_array(output->buffer + output->len + bytes_preceding_body, max_body);
380
381 /* Read body into sub-buffer */
382 if (aws_input_stream_read(body_stream, &body_sub_buf)) {
383 goto error;
384 }
385
386 /* Check if we've reached the end of the body */
387 struct aws_stream_status body_status;
388 if (aws_input_stream_get_status(body_stream, &body_status)) {
389 goto error;
390 }
391
392 if (body_status.is_end_of_stream) {
393 *body_complete = true;
394 if (body_ends_stream) {
395 flags |= AWS_H2_FRAME_F_END_STREAM;
396 }
397 } else {
398 if (body_sub_buf.len < body_sub_buf.capacity) {
399 /* Body stream was unable to provide as much data as it could have */
400 *body_stalled = true;
401
402 if (body_sub_buf.len == 0) {
403 /* This frame would have no useful information, don't even bother sending it */
404 goto handle_nothing_to_send_right_now;
405 }
406 }
407 }
408
409 ENCODER_LOGF(
410 TRACE,
411 encoder,
412 "Encoding frame type=DATA stream_id=%" PRIu32 " data_len=%zu stalled=%d%s",
413 stream_id,
414 body_sub_buf.len,
415 *body_stalled,
416 (flags & AWS_H2_FRAME_F_END_STREAM) ? " END_STREAM" : "");
417
418 /*
419 * Write in the other parts of the frame.
420 */
421 bool writes_ok = true;
422
423 /* Write the frame prefix */
424 const size_t payload_len = body_sub_buf.len + payload_overhead;
425 s_frame_prefix_encode(AWS_H2_FRAME_T_DATA, stream_id, payload_len, flags, output);
426
427 /* Write pad length */
428 if (flags & AWS_H2_FRAME_F_PADDED) {
429 writes_ok &= aws_byte_buf_write_u8(output, pad_length);
430 }
431
432 /* Increment output->len to jump over the body that we already wrote in */
433 AWS_ASSERT(output->buffer + output->len == body_sub_buf.buffer && "Streamed DATA to wrong position");
434 output->len += body_sub_buf.len;
435
436 /* Write padding */
437 if (flags & AWS_H2_FRAME_F_PADDED) {
438 writes_ok &= aws_byte_buf_write_u8_n(output, 0, pad_length);
439 }
440
441 /* update the connection window size now, we will update stream window size when this function returns */
442 AWS_ASSERT(payload_len <= min_window_size);
443 *connection_window_size_peer -= payload_len;
444 *stream_window_size_peer -= (int32_t)payload_len;
445
446 AWS_ASSERT(writes_ok);
447 return AWS_OP_SUCCESS;
448
449 handle_waiting_for_more_space:
450 ENCODER_LOGF(TRACE, encoder, "Insufficient space to encode DATA for stream %" PRIu32 " right now", stream_id);
451 return AWS_OP_SUCCESS;
452
453 handle_nothing_to_send_right_now:
454 ENCODER_LOGF(INFO, encoder, "Stream %" PRIu32 " produced 0 bytes of body data", stream_id);
455 return AWS_OP_SUCCESS;
456
457 error:
458 return AWS_OP_ERR;
459 }
460
461 /***********************************************************************************************************************
462 * HEADERS / PUSH_PROMISE
463 **********************************************************************************************************************/
464 DEFINE_FRAME_VTABLE(headers);
465
466 /* Represents a HEADERS or PUSH_PROMISE frame (followed by zero or more CONTINUATION frames) */
467 struct aws_h2_frame_headers {
468 struct aws_h2_frame base;
469
470 /* Common data */
471 const struct aws_http_headers *headers;
472 uint8_t pad_length; /* Set to 0 to disable AWS_H2_FRAME_F_PADDED */
473
474 /* HEADERS-only data */
475 bool end_stream; /* AWS_H2_FRAME_F_END_STREAM */
476 bool has_priority; /* AWS_H2_FRAME_F_PRIORITY */
477 struct aws_h2_frame_priority_settings priority;
478
479 /* PUSH_PROMISE-only data */
480 uint32_t promised_stream_id;
481
482 /* State */
483 enum {
484 AWS_H2_HEADERS_STATE_INIT,
485 AWS_H2_HEADERS_STATE_FIRST_FRAME, /* header-block pre-encoded, no frames written yet */
486 AWS_H2_HEADERS_STATE_CONTINUATION, /* first frame written, need to write CONTINUATION frames now */
487 AWS_H2_HEADERS_STATE_COMPLETE,
488 } state;
489
490 struct aws_byte_buf whole_encoded_header_block;
491 struct aws_byte_cursor header_block_cursor; /* tracks progress sending encoded header-block in fragments */
492 };
493
s_frame_new_headers_or_push_promise(struct aws_allocator * allocator,enum aws_h2_frame_type frame_type,uint32_t stream_id,const struct aws_http_headers * headers,uint8_t pad_length,bool end_stream,const struct aws_h2_frame_priority_settings * optional_priority,uint32_t promised_stream_id)494 static struct aws_h2_frame *s_frame_new_headers_or_push_promise(
495 struct aws_allocator *allocator,
496 enum aws_h2_frame_type frame_type,
497 uint32_t stream_id,
498 const struct aws_http_headers *headers,
499 uint8_t pad_length,
500 bool end_stream,
501 const struct aws_h2_frame_priority_settings *optional_priority,
502 uint32_t promised_stream_id) {
503
504 AWS_PRECONDITION(allocator);
505 AWS_PRECONDITION(frame_type == AWS_H2_FRAME_T_HEADERS || frame_type == AWS_H2_FRAME_T_PUSH_PROMISE);
506 AWS_PRECONDITION(headers);
507
508 /* Validate args */
509
510 if (aws_h2_validate_stream_id(stream_id)) {
511 return NULL;
512 }
513
514 if (frame_type == AWS_H2_FRAME_T_PUSH_PROMISE) {
515 if (aws_h2_validate_stream_id(promised_stream_id)) {
516 return NULL;
517 }
518 }
519
520 if (optional_priority && aws_h2_validate_stream_id(optional_priority->stream_dependency)) {
521 return NULL;
522 }
523
524 /* Create */
525
526 struct aws_h2_frame_headers *frame = aws_mem_calloc(allocator, 1, sizeof(struct aws_h2_frame_headers));
527 if (!frame) {
528 return NULL;
529 }
530
531 if (aws_byte_buf_init(&frame->whole_encoded_header_block, allocator, s_encoded_header_block_reserve)) {
532 goto error;
533 }
534
535 if (frame_type == AWS_H2_FRAME_T_HEADERS) {
536 frame->end_stream = end_stream;
537 if (optional_priority) {
538 frame->has_priority = true;
539 frame->priority = *optional_priority;
540 }
541 } else {
542 frame->promised_stream_id = promised_stream_id;
543 }
544
545 s_init_frame_base(&frame->base, allocator, frame_type, &s_frame_headers_vtable, stream_id);
546
547 aws_http_headers_acquire((struct aws_http_headers *)headers);
548 frame->headers = headers;
549 frame->pad_length = pad_length;
550
551 return &frame->base;
552
553 error:
554 s_frame_headers_destroy(&frame->base);
555 return NULL;
556 }
557
aws_h2_frame_new_headers(struct aws_allocator * allocator,uint32_t stream_id,const struct aws_http_headers * headers,bool end_stream,uint8_t pad_length,const struct aws_h2_frame_priority_settings * optional_priority)558 struct aws_h2_frame *aws_h2_frame_new_headers(
559 struct aws_allocator *allocator,
560 uint32_t stream_id,
561 const struct aws_http_headers *headers,
562 bool end_stream,
563 uint8_t pad_length,
564 const struct aws_h2_frame_priority_settings *optional_priority) {
565
566 return s_frame_new_headers_or_push_promise(
567 allocator,
568 AWS_H2_FRAME_T_HEADERS,
569 stream_id,
570 headers,
571 pad_length,
572 end_stream,
573 optional_priority,
574 0 /* HEADERS doesn't have promised_stream_id */);
575 }
576
aws_h2_frame_new_push_promise(struct aws_allocator * allocator,uint32_t stream_id,uint32_t promised_stream_id,const struct aws_http_headers * headers,uint8_t pad_length)577 struct aws_h2_frame *aws_h2_frame_new_push_promise(
578 struct aws_allocator *allocator,
579 uint32_t stream_id,
580 uint32_t promised_stream_id,
581 const struct aws_http_headers *headers,
582 uint8_t pad_length) {
583
584 return s_frame_new_headers_or_push_promise(
585 allocator,
586 AWS_H2_FRAME_T_PUSH_PROMISE,
587 stream_id,
588 headers,
589 pad_length,
590 false /* PUSH_PROMISE doesn't have end_stream flag */,
591 NULL /* PUSH_PROMISE doesn't have priority_settings */,
592 promised_stream_id);
593 }
594
s_frame_headers_destroy(struct aws_h2_frame * frame_base)595 static void s_frame_headers_destroy(struct aws_h2_frame *frame_base) {
596 struct aws_h2_frame_headers *frame = AWS_CONTAINER_OF(frame_base, struct aws_h2_frame_headers, base);
597 aws_http_headers_release((struct aws_http_headers *)frame->headers);
598 aws_byte_buf_clean_up(&frame->whole_encoded_header_block);
599 aws_mem_release(frame->base.alloc, frame);
600 }
601
602 /* Encode the next frame for this header-block (or encode nothing if output buffer is too small). */
s_encode_single_header_block_frame(struct aws_h2_frame_headers * frame,struct aws_h2_frame_encoder * encoder,struct aws_byte_buf * output,bool * waiting_for_more_space)603 static void s_encode_single_header_block_frame(
604 struct aws_h2_frame_headers *frame,
605 struct aws_h2_frame_encoder *encoder,
606 struct aws_byte_buf *output,
607 bool *waiting_for_more_space) {
608
609 /*
610 * Figure out the details of the next frame to encode.
611 * The first frame will be either HEADERS or PUSH_PROMISE.
612 * All subsequent frames will be CONTINUATION
613 */
614
615 enum aws_h2_frame_type frame_type;
616 uint8_t flags = 0;
617 uint8_t pad_length = 0;
618 const struct aws_h2_frame_priority_settings *priority_settings = NULL;
619 const uint32_t *promised_stream_id = NULL;
620 size_t payload_overhead = 0; /* Amount of payload holding things other than header-block (padding, etc) */
621
622 if (frame->state == AWS_H2_HEADERS_STATE_FIRST_FRAME) {
623 frame_type = frame->base.type;
624
625 if (frame->pad_length > 0) {
626 flags |= AWS_H2_FRAME_F_PADDED;
627 pad_length = frame->pad_length;
628 payload_overhead += 1 + pad_length;
629 }
630
631 if (frame->has_priority) {
632 priority_settings = &frame->priority;
633 flags |= AWS_H2_FRAME_F_PRIORITY;
634 payload_overhead += s_frame_priority_settings_size;
635 }
636
637 if (frame->end_stream) {
638 flags |= AWS_H2_FRAME_F_END_STREAM;
639 }
640
641 if (frame_type == AWS_H2_FRAME_T_PUSH_PROMISE) {
642 promised_stream_id = &frame->promised_stream_id;
643 payload_overhead += 4;
644 }
645
646 } else /* CONTINUATION */ {
647 frame_type = AWS_H2_FRAME_T_CONTINUATION;
648 }
649
650 /*
651 * Figure out what size header-block fragment should go in this frame.
652 */
653
654 size_t max_payload;
655 if (s_get_max_contiguous_payload_length(encoder, output, &max_payload)) {
656 goto handle_waiting_for_more_space;
657 }
658
659 size_t max_fragment;
660 if (aws_sub_size_checked(max_payload, payload_overhead, &max_fragment)) {
661 goto handle_waiting_for_more_space;
662 }
663
664 const size_t fragment_len = aws_min_size(max_fragment, frame->header_block_cursor.len);
665 if (fragment_len == frame->header_block_cursor.len) {
666 /* This will finish the header-block */
667 flags |= AWS_H2_FRAME_F_END_HEADERS;
668 } else {
669 /* If we're not finishing the header-block, is it even worth trying to send this frame now? */
670 const size_t even_worth_sending_threshold = AWS_H2_FRAME_PREFIX_SIZE + payload_overhead;
671 if (fragment_len < even_worth_sending_threshold) {
672 goto handle_waiting_for_more_space;
673 }
674 }
675
676 /*
677 * Ok, it fits! Write the frame
678 */
679 ENCODER_LOGF(
680 TRACE,
681 encoder,
682 "Encoding frame type=%s stream_id=%" PRIu32 "%s%s",
683 aws_h2_frame_type_to_str(frame_type),
684 frame->base.stream_id,
685 (flags & AWS_H2_FRAME_F_END_HEADERS) ? " END_HEADERS" : "",
686 (flags & AWS_H2_FRAME_F_END_STREAM) ? " END_STREAM" : "");
687
688 bool writes_ok = true;
689
690 /* Write the frame prefix */
691 const size_t payload_len = fragment_len + payload_overhead;
692 s_frame_prefix_encode(frame_type, frame->base.stream_id, payload_len, flags, output);
693
694 /* Write pad length */
695 if (flags & AWS_H2_FRAME_F_PADDED) {
696 AWS_ASSERT(frame_type != AWS_H2_FRAME_T_CONTINUATION);
697 writes_ok &= aws_byte_buf_write_u8(output, pad_length);
698 }
699
700 /* Write priority */
701 if (flags & AWS_H2_FRAME_F_PRIORITY) {
702 AWS_ASSERT(frame_type == AWS_H2_FRAME_T_HEADERS);
703 s_frame_priority_settings_encode(priority_settings, output);
704 }
705
706 /* Write promised stream ID */
707 if (promised_stream_id) {
708 AWS_ASSERT(frame_type == AWS_H2_FRAME_T_PUSH_PROMISE);
709 writes_ok &= aws_byte_buf_write_be32(output, *promised_stream_id);
710 }
711
712 /* Write header-block fragment */
713 if (fragment_len > 0) {
714 struct aws_byte_cursor fragment = aws_byte_cursor_advance(&frame->header_block_cursor, fragment_len);
715 writes_ok &= aws_byte_buf_write_from_whole_cursor(output, fragment);
716 }
717
718 /* Write padding */
719 if (flags & AWS_H2_FRAME_F_PADDED) {
720 writes_ok &= aws_byte_buf_write_u8_n(output, 0, pad_length);
721 }
722
723 AWS_ASSERT(writes_ok);
724
725 /* Success! Wrote entire frame. It's safe to change state now */
726 frame->state =
727 flags & AWS_H2_FRAME_F_END_HEADERS ? AWS_H2_HEADERS_STATE_COMPLETE : AWS_H2_HEADERS_STATE_CONTINUATION;
728 *waiting_for_more_space = false;
729 return;
730
731 handle_waiting_for_more_space:
732 ENCODER_LOGF(
733 TRACE,
734 encoder,
735 "Insufficient space to encode %s for stream %" PRIu32 " right now",
736 aws_h2_frame_type_to_str(frame->base.type),
737 frame->base.stream_id);
738 *waiting_for_more_space = true;
739 }
740
s_frame_headers_encode(struct aws_h2_frame * frame_base,struct aws_h2_frame_encoder * encoder,struct aws_byte_buf * output,bool * complete)741 static int s_frame_headers_encode(
742 struct aws_h2_frame *frame_base,
743 struct aws_h2_frame_encoder *encoder,
744 struct aws_byte_buf *output,
745 bool *complete) {
746
747 struct aws_h2_frame_headers *frame = AWS_CONTAINER_OF(frame_base, struct aws_h2_frame_headers, base);
748
749 /* Pre-encode the entire header-block into another buffer
750 * the first time we're called. */
751 if (frame->state == AWS_H2_HEADERS_STATE_INIT) {
752 if (aws_hpack_encode_header_block(encoder->hpack, frame->headers, &frame->whole_encoded_header_block)) {
753 ENCODER_LOGF(
754 ERROR,
755 encoder,
756 "Error doing HPACK encoding on %s of stream %" PRIu32 ": %s",
757 aws_h2_frame_type_to_str(frame->base.type),
758 frame->base.stream_id,
759 aws_error_name(aws_last_error()));
760 goto error;
761 }
762
763 frame->header_block_cursor = aws_byte_cursor_from_buf(&frame->whole_encoded_header_block);
764 frame->state = AWS_H2_HEADERS_STATE_FIRST_FRAME;
765 }
766
767 /* Write frames (HEADER or PUSH_PROMISE, followed by N CONTINUATION frames)
768 * until we're done writing header-block or the buffer is too full to continue */
769 bool waiting_for_more_space = false;
770 while (frame->state < AWS_H2_HEADERS_STATE_COMPLETE && !waiting_for_more_space) {
771 s_encode_single_header_block_frame(frame, encoder, output, &waiting_for_more_space);
772 }
773
774 *complete = frame->state == AWS_H2_HEADERS_STATE_COMPLETE;
775 return AWS_OP_SUCCESS;
776
777 error:
778 return AWS_OP_ERR;
779 }
780
781 /***********************************************************************************************************************
782 * aws_h2_frame_prebuilt - Used by small simple frame types that we can pre-encode at the time of creation.
783 * The pre-encoded buffer is then just copied bit-by-bit during the actual "encode()" function.
784 *
785 * It's safe to pre-encode a frame if it doesn't query/mutate any external state. So PING is totally great
786 * to pre-encode, but HEADERS (which queries MAX_FRAME_SIZE and mutates the HPACK table) would be a bad candidate.
787 **********************************************************************************************************************/
788 struct aws_h2_frame_prebuilt {
789 struct aws_h2_frame base;
790
791 /* The whole entire frame is pre-encoded to this buffer during construction.
792 * The buffer has the exact capacity necessary to hold the frame */
793 struct aws_byte_buf encoded_buf;
794
795 /* After construction, this cursor points to the full contents of encoded_buf.
796 * As encode() is called, we copy the contents to output and advance the cursor.*/
797 struct aws_byte_cursor cursor;
798 };
799
800 DEFINE_FRAME_VTABLE(prebuilt);
801
802 /* Can't pre-encode a frame unless it's guaranteed to fit, regardless of current settings. */
s_prebuilt_payload_max(void)803 static size_t s_prebuilt_payload_max(void) {
804 return aws_h2_settings_bounds[AWS_HTTP2_SETTINGS_MAX_FRAME_SIZE][0];
805 }
806
807 /* Create aws_h2_frame_prebuilt and encode frame prefix into frame->encoded_buf.
808 * Caller must encode the payload to fill the rest of the encoded_buf. */
s_h2_frame_new_prebuilt(struct aws_allocator * allocator,enum aws_h2_frame_type type,uint32_t stream_id,size_t payload_len,uint8_t flags)809 static struct aws_h2_frame_prebuilt *s_h2_frame_new_prebuilt(
810 struct aws_allocator *allocator,
811 enum aws_h2_frame_type type,
812 uint32_t stream_id,
813 size_t payload_len,
814 uint8_t flags) {
815
816 AWS_PRECONDITION(payload_len <= s_prebuilt_payload_max());
817
818 const size_t encoded_frame_len = AWS_H2_FRAME_PREFIX_SIZE + payload_len;
819
820 /* Use single allocation for frame and buffer storage */
821 struct aws_h2_frame_prebuilt *frame;
822 void *storage;
823 if (!aws_mem_acquire_many(
824 allocator, 2, &frame, sizeof(struct aws_h2_frame_prebuilt), &storage, encoded_frame_len)) {
825 return NULL;
826 }
827
828 AWS_ZERO_STRUCT(*frame);
829 s_init_frame_base(&frame->base, allocator, type, &s_frame_prebuilt_vtable, stream_id);
830
831 /* encoded_buf has the exact amount of space necessary for the full encoded frame.
832 * The constructor of our subclass must finish filling up encoded_buf with the payload. */
833 frame->encoded_buf = aws_byte_buf_from_empty_array(storage, encoded_frame_len);
834
835 /* cursor points to full capacity of encoded_buf.
836 * Our subclass's constructor will finish writing the payload and fill encoded_buf to capacity.
837 * When encode() is called, we'll copy cursor's contents into available output space and advance the cursor. */
838 frame->cursor = aws_byte_cursor_from_array(storage, encoded_frame_len);
839
840 /* Write frame prefix */
841 s_frame_prefix_encode(type, stream_id, payload_len, flags, &frame->encoded_buf);
842
843 return frame;
844 }
845
s_frame_prebuilt_destroy(struct aws_h2_frame * frame_base)846 static void s_frame_prebuilt_destroy(struct aws_h2_frame *frame_base) {
847 aws_mem_release(frame_base->alloc, frame_base);
848 }
849
s_frame_prebuilt_encode(struct aws_h2_frame * frame_base,struct aws_h2_frame_encoder * encoder,struct aws_byte_buf * output,bool * complete)850 static int s_frame_prebuilt_encode(
851 struct aws_h2_frame *frame_base,
852 struct aws_h2_frame_encoder *encoder,
853 struct aws_byte_buf *output,
854 bool *complete) {
855
856 (void)encoder;
857 struct aws_h2_frame_prebuilt *frame = AWS_CONTAINER_OF(frame_base, struct aws_h2_frame_prebuilt, base);
858
859 /* encoded_buf should have been filled to capacity during construction */
860 AWS_ASSERT(frame->encoded_buf.len == frame->encoded_buf.capacity);
861
862 /* After construction, cursor points to the full contents of encoded_buf.
863 * As encode() is called, we copy the contents to output and advance the cursor. */
864 if (frame->cursor.len == frame->encoded_buf.len) {
865 /* We haven't sent anything yet, announce start of frame */
866 ENCODER_LOGF(
867 TRACE,
868 encoder,
869 "Encoding frame type=%s stream_id=%" PRIu32,
870 aws_h2_frame_type_to_str(frame->base.type),
871 frame->base.stream_id);
872 } else {
873 /* We've already sent a bit, announce that we're resuming */
874 ENCODER_LOGF(
875 TRACE,
876 encoder,
877 "Resume encoding frame type=%s stream_id=%" PRIu32,
878 aws_h2_frame_type_to_str(frame->base.type),
879 frame->base.stream_id);
880 }
881
882 bool writes_ok = true;
883
884 /* Copy as much as we can from cursor (pre-encoded frame contents) to output.
885 * Advance the cursor to mark our progress. */
886 size_t chunk_len = aws_min_size(frame->cursor.len, output->capacity - output->len);
887 struct aws_byte_cursor chunk = aws_byte_cursor_advance(&frame->cursor, chunk_len);
888 writes_ok &= aws_byte_buf_write_from_whole_cursor(output, chunk);
889 AWS_ASSERT(writes_ok);
890
891 if (frame->cursor.len == 0) {
892 *complete = true;
893 } else {
894 ENCODER_LOGF(
895 TRACE,
896 encoder,
897 "Incomplete encoding of frame type=%s stream_id=%" PRIu32 ", will resume later...",
898 aws_h2_frame_type_to_str(frame->base.type),
899 frame->base.stream_id);
900
901 *complete = false;
902 }
903 return AWS_OP_SUCCESS;
904 }
905
906 /***********************************************************************************************************************
907 * PRIORITY
908 **********************************************************************************************************************/
aws_h2_frame_new_priority(struct aws_allocator * allocator,uint32_t stream_id,const struct aws_h2_frame_priority_settings * priority)909 struct aws_h2_frame *aws_h2_frame_new_priority(
910 struct aws_allocator *allocator,
911 uint32_t stream_id,
912 const struct aws_h2_frame_priority_settings *priority) {
913
914 AWS_PRECONDITION(allocator);
915 AWS_PRECONDITION(priority);
916
917 if (aws_h2_validate_stream_id(stream_id) || aws_h2_validate_stream_id(priority->stream_dependency)) {
918 return NULL;
919 }
920
921 /* PRIORITY can be pre-encoded */
922 const uint8_t flags = 0;
923 const size_t payload_len = s_frame_priority_settings_size;
924
925 struct aws_h2_frame_prebuilt *frame =
926 s_h2_frame_new_prebuilt(allocator, AWS_H2_FRAME_T_PRIORITY, stream_id, payload_len, flags);
927 if (!frame) {
928 return NULL;
929 }
930
931 /* Write the priority settings */
932 s_frame_priority_settings_encode(priority, &frame->encoded_buf);
933
934 return &frame->base;
935 }
936
937 /***********************************************************************************************************************
938 * RST_STREAM
939 **********************************************************************************************************************/
940 static const size_t s_frame_rst_stream_length = 4;
941
aws_h2_frame_new_rst_stream(struct aws_allocator * allocator,uint32_t stream_id,uint32_t error_code)942 struct aws_h2_frame *aws_h2_frame_new_rst_stream(
943 struct aws_allocator *allocator,
944 uint32_t stream_id,
945 uint32_t error_code) {
946
947 if (aws_h2_validate_stream_id(stream_id)) {
948 return NULL;
949 }
950
951 /* RST_STREAM can be pre-encoded */
952 const uint8_t flags = 0;
953 const size_t payload_len = s_frame_rst_stream_length;
954
955 struct aws_h2_frame_prebuilt *frame =
956 s_h2_frame_new_prebuilt(allocator, AWS_H2_FRAME_T_RST_STREAM, stream_id, payload_len, flags);
957 if (!frame) {
958 return NULL;
959 }
960
961 /* Write RST_STREAM payload (RFC-7540 6.4):
962 * +---------------------------------------------------------------+
963 * | Error Code (32) |
964 * +---------------------------------------------------------------+
965 */
966 bool writes_ok = true;
967 writes_ok &= aws_byte_buf_write_be32(&frame->encoded_buf, error_code);
968 AWS_ASSERT(writes_ok);
969
970 return &frame->base;
971 }
972
973 /***********************************************************************************************************************
974 * SETTINGS
975 **********************************************************************************************************************/
976 static const size_t s_frame_setting_length = 6;
977
aws_h2_frame_new_settings(struct aws_allocator * allocator,const struct aws_http2_setting * settings_array,size_t num_settings,bool ack)978 struct aws_h2_frame *aws_h2_frame_new_settings(
979 struct aws_allocator *allocator,
980 const struct aws_http2_setting *settings_array,
981 size_t num_settings,
982 bool ack) {
983
984 AWS_PRECONDITION(settings_array || num_settings == 0);
985
986 /* Cannot send settings in an ACK frame */
987 if (ack && num_settings > 0) {
988 aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
989 return NULL;
990 }
991
992 /* Check against insane edge case of too many settings to fit in a frame. */
993 const size_t max_settings = s_prebuilt_payload_max() / s_frame_setting_length;
994 if (num_settings > max_settings) {
995 AWS_LOGF_ERROR(
996 AWS_LS_HTTP_ENCODER,
997 "Cannot create SETTINGS frame with %zu settings, the limit is %zu.",
998 num_settings,
999 max_settings);
1000
1001 aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1002 return NULL;
1003 }
1004
1005 /* SETTINGS can be pre-encoded */
1006 const uint8_t flags = ack ? AWS_H2_FRAME_F_ACK : 0;
1007 const size_t payload_len = num_settings * s_frame_setting_length;
1008 const uint32_t stream_id = 0;
1009
1010 struct aws_h2_frame_prebuilt *frame =
1011 s_h2_frame_new_prebuilt(allocator, AWS_H2_FRAME_T_SETTINGS, stream_id, payload_len, flags);
1012 if (!frame) {
1013 return NULL;
1014 }
1015
1016 /* Write the settings, each one is encoded like (RFC-7540 6.5.1):
1017 * +-------------------------------+
1018 * | Identifier (16) |
1019 * +-------------------------------+-------------------------------+
1020 * | Value (32) |
1021 * +---------------------------------------------------------------+
1022 */
1023 bool writes_ok = true;
1024 for (size_t i = 0; i < num_settings; ++i) {
1025 writes_ok &= aws_byte_buf_write_be16(&frame->encoded_buf, settings_array[i].id);
1026 writes_ok &= aws_byte_buf_write_be32(&frame->encoded_buf, settings_array[i].value);
1027 }
1028 AWS_ASSERT(writes_ok);
1029
1030 return &frame->base;
1031 }
1032
1033 /***********************************************************************************************************************
1034 * PING
1035 **********************************************************************************************************************/
aws_h2_frame_new_ping(struct aws_allocator * allocator,bool ack,const uint8_t opaque_data[AWS_HTTP2_PING_DATA_SIZE])1036 struct aws_h2_frame *aws_h2_frame_new_ping(
1037 struct aws_allocator *allocator,
1038 bool ack,
1039 const uint8_t opaque_data[AWS_HTTP2_PING_DATA_SIZE]) {
1040
1041 /* PING can be pre-encoded */
1042 const uint8_t flags = ack ? AWS_H2_FRAME_F_ACK : 0;
1043 const size_t payload_len = AWS_HTTP2_PING_DATA_SIZE;
1044 const uint32_t stream_id = 0;
1045
1046 struct aws_h2_frame_prebuilt *frame =
1047 s_h2_frame_new_prebuilt(allocator, AWS_H2_FRAME_T_PING, stream_id, payload_len, flags);
1048 if (!frame) {
1049 return NULL;
1050 }
1051
1052 /* Write the PING payload (RFC-7540 6.7):
1053 * +---------------------------------------------------------------+
1054 * | |
1055 * | Opaque Data (64) |
1056 * | |
1057 * +---------------------------------------------------------------+
1058 */
1059 bool writes_ok = true;
1060 writes_ok &= aws_byte_buf_write(&frame->encoded_buf, opaque_data, AWS_HTTP2_PING_DATA_SIZE);
1061 AWS_ASSERT(writes_ok);
1062
1063 /* PING responses SHOULD be given higher priority than any other frame */
1064 frame->base.high_priority = ack;
1065 return &frame->base;
1066 }
1067
1068 /***********************************************************************************************************************
1069 * GOAWAY
1070 **********************************************************************************************************************/
1071 static const size_t s_frame_goaway_length_min = 8;
1072
aws_h2_frame_new_goaway(struct aws_allocator * allocator,uint32_t last_stream_id,uint32_t error_code,struct aws_byte_cursor debug_data)1073 struct aws_h2_frame *aws_h2_frame_new_goaway(
1074 struct aws_allocator *allocator,
1075 uint32_t last_stream_id,
1076 uint32_t error_code,
1077 struct aws_byte_cursor debug_data) {
1078
1079 /* If debug_data is too long, don't sent it.
1080 * It's more important that the GOAWAY frame gets sent. */
1081 const size_t debug_data_max = s_prebuilt_payload_max() - s_frame_goaway_length_min;
1082 if (debug_data.len > debug_data_max) {
1083 AWS_LOGF_WARN(
1084 AWS_LS_HTTP_ENCODER,
1085 "Sending GOAWAY without debug-data. Debug-data size %zu exceeds internal limit of %zu",
1086 debug_data.len,
1087 debug_data_max);
1088
1089 debug_data.len = 0;
1090 }
1091
1092 /* It would be illegal to send a lower value, this is unrecoverable */
1093 AWS_FATAL_ASSERT(last_stream_id <= AWS_H2_STREAM_ID_MAX);
1094
1095 /* GOAWAY can be pre-encoded */
1096 const uint8_t flags = 0;
1097 const size_t payload_len = debug_data.len + s_frame_goaway_length_min;
1098 const uint32_t stream_id = 0;
1099
1100 struct aws_h2_frame_prebuilt *frame =
1101 s_h2_frame_new_prebuilt(allocator, AWS_H2_FRAME_T_GOAWAY, stream_id, payload_len, flags);
1102 if (!frame) {
1103 return NULL;
1104 }
1105
1106 /* Write the GOAWAY payload (RFC-7540 6.8):
1107 * +-+-------------------------------------------------------------+
1108 * |R| Last-Stream-ID (31) |
1109 * +-+-------------------------------------------------------------+
1110 * | Error Code (32) |
1111 * +---------------------------------------------------------------+
1112 * | Additional Debug Data (*) |
1113 * +---------------------------------------------------------------+
1114 */
1115 bool writes_ok = true;
1116 writes_ok &= aws_byte_buf_write_be32(&frame->encoded_buf, last_stream_id);
1117 writes_ok &= aws_byte_buf_write_be32(&frame->encoded_buf, error_code);
1118 writes_ok &= aws_byte_buf_write_from_whole_cursor(&frame->encoded_buf, debug_data);
1119 AWS_ASSERT(writes_ok);
1120
1121 return &frame->base;
1122 }
1123
1124 /***********************************************************************************************************************
1125 * WINDOW_UPDATE
1126 **********************************************************************************************************************/
1127 static const size_t s_frame_window_update_length = 4;
1128
aws_h2_frame_new_window_update(struct aws_allocator * allocator,uint32_t stream_id,uint32_t window_size_increment)1129 struct aws_h2_frame *aws_h2_frame_new_window_update(
1130 struct aws_allocator *allocator,
1131 uint32_t stream_id,
1132 uint32_t window_size_increment) {
1133
1134 /* Note: stream_id may be zero or non-zero */
1135 if (stream_id > AWS_H2_STREAM_ID_MAX) {
1136 aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1137 return NULL;
1138 }
1139
1140 if (window_size_increment > AWS_H2_WINDOW_UPDATE_MAX) {
1141 AWS_LOGF_ERROR(
1142 AWS_LS_HTTP_ENCODER,
1143 "Window increment size %" PRIu32 " exceeds HTTP/2 max %" PRIu32,
1144 window_size_increment,
1145 AWS_H2_WINDOW_UPDATE_MAX);
1146 aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1147 return NULL;
1148 }
1149
1150 /* WINDOW_UPDATE can be pre-encoded */
1151 const uint8_t flags = 0;
1152 const size_t payload_len = s_frame_window_update_length;
1153
1154 struct aws_h2_frame_prebuilt *frame =
1155 s_h2_frame_new_prebuilt(allocator, AWS_H2_FRAME_T_WINDOW_UPDATE, stream_id, payload_len, flags);
1156 if (!frame) {
1157 return NULL;
1158 }
1159
1160 /* Write the WINDOW_UPDATE payload (RFC-7540 6.9):
1161 * +-+-------------------------------------------------------------+
1162 * |R| Window Size Increment (31) |
1163 * +-+-------------------------------------------------------------+
1164 */
1165 bool writes_ok = true;
1166 writes_ok &= aws_byte_buf_write_be32(&frame->encoded_buf, window_size_increment);
1167 AWS_ASSERT(writes_ok);
1168
1169 return &frame->base;
1170 }
1171
aws_h2_frame_destroy(struct aws_h2_frame * frame)1172 void aws_h2_frame_destroy(struct aws_h2_frame *frame) {
1173 if (frame) {
1174 frame->vtable->destroy(frame);
1175 }
1176 }
1177
aws_h2_encode_frame(struct aws_h2_frame_encoder * encoder,struct aws_h2_frame * frame,struct aws_byte_buf * output,bool * frame_complete)1178 int aws_h2_encode_frame(
1179 struct aws_h2_frame_encoder *encoder,
1180 struct aws_h2_frame *frame,
1181 struct aws_byte_buf *output,
1182 bool *frame_complete) {
1183
1184 AWS_PRECONDITION(encoder);
1185 AWS_PRECONDITION(frame);
1186 AWS_PRECONDITION(output);
1187 AWS_PRECONDITION(frame_complete);
1188
1189 if (encoder->has_errored) {
1190 ENCODER_LOG(ERROR, encoder, "Encoder cannot be used again after an error");
1191 return aws_raise_error(AWS_ERROR_INVALID_STATE);
1192 }
1193
1194 if (encoder->current_frame && (encoder->current_frame != frame)) {
1195 ENCODER_LOG(ERROR, encoder, "Cannot encode new frame until previous frame completes");
1196 return aws_raise_error(AWS_ERROR_INVALID_STATE);
1197 }
1198
1199 *frame_complete = false;
1200
1201 if (frame->vtable->encode(frame, encoder, output, frame_complete)) {
1202 ENCODER_LOGF(
1203 ERROR,
1204 encoder,
1205 "Failed to encode frame type=%s stream_id=%" PRIu32 ", %s",
1206 aws_h2_frame_type_to_str(frame->type),
1207 frame->stream_id,
1208 aws_error_name(aws_last_error()));
1209 encoder->has_errored = true;
1210 return AWS_OP_ERR;
1211 }
1212
1213 encoder->current_frame = *frame_complete ? NULL : frame;
1214 return AWS_OP_SUCCESS;
1215 }
1216
aws_h2_frame_encoder_set_setting_header_table_size(struct aws_h2_frame_encoder * encoder,uint32_t data)1217 void aws_h2_frame_encoder_set_setting_header_table_size(struct aws_h2_frame_encoder *encoder, uint32_t data) {
1218 /* Setting for dynamic table size changed from peer, we will update the dynamic table size when we encoder the next
1219 * header block */
1220 aws_hpack_set_max_table_size(encoder->hpack, data);
1221 aws_hpack_set_protocol_max_size_setting(encoder->hpack, data);
1222 }
1223
aws_h2_frame_encoder_set_setting_max_frame_size(struct aws_h2_frame_encoder * encoder,uint32_t data)1224 void aws_h2_frame_encoder_set_setting_max_frame_size(struct aws_h2_frame_encoder *encoder, uint32_t data) {
1225 encoder->settings.max_frame_size = data;
1226 }
1227