int aws_h2_encode_data_frame()

in source/h2_frames.c [316:459]


int 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) {

    AWS_PRECONDITION(encoder);
    AWS_PRECONDITION(body_stream);
    AWS_PRECONDITION(output);
    AWS_PRECONDITION(body_complete);
    AWS_PRECONDITION(body_stalled);
    AWS_PRECONDITION(*stream_window_size_peer > 0);

    if (aws_h2_validate_stream_id(stream_id)) {
        return AWS_OP_ERR;
    }

    *body_complete = false;
    *body_stalled = false;
    uint8_t flags = 0;

    /*
     * Payload-length is the first thing encoded in a frame, but we don't know how
     * much data we'll get from the body-stream until we actually read it.
     * Therefore, we determine the exact location that the body data should go,
     * then stream the body directly into that part of the output buffer.
     * Then we will go and write the other parts of the frame in around it.
     */

    size_t bytes_preceding_body = AWS_H2_FRAME_PREFIX_SIZE;
    size_t payload_overhead = 0; /* Amount of "payload" that will not contain body (padding) */
    if (pad_length > 0) {
        flags |= AWS_H2_FRAME_F_PADDED;

        /* Padding len is 1st byte of payload (padding itself goes at end of payload) */
        bytes_preceding_body += 1;
        payload_overhead = 1 + pad_length;
    }

    /* Max amount allowed by stream and connection flow-control window */
    size_t min_window_size = aws_min_size(*stream_window_size_peer, *connection_window_size_peer);

    /* Max amount of payload we can do right now */
    size_t max_payload;
    if (s_get_max_contiguous_payload_length(encoder, output, &max_payload)) {
        goto handle_waiting_for_more_space;
    }
    /* The flow-control window will limit the size for max_payload of a flow-controlled frame */
    max_payload = aws_min_size(max_payload, min_window_size);
    /* Max amount of body we can fit in the payload*/
    size_t max_body;
    if (aws_sub_size_checked(max_payload, payload_overhead, &max_body) || max_body == 0) {
        goto handle_waiting_for_more_space;
    }

    /* Use a sub-buffer to limit where body can go */
    struct aws_byte_buf body_sub_buf =
        aws_byte_buf_from_empty_array(output->buffer + output->len + bytes_preceding_body, max_body);

    /* Read body into sub-buffer */
    if (aws_input_stream_read(body_stream, &body_sub_buf)) {
        goto error;
    }

    /* Check if we've reached the end of the body */
    struct aws_stream_status body_status;
    if (aws_input_stream_get_status(body_stream, &body_status)) {
        goto error;
    }

    if (body_status.is_end_of_stream) {
        *body_complete = true;
        if (body_ends_stream) {
            flags |= AWS_H2_FRAME_F_END_STREAM;
        }
    } else {
        if (body_sub_buf.len < body_sub_buf.capacity) {
            /* Body stream was unable to provide as much data as it could have */
            *body_stalled = true;

            if (body_sub_buf.len == 0) {
                /* This frame would have no useful information, don't even bother sending it */
                goto handle_nothing_to_send_right_now;
            }
        }
    }

    ENCODER_LOGF(
        TRACE,
        encoder,
        "Encoding frame type=DATA stream_id=%" PRIu32 " data_len=%zu stalled=%d%s",
        stream_id,
        body_sub_buf.len,
        *body_stalled,
        (flags & AWS_H2_FRAME_F_END_STREAM) ? " END_STREAM" : "");

    /*
     * Write in the other parts of the frame.
     */
    bool writes_ok = true;

    /* Write the frame prefix */
    const size_t payload_len = body_sub_buf.len + payload_overhead;
    s_frame_prefix_encode(AWS_H2_FRAME_T_DATA, stream_id, payload_len, flags, output);

    /* Write pad length */
    if (flags & AWS_H2_FRAME_F_PADDED) {
        writes_ok &= aws_byte_buf_write_u8(output, pad_length);
    }

    /* Increment output->len to jump over the body that we already wrote in */
    AWS_ASSERT(output->buffer + output->len == body_sub_buf.buffer && "Streamed DATA to wrong position");
    output->len += body_sub_buf.len;

    /* Write padding */
    if (flags & AWS_H2_FRAME_F_PADDED) {
        writes_ok &= aws_byte_buf_write_u8_n(output, 0, pad_length);
    }

    /* update the connection window size now, we will update stream window size when this function returns */
    AWS_ASSERT(payload_len <= min_window_size);
    *connection_window_size_peer -= payload_len;
    *stream_window_size_peer -= (int32_t)payload_len;

    AWS_ASSERT(writes_ok);
    return AWS_OP_SUCCESS;

handle_waiting_for_more_space:
    ENCODER_LOGF(TRACE, encoder, "Insufficient space to encode DATA for stream %" PRIu32 " right now", stream_id);
    return AWS_OP_SUCCESS;

handle_nothing_to_send_right_now:
    ENCODER_LOGF(INFO, encoder, "Stream %" PRIu32 " produced 0 bytes of body data", stream_id);
    return AWS_OP_SUCCESS;

error:
    return AWS_OP_ERR;
}