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