aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Julien Boeuf <jboeuf@google.com>2015-08-06 15:29:45 -0700
committerGravatar Julien Boeuf <jboeuf@google.com>2015-08-06 15:29:45 -0700
commitf3128e6ef3d5aab97216a23e052bba85a6829bfd (patch)
tree7c42ebf0f3c1a936cc3a751002324d4cb13a7519 /src
parent54b5018dd889cbc5059ff77ba083ff7020c63a9b (diff)
parent83f8416f4ccaf1fa50d45f80124c72342244b7bd (diff)
Merge branch 'master' of https://github.com/grpc/grpc into server_creds_auth_md_processor
Diffstat (limited to 'src')
-rw-r--r--src/compiler/csharp_generator.cc82
-rw-r--r--src/core/channel/client_channel.c23
-rw-r--r--src/core/surface/call.c99
-rw-r--r--src/core/surface/call.h4
-rw-r--r--src/core/surface/channel.c19
-rw-r--r--src/core/surface/completion_queue.c5
-rw-r--r--src/core/surface/server.c4
-rw-r--r--src/cpp/client/channel.cc22
-rw-r--r--src/cpp/client/client_context.cc12
-rw-r--r--src/csharp/Grpc.Core.Tests/ClientServerTest.cs74
-rw-r--r--src/csharp/Grpc.Core.Tests/TimeoutsTest.cs30
-rw-r--r--src/csharp/Grpc.Core/CallInvocationDetails.cs (renamed from src/csharp/Grpc.Core/Call.cs)56
-rw-r--r--src/csharp/Grpc.Core/CallOptions.cs89
-rw-r--r--src/csharp/Grpc.Core/Calls.cs48
-rw-r--r--src/csharp/Grpc.Core/Channel.cs16
-rw-r--r--src/csharp/Grpc.Core/ClientBase.cs10
-rw-r--r--src/csharp/Grpc.Core/Grpc.Core.csproj3
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCall.cs84
-rw-r--r--src/csharp/Grpc.Core/Method.cs28
-rw-r--r--src/csharp/Grpc.Core/ServerCallContext.cs15
-rw-r--r--src/csharp/Grpc.Examples/MathGrpc.cs54
-rw-r--r--src/csharp/Grpc.HealthCheck/HealthGrpc.cs21
-rw-r--r--src/csharp/Grpc.IntegrationTesting/TestGrpc.cs86
-rw-r--r--src/csharp/ext/grpc_csharp_ext.c3
-rw-r--r--src/node/ext/call.cc5
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+Tests.h45
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+Tests.m47
-rw-r--r--src/objective-c/GRPCClient/GRPCCall.m12
-rw-r--r--src/objective-c/GRPCClient/private/GRPCChannel.h13
-rw-r--r--src/objective-c/GRPCClient/private/GRPCChannel.m56
-rw-r--r--src/objective-c/GRPCClient/private/GRPCCompletionQueue.m2
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.h58
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.m134
-rw-r--r--src/objective-c/GRPCClient/private/GRPCSecureChannel.h15
-rw-r--r--src/objective-c/GRPCClient/private/GRPCSecureChannel.m78
-rw-r--r--src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h2
-rw-r--r--src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m6
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedCall.h7
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedCall.m39
-rw-r--r--src/objective-c/tests/InteropTests.h44
-rw-r--r--src/objective-c/tests/InteropTests.m16
-rw-r--r--src/objective-c/tests/InteropTestsLocalSSL.m62
-rw-r--r--src/objective-c/tests/TestCertificates.bundle/test-certificates.pem15
-rw-r--r--src/objective-c/tests/Tests.xcodeproj/project.pbxproj10
-rwxr-xr-xsrc/objective-c/tests/run_tests.sh3
-rw-r--r--src/php/ext/grpc/call.c4
-rw-r--r--src/python/grpcio/grpc/_adapter/_c/types/channel.c2
-rw-r--r--src/ruby/ext/grpc/rb_channel.c8
48 files changed, 1193 insertions, 377 deletions
diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc
index e0c1bcda19..9432bdda96 100644
--- a/src/compiler/csharp_generator.cc
+++ b/src/compiler/csharp_generator.cc
@@ -246,6 +246,8 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) {
out->Indent();
out->Print("$methodtype$,\n", "methodtype",
GetCSharpMethodType(GetMethodType(method)));
+ out->Print("$servicenamefield$,\n", "servicenamefield",
+ GetServiceNameFieldName());
out->Print("\"$methodname$\",\n", "methodname", method->name());
out->Print("$requestmarshaller$,\n", "requestmarshaller",
GetMarshallerFieldName(method->input_type()));
@@ -273,6 +275,13 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
"methodname", method->name(), "request",
GetClassName(method->input_type()), "response",
GetClassName(method->output_type()));
+
+ // overload taking CallOptions as a param
+ out->Print(
+ "$response$ $methodname$($request$ request, CallOptions options);\n",
+ "methodname", method->name(), "request",
+ GetClassName(method->input_type()), "response",
+ GetClassName(method->output_type()));
}
std::string method_name = method->name();
@@ -284,6 +293,13 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
"methodname", method_name, "request_maybe",
GetMethodRequestParamMaybe(method), "returntype",
GetMethodReturnTypeClient(method));
+
+ // overload taking CallOptions as a param
+ out->Print(
+ "$returntype$ $methodname$($request_maybe$CallOptions options);\n",
+ "methodname", method_name, "request_maybe",
+ GetMethodRequestParamMaybe(method), "returntype",
+ GetMethodReturnTypeClient(method));
}
out->Outdent();
out->Print("}\n");
@@ -340,10 +356,23 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
GetClassName(method->output_type()));
out->Print("{\n");
out->Indent();
- out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers, deadline);\n",
- "servicenamefield", GetServiceNameFieldName(), "methodfield",
- GetMethodFieldName(method));
- out->Print("return Calls.BlockingUnaryCall(call, request, cancellationToken);\n");
+ out->Print("var call = CreateCall($methodfield$, new CallOptions(headers, deadline, cancellationToken));\n",
+ "methodfield", GetMethodFieldName(method));
+ out->Print("return Calls.BlockingUnaryCall(call, request);\n");
+ out->Outdent();
+ out->Print("}\n");
+
+ // overload taking CallOptions as a param
+ out->Print(
+ "public $response$ $methodname$($request$ request, CallOptions options)\n",
+ "methodname", method->name(), "request",
+ GetClassName(method->input_type()), "response",
+ GetClassName(method->output_type()));
+ out->Print("{\n");
+ out->Indent();
+ out->Print("var call = CreateCall($methodfield$, options);\n",
+ "methodfield", GetMethodFieldName(method));
+ out->Print("return Calls.BlockingUnaryCall(call, request);\n");
out->Outdent();
out->Print("}\n");
}
@@ -359,26 +388,55 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
GetMethodReturnTypeClient(method));
out->Print("{\n");
out->Indent();
- out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers, deadline);\n",
- "servicenamefield", GetServiceNameFieldName(), "methodfield",
- GetMethodFieldName(method));
+ out->Print("var call = CreateCall($methodfield$, new CallOptions(headers, deadline, cancellationToken));\n",
+ "methodfield", GetMethodFieldName(method));
switch (GetMethodType(method)) {
case METHODTYPE_NO_STREAMING:
- out->Print("return Calls.AsyncUnaryCall(call, request, cancellationToken);\n");
+ out->Print("return Calls.AsyncUnaryCall(call, request);\n");
break;
case METHODTYPE_CLIENT_STREAMING:
- out->Print("return Calls.AsyncClientStreamingCall(call, cancellationToken);\n");
+ out->Print("return Calls.AsyncClientStreamingCall(call);\n");
break;
case METHODTYPE_SERVER_STREAMING:
out->Print(
- "return Calls.AsyncServerStreamingCall(call, request, cancellationToken);\n");
+ "return Calls.AsyncServerStreamingCall(call, request);\n");
break;
case METHODTYPE_BIDI_STREAMING:
- out->Print("return Calls.AsyncDuplexStreamingCall(call, cancellationToken);\n");
+ out->Print("return Calls.AsyncDuplexStreamingCall(call);\n");
break;
default:
GOOGLE_LOG(FATAL)<< "Can't get here.";
- }
+ }
+ out->Outdent();
+ out->Print("}\n");
+
+ // overload taking CallOptions as a param
+ out->Print(
+ "public $returntype$ $methodname$($request_maybe$CallOptions options)\n",
+ "methodname", method_name, "request_maybe",
+ GetMethodRequestParamMaybe(method), "returntype",
+ GetMethodReturnTypeClient(method));
+ out->Print("{\n");
+ out->Indent();
+ out->Print("var call = CreateCall($methodfield$, options);\n",
+ "methodfield", GetMethodFieldName(method));
+ switch (GetMethodType(method)) {
+ case METHODTYPE_NO_STREAMING:
+ out->Print("return Calls.AsyncUnaryCall(call, request);\n");
+ break;
+ case METHODTYPE_CLIENT_STREAMING:
+ out->Print("return Calls.AsyncClientStreamingCall(call);\n");
+ break;
+ case METHODTYPE_SERVER_STREAMING:
+ out->Print(
+ "return Calls.AsyncServerStreamingCall(call, request);\n");
+ break;
+ case METHODTYPE_BIDI_STREAMING:
+ out->Print("return Calls.AsyncDuplexStreamingCall(call);\n");
+ break;
+ default:
+ GOOGLE_LOG(FATAL)<< "Can't get here.";
+ }
out->Outdent();
out->Print("}\n");
}
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index 2cfca399b6..2ee260b799 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -56,6 +56,8 @@ typedef struct {
grpc_mdctx *mdctx;
/** resolver for this channel */
grpc_resolver *resolver;
+ /** have we started resolving this channel */
+ int started_resolving;
/** master channel - the grpc_channel instance that ultimately owns
this channel_data via its channel stack.
We occasionally use this to bump the refcount on the master channel
@@ -398,6 +400,12 @@ static void perform_transport_stream_op(grpc_call_element *elem,
} else if (chand->resolver != NULL) {
calld->state = CALL_WAITING_FOR_CONFIG;
add_to_lb_policy_wait_queue_locked_state_config(elem);
+ if (!chand->started_resolving && chand->resolver != NULL) {
+ chand->started_resolving = 1;
+ grpc_resolver_next(chand->resolver,
+ &chand->incoming_configuration,
+ &chand->on_config_changed);
+ }
gpr_mu_unlock(&chand->mu_config);
gpr_mu_unlock(&calld->mu_state);
} else {
@@ -690,12 +698,18 @@ void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack,
/* post construction initialization: set the transport setup pointer */
grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack);
channel_data *chand = elem->channel_data;
+ gpr_mu_lock(&chand->mu_config);
GPR_ASSERT(!chand->resolver);
chand->resolver = resolver;
GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver");
GRPC_RESOLVER_REF(resolver, "channel");
- grpc_resolver_next(resolver, &chand->incoming_configuration,
- &chand->on_config_changed);
+ if (chand->waiting_for_config_closures != NULL ||
+ chand->exit_idle_when_lb_policy_arrives) {
+ chand->started_resolving = 1;
+ grpc_resolver_next(resolver, &chand->incoming_configuration,
+ &chand->on_config_changed);
+ }
+ gpr_mu_unlock(&chand->mu_config);
}
grpc_connectivity_state grpc_client_channel_check_connectivity_state(
@@ -709,6 +723,11 @@ grpc_connectivity_state grpc_client_channel_check_connectivity_state(
grpc_lb_policy_exit_idle(chand->lb_policy);
} else {
chand->exit_idle_when_lb_policy_arrives = 1;
+ if (!chand->started_resolving && chand->resolver != NULL) {
+ chand->started_resolving = 1;
+ grpc_resolver_next(chand->resolver, &chand->incoming_configuration,
+ &chand->on_config_changed);
+ }
}
}
gpr_mu_unlock(&chand->mu_config);
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 327a096ffb..d3e66e9c4c 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -143,6 +143,8 @@ typedef enum {
struct grpc_call {
grpc_completion_queue *cq;
grpc_channel *channel;
+ grpc_call *parent;
+ grpc_call *first_child;
grpc_mdctx *metadata_context;
/* TODO(ctiller): share with cq if possible? */
gpr_mu mu;
@@ -176,6 +178,8 @@ struct grpc_call {
gpr_uint8 cancel_alarm;
/** bitmask of allocated completion events in completions */
gpr_uint8 allocated_completions;
+ /** flag indicating that cancellation is inherited */
+ gpr_uint8 cancellation_is_inherited;
/* flags with bits corresponding to write states allowing us to determine
what was sent */
@@ -267,6 +271,11 @@ struct grpc_call {
/** completion events - for completion queue use */
grpc_cq_completion completions[MAX_CONCURRENT_COMPLETIONS];
+
+ /** siblings: children of the same parent form a list, and this list is protected under
+ parent->mu */
+ grpc_call *sibling_next;
+ grpc_call *sibling_prev;
};
#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1))
@@ -290,7 +299,9 @@ static void finished_loose_op(void *call, int success);
static void lock(grpc_call *call);
static void unlock(grpc_call *call);
-grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
+grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
+ gpr_uint32 propagation_mask,
+ grpc_completion_queue *cq,
const void *server_transport_data,
grpc_mdelem **add_initial_metadata,
size_t add_initial_metadata_count,
@@ -306,9 +317,10 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
gpr_mu_init(&call->completion_mu);
call->channel = channel;
call->cq = cq;
- if (cq) {
+ if (cq != NULL) {
GRPC_CQ_INTERNAL_REF(cq, "bind");
}
+ call->parent = parent_call;
call->is_client = server_transport_data == NULL;
for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) {
call->request_set[i] = REQSET_EMPTY;
@@ -347,6 +359,46 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
}
grpc_call_stack_init(channel_stack, server_transport_data, initial_op_ptr,
CALL_STACK_FROM_CALL(call));
+ if (parent_call != NULL) {
+ GRPC_CALL_INTERNAL_REF(parent_call, "child");
+ GPR_ASSERT(call->is_client);
+ GPR_ASSERT(!parent_call->is_client);
+
+ gpr_mu_lock(&parent_call->mu);
+
+ if (propagation_mask & GRPC_PROPAGATE_DEADLINE) {
+ send_deadline = gpr_time_min(
+ gpr_convert_clock_type(send_deadline,
+ parent_call->send_deadline.clock_type),
+ parent_call->send_deadline);
+ }
+ /* for now GRPC_PROPAGATE_TRACING_CONTEXT *MUST* be passed with
+ * GRPC_PROPAGATE_STATS_CONTEXT */
+ /* TODO(ctiller): This should change to use the appropriate census start_op
+ * call. */
+ if (propagation_mask & GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT) {
+ GPR_ASSERT(propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT);
+ grpc_call_context_set(call, GRPC_CONTEXT_TRACING,
+ parent_call->context[GRPC_CONTEXT_TRACING].value,
+ NULL);
+ } else {
+ GPR_ASSERT(propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT);
+ }
+ if (propagation_mask & GRPC_PROPAGATE_CANCELLATION) {
+ call->cancellation_is_inherited = 1;
+ }
+
+ if (parent_call->first_child == NULL) {
+ parent_call->first_child = call;
+ call->sibling_next = call->sibling_prev = call;
+ } else {
+ call->sibling_next = parent_call->first_child;
+ call->sibling_prev = parent_call->first_child->sibling_prev;
+ call->sibling_next->sibling_prev = call->sibling_prev->sibling_next = call;
+ }
+
+ gpr_mu_unlock(&parent_call->mu);
+ }
if (gpr_time_cmp(send_deadline, gpr_inf_future(send_deadline.clock_type)) !=
0) {
set_deadline_alarm(call, send_deadline);
@@ -404,6 +456,20 @@ void grpc_call_internal_ref(grpc_call *c) {
static void destroy_call(void *call, int ignored_success) {
size_t i;
grpc_call *c = call;
+ grpc_call *parent = c->parent;
+ if (parent) {
+ gpr_mu_lock(&parent->mu);
+ if (call == parent->first_child) {
+ parent->first_child = c->sibling_next;
+ if (c == parent->first_child) {
+ parent->first_child = NULL;
+ }
+ c->sibling_prev->sibling_next = c->sibling_next;
+ c->sibling_next->sibling_prev = c->sibling_prev;
+ }
+ gpr_mu_unlock(&parent->mu);
+ GRPC_CALL_INTERNAL_UNREF(parent, "child", 1);
+ }
grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c));
GRPC_CHANNEL_INTERNAL_UNREF(c->channel, "call");
gpr_mu_destroy(&c->mu);
@@ -870,6 +936,8 @@ static int add_slice_to_message(grpc_call *call, gpr_slice slice) {
static void call_on_done_recv(void *pc, int success) {
grpc_call *call = pc;
+ grpc_call *child_call;
+ grpc_call *next_child_call;
size_t i;
GRPC_TIMER_BEGIN(GRPC_PTAG_CALL_ON_DONE_RECV, 0);
lock(call);
@@ -903,6 +971,19 @@ static void call_on_done_recv(void *pc, int success) {
GPR_ASSERT(call->read_state <= READ_STATE_STREAM_CLOSED);
call->read_state = READ_STATE_STREAM_CLOSED;
call->cancel_alarm |= call->have_alarm;
+ /* propagate cancellation to any interested children */
+ child_call = call->first_child;
+ if (child_call != NULL) {
+ do {
+ next_child_call = child_call->sibling_next;
+ if (child_call->cancellation_is_inherited) {
+ GRPC_CALL_INTERNAL_REF(child_call, "propagate_cancel");
+ grpc_call_cancel(child_call);
+ GRPC_CALL_INTERNAL_UNREF(child_call, "propagate_cancel", 0);
+ }
+ child_call = next_child_call;
+ } while (child_call != call->first_child);
+ }
GRPC_CALL_INTERNAL_UNREF(call, "closed", 0);
}
finish_read_ops(call);
@@ -1283,9 +1364,9 @@ static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) {
}
GRPC_CALL_INTERNAL_REF(call, "alarm");
call->have_alarm = 1;
- grpc_alarm_init(&call->alarm,
- gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
- call_alarm, call, gpr_now(GPR_CLOCK_MONOTONIC));
+ call->send_deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
+ grpc_alarm_init(&call->alarm, call->send_deadline, call_alarm, call,
+ gpr_now(GPR_CLOCK_MONOTONIC));
}
/* we offset status by a small amount when storing it into transport metadata
@@ -1377,7 +1458,8 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) {
}
}
if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) !=
- 0) {
+ 0 &&
+ !call->is_client) {
set_deadline_alarm(call, md->deadline);
}
if (!is_trailing) {
@@ -1465,6 +1547,9 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
if (!are_write_flags_valid(op->flags)) {
return GRPC_CALL_ERROR_INVALID_FLAGS;
}
+ if (op->data.send_message == NULL) {
+ return GRPC_CALL_ERROR_INVALID_MESSAGE;
+ }
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_MESSAGE;
req->data.send_message = op->data.send_message;
@@ -1514,6 +1599,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_INITIAL_METADATA;
req->data.recv_metadata = op->data.recv_initial_metadata;
+ req->data.recv_metadata->count = 0;
req->flags = op->flags;
break;
case GRPC_OP_RECV_MESSAGE:
@@ -1545,6 +1631,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
req->op = GRPC_IOREQ_RECV_TRAILING_METADATA;
req->data.recv_metadata =
op->data.recv_status_on_client.trailing_metadata;
+ req->data.recv_metadata->count = 0;
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_CLOSE;
finish_func = finish_batch_with_close;
diff --git a/src/core/surface/call.h b/src/core/surface/call.h
index 265638d519..75bdbce980 100644
--- a/src/core/surface/call.h
+++ b/src/core/surface/call.h
@@ -85,7 +85,9 @@ typedef struct {
typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success,
void *user_data);
-grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
+grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
+ gpr_uint32 propagation_mask,
+ grpc_completion_queue *cq,
const void *server_transport_data,
grpc_mdelem **add_initial_metadata,
size_t add_initial_metadata_count,
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index 688a586e18..8692aa3903 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -146,7 +146,8 @@ char *grpc_channel_get_target(grpc_channel *channel) {
}
static grpc_call *grpc_channel_create_call_internal(
- grpc_channel *channel, grpc_completion_queue *cq, grpc_mdelem *path_mdelem,
+ grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask,
+ grpc_completion_queue *cq, grpc_mdelem *path_mdelem,
grpc_mdelem *authority_mdelem, gpr_timespec deadline) {
grpc_mdelem *send_metadata[2];
int num_metadata = 0;
@@ -158,16 +159,18 @@ static grpc_call *grpc_channel_create_call_internal(
send_metadata[num_metadata++] = authority_mdelem;
}
- return grpc_call_create(channel, cq, NULL, send_metadata,
- num_metadata, deadline);
+ return grpc_call_create(channel, parent_call, propagation_mask, cq, NULL,
+ send_metadata, num_metadata, deadline);
}
grpc_call *grpc_channel_create_call(grpc_channel *channel,
+ grpc_call *parent_call,
+ gpr_uint32 propagation_mask,
grpc_completion_queue *cq,
const char *method, const char *host,
gpr_timespec deadline) {
return grpc_channel_create_call_internal(
- channel, cq,
+ channel, parent_call, propagation_mask, cq,
grpc_mdelem_from_metadata_strings(
channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
grpc_mdstr_from_string(channel->metadata_context, method, 0)),
@@ -195,11 +198,13 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method,
}
grpc_call *grpc_channel_create_registered_call(
- grpc_channel *channel, grpc_completion_queue *completion_queue,
- void *registered_call_handle, gpr_timespec deadline) {
+ grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask,
+ grpc_completion_queue *completion_queue, void *registered_call_handle,
+ gpr_timespec deadline) {
registered_call *rc = registered_call_handle;
return grpc_channel_create_call_internal(
- channel, completion_queue, GRPC_MDELEM_REF(rc->path),
+ channel, parent_call, propagation_mask, completion_queue,
+ GRPC_MDELEM_REF(rc->path),
rc->authority ? GRPC_MDELEM_REF(rc->authority) : NULL, deadline);
}
diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c
index 00429fac19..36d69cfe5f 100644
--- a/src/core/surface/completion_queue.c
+++ b/src/core/surface/completion_queue.c
@@ -114,6 +114,11 @@ void grpc_cq_internal_unref(grpc_completion_queue *cc) {
}
void grpc_cq_begin_op(grpc_completion_queue *cc) {
+#ifndef NDEBUG
+ gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
+ GPR_ASSERT(!cc->shutdown_called);
+ gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
+#endif
gpr_ref(&cc->pending_events);
}
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index 29d893db71..cd1dc589e1 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -644,8 +644,8 @@ static void accept_stream(void *cd, grpc_transport *transport,
const void *transport_server_data) {
channel_data *chand = cd;
/* create a call */
- grpc_call_create(chand->channel, NULL, transport_server_data, NULL, 0,
- gpr_inf_future(GPR_CLOCK_REALTIME));
+ grpc_call_create(chand->channel, NULL, 0, NULL, transport_server_data, NULL,
+ 0, gpr_inf_future(GPR_CLOCK_MONOTONIC));
}
static void channel_connectivity_changed(void *cd, int iomgr_status_ignored) {
diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc
index ee143d68a0..5f54e7fcc1 100644
--- a/src/cpp/client/channel.cc
+++ b/src/cpp/client/channel.cc
@@ -61,16 +61,18 @@ Channel::~Channel() { grpc_channel_destroy(c_channel_); }
Call Channel::CreateCall(const RpcMethod& method, ClientContext* context,
CompletionQueue* cq) {
const char* host_str = host_.empty() ? NULL : host_.c_str();
- auto c_call =
- method.channel_tag() && context->authority().empty()
- ? grpc_channel_create_registered_call(c_channel_, cq->cq(),
- method.channel_tag(),
- context->raw_deadline())
- : grpc_channel_create_call(c_channel_, cq->cq(), method.name(),
- context->authority().empty()
- ? host_str
- : context->authority().c_str(),
- context->raw_deadline());
+ auto c_call = method.channel_tag() && context->authority().empty()
+ ? grpc_channel_create_registered_call(
+ c_channel_, context->propagate_from_call_,
+ context->propagation_options_.c_bitmask(), cq->cq(),
+ method.channel_tag(), context->raw_deadline())
+ : grpc_channel_create_call(
+ c_channel_, context->propagate_from_call_,
+ context->propagation_options_.c_bitmask(), cq->cq(),
+ method.name(), context->authority().empty()
+ ? host_str
+ : context->authority().c_str(),
+ context->raw_deadline());
grpc_census_call_set_context(c_call, context->census_context());
GRPC_TIMER_MARK(GRPC_PTAG_CPP_CALL_CREATED, c_call);
context->set_call(c_call, shared_from_this());
diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc
index c38d0c1df6..1ed2d38961 100644
--- a/src/cpp/client/client_context.cc
+++ b/src/cpp/client/client_context.cc
@@ -37,6 +37,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/string_util.h>
#include <grpc++/credentials.h>
+#include <grpc++/server_context.h>
#include <grpc++/time.h>
#include "src/core/channel/compress_filter.h"
@@ -48,7 +49,8 @@ ClientContext::ClientContext()
: initial_metadata_received_(false),
call_(nullptr),
cq_(nullptr),
- deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)) {}
+ deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)),
+ propagate_from_call_(nullptr) {}
ClientContext::~ClientContext() {
if (call_) {
@@ -64,6 +66,14 @@ ClientContext::~ClientContext() {
}
}
+std::unique_ptr<ClientContext> ClientContext::FromServerContext(
+ const ServerContext& context, PropagationOptions options) {
+ std::unique_ptr<ClientContext> ctx(new ClientContext);
+ ctx->propagate_from_call_ = context.call_;
+ ctx->propagation_options_ = options;
+ return ctx;
+}
+
void ClientContext::AddMetadata(const grpc::string& meta_key,
const grpc::string& meta_value) {
send_initial_metadata_.insert(std::make_pair(meta_key, meta_value));
diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
index bf7cc3fbf3..64ea21800f 100644
--- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
@@ -46,23 +46,26 @@ namespace Grpc.Core.Tests
public class ClientServerTest
{
const string Host = "127.0.0.1";
- const string ServiceName = "/tests.Test";
+ const string ServiceName = "tests.Test";
static readonly Method<string, string> EchoMethod = new Method<string, string>(
MethodType.Unary,
- "/tests.Test/Echo",
+ ServiceName,
+ "Echo",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
static readonly Method<string, string> ConcatAndEchoMethod = new Method<string, string>(
MethodType.ClientStreaming,
- "/tests.Test/ConcatAndEcho",
+ ServiceName,
+ "ConcatAndEcho",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
static readonly Method<string, string> NonexistentMethod = new Method<string, string>(
MethodType.Unary,
- "/tests.Test/NonexistentMethod",
+ ServiceName,
+ "NonexistentMethod",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
@@ -102,17 +105,17 @@ namespace Grpc.Core.Tests
[Test]
public void UnaryCall()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
- Assert.AreEqual("ABC", Calls.BlockingUnaryCall(internalCall, "ABC", CancellationToken.None));
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
+ Assert.AreEqual("ABC", Calls.BlockingUnaryCall(callDetails, "ABC"));
}
[Test]
public void UnaryCall_ServerHandlerThrows()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
{
- Calls.BlockingUnaryCall(internalCall, "THROW", CancellationToken.None);
+ Calls.BlockingUnaryCall(callDetails, "THROW");
Assert.Fail();
}
catch (RpcException e)
@@ -124,10 +127,10 @@ namespace Grpc.Core.Tests
[Test]
public void UnaryCall_ServerHandlerThrowsRpcException()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
{
- Calls.BlockingUnaryCall(internalCall, "THROW_UNAUTHENTICATED", CancellationToken.None);
+ Calls.BlockingUnaryCall(callDetails, "THROW_UNAUTHENTICATED");
Assert.Fail();
}
catch (RpcException e)
@@ -139,10 +142,10 @@ namespace Grpc.Core.Tests
[Test]
public void UnaryCall_ServerHandlerSetsStatus()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
{
- Calls.BlockingUnaryCall(internalCall, "SET_UNAUTHENTICATED", CancellationToken.None);
+ Calls.BlockingUnaryCall(callDetails, "SET_UNAUTHENTICATED");
Assert.Fail();
}
catch (RpcException e)
@@ -152,20 +155,20 @@ namespace Grpc.Core.Tests
}
[Test]
- public void AsyncUnaryCall()
+ public async Task AsyncUnaryCall()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
- var result = Calls.AsyncUnaryCall(internalCall, "ABC", CancellationToken.None).ResponseAsync.Result;
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
+ var result = await Calls.AsyncUnaryCall(callDetails, "ABC");
Assert.AreEqual("ABC", result);
}
[Test]
public async Task AsyncUnaryCall_ServerHandlerThrows()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
{
- await Calls.AsyncUnaryCall(internalCall, "THROW", CancellationToken.None);
+ await Calls.AsyncUnaryCall(callDetails, "THROW");
Assert.Fail();
}
catch (RpcException e)
@@ -177,8 +180,8 @@ namespace Grpc.Core.Tests
[Test]
public async Task ClientStreamingCall()
{
- var internalCall = new Call<string, string>(ServiceName, ConcatAndEchoMethod, channel, Metadata.Empty);
- var call = Calls.AsyncClientStreamingCall(internalCall, CancellationToken.None);
+ var callDetails = new CallInvocationDetails<string, string>(channel, ConcatAndEchoMethod, new CallOptions());
+ var call = Calls.AsyncClientStreamingCall(callDetails);
await call.RequestStream.WriteAll(new string[] { "A", "B", "C" });
Assert.AreEqual("ABC", await call.ResponseAsync);
@@ -187,10 +190,9 @@ namespace Grpc.Core.Tests
[Test]
public async Task ClientStreamingCall_CancelAfterBegin()
{
- var internalCall = new Call<string, string>(ServiceName, ConcatAndEchoMethod, channel, Metadata.Empty);
-
var cts = new CancellationTokenSource();
- var call = Calls.AsyncClientStreamingCall(internalCall, cts.Token);
+ var callDetails = new CallInvocationDetails<string, string>(channel, ConcatAndEchoMethod, new CallOptions(cancellationToken: cts.Token));
+ var call = Calls.AsyncClientStreamingCall(callDetails);
// TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it.
await Task.Delay(1000);
@@ -214,8 +216,8 @@ namespace Grpc.Core.Tests
new Metadata.Entry("ascii-header", "abcdefg"),
new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }),
};
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, headers);
- var call = Calls.AsyncUnaryCall(internalCall, "ABC", CancellationToken.None);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions(headers: headers));
+ var call = Calls.AsyncUnaryCall(callDetails, "ABC");
Assert.AreEqual("ABC", call.ResponseAsync.Result);
@@ -235,25 +237,25 @@ namespace Grpc.Core.Tests
{
channel.Dispose();
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
- Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(internalCall, "ABC", CancellationToken.None));
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
+ Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(callDetails, "ABC"));
}
[Test]
public void UnaryCallPerformance()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
BenchmarkUtil.RunBenchmark(100, 100,
- () => { Calls.BlockingUnaryCall(internalCall, "ABC", default(CancellationToken)); });
+ () => { Calls.BlockingUnaryCall(callDetails, "ABC"); });
}
[Test]
public void UnknownMethodHandler()
{
- var internalCall = new Call<string, string>(ServiceName, NonexistentMethod, channel, Metadata.Empty);
+ var callDetails = new CallInvocationDetails<string, string>(channel, NonexistentMethod, new CallOptions());
try
{
- Calls.BlockingUnaryCall(internalCall, "ABC", default(CancellationToken));
+ Calls.BlockingUnaryCall(callDetails, "ABC");
Assert.Fail();
}
catch (RpcException e)
@@ -265,16 +267,16 @@ namespace Grpc.Core.Tests
[Test]
public void UserAgentStringPresent()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
- string userAgent = Calls.BlockingUnaryCall(internalCall, "RETURN-USER-AGENT", CancellationToken.None);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
+ string userAgent = Calls.BlockingUnaryCall(callDetails, "RETURN-USER-AGENT");
Assert.IsTrue(userAgent.StartsWith("grpc-csharp/"));
}
[Test]
public void PeerInfoPresent()
{
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
- string peer = Calls.BlockingUnaryCall(internalCall, "RETURN-PEER", CancellationToken.None);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
+ string peer = Calls.BlockingUnaryCall(callDetails, "RETURN-PEER");
Assert.IsTrue(peer.Contains(Host));
}
@@ -286,8 +288,8 @@ namespace Grpc.Core.Tests
var stateChangedTask = channel.WaitForStateChangedAsync(channel.State);
- var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty);
- await Calls.AsyncUnaryCall(internalCall, "abc", CancellationToken.None);
+ var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
+ await Calls.AsyncUnaryCall(callDetails, "abc");
await stateChangedTask;
Assert.AreEqual(ChannelState.Ready, channel.State);
diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
index d84801fbac..fc395b0acd 100644
--- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
@@ -49,11 +49,12 @@ namespace Grpc.Core.Tests
public class TimeoutsTest
{
const string Host = "localhost";
- const string ServiceName = "/tests.Test";
+ const string ServiceName = "tests.Test";
static readonly Method<string, string> TestMethod = new Method<string, string>(
MethodType.Unary,
- "/tests.Test/Test",
+ ServiceName,
+ "Test",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
@@ -98,12 +99,12 @@ namespace Grpc.Core.Tests
public void InfiniteDeadline()
{
// no deadline specified, check server sees infinite deadline
- var internalCall = new Call<string, string>(ServiceName, TestMethod, channel, Metadata.Empty);
- Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE", CancellationToken.None));
+ var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions());
+ Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE"));
// DateTime.MaxValue deadline specified, check server sees infinite deadline
- var internalCall2 = new Call<string, string>(ServiceName, TestMethod, channel, Metadata.Empty, DateTime.MaxValue);
- Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall2, "RETURN_DEADLINE", CancellationToken.None));
+ var callDetails2 = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions());
+ Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails2, "RETURN_DEADLINE"));
}
[Test]
@@ -112,9 +113,9 @@ namespace Grpc.Core.Tests
var remainingTimeClient = TimeSpan.FromDays(7);
var deadline = DateTime.UtcNow + remainingTimeClient;
Thread.Sleep(1000);
- var internalCall = new Call<string, string>(ServiceName, TestMethod, channel, Metadata.Empty, deadline);
+ var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
- var serverDeadlineTicksString = Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE", CancellationToken.None);
+ var serverDeadlineTicksString = Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE");
var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc);
// A fairly relaxed check that the deadline set by client and deadline seen by server
@@ -126,12 +127,11 @@ namespace Grpc.Core.Tests
[Test]
public void DeadlineInThePast()
{
- var deadline = DateTime.MinValue;
- var internalCall = new Call<string, string>(ServiceName, TestMethod, channel, Metadata.Empty, deadline);
+ var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: DateTime.MinValue));
try
{
- Calls.BlockingUnaryCall(internalCall, "TIMEOUT", CancellationToken.None);
+ Calls.BlockingUnaryCall(callDetails, "TIMEOUT");
Assert.Fail();
}
catch (RpcException e)
@@ -145,11 +145,11 @@ namespace Grpc.Core.Tests
public void DeadlineExceededStatusOnTimeout()
{
var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
- var internalCall = new Call<string, string>(ServiceName, TestMethod, channel, Metadata.Empty, deadline);
+ var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
try
{
- Calls.BlockingUnaryCall(internalCall, "TIMEOUT", CancellationToken.None);
+ Calls.BlockingUnaryCall(callDetails, "TIMEOUT");
Assert.Fail();
}
catch (RpcException e)
@@ -163,11 +163,11 @@ namespace Grpc.Core.Tests
public void ServerReceivesCancellationOnTimeout()
{
var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
- var internalCall = new Call<string, string>(ServiceName, TestMethod, channel, Metadata.Empty, deadline);
+ var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
try
{
- Calls.BlockingUnaryCall(internalCall, "CHECK_CANCELLATION_RECEIVED", CancellationToken.None);
+ Calls.BlockingUnaryCall(callDetails, "CHECK_CANCELLATION_RECEIVED");
Assert.Fail();
}
catch (RpcException e)
diff --git a/src/csharp/Grpc.Core/Call.cs b/src/csharp/Grpc.Core/CallInvocationDetails.cs
index 94c5e26082..eb23a3a209 100644
--- a/src/csharp/Grpc.Core/Call.cs
+++ b/src/csharp/Grpc.Core/CallInvocationDetails.cs
@@ -38,30 +38,30 @@ using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
- /// Abstraction of a call to be invoked on a client.
+ /// Details about a client-side call to be invoked.
/// </summary>
- public class Call<TRequest, TResponse>
+ public class CallInvocationDetails<TRequest, TResponse>
{
- readonly string name;
+ readonly Channel channel;
+ readonly string method;
+ readonly string host;
readonly Marshaller<TRequest> requestMarshaller;
readonly Marshaller<TResponse> responseMarshaller;
- readonly Channel channel;
- readonly Metadata headers;
- readonly DateTime deadline;
+ readonly CallOptions options;
- public Call(string serviceName, Method<TRequest, TResponse> method, Channel channel, Metadata headers)
- : this(serviceName, method, channel, headers, DateTime.MaxValue)
+ public CallInvocationDetails(Channel channel, Method<TRequest, TResponse> method, CallOptions options) :
+ this(channel, method.FullName, null, method.RequestMarshaller, method.ResponseMarshaller, options)
{
}
- public Call(string serviceName, Method<TRequest, TResponse> method, Channel channel, Metadata headers, DateTime deadline)
+ public CallInvocationDetails(Channel channel, string method, string host, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller, CallOptions options)
{
- this.name = method.GetFullName(serviceName);
- this.requestMarshaller = method.RequestMarshaller;
- this.responseMarshaller = method.ResponseMarshaller;
this.channel = Preconditions.CheckNotNull(channel);
- this.headers = Preconditions.CheckNotNull(headers);
- this.deadline = deadline;
+ this.method = Preconditions.CheckNotNull(method);
+ this.host = host;
+ this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller);
+ this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller);
+ this.options = Preconditions.CheckNotNull(options);
}
public Channel Channel
@@ -72,49 +72,43 @@ namespace Grpc.Core
}
}
- /// <summary>
- /// Full methods name including the service name.
- /// </summary>
- public string Name
+ public string Method
{
get
{
- return name;
+ return this.method;
}
}
- /// <summary>
- /// Headers to send at the beginning of the call.
- /// </summary>
- public Metadata Headers
+ public string Host
{
get
{
- return headers;
+ return this.host;
}
}
- public DateTime Deadline
+ public Marshaller<TRequest> RequestMarshaller
{
get
{
- return this.deadline;
+ return this.requestMarshaller;
}
}
- public Marshaller<TRequest> RequestMarshaller
+ public Marshaller<TResponse> ResponseMarshaller
{
get
{
- return requestMarshaller;
+ return this.responseMarshaller;
}
}
-
- public Marshaller<TResponse> ResponseMarshaller
+
+ public CallOptions Options
{
get
{
- return responseMarshaller;
+ return options;
}
}
}
diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs
new file mode 100644
index 0000000000..8e9739335f
--- /dev/null
+++ b/src/csharp/Grpc.Core/CallOptions.cs
@@ -0,0 +1,89 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Threading;
+
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core
+{
+ /// <summary>
+ /// Options for calls made by client.
+ /// </summary>
+ public class CallOptions
+ {
+ readonly Metadata headers;
+ readonly DateTime deadline;
+ readonly CancellationToken cancellationToken;
+
+ /// <summary>
+ /// Creates a new instance of <c>CallOptions</c>.
+ /// </summary>
+ /// <param name="headers">Headers to be sent with the call.</param>
+ /// <param name="deadline">Deadline for the call to finish. null means no deadline.</param>
+ /// <param name="cancellationToken">Can be used to request cancellation of the call.</param>
+ public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ // TODO(jtattermusch): consider only creating metadata object once it's really needed.
+ this.headers = headers != null ? headers : new Metadata();
+ this.deadline = deadline.HasValue ? deadline.Value : DateTime.MaxValue;
+ this.cancellationToken = cancellationToken;
+ }
+
+ /// <summary>
+ /// Headers to send at the beginning of the call.
+ /// </summary>
+ public Metadata Headers
+ {
+ get { return headers; }
+ }
+
+ /// <summary>
+ /// Call deadline.
+ /// </summary>
+ public DateTime Deadline
+ {
+ get { return deadline; }
+ }
+
+ /// <summary>
+ /// Token that can be used for cancelling the call.
+ /// </summary>
+ public CancellationToken CancellationToken
+ {
+ get { return cancellationToken; }
+ }
+ }
+}
diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs
index 054fc27491..00a8cabf82 100644
--- a/src/csharp/Grpc.Core/Calls.cs
+++ b/src/csharp/Grpc.Core/Calls.cs
@@ -43,70 +43,52 @@ namespace Grpc.Core
/// </summary>
public static class Calls
{
- public static TResponse BlockingUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
+ public static TResponse BlockingUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req)
where TRequest : class
where TResponse : class
{
- var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- // TODO(jtattermusch): this gives a race that cancellation can be requested before the call even starts.
- RegisterCancellationCallback(asyncCall, token);
- return asyncCall.UnaryCall(call.Channel, call.Name, req, call.Headers, call.Deadline);
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call);
+ return asyncCall.UnaryCall(req);
}
- public static AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
+ public static AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req)
where TRequest : class
where TResponse : class
{
- var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline));
- var asyncResult = asyncCall.UnaryCallAsync(req, call.Headers, call.Deadline);
- RegisterCancellationCallback(asyncCall, token);
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call);
+ var asyncResult = asyncCall.UnaryCallAsync(req);
return new AsyncUnaryCall<TResponse>(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
- public static AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
+ public static AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req)
where TRequest : class
where TResponse : class
{
- var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline));
- asyncCall.StartServerStreamingCall(req, call.Headers, call.Deadline);
- RegisterCancellationCallback(asyncCall, token);
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call);
+ asyncCall.StartServerStreamingCall(req);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
return new AsyncServerStreamingCall<TResponse>(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
- public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token)
+ public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call)
where TRequest : class
where TResponse : class
{
- var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline));
- var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers, call.Deadline);
- RegisterCancellationCallback(asyncCall, token);
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call);
+ var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
- public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token)
+ public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call)
where TRequest : class
where TResponse : class
{
- var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
- asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline));
- asyncCall.StartDuplexStreamingCall(call.Headers, call.Deadline);
- RegisterCancellationCallback(asyncCall, token);
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call);
+ asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
return new AsyncDuplexStreamingCall<TRequest, TResponse>(requestStream, responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
-
- private static void RegisterCancellationCallback<TRequest, TResponse>(AsyncCall<TRequest, TResponse> asyncCall, CancellationToken token)
- {
- if (token.CanBeCanceled)
- {
- token.Register(() => asyncCall.Cancel());
- }
- }
}
}
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index 0b69610443..9273ea4582 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -178,22 +178,6 @@ namespace Grpc.Core
}
}
- internal CompletionQueueSafeHandle CompletionQueue
- {
- get
- {
- return this.environment.CompletionQueue;
- }
- }
-
- internal CompletionRegistry CompletionRegistry
- {
- get
- {
- return this.environment.CompletionRegistry;
- }
- }
-
internal GrpcEnvironment Environment
{
get
diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs
index fd3473128a..88494bb4ac 100644
--- a/src/csharp/Grpc.Core/ClientBase.cs
+++ b/src/csharp/Grpc.Core/ClientBase.cs
@@ -76,19 +76,17 @@ namespace Grpc.Core
/// <summary>
/// Creates a new call to given method.
/// </summary>
- protected Call<TRequest, TResponse> CreateCall<TRequest, TResponse>(string serviceName, Method<TRequest, TResponse> method, Metadata metadata, DateTime? deadline)
+ protected CallInvocationDetails<TRequest, TResponse> CreateCall<TRequest, TResponse>(Method<TRequest, TResponse> method, CallOptions options)
where TRequest : class
where TResponse : class
{
var interceptor = HeaderInterceptor;
if (interceptor != null)
{
- metadata = metadata ?? new Metadata();
- interceptor(metadata);
- metadata.Freeze();
+ interceptor(options.Headers);
+ options.Headers.Freeze();
}
- return new Call<TRequest, TResponse>(serviceName, method, channel,
- metadata ?? Metadata.Empty, deadline ?? DateTime.MaxValue);
+ return new CallInvocationDetails<TRequest, TResponse>(channel, method, options);
}
}
}
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 17add77164..52defd1965 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -57,7 +57,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RpcException.cs" />
<Compile Include="Calls.cs" />
- <Compile Include="Call.cs" />
<Compile Include="AsyncClientStreamingCall.cs" />
<Compile Include="GrpcEnvironment.cs" />
<Compile Include="Status.cs" />
@@ -114,6 +113,8 @@
<Compile Include="Logging\ConsoleLogger.cs" />
<Compile Include="Internal\NativeLogRedirector.cs" />
<Compile Include="ChannelState.cs" />
+ <Compile Include="CallInvocationDetails.cs" />
+ <Compile Include="CallOptions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Grpc.Core.nuspec" />
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index 48f466460f..414b5c4282 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -50,7 +50,7 @@ namespace Grpc.Core.Internal
{
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCall<TRequest, TResponse>>();
- Channel channel;
+ readonly CallInvocationDetails<TRequest, TResponse> callDetails;
// Completion of a pending unary response if not null.
TaskCompletionSource<TResponse> unaryResponseTcs;
@@ -60,26 +60,18 @@ namespace Grpc.Core.Internal
bool readObserverCompleted; // True if readObserver has already been completed.
- public AsyncCall(Func<TRequest, byte[]> serializer, Func<byte[], TResponse> deserializer) : base(serializer, deserializer)
+ public AsyncCall(CallInvocationDetails<TRequest, TResponse> callDetails)
+ : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer)
{
- }
-
- public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName, Timespec deadline)
- {
- this.channel = channel;
- var call = channel.Handle.CreateCall(channel.CompletionRegistry, cq, methodName, null, deadline);
- channel.Environment.DebugStats.ActiveClientCalls.Increment();
- InitializeInternal(call);
+ this.callDetails = callDetails;
}
// TODO: this method is not Async, so it shouldn't be in AsyncCall class, but
// it is reusing fair amount of code in this class, so we are leaving it here.
- // TODO: for other calls, you need to call Initialize, this methods calls initialize
- // on its own, so there's a usage inconsistency.
/// <summary>
/// Blocking unary request - unary response call.
/// </summary>
- public TResponse UnaryCall(Channel channel, string methodName, TRequest msg, Metadata headers, DateTime deadline)
+ public TResponse UnaryCall(TRequest msg)
{
using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create())
{
@@ -89,13 +81,15 @@ namespace Grpc.Core.Internal
lock (myLock)
{
- Initialize(channel, cq, methodName, Timespec.FromDateTime(deadline));
+ Preconditions.CheckState(!started);
started = true;
+ Initialize(cq);
+
halfcloseRequested = true;
readingDone = true;
}
- using (var metadataArray = MetadataArraySafeHandle.Create(headers))
+ using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
{
using (var ctx = BatchContextSafeHandle.Create())
{
@@ -129,20 +123,22 @@ namespace Grpc.Core.Internal
/// <summary>
/// Starts a unary request - unary response call.
/// </summary>
- public Task<TResponse> UnaryCallAsync(TRequest msg, Metadata headers, DateTime deadline)
+ public Task<TResponse> UnaryCallAsync(TRequest msg)
{
lock (myLock)
{
- Preconditions.CheckNotNull(call);
-
+ Preconditions.CheckState(!started);
started = true;
+
+ Initialize(callDetails.Channel.Environment.CompletionQueue);
+
halfcloseRequested = true;
readingDone = true;
byte[] payload = UnsafeSerialize(msg);
unaryResponseTcs = new TaskCompletionSource<TResponse>();
- using (var metadataArray = MetadataArraySafeHandle.Create(headers))
+ using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
{
call.StartUnary(payload, HandleUnaryResponse, metadataArray);
}
@@ -154,17 +150,19 @@ namespace Grpc.Core.Internal
/// Starts a streamed request - unary response call.
/// Use StartSendMessage and StartSendCloseFromClient to stream requests.
/// </summary>
- public Task<TResponse> ClientStreamingCallAsync(Metadata headers, DateTime deadline)
+ public Task<TResponse> ClientStreamingCallAsync()
{
lock (myLock)
{
- Preconditions.CheckNotNull(call);
-
+ Preconditions.CheckState(!started);
started = true;
+
+ Initialize(callDetails.Channel.Environment.CompletionQueue);
+
readingDone = true;
unaryResponseTcs = new TaskCompletionSource<TResponse>();
- using (var metadataArray = MetadataArraySafeHandle.Create(headers))
+ using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
{
call.StartClientStreaming(HandleUnaryResponse, metadataArray);
}
@@ -176,19 +174,21 @@ namespace Grpc.Core.Internal
/// <summary>
/// Starts a unary request - streamed response call.
/// </summary>
- public void StartServerStreamingCall(TRequest msg, Metadata headers, DateTime deadline)
+ public void StartServerStreamingCall(TRequest msg)
{
lock (myLock)
{
- Preconditions.CheckNotNull(call);
-
+ Preconditions.CheckState(!started);
started = true;
+
+ Initialize(callDetails.Channel.Environment.CompletionQueue);
+
halfcloseRequested = true;
halfclosed = true; // halfclose not confirmed yet, but it will be once finishedHandler is called.
byte[] payload = UnsafeSerialize(msg);
- using (var metadataArray = MetadataArraySafeHandle.Create(headers))
+ using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
{
call.StartServerStreaming(payload, HandleFinished, metadataArray);
}
@@ -199,15 +199,16 @@ namespace Grpc.Core.Internal
/// Starts a streaming request - streaming response call.
/// Use StartSendMessage and StartSendCloseFromClient to stream requests.
/// </summary>
- public void StartDuplexStreamingCall(Metadata headers, DateTime deadline)
+ public void StartDuplexStreamingCall()
{
lock (myLock)
{
- Preconditions.CheckNotNull(call);
-
+ Preconditions.CheckState(!started);
started = true;
- using (var metadataArray = MetadataArraySafeHandle.Create(headers))
+ Initialize(callDetails.Channel.Environment.CompletionQueue);
+
+ using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
{
call.StartDuplexStreaming(HandleFinished, metadataArray);
}
@@ -309,7 +310,26 @@ namespace Grpc.Core.Internal
protected override void OnReleaseResources()
{
- channel.Environment.DebugStats.ActiveClientCalls.Decrement();
+ callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Decrement();
+ }
+
+ private void Initialize(CompletionQueueSafeHandle cq)
+ {
+ var call = callDetails.Channel.Handle.CreateCall(callDetails.Channel.Environment.CompletionRegistry, cq,
+ callDetails.Method, callDetails.Host, Timespec.FromDateTime(callDetails.Options.Deadline));
+ callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Increment();
+ InitializeInternal(call);
+ RegisterCancellationCallback();
+ }
+
+ // Make sure that once cancellationToken for this call is cancelled, Cancel() will be called.
+ private void RegisterCancellationCallback()
+ {
+ var token = callDetails.Options.CancellationToken;
+ if (token.CanBeCanceled)
+ {
+ token.Register(() => this.Cancel());
+ }
}
/// <summary>
diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs
index 77d36191c3..cc047ac9f8 100644
--- a/src/csharp/Grpc.Core/Method.cs
+++ b/src/csharp/Grpc.Core/Method.cs
@@ -53,16 +53,20 @@ namespace Grpc.Core
public class Method<TRequest, TResponse>
{
readonly MethodType type;
+ readonly string serviceName;
readonly string name;
readonly Marshaller<TRequest> requestMarshaller;
readonly Marshaller<TResponse> responseMarshaller;
+ readonly string fullName;
- public Method(MethodType type, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller)
+ public Method(MethodType type, string serviceName, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller)
{
this.type = type;
- this.name = name;
- this.requestMarshaller = requestMarshaller;
- this.responseMarshaller = responseMarshaller;
+ this.serviceName = Preconditions.CheckNotNull(serviceName);
+ this.name = Preconditions.CheckNotNull(name);
+ this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller);
+ this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller);
+ this.fullName = GetFullName(serviceName);
}
public MethodType Type
@@ -72,6 +76,14 @@ namespace Grpc.Core
return this.type;
}
}
+
+ public string ServiceName
+ {
+ get
+ {
+ return this.serviceName;
+ }
+ }
public string Name
{
@@ -97,6 +109,14 @@ namespace Grpc.Core
}
}
+ public string FullName
+ {
+ get
+ {
+ return this.fullName;
+ }
+ }
+
/// <summary>
/// Gets full name of the method including the service name.
/// </summary>
diff --git a/src/csharp/Grpc.Core/ServerCallContext.cs b/src/csharp/Grpc.Core/ServerCallContext.cs
index 0c48adaea5..032b1390db 100644
--- a/src/csharp/Grpc.Core/ServerCallContext.cs
+++ b/src/csharp/Grpc.Core/ServerCallContext.cs
@@ -65,7 +65,7 @@ namespace Grpc.Core
this.cancellationToken = cancellationToken;
}
- /// <summary> Name of method called in this RPC. </summary>
+ /// <summary>Name of method called in this RPC.</summary>
public string Method
{
get
@@ -74,7 +74,7 @@ namespace Grpc.Core
}
}
- /// <summary> Name of host called in this RPC. </summary>
+ /// <summary>Name of host called in this RPC.</summary>
public string Host
{
get
@@ -83,7 +83,7 @@ namespace Grpc.Core
}
}
- /// <summary> Address of the remote endpoint in URI format. </summary>
+ /// <summary>Address of the remote endpoint in URI format.</summary>
public string Peer
{
get
@@ -92,7 +92,7 @@ namespace Grpc.Core
}
}
- /// <summary> Deadline for this RPC. </summary>
+ /// <summary>Deadline for this RPC.</summary>
public DateTime Deadline
{
get
@@ -101,7 +101,7 @@ namespace Grpc.Core
}
}
- /// <summary> Initial metadata sent by client. </summary>
+ /// <summary>Initial metadata sent by client.</summary>
public Metadata RequestHeaders
{
get
@@ -110,8 +110,7 @@ namespace Grpc.Core
}
}
- // TODO(jtattermusch): support signalling cancellation.
- /// <summary> Cancellation token signals when call is cancelled. </summary>
+ ///<summary>Cancellation token signals when call is cancelled.</summary>
public CancellationToken CancellationToken
{
get
@@ -120,7 +119,7 @@ namespace Grpc.Core
}
}
- /// <summary> Trailers to send back to client after RPC finishes.</summary>
+ /// <summary>Trailers to send back to client after RPC finishes.</summary>
public Metadata ResponseTrailers
{
get
diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs
index 67827e7b4f..4941ff35f7 100644
--- a/src/csharp/Grpc.Examples/MathGrpc.cs
+++ b/src/csharp/Grpc.Examples/MathGrpc.cs
@@ -19,24 +19,28 @@ namespace math {
static readonly Method<global::math.DivArgs, global::math.DivReply> __Method_Div = new Method<global::math.DivArgs, global::math.DivReply>(
MethodType.Unary,
+ __ServiceName,
"Div",
__Marshaller_DivArgs,
__Marshaller_DivReply);
static readonly Method<global::math.DivArgs, global::math.DivReply> __Method_DivMany = new Method<global::math.DivArgs, global::math.DivReply>(
MethodType.DuplexStreaming,
+ __ServiceName,
"DivMany",
__Marshaller_DivArgs,
__Marshaller_DivReply);
static readonly Method<global::math.FibArgs, global::math.Num> __Method_Fib = new Method<global::math.FibArgs, global::math.Num>(
MethodType.ServerStreaming,
+ __ServiceName,
"Fib",
__Marshaller_FibArgs,
__Marshaller_Num);
static readonly Method<global::math.Num, global::math.Num> __Method_Sum = new Method<global::math.Num, global::math.Num>(
MethodType.ClientStreaming,
+ __ServiceName,
"Sum",
__Marshaller_Num,
__Marshaller_Num);
@@ -45,10 +49,15 @@ namespace math {
public interface IMathClient
{
global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ global::math.DivReply Div(global::math.DivArgs request, CallOptions options);
AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, CallOptions options);
AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(CallOptions options);
AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, CallOptions options);
AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(CallOptions options);
}
// server-side interface
@@ -68,28 +77,53 @@ namespace math {
}
public global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_Div, headers, deadline);
- return Calls.BlockingUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_Div, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.BlockingUnaryCall(call, request);
+ }
+ public global::math.DivReply Div(global::math.DivArgs request, CallOptions options)
+ {
+ var call = CreateCall(__Method_Div, options);
+ return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_Div, headers, deadline);
- return Calls.AsyncUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_Div, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncUnaryCall(call, request);
+ }
+ public AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, CallOptions options)
+ {
+ var call = CreateCall(__Method_Div, options);
+ return Calls.AsyncUnaryCall(call, request);
}
public AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_DivMany, headers, deadline);
- return Calls.AsyncDuplexStreamingCall(call, cancellationToken);
+ var call = CreateCall(__Method_DivMany, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncDuplexStreamingCall(call);
+ }
+ public AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(CallOptions options)
+ {
+ var call = CreateCall(__Method_DivMany, options);
+ return Calls.AsyncDuplexStreamingCall(call);
}
public AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_Fib, headers, deadline);
- return Calls.AsyncServerStreamingCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_Fib, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncServerStreamingCall(call, request);
+ }
+ public AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, CallOptions options)
+ {
+ var call = CreateCall(__Method_Fib, options);
+ return Calls.AsyncServerStreamingCall(call, request);
}
public AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_Sum, headers, deadline);
- return Calls.AsyncClientStreamingCall(call, cancellationToken);
+ var call = CreateCall(__Method_Sum, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncClientStreamingCall(call);
+ }
+ public AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(CallOptions options)
+ {
+ var call = CreateCall(__Method_Sum, options);
+ return Calls.AsyncClientStreamingCall(call);
}
}
diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
index 892cdb3f04..0dabc91f7c 100644
--- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
+++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
@@ -17,6 +17,7 @@ namespace Grpc.Health.V1Alpha {
static readonly Method<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckResponse> __Method_Check = new Method<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckResponse>(
MethodType.Unary,
+ __ServiceName,
"Check",
__Marshaller_HealthCheckRequest,
__Marshaller_HealthCheckResponse);
@@ -25,7 +26,9 @@ namespace Grpc.Health.V1Alpha {
public interface IHealthClient
{
global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options);
AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options);
}
// server-side interface
@@ -42,13 +45,23 @@ namespace Grpc.Health.V1Alpha {
}
public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_Check, headers, deadline);
- return Calls.BlockingUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_Check, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.BlockingUnaryCall(call, request);
+ }
+ public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options)
+ {
+ var call = CreateCall(__Method_Check, options);
+ return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_Check, headers, deadline);
- return Calls.AsyncUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_Check, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncUnaryCall(call, request);
+ }
+ public AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options)
+ {
+ var call = CreateCall(__Method_Check, options);
+ return Calls.AsyncUnaryCall(call, request);
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
index ddcd0c2958..697acb53d8 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
@@ -22,36 +22,42 @@ namespace grpc.testing {
static readonly Method<global::grpc.testing.Empty, global::grpc.testing.Empty> __Method_EmptyCall = new Method<global::grpc.testing.Empty, global::grpc.testing.Empty>(
MethodType.Unary,
+ __ServiceName,
"EmptyCall",
__Marshaller_Empty,
__Marshaller_Empty);
static readonly Method<global::grpc.testing.SimpleRequest, global::grpc.testing.SimpleResponse> __Method_UnaryCall = new Method<global::grpc.testing.SimpleRequest, global::grpc.testing.SimpleResponse>(
MethodType.Unary,
+ __ServiceName,
"UnaryCall",
__Marshaller_SimpleRequest,
__Marshaller_SimpleResponse);
static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_StreamingOutputCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>(
MethodType.ServerStreaming,
+ __ServiceName,
"StreamingOutputCall",
__Marshaller_StreamingOutputCallRequest,
__Marshaller_StreamingOutputCallResponse);
static readonly Method<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> __Method_StreamingInputCall = new Method<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse>(
MethodType.ClientStreaming,
+ __ServiceName,
"StreamingInputCall",
__Marshaller_StreamingInputCallRequest,
__Marshaller_StreamingInputCallResponse);
static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_FullDuplexCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>(
MethodType.DuplexStreaming,
+ __ServiceName,
"FullDuplexCall",
__Marshaller_StreamingOutputCallRequest,
__Marshaller_StreamingOutputCallResponse);
static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_HalfDuplexCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>(
MethodType.DuplexStreaming,
+ __ServiceName,
"HalfDuplexCall",
__Marshaller_StreamingOutputCallRequest,
__Marshaller_StreamingOutputCallResponse);
@@ -60,13 +66,21 @@ namespace grpc.testing {
public interface ITestServiceClient
{
global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CallOptions options);
AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, CallOptions options);
global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CallOptions options);
AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, CallOptions options);
AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CallOptions options);
AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(CallOptions options);
AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(CallOptions options);
AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+ AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(CallOptions options);
}
// server-side interface
@@ -88,43 +102,83 @@ namespace grpc.testing {
}
public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_EmptyCall, headers, deadline);
- return Calls.BlockingUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_EmptyCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.BlockingUnaryCall(call, request);
+ }
+ public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CallOptions options)
+ {
+ var call = CreateCall(__Method_EmptyCall, options);
+ return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_EmptyCall, headers, deadline);
- return Calls.AsyncUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_EmptyCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncUnaryCall(call, request);
+ }
+ public AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, CallOptions options)
+ {
+ var call = CreateCall(__Method_EmptyCall, options);
+ return Calls.AsyncUnaryCall(call, request);
}
public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_UnaryCall, headers, deadline);
- return Calls.BlockingUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.BlockingUnaryCall(call, request);
+ }
+ public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CallOptions options)
+ {
+ var call = CreateCall(__Method_UnaryCall, options);
+ return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_UnaryCall, headers, deadline);
- return Calls.AsyncUnaryCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncUnaryCall(call, request);
+ }
+ public AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, CallOptions options)
+ {
+ var call = CreateCall(__Method_UnaryCall, options);
+ return Calls.AsyncUnaryCall(call, request);
}
public AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_StreamingOutputCall, headers, deadline);
- return Calls.AsyncServerStreamingCall(call, request, cancellationToken);
+ var call = CreateCall(__Method_StreamingOutputCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncServerStreamingCall(call, request);
+ }
+ public AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CallOptions options)
+ {
+ var call = CreateCall(__Method_StreamingOutputCall, options);
+ return Calls.AsyncServerStreamingCall(call, request);
}
public AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_StreamingInputCall, headers, deadline);
- return Calls.AsyncClientStreamingCall(call, cancellationToken);
+ var call = CreateCall(__Method_StreamingInputCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncClientStreamingCall(call);
+ }
+ public AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(CallOptions options)
+ {
+ var call = CreateCall(__Method_StreamingInputCall, options);
+ return Calls.AsyncClientStreamingCall(call);
}
public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_FullDuplexCall, headers, deadline);
- return Calls.AsyncDuplexStreamingCall(call, cancellationToken);
+ var call = CreateCall(__Method_FullDuplexCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncDuplexStreamingCall(call);
+ }
+ public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(CallOptions options)
+ {
+ var call = CreateCall(__Method_FullDuplexCall, options);
+ return Calls.AsyncDuplexStreamingCall(call);
}
public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
- var call = CreateCall(__ServiceName, __Method_HalfDuplexCall, headers, deadline);
- return Calls.AsyncDuplexStreamingCall(call, cancellationToken);
+ var call = CreateCall(__Method_HalfDuplexCall, new CallOptions(headers, deadline, cancellationToken));
+ return Calls.AsyncDuplexStreamingCall(call);
+ }
+ public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(CallOptions options)
+ {
+ var call = CreateCall(__Method_HalfDuplexCall, options);
+ return Calls.AsyncDuplexStreamingCall(call);
}
}
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 37864a62a5..048887bc12 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -379,7 +379,8 @@ GPR_EXPORT grpc_call *GPR_CALLTYPE
grpcsharp_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq,
const char *method, const char *host,
gpr_timespec deadline) {
- return grpc_channel_create_call(channel, cq, method, host, deadline);
+ return grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+ method, host, deadline);
}
GPR_EXPORT grpc_connectivity_state GPR_CALLTYPE
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index dc45c8d8ae..fe585a0d4f 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -511,8 +511,9 @@ NAN_METHOD(Call::New) {
double deadline = args[2]->NumberValue();
grpc_channel *wrapped_channel = channel->GetWrappedChannel();
grpc_call *wrapped_call = grpc_channel_create_call(
- wrapped_channel, CompletionQueueAsyncWorker::GetQueue(), *method,
- channel->GetHost(), MillisecondsToTimespec(deadline));
+ wrapped_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
+ CompletionQueueAsyncWorker::GetQueue(), *method, channel->GetHost(),
+ MillisecondsToTimespec(deadline));
call = new Call(wrapped_call);
args.This()->SetHiddenValue(NanNew("channel_"), channel_object);
}
diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h
new file mode 100644
index 0000000000..3d617b05d9
--- /dev/null
+++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import "GRPCCall.h"
+
+@interface GRPCCall (Tests)
+
+// Establish all SSL connections to the provided host using the passed SSL target name and the root
+// certificates found in the file at |certsPath|.
+// Must be called before any gRPC call to that host is made.
++ (void)useTestCertsPath:(NSString *)certsPath
+ testName:(NSString *)testName
+ forHost:(NSString *)host;
+
+@end
diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m
new file mode 100644
index 0000000000..7c5b81d661
--- /dev/null
+++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import "GRPCCall+Tests.h"
+
+#import "private/GRPCHost.h"
+
+@implementation GRPCCall (Tests)
++ (void)useTestCertsPath:(NSString *)certsPath
+ testName:(NSString *)testName
+ forHost:(NSString *)host {
+ GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
+ hostConfig.secure = YES;
+ hostConfig.pathToCertificates = certsPath;
+ hostConfig.hostNameOverride = testName;
+}
+@end
diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m
index 9d9648ae28..5f7d74bca8 100644
--- a/src/objective-c/GRPCClient/GRPCCall.m
+++ b/src/objective-c/GRPCClient/GRPCCall.m
@@ -37,7 +37,6 @@
#include <grpc/support/time.h>
#import <RxLibrary/GRXConcurrentWriteable.h>
-#import "private/GRPCChannel.h"
#import "private/GRPCWrappedCall.h"
#import "private/NSData+GRPC.h"
#import "private/NSDictionary+GRPC.h"
@@ -70,8 +69,6 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey";
GRPCWrappedCall *_wrappedCall;
dispatch_once_t _callAlreadyInvoked;
- GRPCChannel *_channel;
-
// The C gRPC library has less guarantees on the ordering of events than we
// do. Particularly, in the face of errors, there's no ordering guarantee at
// all. This wrapper over our actual writeable ensures thread-safety and
@@ -105,11 +102,10 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey";
format:@"The requests writer can't be already started."];
}
if ((self = [super init])) {
- _channel = [GRPCChannel channelToHost:host];
-
- _wrappedCall = [[GRPCWrappedCall alloc] initWithChannel:_channel
- path:path
- host:host];
+ _wrappedCall = [[GRPCWrappedCall alloc] initWithHost:host path:path];
+ if (!_wrappedCall) {
+ return nil;
+ }
// Serial queue to invoke the non-reentrant methods of the grpc_call object.
_callQueue = dispatch_queue_create("org.grpc.call", NULL);
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h
index bc6a47d469..2a7b701576 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.h
@@ -35,17 +35,12 @@
struct grpc_channel;
-// Each separate instance of this class represents at least one TCP
-// connection to the provided host. To create a grpc_call, pass the
-// value of the unmanagedChannel property to grpc_channel_create_call.
-// Release this object when the call is finished.
+// Each separate instance of this class represents at least one TCP connection to the provided host.
+// Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|.
@interface GRPCChannel : NSObject
@property(nonatomic, readonly) struct grpc_channel *unmanagedChannel;
-// Convenience constructor to allow for reuse of connections.
-+ (instancetype)channelToHost:(NSString *)host;
-
-- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER;
-
+// This initializer takes ownership of the passed channel, and will destroy it when this object is
+// deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects.
- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER;
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m
index af4326332f..4366e63320 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.m
@@ -35,53 +35,17 @@
#include <grpc/grpc.h>
-#import "GRPCSecureChannel.h"
-#import "GRPCUnsecuredChannel.h"
-
@implementation GRPCChannel
-+ (instancetype)channelToHost:(NSString *)host {
- // TODO(mlumish): Investigate whether a cache with strong links is a good idea
- static NSMutableDictionary *channelCache;
- static dispatch_once_t cacheInitialization;
- dispatch_once(&cacheInitialization, ^{
- channelCache = [NSMutableDictionary dictionary];
- });
- GRPCChannel *channel = channelCache[host];
- if (!channel) {
- channel = [[self alloc] initWithHost:host];
- channelCache[host] = channel;
- }
- return channel;
-}
-
- (instancetype)init {
- return [self initWithHost:nil];
+ return [self initWithChannel:NULL];
}
-- (instancetype)initWithHost:(NSString *)host {
- if (![host rangeOfString:@"://"].length) {
- // No scheme provided; assume https.
- host = [@"https://" stringByAppendingString:host];
- }
- NSURL *hostURL = [NSURL URLWithString:host];
- if (!hostURL) {
- [NSException raise:NSInvalidArgumentException format:@"Invalid URL: %@", host];
+// Designated initializer
+- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel {
+ if (!unmanagedChannel) {
+ return nil;
}
- if ([hostURL.scheme isEqualToString:@"https"]) {
- host = [@[hostURL.host, hostURL.port ?: @443] componentsJoinedByString:@":"];
- return [[GRPCSecureChannel alloc] initWithHost:host];
- }
- if ([hostURL.scheme isEqualToString:@"http"]) {
- host = [@[hostURL.host, hostURL.port ?: @80] componentsJoinedByString:@":"];
- return [[GRPCUnsecuredChannel alloc] initWithHost:host];
- }
- [NSException raise:NSInvalidArgumentException
- format:@"URL scheme %@ isn't supported.", hostURL.scheme];
- return nil; // silence warning.
-}
-
-- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel {
if ((self = [super init])) {
_unmanagedChannel = unmanagedChannel;
}
@@ -89,12 +53,8 @@
}
- (void)dealloc {
- // _unmanagedChannel is NULL when deallocating an object of the base class (because the
- // initializer returns a different object).
- if (_unmanagedChannel) {
- // TODO(jcanizales): Be sure to add a test with a server that closes the connection prematurely,
- // as in the past that made this call to crash.
- grpc_channel_destroy(_unmanagedChannel);
- }
+ // TODO(jcanizales): Be sure to add a test with a server that closes the connection prematurely,
+ // as in the past that made this call to crash.
+ grpc_channel_destroy(_unmanagedChannel);
}
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m
index 12535c9616..696069c200 100644
--- a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m
+++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m
@@ -38,8 +38,6 @@
@implementation GRPCCompletionQueue
+ (instancetype)completionQueue {
- // TODO(jcanizales): Reuse completion queues to consume only one thread,
- // instead of one per call.
return [[self alloc] init];
}
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h
new file mode 100644
index 0000000000..f0bbd53023
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCHost.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+@class GRPCCompletionQueue;
+struct grpc_call;
+
+@interface GRPCHost : NSObject
+
+@property(nonatomic, readonly) NSString *address;
+
+// The following properties should only be modified for testing:
+
+@property(nonatomic, getter=isSecure) BOOL secure;
+
+@property(nonatomic, copy) NSString *pathToCertificates;
+@property(nonatomic, copy) NSString *hostNameOverride;
+
+// Host objects initialized with the same address are the same.
++ (instancetype)hostWithAddress:(NSString *)address;
+- (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
+
+// Create a grpc_call object to the provided path on this host.
+- (struct grpc_call *)unmanagedCallWithPath:(NSString *)path
+ completionQueue:(GRPCCompletionQueue *)queue;
+
+@end
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
new file mode 100644
index 0000000000..5d9c48a524
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -0,0 +1,134 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import "GRPCHost.h"
+
+#include <grpc/grpc.h>
+
+#import "GRPCChannel.h"
+#import "GRPCCompletionQueue.h"
+#import "GRPCSecureChannel.h"
+#import "GRPCUnsecuredChannel.h"
+
+@interface GRPCHost ()
+// TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
+@property(nonatomic, strong) GRPCChannel *channel;
+@end
+
+@implementation GRPCHost
+
++ (instancetype)hostWithAddress:(NSString *)address {
+ return [[self alloc] initWithAddress:address];
+}
+
+- (instancetype)init {
+ return [self initWithAddress:nil];
+}
+
+// Default initializer.
+- (instancetype)initWithAddress:(NSString *)address {
+
+ // Verify and normalize the address, and decide whether to use SSL.
+ if (![address rangeOfString:@"://"].length) {
+ // No scheme provided; assume https.
+ address = [@"https://" stringByAppendingString:address];
+ }
+ NSURL *hostURL = [NSURL URLWithString:address];
+ if (!hostURL) {
+ [NSException raise:NSInvalidArgumentException format:@"Invalid URL: %@", address];
+ }
+ NSString *scheme = hostURL.scheme;
+ if (![scheme isEqualToString:@"https"] && ![scheme isEqualToString:@"http"]) {
+ [NSException raise:NSInvalidArgumentException format:@"URL scheme %@ isn't supported.", scheme];
+ }
+ // If the user didn't specify a port (hostURL.port is nil), provide a default one.
+ NSNumber *port = hostURL.port ?: [scheme isEqualToString:@"https"] ? @443 : @80;
+ address = [@[hostURL.host, port] componentsJoinedByString:@":"];
+
+ // Look up the GRPCHost in the cache.
+ static NSMutableDictionary *hostCache;
+ static dispatch_once_t cacheInitialization;
+ dispatch_once(&cacheInitialization, ^{
+ hostCache = [NSMutableDictionary dictionary];
+ });
+ @synchronized(hostCache) {
+ GRPCHost *cachedHost = hostCache[address];
+ if (cachedHost) {
+ // We could verify here that the cached host uses the same protocol that we're expecting. But
+ // creating non-SSL channels by adding "http://" to the address is going away (to make the use
+ // of insecure channels less subtle), so it's not worth it now.
+ return cachedHost;
+ }
+
+ if ((self = [super init])) {
+ _address = address;
+ _secure = [scheme isEqualToString:@"https"];
+ hostCache[address] = self;
+ }
+ return self;
+ }
+}
+
+- (grpc_call *)unmanagedCallWithPath:(NSString *)path completionQueue:(GRPCCompletionQueue *)queue {
+ if (!queue || !path || !self.channel) {
+ return NULL;
+ }
+ return grpc_channel_create_call(self.channel.unmanagedChannel,
+ NULL, GRPC_PROPAGATE_DEFAULTS,
+ queue.unmanagedQueue,
+ path.UTF8String,
+ self.hostName.UTF8String,
+ gpr_inf_future(GPR_CLOCK_REALTIME));
+}
+
+- (GRPCChannel *)channel {
+ // Create it lazily, because we don't want to open a connection just because someone is
+ // configuring a host.
+ if (!_channel) {
+ if (_secure) {
+ _channel = [[GRPCSecureChannel alloc] initWithHost:_address
+ pathToCertificates:_pathToCertificates
+ hostNameOverride:_hostNameOverride];
+ } else {
+ _channel = [[GRPCUnsecuredChannel alloc] initWithHost:_address];
+ }
+ }
+ return _channel;
+}
+
+- (NSString *)hostName {
+ // TODO(jcanizales): Default to nil instead of _address when Issue #2635 is clarified.
+ return _hostNameOverride ?: _address;
+}
+
+@end
diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
index d34ceaea0c..74257eb058 100644
--- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
@@ -31,8 +31,23 @@
*
*/
+#include <grpc/grpc.h>
+
#import "GRPCChannel.h"
+struct grpc_credentials;
+
@interface GRPCSecureChannel : GRPCChannel
+- (instancetype)initWithHost:(NSString *)host;
+
+// Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for
+// pathToCertificates results in using the default root certificates distributed with the library.
+- (instancetype)initWithHost:(NSString *)host
+ pathToCertificates:(NSString *)path
+ hostNameOverride:(NSString *)hostNameOverride;
+// The passed arguments aren't required to be valid beyond the invocation of this initializer.
+- (instancetype)initWithHost:(NSString *)host
+ credentials:(struct grpc_credentials *)credentials
+ args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER;
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m
index 43a8bd539e..9b4b6768f8 100644
--- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m
@@ -33,28 +33,80 @@
#import "GRPCSecureChannel.h"
-#import <grpc/grpc_security.h>
+#include <grpc/grpc_security.h>
+
+// Returns NULL if the file at path couldn't be read. In that case, if errorPtr isn't NULL,
+// *errorPtr will be an object describing what went wrong.
+static grpc_credentials *CertificatesAtPath(NSString *path, NSError **errorPtr) {
+ NSString *certsContent = [NSString stringWithContentsOfFile:path
+ encoding:NSASCIIStringEncoding
+ error:errorPtr];
+ if (!certsContent) {
+ // Passing NULL to grpc_ssl_credentials_create produces behavior we don't want, so return.
+ return NULL;
+ }
+ const char * asCString = [certsContent cStringUsingEncoding:NSASCIIStringEncoding];
+ return grpc_ssl_credentials_create(asCString, NULL);
+}
@implementation GRPCSecureChannel
- (instancetype)initWithHost:(NSString *)host {
- static grpc_credentials *kCredentials;
+ return [self initWithHost:host pathToCertificates:nil hostNameOverride:nil];
+}
+
+- (instancetype)initWithHost:(NSString *)host
+ pathToCertificates:(NSString *)path
+ hostNameOverride:(NSString *)hostNameOverride {
+ // Load default SSL certificates once.
+ static grpc_credentials *kDefaultCertificates;
static dispatch_once_t loading;
dispatch_once(&loading, ^{
+ NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem
// Do not use NSBundle.mainBundle, as it's nil for tests of library projects.
NSBundle *bundle = [NSBundle bundleForClass:self.class];
- NSString *certsPath = [bundle pathForResource:@"gRPCCertificates.bundle/roots" ofType:@"pem"];
- NSAssert(certsPath.length,
- @"gRPCCertificates.bundle/roots.pem not found under %@. This file, with the root "
- "certificates, is needed to establish TLS (HTTPS) connections.", bundle.bundlePath);
- NSData *certsData = [NSData dataWithContentsOfFile:certsPath];
- NSAssert(certsData.length, @"No data read from %@", certsPath);
- NSString *certsString = [[NSString alloc] initWithData:certsData encoding:NSUTF8StringEncoding];
- kCredentials = grpc_ssl_credentials_create(certsString.UTF8String, NULL);
+ NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"];
+ NSError *error;
+ kDefaultCertificates = CertificatesAtPath(path, &error);
+ NSAssert(kDefaultCertificates, @"Could not read %@/%@.pem. This file, with the root "
+ "certificates, is needed to establish secure (TLS) connections. Because the file is "
+ "distributed with the gRPC library, this error is usually a sign that the library "
+ "wasn't configured correctly for your project. Error: %@",
+ bundle.bundlePath, defaultPath, error);
});
- return (self = [super initWithChannel:grpc_secure_channel_create(kCredentials,
- host.UTF8String,
- NULL)]);
+
+ //TODO(jcanizales): Add NSError** parameter to the initializer.
+ grpc_credentials *certificates = path ? CertificatesAtPath(path, NULL) : kDefaultCertificates;
+ if (!certificates) {
+ return nil;
+ }
+
+ // Ritual to pass the SSL host name override to the C library.
+ grpc_channel_args channelArgs;
+ grpc_arg nameOverrideArg;
+ channelArgs.num_args = 1;
+ channelArgs.args = &nameOverrideArg;
+ nameOverrideArg.type = GRPC_ARG_STRING;
+ nameOverrideArg.key = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG;
+ // Cast const away. Hope C gRPC doesn't modify it!
+ nameOverrideArg.value.string = (char *) hostNameOverride.UTF8String;
+ grpc_channel_args *args = hostNameOverride ? &channelArgs : NULL;
+
+ return [self initWithHost:host credentials:certificates args:args];
+}
+
+- (instancetype)initWithHost:(NSString *)host
+ credentials:(grpc_credentials *)credentials
+ args:(grpc_channel_args *)args {
+ return (self =
+ [super initWithChannel:grpc_secure_channel_create(credentials, host.UTF8String, args)]);
+}
+
+// TODO(jcanizales): GRPCSecureChannel and GRPCUnsecuredChannel are just convenience initializers
+// for GRPCChannel. Move them into GRPCChannel, which will make the following unnecessary.
+- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel {
+ [NSException raise:NSInternalInconsistencyException format:@"use another initializer"];
+ return [self initWithHost:nil]; // silence warnings
}
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h
index 9d89cfb541..8528be44c0 100644
--- a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h
@@ -34,5 +34,5 @@
#import "GRPCChannel.h"
@interface GRPCUnsecuredChannel : GRPCChannel
-
+- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER;
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m
index 8518f78c5b..070a529629 100644
--- a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m
@@ -41,4 +41,10 @@
return (self = [super initWithChannel:grpc_insecure_channel_create(host.UTF8String, NULL)]);
}
+// TODO(jcanizales): GRPCSecureChannel and GRPCUnsecuredChannel are just convenience initializers
+// for GRPCChannel. Move them into GRPCChannel, which will make the following unnecessary.
+- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel {
+ [NSException raise:NSInternalInconsistencyException format:@"use the other initializer"];
+ return [self initWithHost:nil]; // silence warnings
+}
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
index 18f8bb5531..da11cbb761 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
@@ -81,11 +81,12 @@
@end
+#pragma mark GRPCWrappedCall
+
@interface GRPCWrappedCall : NSObject
-- (instancetype)initWithChannel:(GRPCChannel *)channel
- path:(NSString *)path
- host:(NSString *)host NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithHost:(NSString *)host
+ path:(NSString *)path NS_DESIGNATED_INITIALIZER;
- (void)startBatchWithOperations:(NSArray *)ops errorHandler:(void(^)())errorHandler;
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index 1db63df77f..951c051036 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -32,11 +32,14 @@
*/
#import "GRPCWrappedCall.h"
+
#import <Foundation/Foundation.h>
#include <grpc/grpc.h>
#include <grpc/byte_buffer.h>
#include <grpc/support/alloc.h>
+
#import "GRPCCompletionQueue.h"
+#import "GRPCHost.h"
#import "NSDictionary+GRPC.h"
#import "NSData+GRPC.h"
#import "NSError+GRPC.h"
@@ -219,38 +222,36 @@
@end
-@implementation GRPCWrappedCall{
- grpc_call *_call;
+#pragma mark GRPCWrappedCall
+
+@implementation GRPCWrappedCall {
GRPCCompletionQueue *_queue;
+ grpc_call *_call;
}
- (instancetype)init {
- return [self initWithChannel:nil path:nil host:nil];
+ return [self initWithHost:nil path:nil];
}
-- (instancetype)initWithChannel:(GRPCChannel *)channel
- path:(NSString *)path
- host:(NSString *)host {
- if (!channel || !path || !host) {
+- (instancetype)initWithHost:(NSString *)host
+ path:(NSString *)path {
+ if (!path || !host) {
[NSException raise:NSInvalidArgumentException
- format:@"channel, method, and host cannot be nil."];
+ format:@"path and host cannot be nil."];
}
-
+
if (self = [super init]) {
static dispatch_once_t initialization;
dispatch_once(&initialization, ^{
grpc_init();
});
-
+
+ // Each completion queue consumes one thread. There's a trade to be made between creating and
+ // consuming too many threads and having contention of multiple calls in a single completion
+ // queue. Currently we favor latency and use one per call.
_queue = [GRPCCompletionQueue completionQueue];
- if (!_queue) {
- return nil;
- }
- _call = grpc_channel_create_call(channel.unmanagedChannel,
- _queue.unmanagedQueue,
- path.UTF8String,
- host.UTF8String,
- gpr_inf_future(GPR_CLOCK_REALTIME));
+
+ _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path completionQueue:_queue];
if (_call == NULL) {
return nil;
}
@@ -299,4 +300,4 @@
grpc_call_destroy(_call);
}
-@end \ No newline at end of file
+@end
diff --git a/src/objective-c/tests/InteropTests.h b/src/objective-c/tests/InteropTests.h
new file mode 100644
index 0000000000..c675c8d241
--- /dev/null
+++ b/src/objective-c/tests/InteropTests.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <XCTest/XCTest.h>
+
+// Implements tests as described here:
+// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
+
+@interface InteropTests : XCTestCase
+// Returns @"http://localhost:5050".
+// Override in a subclass to perform the same tests against a different address.
+// For interop tests, use @"grpc-test.sandbox.google.com".
++ (NSString *)host;
+@end
diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m
index b473d73422..a6611d27be 100644
--- a/src/objective-c/tests/InteropTests.m
+++ b/src/objective-c/tests/InteropTests.m
@@ -31,10 +31,9 @@
*
*/
-#include <grpc/status.h>
+#import "InteropTests.h"
-#import <UIKit/UIKit.h>
-#import <XCTest/XCTest.h>
+#include <grpc/status.h>
#import <ProtoRPC/ProtoRPC.h>
#import <RemoteTest/Empty.pbobjc.h>
@@ -76,21 +75,18 @@
}
@end
-@interface InteropTests : XCTestCase
-@end
-
@implementation InteropTests {
RMTTestService *_service;
}
-// grpc-test.sandbox.google.com
++ (NSString *)host {
+ return @"http://localhost:5050";
+}
- (void)setUp {
- _service = [[RMTTestService alloc] initWithHost:@"http://localhost:5050"];
+ _service = [[RMTTestService alloc] initWithHost:self.class.host];
}
-// Tests as described here: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
-
- (void)testEmptyUnaryRPC {
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyUnary"];
diff --git a/src/objective-c/tests/InteropTestsLocalSSL.m b/src/objective-c/tests/InteropTestsLocalSSL.m
new file mode 100644
index 0000000000..227ca79659
--- /dev/null
+++ b/src/objective-c/tests/InteropTestsLocalSSL.m
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+// Repeat of the tests in InteropTests.m, but using SSL to communicate with the local server instead
+// of cleartext.
+
+#import <GRPCClient/GRPCCall+Tests.h>
+
+#import "InteropTests.h"
+
+static NSString * const kLocalSSLHost = @"localhost:5051";
+
+@interface InteropTestsLocalSSL : InteropTests
+@end
+
+@implementation InteropTestsLocalSSL
+
++ (NSString *)host {
+ return kLocalSSLHost;
+}
+
+- (void)setUp {
+ // Register test server certificates and name.
+ NSBundle *bundle = [NSBundle bundleForClass:self.class];
+ NSString *certsPath = [bundle pathForResource:@"TestCertificates.bundle/test-certificates"
+ ofType:@"pem"];
+ [GRPCCall useTestCertsPath:certsPath testName:@"foo.test.google.fr" forHost:kLocalSSLHost];
+
+ [super setUp];
+}
+
+@end
diff --git a/src/objective-c/tests/TestCertificates.bundle/test-certificates.pem b/src/objective-c/tests/TestCertificates.bundle/test-certificates.pem
new file mode 100644
index 0000000000..6c8511a73c
--- /dev/null
+++ b/src/objective-c/tests/TestCertificates.bundle/test-certificates.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
+Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
+BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
+g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
+Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
+sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
+oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
+Dfcog5wrJytaQ6UA0wE=
+-----END CERTIFICATE-----
diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
index f13fb8288b..af98aba9c0 100644
--- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
+++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
@@ -13,6 +13,8 @@
63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; };
635697CD1B14FC11007A7283 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635697CC1B14FC11007A7283 /* Tests.m */; };
635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; };
+ 63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; };
+ 63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; };
7D8A186224D39101F90230F6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
/* End PBXBuildFile section */
@@ -49,6 +51,9 @@
635697CC1B14FC11007A7283 /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; };
635697D81B14FC11007A7283 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTests.m; sourceTree = "<group>"; };
+ 63E240CC1B6C4D3A005F3B0E /* InteropTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteropTests.h; sourceTree = "<group>"; };
+ 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalSSL.m; sourceTree = "<group>"; };
+ 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TestCertificates.bundle; sourceTree = "<group>"; };
FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -93,6 +98,7 @@
isa = PBXGroup;
children = (
635697C91B14FC11007A7283 /* Tests */,
+ 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */,
635697C81B14FC11007A7283 /* Products */,
51E4650F34F854F41FF053B3 /* Pods */,
136D535E19727099B941D7B1 /* Frameworks */,
@@ -111,12 +117,14 @@
635697C91B14FC11007A7283 /* Tests */ = {
isa = PBXGroup;
children = (
+ 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */,
6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */,
63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */,
635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */,
63423F501B151B77006CF63C /* RxLibraryUnitTests.m */,
635697CC1B14FC11007A7283 /* Tests.m */,
635697D71B14FC11007A7283 /* Supporting Files */,
+ 63E240CC1B6C4D3A005F3B0E /* InteropTests.h */,
);
name = Tests;
sourceTree = SOURCE_ROOT;
@@ -209,6 +217,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -254,6 +263,7 @@
files = (
63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */,
63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */,
+ 63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */,
6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */,
635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */,
);
diff --git a/src/objective-c/tests/run_tests.sh b/src/objective-c/tests/run_tests.sh
index b13c0f0633..7b133c1782 100755
--- a/src/objective-c/tests/run_tests.sh
+++ b/src/objective-c/tests/run_tests.sh
@@ -37,7 +37,8 @@ cd $(dirname $0)
# Run the tests server.
../../../bins/$CONFIG/interop_server --port=5050 &
-# Kill it when this script exits.
+../../../bins/$CONFIG/interop_server --port=5051 --enable_ssl &
+# Kill them when this script exits.
trap 'kill -9 `jobs -p`' EXIT
# xcodebuild is very verbose. We filter its output and tell Bash to fail if any
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 1f76c7359d..01ec909b79 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -240,8 +240,8 @@ PHP_METHOD(Call, __construct) {
(wrapped_grpc_timeval *)zend_object_store_get_object(
deadline_obj TSRMLS_CC);
call->wrapped = grpc_channel_create_call(
- channel->wrapped, completion_queue, method, channel->target,
- deadline->wrapped);
+ channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, completion_queue, method,
+ channel->target, deadline->wrapped);
}
/**
diff --git a/src/python/grpcio/grpc/_adapter/_c/types/channel.c b/src/python/grpcio/grpc/_adapter/_c/types/channel.c
index feb256cf00..963104742f 100644
--- a/src/python/grpcio/grpc/_adapter/_c/types/channel.c
+++ b/src/python/grpcio/grpc/_adapter/_c/types/channel.c
@@ -128,7 +128,7 @@ Call *pygrpc_Channel_create_call(
}
call = pygrpc_Call_new_empty(cq);
call->c_call = grpc_channel_create_call(
- self->c_chan, cq->c_cq, method, host,
+ self->c_chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq->c_cq, method, host,
pygrpc_cast_double_to_gpr_timespec(deadline));
return call;
}
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index 0cb6fa2f80..a0663607c2 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -212,10 +212,10 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE cqueue, VALUE method,
return Qnil;
}
- call =
- grpc_channel_create_call(ch, cq, method_chars, host_chars,
- grpc_rb_time_timeval(deadline,
- /* absolute time */ 0));
+ call = grpc_channel_create_call(ch, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+ method_chars, host_chars,
+ grpc_rb_time_timeval(deadline,
+ /* absolute time */ 0));
if (call == NULL) {
rb_raise(rb_eRuntimeError, "cannot create call with method %s",
method_chars);