|
|
|
@ -162,6 +162,20 @@ static uint32_t target_write_size(grpc_chttp2_transport *t) { |
|
|
|
|
return 1024 * 1024; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns true if initial_metadata contains only default headers.
|
|
|
|
|
//
|
|
|
|
|
// TODO(roth): The fact that we hard-code these particular headers here
|
|
|
|
|
// is fairly ugly. Need some better way to know which headers are
|
|
|
|
|
// default, maybe via a bit in the static metadata table?
|
|
|
|
|
static bool is_default_initial_metadata(grpc_metadata_batch *initial_metadata) { |
|
|
|
|
int num_default_fields = |
|
|
|
|
(initial_metadata->idx.named.status != NULL) + |
|
|
|
|
(initial_metadata->idx.named.content_type != NULL) + |
|
|
|
|
(initial_metadata->idx.named.grpc_encoding != NULL) + |
|
|
|
|
(initial_metadata->idx.named.grpc_accept_encoding != NULL); |
|
|
|
|
return (size_t)num_default_fields == initial_metadata->list.count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_chttp2_begin_write_result grpc_chttp2_begin_write( |
|
|
|
|
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { |
|
|
|
|
grpc_chttp2_stream *s; |
|
|
|
@ -218,31 +232,59 @@ grpc_chttp2_begin_write_result grpc_chttp2_begin_write( |
|
|
|
|
t->is_client ? "CLIENT" : "SERVER", s->id, sent_initial_metadata, |
|
|
|
|
s->send_initial_metadata != NULL, s->announce_window)); |
|
|
|
|
|
|
|
|
|
grpc_mdelem *extra_headers_for_trailing_metadata[2]; |
|
|
|
|
size_t num_extra_headers_for_trailing_metadata = 0; |
|
|
|
|
|
|
|
|
|
/* send initial metadata if it's available */ |
|
|
|
|
if (!sent_initial_metadata && s->send_initial_metadata) { |
|
|
|
|
grpc_encode_header_options hopt = { |
|
|
|
|
.stream_id = s->id, |
|
|
|
|
.is_eof = false, |
|
|
|
|
.use_true_binary_metadata = |
|
|
|
|
t->settings |
|
|
|
|
[GRPC_PEER_SETTINGS] |
|
|
|
|
[GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != 0, |
|
|
|
|
.max_frame_size = t->settings[GRPC_PEER_SETTINGS] |
|
|
|
|
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], |
|
|
|
|
.stats = &s->stats.outgoing}; |
|
|
|
|
grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, |
|
|
|
|
s->send_initial_metadata, &hopt, &t->outbuf); |
|
|
|
|
if (!sent_initial_metadata && s->send_initial_metadata != NULL) { |
|
|
|
|
// We skip this on the server side if there is no custom initial
|
|
|
|
|
// metadata, there are no messages to send, and we are also sending
|
|
|
|
|
// trailing metadata. This results in a Trailers-Only response,
|
|
|
|
|
// which is required for retries, as per:
|
|
|
|
|
// https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid
|
|
|
|
|
if (t->is_client || s->fetching_send_message != NULL || |
|
|
|
|
s->flow_controlled_buffer.length != 0 || |
|
|
|
|
s->send_trailing_metadata == NULL || |
|
|
|
|
!is_default_initial_metadata(s->send_initial_metadata)) { |
|
|
|
|
grpc_encode_header_options hopt = { |
|
|
|
|
.stream_id = s->id, |
|
|
|
|
.is_eof = false, |
|
|
|
|
.use_true_binary_metadata = |
|
|
|
|
t->settings |
|
|
|
|
[GRPC_PEER_SETTINGS] |
|
|
|
|
[GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != 0, |
|
|
|
|
.max_frame_size = t->settings[GRPC_PEER_SETTINGS] |
|
|
|
|
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], |
|
|
|
|
.stats = &s->stats.outgoing}; |
|
|
|
|
grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, NULL, 0, |
|
|
|
|
s->send_initial_metadata, &hopt, &t->outbuf); |
|
|
|
|
now_writing = true; |
|
|
|
|
t->ping_state.pings_before_data_required = |
|
|
|
|
t->ping_policy.max_pings_without_data; |
|
|
|
|
if (!t->is_client) { |
|
|
|
|
t->ping_recv_state.last_ping_recv_time = |
|
|
|
|
gpr_inf_past(GPR_CLOCK_MONOTONIC); |
|
|
|
|
t->ping_recv_state.ping_strikes = 0; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
GRPC_CHTTP2_IF_TRACING( |
|
|
|
|
gpr_log(GPR_INFO, "not sending initial_metadata (Trailers-Only)")); |
|
|
|
|
// When sending Trailers-Only, we need to move the :status and
|
|
|
|
|
// content-type headers to the trailers.
|
|
|
|
|
if (s->send_initial_metadata->idx.named.status != NULL) { |
|
|
|
|
extra_headers_for_trailing_metadata |
|
|
|
|
[num_extra_headers_for_trailing_metadata++] = |
|
|
|
|
&s->send_initial_metadata->idx.named.status->md; |
|
|
|
|
} |
|
|
|
|
if (s->send_initial_metadata->idx.named.content_type != NULL) { |
|
|
|
|
extra_headers_for_trailing_metadata |
|
|
|
|
[num_extra_headers_for_trailing_metadata++] = |
|
|
|
|
&s->send_initial_metadata->idx.named.content_type->md; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
s->send_initial_metadata = NULL; |
|
|
|
|
s->sent_initial_metadata = true; |
|
|
|
|
sent_initial_metadata = true; |
|
|
|
|
now_writing = true; |
|
|
|
|
t->ping_state.pings_before_data_required = |
|
|
|
|
t->ping_policy.max_pings_without_data; |
|
|
|
|
if (!t->is_client) { |
|
|
|
|
t->ping_recv_state.last_ping_recv_time = |
|
|
|
|
gpr_inf_past(GPR_CLOCK_MONOTONIC); |
|
|
|
|
t->ping_recv_state.ping_strikes = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* send any window updates */ |
|
|
|
|
if (s->announce_window > 0) { |
|
|
|
@ -320,6 +362,7 @@ grpc_chttp2_begin_write_result grpc_chttp2_begin_write( |
|
|
|
|
if (s->send_trailing_metadata != NULL && |
|
|
|
|
s->fetching_send_message == NULL && |
|
|
|
|
s->flow_controlled_buffer.length == 0) { |
|
|
|
|
GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata")); |
|
|
|
|
if (grpc_metadata_batch_is_empty(s->send_trailing_metadata)) { |
|
|
|
|
grpc_chttp2_encode_data(s->id, &s->flow_controlled_buffer, 0, true, |
|
|
|
|
&s->stats.outgoing, &t->outbuf); |
|
|
|
@ -337,6 +380,8 @@ grpc_chttp2_begin_write_result grpc_chttp2_begin_write( |
|
|
|
|
[GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], |
|
|
|
|
.stats = &s->stats.outgoing}; |
|
|
|
|
grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, |
|
|
|
|
extra_headers_for_trailing_metadata, |
|
|
|
|
num_extra_headers_for_trailing_metadata, |
|
|
|
|
s->send_trailing_metadata, &hopt, |
|
|
|
|
&t->outbuf); |
|
|
|
|
} |
|
|
|
|