|  | @@ -267,6 +267,11 @@ struct inproc_stream {
 | 
	
		
			
				|  |  |    bool trailing_md_sent = false;
 | 
	
		
			
				|  |  |    bool initial_md_recvd = false;
 | 
	
		
			
				|  |  |    bool trailing_md_recvd = false;
 | 
	
		
			
				|  |  | +  // The following tracks if the server-side only pretends to have received
 | 
	
		
			
				|  |  | +  // trailing metadata since it no longer cares about the RPC. If that is the
 | 
	
		
			
				|  |  | +  // case, it is still ok for the client to send trailing metadata (in which
 | 
	
		
			
				|  |  | +  // case it will be ignored).
 | 
	
		
			
				|  |  | +  bool trailing_md_recvd_implicit_only = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    bool closed = false;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -617,7 +622,7 @@ void op_state_machine_locked(inproc_stream* s, grpc_error* error) {
 | 
	
		
			
				|  |  |        s->send_message_op->payload->send_message.send_message.reset();
 | 
	
		
			
				|  |  |        complete_if_batch_end_locked(
 | 
	
		
			
				|  |  |            s, GRPC_ERROR_NONE, s->send_message_op,
 | 
	
		
			
				|  |  | -          "op_state_machine scheduling send-message-on-complete");
 | 
	
		
			
				|  |  | +          "op_state_machine scheduling send-message-on-complete case 1");
 | 
	
		
			
				|  |  |        s->send_message_op = nullptr;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -739,15 +744,25 @@ void op_state_machine_locked(inproc_stream* s, grpc_error* error) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (s->to_read_trailing_md_filled) {
 | 
	
		
			
				|  |  |      if (s->trailing_md_recvd) {
 | 
	
		
			
				|  |  | -      new_err =
 | 
	
		
			
				|  |  | -          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd trailing md");
 | 
	
		
			
				|  |  | -      INPROC_LOG(
 | 
	
		
			
				|  |  | -          GPR_INFO,
 | 
	
		
			
				|  |  | -          "op_state_machine %p scheduling on_complete errors for already "
 | 
	
		
			
				|  |  | -          "recvd trailing md %p",
 | 
	
		
			
				|  |  | -          s, new_err);
 | 
	
		
			
				|  |  | -      fail_helper_locked(s, GRPC_ERROR_REF(new_err));
 | 
	
		
			
				|  |  | -      goto done;
 | 
	
		
			
				|  |  | +      if (s->trailing_md_recvd_implicit_only) {
 | 
	
		
			
				|  |  | +        INPROC_LOG(GPR_INFO,
 | 
	
		
			
				|  |  | +                   "op_state_machine %p already implicitly received trailing "
 | 
	
		
			
				|  |  | +                   "metadata, so ignoring new trailing metadata from client",
 | 
	
		
			
				|  |  | +                   s);
 | 
	
		
			
				|  |  | +        grpc_metadata_batch_clear(&s->to_read_trailing_md);
 | 
	
		
			
				|  |  | +        s->to_read_trailing_md_filled = false;
 | 
	
		
			
				|  |  | +        s->trailing_md_recvd_implicit_only = false;
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        new_err =
 | 
	
		
			
				|  |  | +            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd trailing md");
 | 
	
		
			
				|  |  | +        INPROC_LOG(
 | 
	
		
			
				|  |  | +            GPR_INFO,
 | 
	
		
			
				|  |  | +            "op_state_machine %p scheduling on_complete errors for already "
 | 
	
		
			
				|  |  | +            "recvd trailing md %p",
 | 
	
		
			
				|  |  | +            s, new_err);
 | 
	
		
			
				|  |  | +        fail_helper_locked(s, GRPC_ERROR_REF(new_err));
 | 
	
		
			
				|  |  | +        goto done;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (s->recv_message_op != nullptr) {
 | 
	
		
			
				|  |  |        // This message needs to be wrapped up because it will never be
 | 
	
	
		
			
				|  | @@ -770,7 +785,7 @@ void op_state_machine_locked(inproc_stream* s, grpc_error* error) {
 | 
	
		
			
				|  |  |        s->send_message_op->payload->send_message.stream_write_closed = true;
 | 
	
		
			
				|  |  |        complete_if_batch_end_locked(
 | 
	
		
			
				|  |  |            s, new_err, s->send_message_op,
 | 
	
		
			
				|  |  | -          "op_state_machine scheduling send-message-on-complete");
 | 
	
		
			
				|  |  | +          "op_state_machine scheduling send-message-on-complete case 2");
 | 
	
		
			
				|  |  |        s->send_message_op = nullptr;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (s->recv_trailing_md_op != nullptr) {
 | 
	
	
		
			
				|  | @@ -809,7 +824,7 @@ void op_state_machine_locked(inproc_stream* s, grpc_error* error) {
 | 
	
		
			
				|  |  |                     "trailing-md-on-complete %p",
 | 
	
		
			
				|  |  |                     s, new_err);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | +    } else if (!s->trailing_md_recvd) {
 | 
	
		
			
				|  |  |        INPROC_LOG(
 | 
	
		
			
				|  |  |            GPR_INFO,
 | 
	
		
			
				|  |  |            "op_state_machine %p has trailing md but not yet waiting for it", s);
 | 
	
	
		
			
				|  | @@ -832,6 +847,9 @@ void op_state_machine_locked(inproc_stream* s, grpc_error* error) {
 | 
	
		
			
				|  |  |          "op_state_machine scheduling recv-trailing-md-on-complete");
 | 
	
		
			
				|  |  |      s->trailing_md_recvd = true;
 | 
	
		
			
				|  |  |      s->recv_trailing_md_op = nullptr;
 | 
	
		
			
				|  |  | +    // Since we are only pretending to have received the trailing MD, it would
 | 
	
		
			
				|  |  | +    // be ok (not an error) if the client actually sends it later.
 | 
	
		
			
				|  |  | +    s->trailing_md_recvd_implicit_only = true;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (s->trailing_md_recvd && s->recv_message_op) {
 | 
	
		
			
				|  |  |      // No further message will come on this stream, so finish off the
 | 
	
	
		
			
				|  | @@ -847,14 +865,13 @@ void op_state_machine_locked(inproc_stream* s, grpc_error* error) {
 | 
	
		
			
				|  |  |          "op_state_machine scheduling recv-message-on-complete");
 | 
	
		
			
				|  |  |      s->recv_message_op = nullptr;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  if (s->trailing_md_recvd && (s->trailing_md_sent || s->t->is_client) &&
 | 
	
		
			
				|  |  | -      s->send_message_op) {
 | 
	
		
			
				|  |  | +  if (s->trailing_md_recvd && s->send_message_op && s->t->is_client) {
 | 
	
		
			
				|  |  |      // Nothing further will try to receive from this stream, so finish off
 | 
	
		
			
				|  |  |      // any outstanding send_message op
 | 
	
		
			
				|  |  |      s->send_message_op->payload->send_message.send_message.reset();
 | 
	
		
			
				|  |  |      complete_if_batch_end_locked(
 | 
	
		
			
				|  |  |          s, new_err, s->send_message_op,
 | 
	
		
			
				|  |  | -        "op_state_machine scheduling send-message-on-complete");
 | 
	
		
			
				|  |  | +        "op_state_machine scheduling send-message-on-complete case 3");
 | 
	
		
			
				|  |  |      s->send_message_op = nullptr;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (s->send_message_op || s->send_trailing_md_op || s->recv_initial_md_op ||
 |