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