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