static void s_encode_single_header_block_frame()

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