in source/h2_frames.c [603:739]
static void 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) {
/*
* Figure out the details of the next frame to encode.
* The first frame will be either HEADERS or PUSH_PROMISE.
* All subsequent frames will be CONTINUATION
*/
enum aws_h2_frame_type frame_type;
uint8_t flags = 0;
uint8_t pad_length = 0;
const struct aws_h2_frame_priority_settings *priority_settings = NULL;
const uint32_t *promised_stream_id = NULL;
size_t payload_overhead = 0; /* Amount of payload holding things other than header-block (padding, etc) */
if (frame->state == AWS_H2_HEADERS_STATE_FIRST_FRAME) {
frame_type = frame->base.type;
if (frame->pad_length > 0) {
flags |= AWS_H2_FRAME_F_PADDED;
pad_length = frame->pad_length;
payload_overhead += 1 + pad_length;
}
if (frame->has_priority) {
priority_settings = &frame->priority;
flags |= AWS_H2_FRAME_F_PRIORITY;
payload_overhead += s_frame_priority_settings_size;
}
if (frame->end_stream) {
flags |= AWS_H2_FRAME_F_END_STREAM;
}
if (frame_type == AWS_H2_FRAME_T_PUSH_PROMISE) {
promised_stream_id = &frame->promised_stream_id;
payload_overhead += 4;
}
} else /* CONTINUATION */ {
frame_type = AWS_H2_FRAME_T_CONTINUATION;
}
/*
* Figure out what size header-block fragment should go in this frame.
*/
size_t max_payload;
if (s_get_max_contiguous_payload_length(encoder, output, &max_payload)) {
goto handle_waiting_for_more_space;
}
size_t max_fragment;
if (aws_sub_size_checked(max_payload, payload_overhead, &max_fragment)) {
goto handle_waiting_for_more_space;
}
const size_t fragment_len = aws_min_size(max_fragment, frame->header_block_cursor.len);
if (fragment_len == frame->header_block_cursor.len) {
/* This will finish the header-block */
flags |= AWS_H2_FRAME_F_END_HEADERS;
} else {
/* If we're not finishing the header-block, is it even worth trying to send this frame now? */
const size_t even_worth_sending_threshold = AWS_H2_FRAME_PREFIX_SIZE + payload_overhead;
if (fragment_len < even_worth_sending_threshold) {
goto handle_waiting_for_more_space;
}
}
/*
* Ok, it fits! Write the frame
*/
ENCODER_LOGF(
TRACE,
encoder,
"Encoding frame type=%s stream_id=%" PRIu32 "%s%s",
aws_h2_frame_type_to_str(frame_type),
frame->base.stream_id,
(flags & AWS_H2_FRAME_F_END_HEADERS) ? " END_HEADERS" : "",
(flags & AWS_H2_FRAME_F_END_STREAM) ? " END_STREAM" : "");
bool writes_ok = true;
/* Write the frame prefix */
const size_t payload_len = fragment_len + payload_overhead;
s_frame_prefix_encode(frame_type, frame->base.stream_id, payload_len, flags, output);
/* Write pad length */
if (flags & AWS_H2_FRAME_F_PADDED) {
AWS_ASSERT(frame_type != AWS_H2_FRAME_T_CONTINUATION);
writes_ok &= aws_byte_buf_write_u8(output, pad_length);
}
/* Write priority */
if (flags & AWS_H2_FRAME_F_PRIORITY) {
AWS_ASSERT(frame_type == AWS_H2_FRAME_T_HEADERS);
s_frame_priority_settings_encode(priority_settings, output);
}
/* Write promised stream ID */
if (promised_stream_id) {
AWS_ASSERT(frame_type == AWS_H2_FRAME_T_PUSH_PROMISE);
writes_ok &= aws_byte_buf_write_be32(output, *promised_stream_id);
}
/* Write header-block fragment */
if (fragment_len > 0) {
struct aws_byte_cursor fragment = aws_byte_cursor_advance(&frame->header_block_cursor, fragment_len);
writes_ok &= aws_byte_buf_write_from_whole_cursor(output, fragment);
}
/* Write padding */
if (flags & AWS_H2_FRAME_F_PADDED) {
writes_ok &= aws_byte_buf_write_u8_n(output, 0, pad_length);
}
AWS_ASSERT(writes_ok);
/* Success! Wrote entire frame. It's safe to change state now */
frame->state =
flags & AWS_H2_FRAME_F_END_HEADERS ? AWS_H2_HEADERS_STATE_COMPLETE : AWS_H2_HEADERS_STATE_CONTINUATION;
*waiting_for_more_space = false;
return;
handle_waiting_for_more_space:
ENCODER_LOGF(
TRACE,
encoder,
"Insufficient space to encode %s for stream %" PRIu32 " right now",
aws_h2_frame_type_to_str(frame->base.type),
frame->base.stream_id);
*waiting_for_more_space = true;
}