|  | @@ -42,6 +42,7 @@
 | 
	
		
			
				|  |  |  #include "src/core/surface/completion_queue.h"
 | 
	
		
			
				|  |  |  #include <grpc/support/alloc.h>
 | 
	
		
			
				|  |  |  #include <grpc/support/log.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/string_util.h>
 | 
	
		
			
				|  |  |  #include <assert.h>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include <stdio.h>
 | 
	
	
		
			
				|  | @@ -98,6 +99,8 @@ typedef enum {
 | 
	
		
			
				|  |  |    /* Status came from 'the wire' - or somewhere below the surface
 | 
	
		
			
				|  |  |       layer */
 | 
	
		
			
				|  |  |    STATUS_FROM_WIRE,
 | 
	
		
			
				|  |  | +  /* Status came from the server sending status */
 | 
	
		
			
				|  |  | +  STATUS_FROM_SERVER_STATUS,
 | 
	
		
			
				|  |  |    STATUS_SOURCE_COUNT
 | 
	
		
			
				|  |  |  } status_source;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -188,6 +191,7 @@ struct grpc_call {
 | 
	
		
			
				|  |  |       and a strong upper bound of a count of masters to be calculated. */
 | 
	
		
			
				|  |  |    gpr_uint8 request_set[GRPC_IOREQ_OP_COUNT];
 | 
	
		
			
				|  |  |    grpc_ioreq_data request_data[GRPC_IOREQ_OP_COUNT];
 | 
	
		
			
				|  |  | +  gpr_uint32 request_flags[GRPC_IOREQ_OP_COUNT];
 | 
	
		
			
				|  |  |    reqinfo_master masters[GRPC_IOREQ_OP_COUNT];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* Dynamic array of ioreq's that have completed: the count of
 | 
	
	
		
			
				|  | @@ -231,6 +235,7 @@ struct grpc_call {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    gpr_slice_buffer incoming_message;
 | 
	
		
			
				|  |  |    gpr_uint32 incoming_message_length;
 | 
	
		
			
				|  |  | +  gpr_uint32 incoming_message_flags;
 | 
	
		
			
				|  |  |    grpc_iomgr_closure destroy_closure;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -590,10 +595,18 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
 | 
	
		
			
				|  |  |              call->write_state = WRITE_STATE_WRITE_CLOSED;
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |            break;
 | 
	
		
			
				|  |  | +        case GRPC_IOREQ_SEND_STATUS:
 | 
	
		
			
				|  |  | +          if (call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details !=
 | 
	
		
			
				|  |  | +              NULL) {
 | 
	
		
			
				|  |  | +            grpc_mdstr_unref(
 | 
	
		
			
				|  |  | +                call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details);
 | 
	
		
			
				|  |  | +            call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
 | 
	
		
			
				|  |  | +                NULL;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  |          case GRPC_IOREQ_RECV_CLOSE:
 | 
	
		
			
				|  |  |          case GRPC_IOREQ_SEND_INITIAL_METADATA:
 | 
	
		
			
				|  |  |          case GRPC_IOREQ_SEND_TRAILING_METADATA:
 | 
	
		
			
				|  |  | -        case GRPC_IOREQ_SEND_STATUS:
 | 
	
		
			
				|  |  |          case GRPC_IOREQ_SEND_CLOSE:
 | 
	
		
			
				|  |  |            break;
 | 
	
		
			
				|  |  |          case GRPC_IOREQ_RECV_STATUS:
 | 
	
	
		
			
				|  | @@ -677,7 +690,7 @@ static void call_on_done_send(void *pc, int success) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void finish_message(grpc_call *call) {
 | 
	
		
			
				|  |  |    /* TODO(ctiller): this could be a lot faster if coded directly */
 | 
	
		
			
				|  |  | -  grpc_byte_buffer *byte_buffer = grpc_byte_buffer_create(
 | 
	
		
			
				|  |  | +  grpc_byte_buffer *byte_buffer = grpc_raw_byte_buffer_create(
 | 
	
		
			
				|  |  |        call->incoming_message.slices, call->incoming_message.count);
 | 
	
		
			
				|  |  |    gpr_slice_buffer_reset_and_unref(&call->incoming_message);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -711,6 +724,7 @@ static int begin_message(grpc_call *call, grpc_begin_message msg) {
 | 
	
		
			
				|  |  |    } else if (msg.length > 0) {
 | 
	
		
			
				|  |  |      call->reading_message = 1;
 | 
	
		
			
				|  |  |      call->incoming_message_length = msg.length;
 | 
	
		
			
				|  |  | +    call->incoming_message_flags = msg.flags;
 | 
	
		
			
				|  |  |      return 1;
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  |      finish_message(call);
 | 
	
	
		
			
				|  | @@ -800,7 +814,7 @@ static void call_on_done_recv(void *pc, int success) {
 | 
	
		
			
				|  |  |    unlock(call);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    GRPC_CALL_INTERNAL_UNREF(call, "receiving", 0);
 | 
	
		
			
				|  |  | -  GRPC_TIMER_BEGIN(GRPC_PTAG_CALL_ON_DONE_RECV, 0);
 | 
	
		
			
				|  |  | +  GRPC_TIMER_END(GRPC_PTAG_CALL_ON_DONE_RECV, 0);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int prepare_application_metadata(grpc_call *call, size_t count,
 | 
	
	
		
			
				|  | @@ -847,9 +861,9 @@ static void copy_byte_buffer_to_stream_ops(grpc_byte_buffer *byte_buffer,
 | 
	
		
			
				|  |  |    size_t i;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    switch (byte_buffer->type) {
 | 
	
		
			
				|  |  | -    case GRPC_BB_SLICE_BUFFER:
 | 
	
		
			
				|  |  | -      for (i = 0; i < byte_buffer->data.slice_buffer.count; i++) {
 | 
	
		
			
				|  |  | -        gpr_slice slice = byte_buffer->data.slice_buffer.slices[i];
 | 
	
		
			
				|  |  | +    case GRPC_BB_RAW:
 | 
	
		
			
				|  |  | +      for (i = 0; i < byte_buffer->data.raw.slice_buffer.count; i++) {
 | 
	
		
			
				|  |  | +        gpr_slice slice = byte_buffer->data.raw.slice_buffer.slices[i];
 | 
	
		
			
				|  |  |          gpr_slice_ref(slice);
 | 
	
		
			
				|  |  |          grpc_sopb_add_slice(sopb, slice);
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -859,9 +873,9 @@ static void copy_byte_buffer_to_stream_ops(grpc_byte_buffer *byte_buffer,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
 | 
	
		
			
				|  |  |    grpc_ioreq_data data;
 | 
	
		
			
				|  |  | +  gpr_uint32 flags;
 | 
	
		
			
				|  |  |    grpc_metadata_batch mdb;
 | 
	
		
			
				|  |  |    size_t i;
 | 
	
		
			
				|  |  | -  char status_str[GPR_LTOA_MIN_BUFSIZE];
 | 
	
		
			
				|  |  |    GPR_ASSERT(op->send_ops == NULL);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    switch (call->write_state) {
 | 
	
	
		
			
				|  | @@ -885,8 +899,9 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
 | 
	
		
			
				|  |  |      case WRITE_STATE_STARTED:
 | 
	
		
			
				|  |  |        if (is_op_live(call, GRPC_IOREQ_SEND_MESSAGE)) {
 | 
	
		
			
				|  |  |          data = call->request_data[GRPC_IOREQ_SEND_MESSAGE];
 | 
	
		
			
				|  |  | +        flags = call->request_flags[GRPC_IOREQ_SEND_MESSAGE];
 | 
	
		
			
				|  |  |          grpc_sopb_add_begin_message(
 | 
	
		
			
				|  |  | -            &call->send_ops, grpc_byte_buffer_length(data.send_message), 0);
 | 
	
		
			
				|  |  | +            &call->send_ops, grpc_byte_buffer_length(data.send_message), flags);
 | 
	
		
			
				|  |  |          copy_byte_buffer_to_stream_ops(data.send_message, &call->send_ops);
 | 
	
		
			
				|  |  |          op->send_ops = &call->send_ops;
 | 
	
		
			
				|  |  |          call->last_send_contains |= 1 << GRPC_IOREQ_SEND_MESSAGE;
 | 
	
	
		
			
				|  | @@ -905,13 +920,10 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
 | 
	
		
			
				|  |  |            /* send status */
 | 
	
		
			
				|  |  |            /* TODO(ctiller): cache common status values */
 | 
	
		
			
				|  |  |            data = call->request_data[GRPC_IOREQ_SEND_STATUS];
 | 
	
		
			
				|  |  | -          gpr_ltoa(data.send_status.code, status_str);
 | 
	
		
			
				|  |  |            grpc_metadata_batch_add_tail(
 | 
	
		
			
				|  |  |                &mdb, &call->status_link,
 | 
	
		
			
				|  |  | -              grpc_mdelem_from_metadata_strings(
 | 
	
		
			
				|  |  | -                  call->metadata_context,
 | 
	
		
			
				|  |  | -                  grpc_mdstr_ref(grpc_channel_get_status_string(call->channel)),
 | 
	
		
			
				|  |  | -                  grpc_mdstr_from_string(call->metadata_context, status_str)));
 | 
	
		
			
				|  |  | +              grpc_channel_get_reffed_status_elem(call->channel,
 | 
	
		
			
				|  |  | +                                                  data.send_status.code));
 | 
	
		
			
				|  |  |            if (data.send_status.details) {
 | 
	
		
			
				|  |  |              grpc_metadata_batch_add_tail(
 | 
	
		
			
				|  |  |                  &mdb, &call->details_link,
 | 
	
	
		
			
				|  | @@ -919,8 +931,9 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
 | 
	
		
			
				|  |  |                      call->metadata_context,
 | 
	
		
			
				|  |  |                      grpc_mdstr_ref(
 | 
	
		
			
				|  |  |                          grpc_channel_get_message_string(call->channel)),
 | 
	
		
			
				|  |  | -                    grpc_mdstr_from_string(call->metadata_context,
 | 
	
		
			
				|  |  | -                                           data.send_status.details)));
 | 
	
		
			
				|  |  | +                    data.send_status.details));
 | 
	
		
			
				|  |  | +            call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
 | 
	
		
			
				|  |  | +                NULL;
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |            grpc_sopb_add_metadata(&call->send_ops, mdb);
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -1020,9 +1033,18 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
 | 
	
		
			
				|  |  |                                   GRPC_CALL_ERROR_INVALID_METADATA);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    if (op == GRPC_IOREQ_SEND_STATUS) {
 | 
	
		
			
				|  |  | +      set_status_code(call, STATUS_FROM_SERVER_STATUS,
 | 
	
		
			
				|  |  | +                      reqs[i].data.send_status.code);
 | 
	
		
			
				|  |  | +      if (reqs[i].data.send_status.details) {
 | 
	
		
			
				|  |  | +        set_status_details(call, STATUS_FROM_SERVER_STATUS,
 | 
	
		
			
				|  |  | +                           grpc_mdstr_ref(reqs[i].data.send_status.details));
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      have_ops |= 1u << op;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      call->request_data[op] = data;
 | 
	
		
			
				|  |  | +    call->request_flags[op] = reqs[i].flags;
 | 
	
		
			
				|  |  |      call->request_set[op] = set;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1239,6 +1261,14 @@ static void finish_batch_with_close(grpc_call *call, int success, void *tag) {
 | 
	
		
			
				|  |  |    grpc_cq_end_op(call->cq, tag, call, 1);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static int are_write_flags_valid(gpr_uint32 flags) {
 | 
	
		
			
				|  |  | +  /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */
 | 
	
		
			
				|  |  | +  const gpr_uint32 allowed_write_positions =
 | 
	
		
			
				|  |  | +      (GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK);
 | 
	
		
			
				|  |  | +  const gpr_uint32 invalid_positions = ~allowed_write_positions;
 | 
	
		
			
				|  |  | +  return !(flags & invalid_positions);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
 | 
	
		
			
				|  |  |                                        size_t nops, void *tag) {
 | 
	
		
			
				|  |  |    grpc_ioreq reqs[GRPC_IOREQ_OP_COUNT];
 | 
	
	
		
			
				|  | @@ -1261,30 +1291,43 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
 | 
	
		
			
				|  |  |      op = &ops[in];
 | 
	
		
			
				|  |  |      switch (op->op) {
 | 
	
		
			
				|  |  |        case GRPC_OP_SEND_INITIAL_METADATA:
 | 
	
		
			
				|  |  | +        /* Flag validation: currently allow no flags */
 | 
	
		
			
				|  |  | +        if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_SEND_INITIAL_METADATA;
 | 
	
		
			
				|  |  |          req->data.send_metadata.count = op->data.send_initial_metadata.count;
 | 
	
		
			
				|  |  |          req->data.send_metadata.metadata =
 | 
	
		
			
				|  |  |              op->data.send_initial_metadata.metadata;
 | 
	
		
			
				|  |  | +        req->flags = op->flags;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        case GRPC_OP_SEND_MESSAGE:
 | 
	
		
			
				|  |  | +        if (!are_write_flags_valid(op->flags)) {
 | 
	
		
			
				|  |  | +          return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_SEND_MESSAGE;
 | 
	
		
			
				|  |  |          req->data.send_message = op->data.send_message;
 | 
	
		
			
				|  |  | +        req->flags = ops->flags;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
 | 
	
		
			
				|  |  | +        /* Flag validation: currently allow no flags */
 | 
	
		
			
				|  |  | +        if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  |          if (!call->is_client) {
 | 
	
		
			
				|  |  |            return GRPC_CALL_ERROR_NOT_ON_SERVER;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_SEND_CLOSE;
 | 
	
		
			
				|  |  | +        req->flags = op->flags;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        case GRPC_OP_SEND_STATUS_FROM_SERVER:
 | 
	
		
			
				|  |  | +        /* Flag validation: currently allow no flags */
 | 
	
		
			
				|  |  | +        if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  |          if (call->is_client) {
 | 
	
		
			
				|  |  |            return GRPC_CALL_ERROR_NOT_ON_CLIENT;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_SEND_TRAILING_METADATA;
 | 
	
		
			
				|  |  | +        req->flags = op->flags;
 | 
	
		
			
				|  |  |          req->data.send_metadata.count =
 | 
	
		
			
				|  |  |              op->data.send_status_from_server.trailing_metadata_count;
 | 
	
		
			
				|  |  |          req->data.send_metadata.metadata =
 | 
	
	
		
			
				|  | @@ -1293,29 +1336,42 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_SEND_STATUS;
 | 
	
		
			
				|  |  |          req->data.send_status.code = op->data.send_status_from_server.status;
 | 
	
		
			
				|  |  |          req->data.send_status.details =
 | 
	
		
			
				|  |  | -            op->data.send_status_from_server.status_details;
 | 
	
		
			
				|  |  | +            op->data.send_status_from_server.status_details != NULL
 | 
	
		
			
				|  |  | +                ? grpc_mdstr_from_string(
 | 
	
		
			
				|  |  | +                      call->metadata_context,
 | 
	
		
			
				|  |  | +                      op->data.send_status_from_server.status_details)
 | 
	
		
			
				|  |  | +                : NULL;
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_SEND_CLOSE;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        case GRPC_OP_RECV_INITIAL_METADATA:
 | 
	
		
			
				|  |  | +        /* Flag validation: currently allow no flags */
 | 
	
		
			
				|  |  | +        if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  |          if (!call->is_client) {
 | 
	
		
			
				|  |  |            return GRPC_CALL_ERROR_NOT_ON_SERVER;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_RECV_INITIAL_METADATA;
 | 
	
		
			
				|  |  |          req->data.recv_metadata = op->data.recv_initial_metadata;
 | 
	
		
			
				|  |  | +        req->flags = op->flags;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        case GRPC_OP_RECV_MESSAGE:
 | 
	
		
			
				|  |  | +        /* Flag validation: currently allow no flags */
 | 
	
		
			
				|  |  | +        if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_RECV_MESSAGE;
 | 
	
		
			
				|  |  |          req->data.recv_message = op->data.recv_message;
 | 
	
		
			
				|  |  | +        req->flags = op->flags;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        case GRPC_OP_RECV_STATUS_ON_CLIENT:
 | 
	
		
			
				|  |  | +        /* Flag validation: currently allow no flags */
 | 
	
		
			
				|  |  | +        if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  |          if (!call->is_client) {
 | 
	
		
			
				|  |  |            return GRPC_CALL_ERROR_NOT_ON_SERVER;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_RECV_STATUS;
 | 
	
		
			
				|  |  | +        req->flags = op->flags;
 | 
	
		
			
				|  |  |          req->data.recv_status.set_value = set_status_value_directly;
 | 
	
		
			
				|  |  |          req->data.recv_status.user_data = op->data.recv_status_on_client.status;
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
	
		
			
				|  | @@ -1333,8 +1389,11 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
 | 
	
		
			
				|  |  |          finish_func = finish_batch_with_close;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        case GRPC_OP_RECV_CLOSE_ON_SERVER:
 | 
	
		
			
				|  |  | +        /* Flag validation: currently allow no flags */
 | 
	
		
			
				|  |  | +        if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
 | 
	
		
			
				|  |  |          req = &reqs[out++];
 | 
	
		
			
				|  |  |          req->op = GRPC_IOREQ_RECV_STATUS;
 | 
	
		
			
				|  |  | +        req->flags = op->flags;
 | 
	
		
			
				|  |  |          req->data.recv_status.set_value = set_cancelled_value;
 | 
	
		
			
				|  |  |          req->data.recv_status.user_data =
 | 
	
		
			
				|  |  |              op->data.recv_close_on_server.cancelled;
 |