Merge branch 'master' of github.com:grpc/grpc into compression-interop

pull/2603/head
David Garcia Quintas 9 years ago
commit 74f4f0bb4b
  1. 3
      BUILD
  2. 1
      INSTALL
  3. 4
      Makefile
  4. 3
      build.json
  5. 2
      doc/connectivity-semantics-and-api.md
  6. 70
      doc/health-checking.md
  7. 32
      include/grpc++/auth_context.h
  8. 4
      include/grpc++/client_context.h
  9. 55
      include/grpc/census.h
  10. 39
      include/grpc/grpc.h
  11. 4
      src/compiler/objective_c_generator.cc
  12. 1
      src/core/channel/client_channel.c
  13. 15
      src/core/security/client_auth_filter.c
  14. 6
      src/core/security/google_default_credentials.c
  15. 14
      src/core/surface/call.c
  16. 2
      src/core/surface/version.c
  17. 2
      src/core/tsi/transport_security_interface.h
  18. 32
      src/cpp/client/channel.cc
  19. 9
      src/cpp/client/client_context.cc
  20. 9
      src/cpp/client/secure_credentials.cc
  21. 2
      src/cpp/common/auth_property_iterator.cc
  22. 125
      src/csharp/Grpc.Auth/GoogleCredential.cs
  23. 27
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  24. 26
      src/csharp/Grpc.Auth/OAuth2Interceptors.cs
  25. 4
      src/csharp/Grpc.Auth/app.config
  26. 4
      src/csharp/Grpc.Auth/packages.config
  27. 6
      src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs
  28. 6
      src/csharp/Grpc.Core.Tests/ChannelTest.cs
  29. 62
      src/csharp/Grpc.Core.Tests/ClientBaseTest.cs
  30. 298
      src/csharp/Grpc.Core.Tests/ClientServerTest.cs
  31. 128
      src/csharp/Grpc.Core.Tests/CompressionTest.cs
  32. 153
      src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs
  33. 5
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  34. 8
      src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
  35. 8
      src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs
  36. 244
      src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
  37. 136
      src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs
  38. 152
      src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
  39. 72
      src/csharp/Grpc.Core/CallInvocationDetails.cs
  40. 109
      src/csharp/Grpc.Core/CallOptions.cs
  41. 46
      src/csharp/Grpc.Core/Calls.cs
  42. 24
      src/csharp/Grpc.Core/Channel.cs
  43. 6
      src/csharp/Grpc.Core/ChannelOptions.cs
  44. 44
      src/csharp/Grpc.Core/ClientBase.cs
  45. 38
      src/csharp/Grpc.Core/CompressionLevel.cs
  46. 171
      src/csharp/Grpc.Core/ContextPropagationToken.cs
  47. 5
      src/csharp/Grpc.Core/Grpc.Core.csproj
  48. 32
      src/csharp/Grpc.Core/GrpcEnvironment.cs
  49. 2
      src/csharp/Grpc.Core/IAsyncStreamReader.cs
  50. 8
      src/csharp/Grpc.Core/IAsyncStreamWriter.cs
  51. 75
      src/csharp/Grpc.Core/Internal/AsyncCall.cs
  52. 14
      src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
  53. 35
      src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
  54. 41
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  55. 6
      src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
  56. 23
      src/csharp/Grpc.Core/Internal/ClientRequestStream.cs
  57. 17
      src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
  58. 31
      src/csharp/Grpc.Core/Internal/ServerResponseStream.cs
  59. 2
      src/csharp/Grpc.Core/Internal/Timespec.cs
  60. 4
      src/csharp/Grpc.Core/KeyCertificatePair.cs
  61. 13
      src/csharp/Grpc.Core/Logging/ConsoleLogger.cs
  62. 6
      src/csharp/Grpc.Core/Logging/ILogger.cs
  63. 23
      src/csharp/Grpc.Core/Marshaller.cs
  64. 18
      src/csharp/Grpc.Core/Metadata.cs
  65. 60
      src/csharp/Grpc.Core/Method.cs
  66. 14
      src/csharp/Grpc.Core/RpcException.cs
  67. 2
      src/csharp/Grpc.Core/Server.cs
  68. 56
      src/csharp/Grpc.Core/ServerCallContext.cs
  69. 2
      src/csharp/Grpc.Core/ServerCredentials.cs
  70. 4
      src/csharp/Grpc.Core/ServerMethods.cs
  71. 4
      src/csharp/Grpc.Core/ServerPort.cs
  72. 8
      src/csharp/Grpc.Core/ServerServiceDefinition.cs
  73. 13
      src/csharp/Grpc.Core/Status.cs
  74. 156
      src/csharp/Grpc.Core/StatusCode.cs
  75. 9
      src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs
  76. 3
      src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs
  77. 25
      src/csharp/Grpc.Core/Utils/Preconditions.cs
  78. 36
      src/csharp/Grpc.Core/Version.cs
  79. 39
      src/csharp/Grpc.Core/VersionInfo.cs
  80. 82
      src/csharp/Grpc.Core/WriteOptions.cs
  81. 32
      src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
  82. 10
      src/csharp/Grpc.Examples/MathExamples.cs
  83. 7
      src/csharp/Grpc.Examples/MathServiceImpl.cs
  84. 8
      src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs
  85. 4
      src/csharp/Grpc.IntegrationTesting.Client/app.config
  86. 4
      src/csharp/Grpc.IntegrationTesting.Server/app.config
  87. 38
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  88. 108
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  89. 4
      src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs
  90. 4
      src/csharp/Grpc.IntegrationTesting/app.config
  91. 4
      src/csharp/Grpc.IntegrationTesting/packages.config
  92. 4
      src/csharp/build_packages.bat
  93. 2
      src/csharp/doc/README.md
  94. 70
      src/csharp/doc/grpc_csharp_public.shfbproj
  95. 74
      src/csharp/ext/grpc_csharp_ext.c
  96. 3
      src/node/binding.gyp
  97. 2
      src/node/examples/perf_test.js
  98. 19
      src/node/ext/call.cc
  99. 25
      src/node/ext/channel.cc
  100. 6
      src/node/ext/channel.h
  101. Some files were not shown because too many files have changed in this diff Show More

@ -400,6 +400,7 @@ cc_library(
],
deps = [
"//external:libssl",
"//external:zlib",
":gpr",
],
)
@ -690,7 +691,6 @@ cc_library(
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/auth_property_iterator.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",
@ -778,7 +778,6 @@ cc_library(
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/auth_property_iterator.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",

@ -132,6 +132,7 @@ We will also need to make openssl and install it appropriately
$ cd <git directory>
$ cd third_party/openssl
$ ./config
$ sudo make install
$ cd ../../

@ -313,7 +313,7 @@ E = @echo
Q = @
endif
VERSION = 0.10.0.0
VERSION = 0.10.1.0
CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
@ -4487,7 +4487,6 @@ PUBLIC_HEADERS_CXX += \
include/grpc++/async_generic_service.h \
include/grpc++/async_unary_call.h \
include/grpc++/auth_context.h \
include/grpc++/auth_property_iterator.h \
include/grpc++/byte_buffer.h \
include/grpc++/channel_arguments.h \
include/grpc++/channel_interface.h \
@ -4731,7 +4730,6 @@ PUBLIC_HEADERS_CXX += \
include/grpc++/async_generic_service.h \
include/grpc++/async_unary_call.h \
include/grpc++/auth_context.h \
include/grpc++/auth_property_iterator.h \
include/grpc++/byte_buffer.h \
include/grpc++/channel_arguments.h \
include/grpc++/channel_interface.h \

@ -7,7 +7,7 @@
"version": {
"major": 0,
"minor": 10,
"micro": 0,
"micro": 1,
"build": 0
}
},
@ -33,7 +33,6 @@
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/auth_property_iterator.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",

@ -38,7 +38,7 @@ because the server is not yet available), the channel may spend increasingly
large amounts of time in this state.
IDLE: This is the state where the channel is not even trying to create a
connection because of a lack of new or pending RPCs. New channels MAY be created
connection because of a lack of new or pending RPCs. New RPCs MAY be created
in this state. Any attempt to start an RPC on the channel will push the channel
out of this state to connecting. When there has been no RPC activity on a channel
for a specified IDLE_TIMEOUT, i.e., no new or pending (active) RPCs for this

@ -0,0 +1,70 @@
GRPC Health Checking Protocol
================================
Health checks are used to probe whether the server is able to handle rpcs. The
client-to-server health checking can happen from point to point or via some
control system. A server may choose to reply “unhealthy” because it
is not ready to take requests, it is shutting down or some other reason.
The client can act accordingly if the response is not received within some time
window or the response says unhealthy in it.
A GRPC service is used as the health checking mechanism for both simple
client-to-server scenario and other control systems such as load-balancing.
Being a high
level service provides some benefits. Firstly, since it is a GRPC service
itself, doing a health check is in the same format as a normal rpc. Secondly,
it has rich semantics such as per-service health status. Thirdly, as a GRPC
service, it is able reuse all the existing billing, quota infrastructure, etc,
and thus the server has full control over the access of the health checking
service.
## Service Definition
The server should export a service defined in the following proto:
```
syntax = "proto3";
package grpc.health.v1alpha;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}
```
A client can query the server’s health status by calling the `Check` method, and
a deadline should be set on the rpc. The client can optionally set the service
name it wants to query for health status. The suggested format of service name
is `package_names.ServiceName`, such as `grpc.health.v1alpha.Health`.
The server should register all the services manually and set
the individual status, including an empty service name and its status. For each
request received, if the service name can be found in the registry,
a response must be sent back with an `OK` status and the status field should be
set to `SERVING` or `NOT_SERVING` accordingly. If the service name is not
registered, the server returns a `NOT_FOUND` GRPC status.
The server should use an empty string as the key for server’s
overall health status, so that a client not interested in a specific service can
query the server's status with an empty request. The server can just do exact
matching of the service name without support of any kind of wildcard matching.
However, the service owner has the freedom to implement more complicated
matching semantics that both the client and server agree upon.
A client can declare the server as unhealthy if the rpc is not finished after
some amount of time. The client should be able to handle the case where server
does not have the Health service.

@ -34,12 +34,42 @@
#ifndef GRPCXX_AUTH_CONTEXT_H
#define GRPCXX_AUTH_CONTEXT_H
#include <iterator>
#include <vector>
#include <grpc++/auth_property_iterator.h>
#include <grpc++/config.h>
struct grpc_auth_context;
struct grpc_auth_property;
struct grpc_auth_property_iterator;
namespace grpc {
class SecureAuthContext;
typedef std::pair<grpc::string, grpc::string> AuthProperty;
class AuthPropertyIterator
: public std::iterator<std::input_iterator_tag, const AuthProperty> {
public:
~AuthPropertyIterator();
AuthPropertyIterator& operator++();
AuthPropertyIterator operator++(int);
bool operator==(const AuthPropertyIterator& rhs) const;
bool operator!=(const AuthPropertyIterator& rhs) const;
const AuthProperty operator*();
protected:
AuthPropertyIterator();
AuthPropertyIterator(const grpc_auth_property* property,
const grpc_auth_property_iterator* iter);
private:
friend class SecureAuthContext;
const grpc_auth_property* property_;
// The following items form a grpc_auth_property_iterator.
const grpc_auth_context* ctx_;
size_t index_;
const char* name_;
};
class AuthContext {
public:

@ -223,15 +223,11 @@ class ClientContext {
void set_call(grpc_call* call,
const std::shared_ptr<ChannelInterface>& channel);
grpc_completion_queue* cq() { return cq_; }
void set_cq(grpc_completion_queue* cq) { cq_ = cq; }
grpc::string authority() { return authority_; }
bool initial_metadata_received_;
std::shared_ptr<ChannelInterface> channel_;
grpc_call* call_;
grpc_completion_queue* cq_;
gpr_timespec deadline_;
grpc::string authority_;
std::shared_ptr<Credentials> creds_;

@ -104,6 +104,61 @@ int census_context_deserialize(const char *buffer, census_context **context);
* future census calls will result in undefined behavior. */
void census_context_destroy(census_context *context);
/* Max number of characters in tag key */
#define CENSUS_MAX_TAG_KEY_LENGTH 20
/* Max number of tag value characters */
#define CENSUS_MAX_TAG_VALUE_LENGTH 50
/* A Census tag set is a collection of key:value string pairs; these form the
basis against which Census metrics will be recorded. Keys are unique within
a tag set. All contexts have an associated tag set. */
typedef struct census_tag_set census_tag_set;
/* Returns a pointer to a newly created, empty tag set. If size_hint > 0,
indicates that the tag set is intended to hold approximately that number
of tags. */
census_tag_set *census_tag_set_create(size_t size_hint);
/* Add a new tag key/value to an existing tag set; if the tag key already exists
in the tag set, then its value is overwritten with the new one. Can also be
used to delete a tag, by specifying a NULL value. If key is NULL, returns
the number of tags in the tag set.
Return values:
-1: invalid length key or value
non-negative value: the number of tags in the tag set. */
int census_tag_set_add(census_tag_set *tags, const char *key,
const char *value);
/* Destroys a tag set. This function must be called to prevent memory leaks.
Once called, the tag set cannot be used again. */
void census_tag_set_destroy(census_tag_set *tags);
/* Get a contexts tag set. */
census_tag_set *census_context_tag_set(census_context *context);
/* A read-only representation of a tag for use by census clients. */
typedef struct {
size_t key_len; /* Number of bytes in tag key. */
const char *key; /* A pointer to the tag key. May not be null-terminated. */
size_t value_len; /* Number of bytes in tag value. */
const char *value; /* Pointer to the tag value. May not be null-terminated. */
} census_tag_const;
/* Used to iterate through a tag sets contents. */
typedef struct census_tag_set_iterator census_tag_set_iterator;
/* Open a tag set for iteration. The tag set must not be modified while
iteration is ongoing. Returns an iterator for use in following functions. */
census_tag_set_iterator *census_tag_set_open(census_tag_set *tags);
/* Get the next tag in the tag set, by writing into the 'tag' argument. Returns
1 if there is a "next" tag, 0 if there are no more tags. */
int census_tag_set_next(census_tag_set_iterator *it, census_tag_const *tag);
/* Close an iterator opened by census_tag_set_open(). The iterator will be
invalidated, and should not be used once close is called. */
void census_tag_set_close(census_tag_set_iterator *it);
/* A census statistic to be recorded comprises two parts: an ID for the
* particular statistic and the value to be recorded against it. */
typedef struct {

@ -181,7 +181,9 @@ typedef enum grpc_call_error {
GRPC_CALL_ERROR_INVALID_MESSAGE,
/** completion queue for notification has not been registered with the
server */
GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE
GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE,
/** this batch of operations leads to more operations than allowed */
GRPC_CALL_ERROR_BATCH_TOO_BIG
} grpc_call_error;
/* Write Flags: */
@ -256,31 +258,44 @@ void grpc_call_details_destroy(grpc_call_details *details);
typedef enum {
/** Send initial metadata: one and only one instance MUST be sent for each
call, unless the call was cancelled - in which case this can be skipped */
call, unless the call was cancelled - in which case this can be skipped.
This op completes after all bytes of metadata have been accepted by
outgoing flow control. */
GRPC_OP_SEND_INITIAL_METADATA = 0,
/** Send a message: 0 or more of these operations can occur for each call */
/** Send a message: 0 or more of these operations can occur for each call.
This op completes after all bytes for the message have been accepted by
outgoing flow control. */
GRPC_OP_SEND_MESSAGE,
/** Send a close from the client: one and only one instance MUST be sent from
the client, unless the call was cancelled - in which case this can be
skipped */
skipped.
This op completes after all bytes for the call (including the close)
have passed outgoing flow control. */
GRPC_OP_SEND_CLOSE_FROM_CLIENT,
/** Send status from the server: one and only one instance MUST be sent from
the server unless the call was cancelled - in which case this can be
skipped */
skipped.
This op completes after all bytes for the call (including the status)
have passed outgoing flow control. */
GRPC_OP_SEND_STATUS_FROM_SERVER,
/** Receive initial metadata: one and only one MUST be made on the client,
must not be made on the server */
must not be made on the server.
This op completes after all initial metadata has been read from the
peer. */
GRPC_OP_RECV_INITIAL_METADATA,
/** Receive a message: 0 or more of these operations can occur for each call
*/
/** Receive a message: 0 or more of these operations can occur for each call.
This op completes after all bytes of the received message have been
read, or after a half-close has been received on this call. */
GRPC_OP_RECV_MESSAGE,
/** Receive status on the client: one and only one must be made on the client.
This operation always succeeds, meaning ops paired with this operation
will also appear to succeed, even though they may not have. In that case
the status will indicate some failure. */
This operation always succeeds, meaning ops paired with this operation
will also appear to succeed, even though they may not have. In that case
the status will indicate some failure.
This op completes after all activity on the call has completed. */
GRPC_OP_RECV_STATUS_ON_CLIENT,
/** Receive close on the server: one and only one must be made on the
server */
server.
This op completes after the close has been received by the server. */
GRPC_OP_RECV_CLOSE_ON_SERVER
} grpc_op_type;

@ -154,9 +154,9 @@ void PrintAdvancedImplementation(Printer *printer,
printer->Print(" responsesWriteable:[GRXWriteable ");
if (method->server_streaming()) {
printer->Print("writeableWithStreamHandler:eventHandler]];\n");
printer->Print("writeableWithEventHandler:eventHandler]];\n");
} else {
printer->Print("writeableWithSingleValueHandler:handler]];\n");
printer->Print("writeableWithSingleHandler:handler]];\n");
}
printer->Print("}\n");

@ -527,6 +527,7 @@ static void cc_on_config_changed(void *arg, int iomgr_success) {
}
if (old_lb_policy != NULL) {
grpc_lb_policy_shutdown(old_lb_policy);
GRPC_LB_POLICY_UNREF(old_lb_policy, "channel");
}

@ -75,11 +75,11 @@ typedef struct {
grpc_mdstr *status_key;
} channel_data;
static void bubble_up_error(grpc_call_element *elem, const char *error_msg) {
static void bubble_up_error(grpc_call_element *elem, grpc_status_code status,
const char *error_msg) {
call_data *calld = elem->call_data;
gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg);
grpc_transport_stream_op_add_cancellation(&calld->op,
GRPC_STATUS_UNAUTHENTICATED);
grpc_transport_stream_op_add_cancellation(&calld->op, status);
grpc_call_next_op(elem, &calld->op);
}
@ -94,7 +94,8 @@ static void on_credentials_metadata(void *user_data,
grpc_metadata_batch *mdb;
size_t i;
if (status != GRPC_CREDENTIALS_OK) {
bubble_up_error(elem, "Credentials failed to get metadata.");
bubble_up_error(elem, GRPC_STATUS_UNAUTHENTICATED,
"Credentials failed to get metadata.");
return;
}
GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
@ -154,7 +155,7 @@ static void send_security_metadata(grpc_call_element *elem,
if (channel_creds_has_md && call_creds_has_md) {
calld->creds = grpc_composite_credentials_create(channel_creds, ctx->creds);
if (calld->creds == NULL) {
bubble_up_error(elem,
bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT,
"Incompatible credentials set on channel and call.");
return;
}
@ -182,7 +183,7 @@ static void on_host_checked(void *user_data, grpc_security_status status) {
char *error_msg;
gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
grpc_mdstr_as_c_string(calld->host));
bubble_up_error(elem, error_msg);
bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg);
gpr_free(error_msg);
}
}
@ -252,7 +253,7 @@ static void auth_start_transport_op(grpc_call_element *elem,
gpr_asprintf(&error_msg,
"Invalid host %s set in :authority metadata.",
call_host);
bubble_up_error(elem, error_msg);
bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg);
gpr_free(error_msg);
}
return; /* early exit */

@ -84,6 +84,8 @@ static void on_compute_engine_detection_http_response(
gpr_mu_unlock(GRPC_POLLSET_MU(&detector->pollset));
}
static void destroy_pollset(void *p) { grpc_pollset_destroy(p); }
static int is_stack_running_on_compute_engine(void) {
compute_engine_detector detector;
grpc_httpcli_request request;
@ -114,12 +116,12 @@ static int is_stack_running_on_compute_engine(void) {
while (!detector.is_done) {
grpc_pollset_worker worker;
grpc_pollset_work(&detector.pollset, &worker,
gpr_inf_future(GPR_CLOCK_REALTIME));
gpr_inf_future(GPR_CLOCK_MONOTONIC));
}
gpr_mu_unlock(GRPC_POLLSET_MU(&detector.pollset));
grpc_httpcli_context_destroy(&context);
grpc_pollset_destroy(&detector.pollset);
grpc_pollset_shutdown(&detector.pollset, destroy_pollset, &detector.pollset);
return detector.success;
}

@ -1587,6 +1587,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_SEND_INITIAL_METADATA;
req->data.send_metadata.count = op->data.send_initial_metadata.count;
req->data.send_metadata.metadata =
@ -1601,6 +1602,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
return GRPC_CALL_ERROR_INVALID_MESSAGE;
}
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_SEND_MESSAGE;
req->data.send_message = op->data.send_message;
req->flags = op->flags;
@ -1612,6 +1614,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_SEND_CLOSE;
req->flags = op->flags;
break;
@ -1622,6 +1625,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
return GRPC_CALL_ERROR_NOT_ON_CLIENT;
}
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_SEND_TRAILING_METADATA;
req->flags = op->flags;
req->data.send_metadata.count =
@ -1629,6 +1633,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
req->data.send_metadata.metadata =
op->data.send_status_from_server.trailing_metadata;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_SEND_STATUS;
req->data.send_status.code = op->data.send_status_from_server.status;
req->data.send_status.details =
@ -1638,6 +1643,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
op->data.send_status_from_server.status_details, 0)
: NULL;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_SEND_CLOSE;
break;
case GRPC_OP_RECV_INITIAL_METADATA:
@ -1647,6 +1653,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_RECV_INITIAL_METADATA;
req->data.recv_metadata = op->data.recv_initial_metadata;
req->data.recv_metadata->count = 0;
@ -1656,6 +1663,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_RECV_MESSAGE;
req->data.recv_message = op->data.recv_message;
req->flags = op->flags;
@ -1667,22 +1675,26 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_RECV_STATUS;
req->flags = op->flags;
req->data.recv_status.set_value = set_status_value_directly;
req->data.recv_status.user_data = op->data.recv_status_on_client.status;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_RECV_STATUS_DETAILS;
req->data.recv_status_details.details =
op->data.recv_status_on_client.status_details;
req->data.recv_status_details.details_capacity =
op->data.recv_status_on_client.status_details_capacity;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
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++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_RECV_CLOSE;
finish_func = finish_batch_with_close;
break;
@ -1690,12 +1702,14 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_RECV_STATUS;
req->flags = op->flags;
req->data.recv_status.set_value = set_cancelled_value;
req->data.recv_status.user_data =
op->data.recv_close_on_server.cancelled;
req = &reqs[out++];
if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG;
req->op = GRPC_IOREQ_RECV_CLOSE;
finish_func = finish_batch_with_close;
break;

@ -37,5 +37,5 @@
#include <grpc/grpc.h>
const char *grpc_version_string(void) {
return "0.10.0.0";
return "0.10.1.0";
}

@ -158,6 +158,8 @@ tsi_result tsi_frame_protector_protect_flush(
value is expected to be at most max_protected_frame_size minus overhead
which means that max_protected_frame_size is a safe bet. The output value
is the number of bytes actually written.
If *unprotected_bytes_size is unchanged, there may be more data remaining
to unprotect, and the caller should call this function again.
- This method returns TSI_OK in case of success. Success includes cases where
there is not enough data to output a frame in which case

@ -61,19 +61,25 @@ 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_, 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());
const bool kRegistered = method.channel_tag() && context->authority().empty();
grpc_call* c_call = NULL;
if (kRegistered) {
c_call = grpc_channel_create_registered_call(
c_channel_, context->propagate_from_call_,
context->propagation_options_.c_bitmask(), cq->cq(),
method.channel_tag(), context->raw_deadline());
} else {
const char* host_str = NULL;
if (!context->authority().empty()) {
host_str = context->authority().c_str();
} else if (!host_.empty()) {
host_str = host_.c_str();
}
c_call = grpc_channel_create_call(c_channel_, context->propagate_from_call_,
context->propagation_options_.c_bitmask(),
cq->cq(), method.name(), host_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());

@ -48,7 +48,6 @@ namespace grpc {
ClientContext::ClientContext()
: initial_metadata_received_(false),
call_(nullptr),
cq_(nullptr),
deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)),
propagate_from_call_(nullptr) {}
@ -56,14 +55,6 @@ ClientContext::~ClientContext() {
if (call_) {
grpc_call_destroy(call_);
}
if (cq_) {
// Drain cq_.
grpc_completion_queue_shutdown(cq_);
while (grpc_completion_queue_next(cq_, gpr_inf_future(GPR_CLOCK_REALTIME))
.type != GRPC_QUEUE_SHUTDOWN)
;
grpc_completion_queue_destroy(cq_);
}
}
std::unique_ptr<ClientContext> ClientContext::FromServerContext(

@ -34,6 +34,7 @@
#include <grpc/support/log.h>
#include <grpc++/channel_arguments.h>
#include <grpc++/impl/grpc_library.h>
#include "src/cpp/client/channel.h"
#include "src/cpp/client/secure_credentials.h"
@ -61,12 +62,14 @@ std::shared_ptr<Credentials> WrapCredentials(grpc_credentials* creds) {
} // namespace
std::shared_ptr<Credentials> GoogleDefaultCredentials() {
GrpcLibrary init; // To call grpc_init().
return WrapCredentials(grpc_google_default_credentials_create());
}
// Builds SSL Credentials given SSL specific options
std::shared_ptr<Credentials> SslCredentials(
const SslCredentialsOptions& options) {
GrpcLibrary init; // To call grpc_init().
grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
@ -78,6 +81,7 @@ std::shared_ptr<Credentials> SslCredentials(
// Builds credentials for use when running in GCE
std::shared_ptr<Credentials> ComputeEngineCredentials() {
GrpcLibrary init; // To call grpc_init().
return WrapCredentials(grpc_compute_engine_credentials_create());
}
@ -85,6 +89,7 @@ std::shared_ptr<Credentials> ComputeEngineCredentials() {
std::shared_ptr<Credentials> ServiceAccountCredentials(
const grpc::string& json_key, const grpc::string& scope,
long token_lifetime_seconds) {
GrpcLibrary init; // To call grpc_init().
if (token_lifetime_seconds <= 0) {
gpr_log(GPR_ERROR,
"Trying to create ServiceAccountCredentials "
@ -100,6 +105,7 @@ std::shared_ptr<Credentials> ServiceAccountCredentials(
// Builds JWT credentials.
std::shared_ptr<Credentials> ServiceAccountJWTAccessCredentials(
const grpc::string& json_key, long token_lifetime_seconds) {
GrpcLibrary init; // To call grpc_init().
if (token_lifetime_seconds <= 0) {
gpr_log(GPR_ERROR,
"Trying to create JWTCredentials with non-positive lifetime");
@ -114,6 +120,7 @@ std::shared_ptr<Credentials> ServiceAccountJWTAccessCredentials(
// Builds refresh token credentials.
std::shared_ptr<Credentials> RefreshTokenCredentials(
const grpc::string& json_refresh_token) {
GrpcLibrary init; // To call grpc_init().
return WrapCredentials(
grpc_refresh_token_credentials_create(json_refresh_token.c_str()));
}
@ -121,6 +128,7 @@ std::shared_ptr<Credentials> RefreshTokenCredentials(
// Builds access token credentials.
std::shared_ptr<Credentials> AccessTokenCredentials(
const grpc::string& access_token) {
GrpcLibrary init; // To call grpc_init().
return WrapCredentials(
grpc_access_token_credentials_create(access_token.c_str()));
}
@ -129,6 +137,7 @@ std::shared_ptr<Credentials> AccessTokenCredentials(
std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token,
const grpc::string& authority_selector) {
GrpcLibrary init; // To call grpc_init().
return WrapCredentials(grpc_iam_credentials_create(
authorization_token.c_str(), authority_selector.c_str()));
}

@ -31,7 +31,7 @@
*
*/
#include <grpc++/auth_property_iterator.h>
#include <grpc++/auth_context.h>
#include <grpc/grpc_security.h>

@ -1,125 +0,0 @@
#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.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Responses;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Grpc.Auth
{
// TODO(jtattermusch): Remove this class once possible.
/// <summary>
/// A temporary placeholder for Google credential from
/// Google Auth library for .NET. It emulates the usage pattern
/// for Usable auth.
/// </summary>
public class GoogleCredential
{
private const string GoogleApplicationCredentialsEnvName = "GOOGLE_APPLICATION_CREDENTIALS";
private const string ClientEmailFieldName = "client_email";
private const string PrivateKeyFieldName = "private_key";
private ServiceCredential credential;
private GoogleCredential(ServiceCredential credential)
{
this.credential = credential;
}
public static GoogleCredential GetApplicationDefault()
{
return new GoogleCredential(null);
}
public bool IsCreateScopedRequired
{
get
{
return true;
}
}
public GoogleCredential CreateScoped(IEnumerable<string> scopes)
{
var credsPath = Environment.GetEnvironmentVariable(GoogleApplicationCredentialsEnvName);
if (credsPath == null)
{
// Default to ComputeCredentials if path to JSON key is not set.
// ComputeCredential is not scoped actually, but for our use case it's
// fine to treat is as such.
return new GoogleCredential(new ComputeCredential(new ComputeCredential.Initializer()));
}
JObject jsonCredentialParameters = JObject.Parse(File.ReadAllText(credsPath));
string clientEmail = jsonCredentialParameters.GetValue(ClientEmailFieldName).Value<string>();
string privateKeyString = jsonCredentialParameters.GetValue(PrivateKeyFieldName).Value<string>();
var serviceCredential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(clientEmail)
{
Scopes = scopes,
}.FromPrivateKey(privateKeyString));
return new GoogleCredential(serviceCredential);
}
public Task<bool> RequestAccessTokenAsync(CancellationToken taskCancellationToken)
{
return credential.RequestAccessTokenAsync(taskCancellationToken);
}
public TokenResponse Token
{
get
{
return credential.Token;
}
}
internal ServiceCredential InternalCredential
{
get
{
return credential;
}
}
}
}

@ -11,7 +11,7 @@
<AssemblyName>Grpc.Auth</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DocumentationFile>bin\$(Configuration)\Grpc.Auth.Xml</DocumentationFile>
<NuGetPackageImportStamp>9b408026</NuGetPackageImportStamp>
<NuGetPackageImportStamp>4f8487a9</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -41,28 +41,32 @@
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto">
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.9.2.27816, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.2\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
<HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks">
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@ -87,7 +91,6 @@
<Link>Version.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GoogleCredential.cs" />
<Compile Include="OAuth2Interceptors.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

@ -54,7 +54,7 @@ namespace Grpc.Auth
/// </summary>
public static MetadataInterceptorDelegate FromCredential(GoogleCredential googleCredential)
{
var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default);
var interceptor = new OAuth2Interceptor(googleCredential, SystemClock.Default);
return new MetadataInterceptorDelegate(interceptor.InterceptHeaders);
}
@ -66,7 +66,7 @@ namespace Grpc.Auth
public static MetadataInterceptorDelegate FromAccessToken(string oauth2Token)
{
Preconditions.CheckNotNull(oauth2Token);
return new MetadataInterceptorDelegate((metadata) =>
return new MetadataInterceptorDelegate((authUri, metadata) =>
{
metadata.Add(OAuth2Interceptor.CreateBearerTokenHeader(oauth2Token));
});
@ -80,10 +80,10 @@ namespace Grpc.Auth
private const string AuthorizationHeader = "Authorization";
private const string Schema = "Bearer";
private ServiceCredential credential;
private ITokenAccess credential;
private IClock clock;
public OAuth2Interceptor(ServiceCredential credential, IClock clock)
public OAuth2Interceptor(ITokenAccess credential, IClock clock)
{
this.credential = credential;
this.clock = clock;
@ -94,23 +94,15 @@ namespace Grpc.Auth
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public string GetAccessToken(CancellationToken cancellationToken)
public string GetAccessToken(string authUri, CancellationToken cancellationToken)
{
if (credential.Token == null || credential.Token.IsExpired(clock))
{
// TODO(jtattermusch): Parallel requests will spawn multiple requests to refresh the token once the token expires.
// TODO(jtattermusch): Rethink synchronous wait to obtain the result.
if (!credential.RequestAccessTokenAsync(cancellationToken).Result)
{
throw new InvalidOperationException("The access token has expired but we can't refresh it");
}
}
return credential.Token.AccessToken;
// TODO(jtattermusch): Rethink synchronous wait to obtain the result.
return credential.GetAccessTokenForRequestAsync(authUri, cancellationToken: cancellationToken).GetAwaiter().GetResult();
}
public void InterceptHeaders(Metadata metadata)
public void InterceptHeaders(string authUri, Metadata metadata)
{
var accessToken = GetAccessToken(CancellationToken.None);
var accessToken = GetAccessToken(authUri, CancellationToken.None);
metadata.Add(CreateBearerTokenHeader(accessToken));
}

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.2" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.2" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />

@ -67,9 +67,9 @@ namespace Grpc.Core.Internal.Tests
[Test]
public void ConstructorPreconditions()
{
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, "abc"); });
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, 1); });
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption("abc", null); });
Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, "abc"); });
Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, 1); });
Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption("abc", null); });
}
[Test]

@ -50,7 +50,7 @@ namespace Grpc.Core.Tests
[Test]
public void Constructor_RejectsInvalidParams()
{
Assert.Throws(typeof(NullReferenceException), () => new Channel(null, Credentials.Insecure));
Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, Credentials.Insecure));
}
[Test]
@ -72,11 +72,11 @@ namespace Grpc.Core.Tests
}
[Test]
public void Target()
public void ResolvedTarget()
{
using (var channel = new Channel("127.0.0.1", Credentials.Insecure))
{
Assert.IsTrue(channel.Target.Contains("127.0.0.1"));
Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1"));
}
}

@ -0,0 +1,62 @@
#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 Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ClientBaseTest
{
[Test]
public void GetAuthUriBase_Valid()
{
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com"));
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com/"));
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com:443/"));
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com:443/"));
}
[Test]
public void GetAuthUriBase_Invalid()
{
Assert.IsNull(ClientBase.GetAuthUriBase("some.googleapi.com:"));
Assert.IsNull(ClientBase.GetAuthUriBase("https://some.googleapi.com/"));
Assert.IsNull(ClientBase.GetAuthUriBase("dns://some.googleapi.com:443")); // just two slashes
Assert.IsNull(ClientBase.GetAuthUriBase(""));
}
}
}

@ -46,47 +46,18 @@ namespace Grpc.Core.Tests
public class ClientServerTest
{
const string Host = "127.0.0.1";
const string ServiceName = "tests.Test";
static readonly Method<string, string> EchoMethod = new Method<string, string>(
MethodType.Unary,
ServiceName,
"Echo",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
static readonly Method<string, string> ConcatAndEchoMethod = new Method<string, string>(
MethodType.ClientStreaming,
ServiceName,
"ConcatAndEcho",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
static readonly Method<string, string> NonexistentMethod = new Method<string, string>(
MethodType.Unary,
ServiceName,
"NonexistentMethod",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
static readonly ServerServiceDefinition ServiceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName)
.AddMethod(EchoMethod, EchoHandler)
.AddMethod(ConcatAndEchoMethod, ConcatAndEchoHandler)
.Build();
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
server = new Server
{
Services = { ServiceDefinition },
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
helper = new MockServiceHelper(Host);
server = helper.GetServer();
server.Start();
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
channel = helper.GetChannel();
}
[TearDown]
@ -103,123 +74,127 @@ namespace Grpc.Core.Tests
}
[Test]
public void UnaryCall()
public async Task UnaryCall()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
Assert.AreEqual("ABC", Calls.BlockingUnaryCall(callDetails, "ABC"));
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
return request;
});
Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC"));
Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC"));
}
[Test]
public void UnaryCall_ServerHandlerThrows()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Calls.BlockingUnaryCall(callDetails, "THROW");
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
}
throw new Exception("This was thrown on purpose by a test");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
var ex2 = Assert.Throws<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex2.Status.StatusCode);
}
[Test]
public void UnaryCall_ServerHandlerThrowsRpcException()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
{
Calls.BlockingUnaryCall(callDetails, "THROW_UNAUTHENTICATED");
Assert.Fail();
}
catch (RpcException e)
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Assert.AreEqual(StatusCode.Unauthenticated, e.Status.StatusCode);
}
throw new RpcException(new Status(StatusCode.Unauthenticated, ""));
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
var ex2 = Assert.Throws<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
}
[Test]
public void UnaryCall_ServerHandlerSetsStatus()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
Calls.BlockingUnaryCall(callDetails, "SET_UNAUTHENTICATED");
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Unauthenticated, e.Status.StatusCode);
}
}
context.Status = new Status(StatusCode.Unauthenticated, "");
return "";
});
[Test]
public async Task AsyncUnaryCall()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
var result = await Calls.AsyncUnaryCall(callDetails, "ABC");
Assert.AreEqual("ABC", result);
}
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
[Test]
public async Task AsyncUnaryCall_ServerHandlerThrows()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
try
{
await Calls.AsyncUnaryCall(callDetails, "THROW");
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
}
var ex2 = Assert.Throws<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
}
[Test]
public async Task ClientStreamingCall()
{
var callDetails = new CallInvocationDetails<string, string>(channel, ConcatAndEchoMethod, new CallOptions());
var call = Calls.AsyncClientStreamingCall(callDetails);
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
string result = "";
await requestStream.ForEachAsync(async (request) =>
{
result += request;
});
await Task.Delay(100);
return result;
});
await call.RequestStream.WriteAll(new string[] { "A", "B", "C" });
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
Assert.AreEqual("ABC", await call.ResponseAsync);
}
[Test]
public async Task ClientStreamingCall_CancelAfterBegin()
{
var barrier = new TaskCompletionSource<object>();
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
barrier.SetResult(null);
await requestStream.ToListAsync();
return "";
});
var cts = new CancellationTokenSource();
var callDetails = new CallInvocationDetails<string, string>(channel, ConcatAndEchoMethod, new CallOptions(cancellationToken: cts.Token));
var call = Calls.AsyncClientStreamingCall(callDetails);
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token)));
// TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it.
await Task.Delay(1000);
await barrier.Task; // make sure the handler has started.
cts.Cancel();
try
{
await call.ResponseAsync;
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode);
}
var ex = Assert.Throws<RpcException>(async () => await call.ResponseAsync);
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
[Test]
public void AsyncUnaryCall_EchoMetadata()
public async Task AsyncUnaryCall_EchoMetadata()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
foreach (Metadata.Entry metadataEntry in context.RequestHeaders)
{
if (metadataEntry.Key != "user-agent")
{
context.ResponseTrailers.Add(metadataEntry);
}
}
return "";
});
var headers = new Metadata
{
new Metadata.Entry("ascii-header", "abcdefg"),
new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }),
{ "ascii-header", "abcdefg" },
{ "binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff } }
};
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions(headers: headers));
var call = Calls.AsyncUnaryCall(callDetails, "ABC");
Assert.AreEqual("ABC", call.ResponseAsync.Result);
var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC");
await call;
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
@ -236,15 +211,18 @@ namespace Grpc.Core.Tests
public void UnaryCall_DisposedChannel()
{
channel.Dispose();
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(callDetails, "ABC"));
Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC"));
}
[Test]
public void UnaryCallPerformance()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
return request;
});
var callDetails = helper.CreateUnaryCall();
BenchmarkUtil.RunBenchmark(100, 100,
() => { Calls.BlockingUnaryCall(callDetails, "ABC"); });
}
@ -252,44 +230,57 @@ namespace Grpc.Core.Tests
[Test]
public void UnknownMethodHandler()
{
var callDetails = new CallInvocationDetails<string, string>(channel, NonexistentMethod, new CallOptions());
try
{
Calls.BlockingUnaryCall(callDetails, "ABC");
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode);
}
var nonexistentMethod = new Method<string, string>(
MethodType.Unary,
MockServiceHelper.ServiceName,
"NonExistentMethod",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
var callDetails = new CallInvocationDetails<string, string>(channel, nonexistentMethod, new CallOptions());
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(callDetails, "abc"));
Assert.AreEqual(StatusCode.Unimplemented, ex.Status.StatusCode);
}
[Test]
public void UserAgentStringPresent()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
string userAgent = Calls.BlockingUnaryCall(callDetails, "RETURN-USER-AGENT");
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
return context.RequestHeaders.Where(entry => entry.Key == "user-agent").Single().Value;
});
string userAgent = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc");
Assert.IsTrue(userAgent.StartsWith("grpc-csharp/"));
}
[Test]
public void PeerInfoPresent()
{
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
string peer = Calls.BlockingUnaryCall(callDetails, "RETURN-PEER");
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
return context.Peer;
});
string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc");
Assert.IsTrue(peer.Contains(Host));
}
[Test]
public async Task Channel_WaitForStateChangedAsync()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
return request;
});
Assert.Throws(typeof(TaskCanceledException),
async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(10)));
var stateChangedTask = channel.WaitForStateChangedAsync(channel.State);
var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions());
await Calls.AsyncUnaryCall(callDetails, "abc");
await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc");
await stateChangedTask;
Assert.AreEqual(ChannelState.Ready, channel.State);
@ -300,62 +291,9 @@ namespace Grpc.Core.Tests
{
await channel.ConnectAsync();
Assert.AreEqual(ChannelState.Ready, channel.State);
await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(1000));
Assert.AreEqual(ChannelState.Ready, channel.State);
}
private static async Task<string> EchoHandler(string request, ServerCallContext context)
{
foreach (Metadata.Entry metadataEntry in context.RequestHeaders)
{
if (metadataEntry.Key != "user-agent")
{
context.ResponseTrailers.Add(metadataEntry);
}
}
if (request == "RETURN-USER-AGENT")
{
return context.RequestHeaders.Where(entry => entry.Key == "user-agent").Single().Value;
}
if (request == "RETURN-PEER")
{
return context.Peer;
}
if (request == "THROW")
{
throw new Exception("This was thrown on purpose by a test");
}
if (request == "THROW_UNAUTHENTICATED")
{
throw new RpcException(new Status(StatusCode.Unauthenticated, ""));
}
if (request == "SET_UNAUTHENTICATED")
{
context.Status = new Status(StatusCode.Unauthenticated, "");
}
return request;
}
private static async Task<string> ConcatAndEchoHandler(IAsyncStreamReader<string> requestStream, ServerCallContext context)
{
string result = "";
await requestStream.ForEach(async (request) =>
{
if (request == "THROW")
{
throw new Exception("This was thrown on purpose by a test");
}
result += request;
});
// simulate processing takes some time.
await Task.Delay(250);
return result;
}
}
}

@ -0,0 +1,128 @@
#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.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class CompressionTest
{
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper();
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.Dispose();
server.ShutdownAsync().Wait();
}
[TestFixtureTearDown]
public void CleanupClass()
{
GrpcEnvironment.Shutdown();
}
[Test]
public void WriteOptions_Unary()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
return request;
});
var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress));
Calls.BlockingUnaryCall(helper.CreateUnaryCall(callOptions), "abc");
}
[Test]
public async Task WriteOptions_DuplexStreaming()
{
helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) =>
{
await requestStream.ToListAsync();
context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
await context.WriteResponseHeadersAsync(new Metadata { { "ascii-header", "abcdefg" } });
await responseStream.WriteAsync("X");
responseStream.WriteOptions = null;
await responseStream.WriteAsync("Y");
responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
await responseStream.WriteAsync("Z");
});
var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress));
var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(callOptions));
// check that write options from call options are propagated to request stream.
Assert.IsTrue((call.RequestStream.WriteOptions.Flags & WriteFlags.NoCompress) != 0);
call.RequestStream.WriteOptions = new WriteOptions();
await call.RequestStream.WriteAsync("A");
call.RequestStream.WriteOptions = null;
await call.RequestStream.WriteAsync("B");
call.RequestStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
await call.RequestStream.WriteAsync("C");
await call.RequestStream.CompleteAsync();
await call.ResponseStream.ToListAsync();
}
}
}

@ -0,0 +1,153 @@
#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.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ContextPropagationTest
{
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper();
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.Dispose();
server.ShutdownAsync().Wait();
}
[TestFixtureTearDown]
public void CleanupClass()
{
GrpcEnvironment.Shutdown();
}
[Test]
public async Task PropagateCancellation()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
// check that we didn't obtain the default cancellation token.
Assert.IsTrue(context.CancellationToken.CanBeCanceled);
return "PASS";
});
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
var propagationToken = context.CreatePropagationToken();
Assert.IsNotNull(propagationToken.ParentCall);
var callOptions = new CallOptions(propagationToken: propagationToken);
return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz");
});
var cts = new CancellationTokenSource();
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token)));
await call.RequestStream.CompleteAsync();
Assert.AreEqual("PASS", await call);
}
[Test]
public async Task PropagateDeadline()
{
var deadline = DateTime.UtcNow.AddDays(7);
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
Assert.IsTrue(context.Deadline < deadline.AddMinutes(1));
Assert.IsTrue(context.Deadline > deadline.AddMinutes(-1));
return "PASS";
});
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
Assert.Throws(typeof(ArgumentException), () =>
{
// Trying to override deadline while propagating deadline from parent call will throw.
Calls.BlockingUnaryCall(helper.CreateUnaryCall(
new CallOptions(deadline: DateTime.UtcNow.AddDays(8),
propagationToken: context.CreatePropagationToken())), "");
});
var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken());
return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz");
});
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: deadline)));
await call.RequestStream.CompleteAsync();
Assert.AreEqual("PASS", await call);
}
[Test]
public async Task SuppressDeadlinePropagation()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
Assert.AreEqual(DateTime.MaxValue, context.Deadline);
return "PASS";
});
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
Assert.IsTrue(context.CancellationToken.CanBeCanceled);
var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken(new ContextPropagationOptions(propagateDeadline: false)));
return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz");
});
var cts = new CancellationTokenSource();
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddDays(7))));
await call.RequestStream.CompleteAsync();
Assert.AreEqual("PASS", await call);
}
}
}

@ -63,6 +63,7 @@
<Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link>
</Compile>
<Compile Include="ClientBaseTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ClientServerTest.cs" />
<Compile Include="ServerTest.cs" />
@ -77,6 +78,10 @@
<Compile Include="TimeoutsTest.cs" />
<Compile Include="NUnitVersionTest.cs" />
<Compile Include="ChannelTest.cs" />
<Compile Include="MockServiceHelper.cs" />
<Compile Include="ResponseHeadersTest.cs" />
<Compile Include="CompressionTest.cs" />
<Compile Include="ContextPropagationTest.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

@ -69,5 +69,13 @@ namespace Grpc.Core.Tests
Assert.IsFalse(object.ReferenceEquals(env1, env2));
}
[Test]
public void GetCoreVersionString()
{
var coreVersion = GrpcEnvironment.GetCoreVersionString();
var parts = coreVersion.Split('.');
Assert.AreEqual(4, parts.Length);
}
}
}

@ -53,8 +53,8 @@ namespace Grpc.Core.Internal.Tests
{
var metadata = new Metadata
{
new Metadata.Entry("host", "somehost"),
new Metadata.Entry("header2", "header value"),
{ "host", "somehost" },
{ "header2", "header value" },
};
var nativeMetadata = MetadataArraySafeHandle.Create(metadata);
nativeMetadata.Dispose();
@ -65,8 +65,8 @@ namespace Grpc.Core.Internal.Tests
{
var metadata = new Metadata
{
new Metadata.Entry("host", "somehost"),
new Metadata.Entry("header2", "header value"),
{ "host", "somehost" },
{ "header2", "header value" }
};
var nativeMetadata = MetadataArraySafeHandle.Create(metadata);

@ -0,0 +1,244 @@
#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.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
/// <summary>
/// Allows setting up a mock service in the client-server tests easily.
/// </summary>
public class MockServiceHelper
{
public const string ServiceName = "tests.Test";
public static readonly Method<string, string> UnaryMethod = new Method<string, string>(
MethodType.Unary,
ServiceName,
"Unary",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
public static readonly Method<string, string> ClientStreamingMethod = new Method<string, string>(
MethodType.ClientStreaming,
ServiceName,
"ClientStreaming",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
public static readonly Method<string, string> ServerStreamingMethod = new Method<string, string>(
MethodType.ServerStreaming,
ServiceName,
"ServerStreaming",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
public static readonly Method<string, string> DuplexStreamingMethod = new Method<string, string>(
MethodType.DuplexStreaming,
ServiceName,
"DuplexStreaming",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
readonly string host;
readonly ServerServiceDefinition serviceDefinition;
UnaryServerMethod<string, string> unaryHandler;
ClientStreamingServerMethod<string, string> clientStreamingHandler;
ServerStreamingServerMethod<string, string> serverStreamingHandler;
DuplexStreamingServerMethod<string, string> duplexStreamingHandler;
Server server;
Channel channel;
public MockServiceHelper(string host = null)
{
this.host = host ?? "localhost";
serviceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName)
.AddMethod(UnaryMethod, (request, context) => unaryHandler(request, context))
.AddMethod(ClientStreamingMethod, (requestStream, context) => clientStreamingHandler(requestStream, context))
.AddMethod(ServerStreamingMethod, (request, responseStream, context) => serverStreamingHandler(request, responseStream, context))
.AddMethod(DuplexStreamingMethod, (requestStream, responseStream, context) => duplexStreamingHandler(requestStream, responseStream, context))
.Build();
var defaultStatus = new Status(StatusCode.Unknown, "Default mock implementation. Please provide your own.");
unaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
context.Status = defaultStatus;
return "";
});
clientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
context.Status = defaultStatus;
return "";
});
serverStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) =>
{
context.Status = defaultStatus;
});
duplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) =>
{
context.Status = defaultStatus;
});
}
/// <summary>
/// Returns the default server for this service and creates one if not yet created.
/// </summary>
public Server GetServer()
{
if (server == null)
{
server = new Server
{
Services = { serviceDefinition },
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
}
return server;
}
/// <summary>
/// Returns the default channel for this service and creates one if not yet created.
/// </summary>
public Channel GetChannel()
{
if (channel == null)
{
channel = new Channel(Host, GetServer().Ports.Single().BoundPort, Credentials.Insecure);
}
return channel;
}
public CallInvocationDetails<string, string> CreateUnaryCall(CallOptions options = default(CallOptions))
{
return new CallInvocationDetails<string, string>(channel, UnaryMethod, options);
}
public CallInvocationDetails<string, string> CreateClientStreamingCall(CallOptions options = default(CallOptions))
{
return new CallInvocationDetails<string, string>(channel, ClientStreamingMethod, options);
}
public CallInvocationDetails<string, string> CreateServerStreamingCall(CallOptions options = default(CallOptions))
{
return new CallInvocationDetails<string, string>(channel, ServerStreamingMethod, options);
}
public CallInvocationDetails<string, string> CreateDuplexStreamingCall(CallOptions options = default(CallOptions))
{
return new CallInvocationDetails<string, string>(channel, DuplexStreamingMethod, options);
}
public string Host
{
get
{
return this.host;
}
}
public ServerServiceDefinition ServiceDefinition
{
get
{
return this.serviceDefinition;
}
}
public UnaryServerMethod<string, string> UnaryHandler
{
get
{
return this.unaryHandler;
}
set
{
unaryHandler = value;
}
}
public ClientStreamingServerMethod<string, string> ClientStreamingHandler
{
get
{
return this.clientStreamingHandler;
}
set
{
clientStreamingHandler = value;
}
}
public ServerStreamingServerMethod<string, string> ServerStreamingHandler
{
get
{
return this.serverStreamingHandler;
}
set
{
serverStreamingHandler = value;
}
}
public DuplexStreamingServerMethod<string, string> DuplexStreamingHandler
{
get
{
return this.duplexStreamingHandler;
}
set
{
duplexStreamingHandler = value;
}
}
}
}

@ -0,0 +1,136 @@
#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.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
/// <summary>
/// Tests for response headers support.
/// </summary>
public class ResponseHeadersTest
{
MockServiceHelper helper;
Server server;
Channel channel;
Metadata headers;
[SetUp]
public void Init()
{
helper = new MockServiceHelper();
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
headers = new Metadata { { "ascii-header", "abcdefg" } };
}
[TearDown]
public void Cleanup()
{
channel.Dispose();
server.ShutdownAsync().Wait();
}
[TestFixtureTearDown]
public void CleanupClass()
{
GrpcEnvironment.Shutdown();
}
[Test]
public void WriteResponseHeaders_NullNotAllowed()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
Assert.Throws(typeof(ArgumentNullException), async () => await context.WriteResponseHeadersAsync(null));
return "PASS";
});
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
}
[Test]
public void WriteResponseHeaders_AllowedOnlyOnce()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
await context.WriteResponseHeadersAsync(headers);
try
{
await context.WriteResponseHeadersAsync(headers);
Assert.Fail();
}
catch (InvalidOperationException expected)
{
}
return "PASS";
});
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
}
[Test]
public async Task WriteResponseHeaders_NotAllowedAfterWrite()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) =>
{
await responseStream.WriteAsync("A");
try
{
await context.WriteResponseHeadersAsync(headers);
Assert.Fail();
}
catch (InvalidOperationException expected)
{
}
await responseStream.WriteAsync("B");
});
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
var responses = await call.ResponseStream.ToListAsync();
CollectionAssert.AreEqual(new[] { "A", "B" }, responses);
}
}
}

@ -48,38 +48,18 @@ namespace Grpc.Core.Tests
/// </summary>
public class TimeoutsTest
{
const string Host = "localhost";
const string ServiceName = "tests.Test";
static readonly Method<string, string> TestMethod = new Method<string, string>(
MethodType.Unary,
ServiceName,
"Test",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
static readonly ServerServiceDefinition ServiceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName)
.AddMethod(TestMethod, TestMethodHandler)
.Build();
// provides a way how to retrieve an out-of-band result value from server handler
static TaskCompletionSource<string> stringFromServerHandlerTcs;
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
server = new Server
{
Services = { ServiceDefinition },
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
server.Start();
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
helper = new MockServiceHelper();
stringFromServerHandlerTcs = new TaskCompletionSource<string>();
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
@ -98,115 +78,83 @@ namespace Grpc.Core.Tests
[Test]
public void InfiniteDeadline()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
Assert.AreEqual(DateTime.MaxValue, context.Deadline);
return "PASS";
});
// no deadline specified, check server sees infinite deadline
var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions());
Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE"));
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
// DateTime.MaxValue deadline specified, check server sees infinite deadline
var callDetails2 = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions());
Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails2, "RETURN_DEADLINE"));
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MaxValue)), "abc"));
}
[Test]
public void DeadlineTransferredToServer()
{
var remainingTimeClient = TimeSpan.FromDays(7);
var deadline = DateTime.UtcNow + remainingTimeClient;
Thread.Sleep(1000);
var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
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
// are in agreement. C core takes care of the work with transferring deadline over the wire,
// so we don't need an exact check here.
Assert.IsTrue(Math.Abs((deadline - serverDeadline).TotalMilliseconds) < 5000);
var clientDeadline = DateTime.UtcNow + TimeSpan.FromDays(7);
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
// A fairly relaxed check that the deadline set by client and deadline seen by server
// are in agreement. C core takes care of the work with transferring deadline over the wire,
// so we don't need an exact check here.
Assert.IsTrue(Math.Abs((clientDeadline - context.Deadline).TotalMilliseconds) < 5000);
return "PASS";
});
Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: clientDeadline)), "abc");
}
[Test]
public void DeadlineInThePast()
{
var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: DateTime.MinValue));
try
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
Calls.BlockingUnaryCall(callDetails, "TIMEOUT");
Assert.Fail();
}
catch (RpcException e)
{
// We can't guarantee the status code always DeadlineExceeded. See issue #2685.
Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
}
await Task.Delay(60000);
return "FAIL";
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MinValue)), "abc"));
// We can't guarantee the status code always DeadlineExceeded. See issue #2685.
Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
}
[Test]
public void DeadlineExceededStatusOnTimeout()
{
var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
try
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
Calls.BlockingUnaryCall(callDetails, "TIMEOUT");
Assert.Fail();
}
catch (RpcException e)
{
// We can't guarantee the status code always DeadlineExceeded. See issue #2685.
Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
}
await Task.Delay(60000);
return "FAIL";
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc"));
// We can't guarantee the status code always DeadlineExceeded. See issue #2685.
Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
}
[Test]
public void ServerReceivesCancellationOnTimeout()
public async Task ServerReceivesCancellationOnTimeout()
{
var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
var serverReceivedCancellationTcs = new TaskCompletionSource<bool>();
try
{
Calls.BlockingUnaryCall(callDetails, "CHECK_CANCELLATION_RECEIVED");
Assert.Fail();
}
catch (RpcException e)
{
// We can't guarantee the status code is always DeadlineExceeded. See issue #2685.
Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
}
Assert.AreEqual("CANCELLED", stringFromServerHandlerTcs.Task.Result);
}
private static async Task<string> TestMethodHandler(string request, ServerCallContext context)
{
if (request == "TIMEOUT")
{
await Task.Delay(60000);
return "";
}
if (request == "RETURN_DEADLINE")
{
if (context.Deadline == DateTime.MaxValue)
{
return "DATETIME_MAXVALUE";
}
return context.Deadline.Ticks.ToString();
}
if (request == "CHECK_CANCELLATION_RECEIVED")
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
// wait until cancellation token is fired.
var tcs = new TaskCompletionSource<object>();
context.CancellationToken.Register(() => { tcs.SetResult(null); });
await tcs.Task;
stringFromServerHandlerTcs.SetResult("CANCELLED");
serverReceivedCancellationTcs.SetResult(true);
return "";
}
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc"));
// We can't guarantee the status code always DeadlineExceeded. See issue #2685.
Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
return "";
Assert.IsTrue(await serverReceivedCancellationTcs.Task);
}
}
}

@ -40,30 +40,60 @@ namespace Grpc.Core
/// <summary>
/// Details about a client-side call to be invoked.
/// </summary>
public class CallInvocationDetails<TRequest, TResponse>
public struct CallInvocationDetails<TRequest, TResponse>
{
readonly Channel channel;
readonly string method;
readonly string host;
readonly Marshaller<TRequest> requestMarshaller;
readonly Marshaller<TResponse> responseMarshaller;
readonly CallOptions options;
CallOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct.
/// </summary>
/// <param name="channel">Channel to use for this call.</param>
/// <param name="method">Method to call.</param>
/// <param name="options">Call options.</param>
public CallInvocationDetails(Channel channel, Method<TRequest, TResponse> method, CallOptions options) :
this(channel, method.FullName, null, method.RequestMarshaller, method.ResponseMarshaller, options)
this(channel, method, null, options)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct.
/// </summary>
/// <param name="channel">Channel to use for this call.</param>
/// <param name="method">Method to call.</param>
/// <param name="host">Host that contains the method. if <c>null</c>, default host will be used.</param>
/// <param name="options">Call options.</param>
public CallInvocationDetails(Channel channel, Method<TRequest, TResponse> method, string host, CallOptions options) :
this(channel, method.FullName, host, method.RequestMarshaller, method.ResponseMarshaller, options)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct.
/// </summary>
/// <param name="channel">Channel to use for this call.</param>
/// <param name="method">Qualified method name.</param>
/// <param name="host">Host that contains the method.</param>
/// <param name="requestMarshaller">Request marshaller.</param>
/// <param name="responseMarshaller">Response marshaller.</param>
/// <param name="options">Call options.</param>
public CallInvocationDetails(Channel channel, string method, string host, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller, CallOptions options)
{
this.channel = Preconditions.CheckNotNull(channel);
this.method = Preconditions.CheckNotNull(method);
this.channel = Preconditions.CheckNotNull(channel, "channel");
this.method = Preconditions.CheckNotNull(method, "method");
this.host = host;
this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller);
this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller);
this.options = Preconditions.CheckNotNull(options);
this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller, "requestMarshaller");
this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller, "responseMarshaller");
this.options = options;
}
/// <summary>
/// Get channel associated with this call.
/// </summary>
public Channel Channel
{
get
@ -72,6 +102,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets name of method to be called.
/// </summary>
public string Method
{
get
@ -80,6 +113,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Get name of host.
/// </summary>
public string Host
{
get
@ -88,6 +124,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets marshaller used to serialize requests.
/// </summary>
public Marshaller<TRequest> RequestMarshaller
{
get
@ -96,6 +135,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets marshaller used to deserialized responses.
/// </summary>
public Marshaller<TResponse> ResponseMarshaller
{
get
@ -104,6 +146,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets the call options.
/// </summary>
public CallOptions Options
{
get
@ -111,5 +156,16 @@ namespace Grpc.Core
return options;
}
}
/// <summary>
/// Returns new instance of <see cref="CallInvocationDetails"/> with
/// <c>Options</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
public CallInvocationDetails<TRequest, TResponse> WithOptions(CallOptions options)
{
var newDetails = this;
newDetails.options = options;
return newDetails;
}
}
}

@ -42,24 +42,30 @@ namespace Grpc.Core
/// <summary>
/// Options for calls made by client.
/// </summary>
public class CallOptions
public struct CallOptions
{
readonly Metadata headers;
readonly DateTime deadline;
readonly CancellationToken cancellationToken;
Metadata headers;
DateTime? deadline;
CancellationToken cancellationToken;
WriteOptions writeOptions;
ContextPropagationToken propagationToken;
/// <summary>
/// Creates a new instance of <c>CallOptions</c>.
/// Creates a new instance of <c>CallOptions</c> struct.
/// </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))
/// <param name="writeOptions">Write options that will be used for this call.</param>
/// <param name="propagationToken">Context propagation token obtained from <see cref="ServerCallContext"/>.</param>
public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken),
WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null)
{
// 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.headers = headers;
this.deadline = deadline;
this.cancellationToken = cancellationToken;
this.writeOptions = writeOptions;
this.propagationToken = propagationToken;
}
/// <summary>
@ -73,7 +79,7 @@ namespace Grpc.Core
/// <summary>
/// Call deadline.
/// </summary>
public DateTime Deadline
public DateTime? Deadline
{
get { return deadline; }
}
@ -85,5 +91,88 @@ namespace Grpc.Core
{
get { return cancellationToken; }
}
/// <summary>
/// Write options that will be used for this call.
/// </summary>
public WriteOptions WriteOptions
{
get
{
return this.writeOptions;
}
}
/// <summary>
/// Token for propagating parent call context.
/// </summary>
public ContextPropagationToken PropagationToken
{
get
{
return this.propagationToken;
}
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Headers</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
public CallOptions WithHeaders(Metadata headers)
{
var newOptions = this;
newOptions.headers = headers;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Deadline</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
public CallOptions WithDeadline(DateTime deadline)
{
var newOptions = this;
newOptions.deadline = deadline;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>CancellationToken</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
public CallOptions WithCancellationToken(CancellationToken cancellationToken)
{
var newOptions = this;
newOptions.cancellationToken = cancellationToken;
return newOptions;
}
/// <summary>
/// Returns a new instance of <see cref="CallOptions"/> with
/// all previously unset values set to their defaults and deadline and cancellation
/// token propagated when appropriate.
/// </summary>
internal CallOptions Normalize()
{
var newOptions = this;
if (propagationToken != null)
{
if (propagationToken.Options.IsPropagateDeadline)
{
Preconditions.CheckArgument(!newOptions.deadline.HasValue,
"Cannot propagate deadline from parent call. The deadline has already been set explicitly.");
newOptions.deadline = propagationToken.ParentDeadline;
}
if (propagationToken.Options.IsPropagateCancellation)
{
Preconditions.CheckArgument(!newOptions.cancellationToken.CanBeCanceled,
"Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value.");
}
}
newOptions.headers = newOptions.headers ?? Metadata.Empty;
newOptions.deadline = newOptions.deadline ?? DateTime.MaxValue;
return newOptions;
}
}
}

@ -31,8 +31,6 @@
#endregion
using System;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Internal;
@ -40,9 +38,20 @@ namespace Grpc.Core
{
/// <summary>
/// Helper methods for generated clients to make RPC calls.
/// Most users will use this class only indirectly and will be
/// making calls using client object generated from protocol
/// buffer definition files.
/// </summary>
public static class Calls
{
/// <summary>
/// Invokes a simple remote call in a blocking fashion.
/// </summary>
/// <returns>The response.</returns>
/// <param name="call">The call defintion.</param>
/// <param name="req">Request message.</param>
/// <typeparam name="TRequest">Type of request message.</typeparam>
/// <typeparam name="TResponse">The of response message.</typeparam>
public static TResponse BlockingUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req)
where TRequest : class
where TResponse : class
@ -51,6 +60,14 @@ namespace Grpc.Core
return asyncCall.UnaryCall(req);
}
/// <summary>
/// Invokes a simple remote call asynchronously.
/// </summary>
/// <returns>An awaitable call object providing access to the response.</returns>
/// <param name="call">The call defintion.</param>
/// <param name="req">Request message.</param>
/// <typeparam name="TRequest">Type of request message.</typeparam>
/// <typeparam name="TResponse">The of response message.</typeparam>
public static AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req)
where TRequest : class
where TResponse : class
@ -60,6 +77,15 @@ namespace Grpc.Core
return new AsyncUnaryCall<TResponse>(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
/// <summary>
/// Invokes a server streaming call asynchronously.
/// In server streaming scenario, client sends on request and server responds with a stream of responses.
/// </summary>
/// <returns>A call object providing access to the asynchronous response stream.</returns>
/// <param name="call">The call defintion.</param>
/// <param name="req">Request message.</param>
/// <typeparam name="TRequest">Type of request message.</typeparam>
/// <typeparam name="TResponse">The of response messages.</typeparam>
public static AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req)
where TRequest : class
where TResponse : class
@ -70,6 +96,13 @@ namespace Grpc.Core
return new AsyncServerStreamingCall<TResponse>(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
/// <summary>
/// Invokes a client streaming call asynchronously.
/// In client streaming scenario, client sends a stream of requests and server responds with a single response.
/// </summary>
/// <returns>An awaitable call object providing access to the response.</returns>
/// <typeparam name="TRequest">Type of request messages.</typeparam>
/// <typeparam name="TResponse">The of response message.</typeparam>
public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call)
where TRequest : class
where TResponse : class
@ -80,6 +113,15 @@ namespace Grpc.Core
return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
/// <summary>
/// Invokes a duplex streaming call asynchronously.
/// In duplex streaming scenario, client sends a stream of requests and server responds with a stream of responses.
/// The response stream is completely independent and both side can be sending messages at the same time.
/// </summary>
/// <returns>A call object providing access to the asynchronous request and response streams.</returns>
/// <param name="call">The call definition.</param>
/// <typeparam name="TRequest">Type of request messages.</typeparam>
/// <typeparam name="TResponse">Type of reponse messages.</typeparam>
public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call)
where TRequest : class
where TResponse : class

@ -49,6 +49,7 @@ namespace Grpc.Core
{
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Channel>();
readonly string target;
readonly GrpcEnvironment environment;
readonly ChannelSafeHandle handle;
readonly List<ChannelOption> options;
@ -58,12 +59,12 @@ namespace Grpc.Core
/// Creates a channel that connects to a specific host.
/// Port will default to 80 for an unsecure channel and to 443 for a secure channel.
/// </summary>
/// <param name="host">The name or IP address of the host.</param>
/// <param name="target">Target of the channel.</param>
/// <param name="credentials">Credentials to secure the channel.</param>
/// <param name="options">Channel options.</param>
public Channel(string host, Credentials credentials, IEnumerable<ChannelOption> options = null)
public Channel(string target, Credentials credentials, IEnumerable<ChannelOption> options = null)
{
Preconditions.CheckNotNull(host);
this.target = Preconditions.CheckNotNull(target, "target");
this.environment = GrpcEnvironment.GetInstance();
this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>();
@ -73,11 +74,11 @@ namespace Grpc.Core
{
if (nativeCredentials != null)
{
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, host, nativeChannelArgs);
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs);
}
else
{
this.handle = ChannelSafeHandle.CreateInsecure(host, nativeChannelArgs);
this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs);
}
}
}
@ -131,8 +132,8 @@ namespace Grpc.Core
return tcs.Task;
}
/// <summary> Address of the remote endpoint in URI format.</summary>
public string Target
/// <summary>Resolved address of the remote endpoint in URI format.</summary>
public string ResolvedTarget
{
get
{
@ -140,6 +141,15 @@ namespace Grpc.Core
}
}
/// <summary>The original target used to create the channel.</summary>
public string Target
{
get
{
return this.target;
}
}
/// <summary>
/// Allows explicitly requesting channel to connect without starting an RPC.
/// Returned task completes once state Ready was seen. If the deadline is reached,

@ -63,8 +63,8 @@ namespace Grpc.Core
public ChannelOption(string name, string stringValue)
{
this.type = OptionType.String;
this.name = Preconditions.CheckNotNull(name);
this.stringValue = Preconditions.CheckNotNull(stringValue);
this.name = Preconditions.CheckNotNull(name, "name");
this.stringValue = Preconditions.CheckNotNull(stringValue, "stringValue");
}
/// <summary>
@ -75,7 +75,7 @@ namespace Grpc.Core
public ChannelOption(string name, int intValue)
{
this.type = OptionType.Integer;
this.name = Preconditions.CheckNotNull(name);
this.name = Preconditions.CheckNotNull(name, "name");
this.intValue = intValue;
}

@ -33,23 +33,30 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
public delegate void MetadataInterceptorDelegate(Metadata metadata);
public delegate void MetadataInterceptorDelegate(string authUri, Metadata metadata);
/// <summary>
/// Base class for client-side stubs.
/// </summary>
public abstract class ClientBase
{
// Regex for removal of the optional DNS scheme, trailing port, and trailing backslash
static readonly Regex ChannelTargetPattern = new Regex(@"^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$");
readonly Channel channel;
readonly string authUriBase;
public ClientBase(Channel channel)
{
this.channel = channel;
this.authUriBase = GetAuthUriBase(channel.Target);
}
/// <summary>
@ -62,6 +69,18 @@ namespace Grpc.Core
set;
}
/// <summary>
/// gRPC supports multiple "hosts" being served by a single server.
/// This property can be used to set the target host explicitly.
/// By default, this will be set to <c>null</c> with the meaning
/// "use default host".
/// </summary>
public string Host
{
get;
set;
}
/// <summary>
/// Channel associated with this client.
/// </summary>
@ -83,10 +102,27 @@ namespace Grpc.Core
var interceptor = HeaderInterceptor;
if (interceptor != null)
{
interceptor(options.Headers);
options.Headers.Freeze();
if (options.Headers == null)
{
options = options.WithHeaders(new Metadata());
}
var authUri = authUriBase != null ? authUriBase + method.ServiceName : null;
interceptor(authUri, options.Headers);
}
return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options);
}
/// <summary>
/// Creates Auth URI base from channel's target (the one passed at channel creation).
/// Fully-qualified service name is to be appended to this.
/// </summary>
internal static string GetAuthUriBase(string target)
{
var match = ChannelTargetPattern.Match(target);
if (!match.Success) {
return null;
}
return new CallInvocationDetails<TRequest, TResponse>(channel, method, options);
return "https://" + match.Groups[2].Value + "/";
}
}
}

@ -33,25 +33,31 @@
using System;
namespace Grpc.Core.Utils
namespace Grpc.Core
{
public static class ExceptionHelper
/// <summary>
/// Compression level based on grpc_compression_level from grpc/compression.h
/// </summary>
public enum CompressionLevel
{
/// <summary>
/// If inner exceptions contain RpcException, rethrows it.
/// Otherwise, rethrows the original aggregate exception.
/// Always throws, the exception return type is here only to make the.
/// No compression.
/// </summary>
public static Exception UnwrapRpcException(AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
{
if (e is RpcException)
{
throw e;
}
}
throw ae;
}
None = 0,
/// <summary>
/// Low compression.
/// </summary>
Low,
/// <summary>
/// Medium compression.
/// </summary>
Medium,
/// <summary>
/// High compression.
/// </summary>
High,
}
}

@ -0,0 +1,171 @@
#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>
/// Token for propagating context of server side handlers to child calls.
/// In situations when a backend is making calls to another backend,
/// it makes sense to propagate properties like deadline and cancellation
/// token of the server call to the child call.
/// C core provides some other contexts (like tracing context) that
/// are not accessible to C# layer, but this token still allows propagating them.
/// </summary>
public class ContextPropagationToken
{
/// <summary>
/// Default propagation mask used by C core.
/// </summary>
private const ContextPropagationFlags DefaultCoreMask = (ContextPropagationFlags)0xffff;
/// <summary>
/// Default propagation mask used by C# - we want to propagate deadline
/// and cancellation token by our own means.
/// </summary>
internal const ContextPropagationFlags DefaultMask = DefaultCoreMask
& ~ContextPropagationFlags.Deadline & ~ContextPropagationFlags.Cancellation;
readonly CallSafeHandle parentCall;
readonly DateTime deadline;
readonly CancellationToken cancellationToken;
readonly ContextPropagationOptions options;
internal ContextPropagationToken(CallSafeHandle parentCall, DateTime deadline, CancellationToken cancellationToken, ContextPropagationOptions options)
{
this.parentCall = Preconditions.CheckNotNull(parentCall);
this.deadline = deadline;
this.cancellationToken = cancellationToken;
this.options = options ?? ContextPropagationOptions.Default;
}
/// <summary>
/// Gets the native handle of the parent call.
/// </summary>
internal CallSafeHandle ParentCall
{
get
{
return this.parentCall;
}
}
/// <summary>
/// Gets the parent call's deadline.
/// </summary>
internal DateTime ParentDeadline
{
get
{
return this.deadline;
}
}
/// <summary>
/// Gets the parent call's cancellation token.
/// </summary>
internal CancellationToken ParentCancellationToken
{
get
{
return this.cancellationToken;
}
}
/// <summary>
/// Get the context propagation options.
/// </summary>
internal ContextPropagationOptions Options
{
get
{
return this.options;
}
}
}
/// <summary>
/// Options for <see cref="ContextPropagationToken"/>.
/// </summary>
public class ContextPropagationOptions
{
/// <summary>
/// The context propagation options that will be used by default.
/// </summary>
public static readonly ContextPropagationOptions Default = new ContextPropagationOptions();
bool propagateDeadline;
bool propagateCancellation;
/// <summary>
/// Creates new context propagation options.
/// </summary>
/// <param name="propagateDeadline">If set to <c>true</c> parent call's deadline will be propagated to the child call.</param>
/// <param name="propagateCancellation">If set to <c>true</c> parent call's cancellation token will be propagated to the child call.</param>
public ContextPropagationOptions(bool propagateDeadline = true, bool propagateCancellation = true)
{
this.propagateDeadline = propagateDeadline;
this.propagateCancellation = propagateCancellation;
}
/// <value><c>true</c> if parent call's deadline should be propagated to the child call.</value>
public bool IsPropagateDeadline
{
get { return this.propagateDeadline; }
}
/// <value><c>true</c> if parent call's cancellation token should be propagated to the child call.</value>
public bool IsPropagateCancellation
{
get { return this.propagateCancellation; }
}
}
/// <summary>
/// Context propagation flags from grpc/grpc.h.
/// </summary>
[Flags]
internal enum ContextPropagationFlags
{
Deadline = 1,
CensusStatsContext = 2,
CensusTracingContext = 4,
Cancellation = 8
}
}

@ -77,14 +77,12 @@
<Compile Include="ServerServiceDefinition.cs" />
<Compile Include="Utils\AsyncStreamExtensions.cs" />
<Compile Include="Utils\BenchmarkUtil.cs" />
<Compile Include="Utils\ExceptionHelper.cs" />
<Compile Include="Internal\CredentialsSafeHandle.cs" />
<Compile Include="Credentials.cs" />
<Compile Include="Internal\ChannelArgsSafeHandle.cs" />
<Compile Include="Internal\AsyncCompletion.cs" />
<Compile Include="Internal\AsyncCallBase.cs" />
<Compile Include="Internal\AsyncCallServer.cs" />
<Compile Include="OperationFailedException.cs" />
<Compile Include="Internal\AsyncCall.cs" />
<Compile Include="Utils\Preconditions.cs" />
<Compile Include="Internal\ServerCredentialsSafeHandle.cs" />
@ -115,6 +113,9 @@
<Compile Include="ChannelState.cs" />
<Compile Include="CallInvocationDetails.cs" />
<Compile Include="CallOptions.cs" />
<Compile Include="CompressionLevel.cs" />
<Compile Include="WriteOptions.cs" />
<Compile Include="ContextPropagationToken.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Grpc.Core.nuspec" />

@ -53,6 +53,9 @@ namespace Grpc.Core
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_shutdown();
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpcsharp_version_string(); // returns not-owned const char*
static object staticLock = new object();
static GrpcEnvironment instance;
@ -112,7 +115,7 @@ namespace Grpc.Core
/// </summary>
public static void SetLogger(ILogger customLogger)
{
Preconditions.CheckNotNull(customLogger);
Preconditions.CheckNotNull(customLogger, "customLogger");
logger = customLogger;
}
@ -163,6 +166,15 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets version of gRPC C core.
/// </summary>
internal static string GetCoreVersionString()
{
var ptr = grpcsharp_version_string(); // the pointer is not owned
return Marshal.PtrToStringAnsi(ptr);
}
/// <summary>
/// Shuts down this environment.
/// </summary>
@ -180,23 +192,5 @@ namespace Grpc.Core
Logger.Info("gRPC shutdown.");
}
/// <summary>
/// Shuts down this environment asynchronously.
/// </summary>
private Task CloseAsync()
{
return Task.Run(() =>
{
try
{
Close();
}
catch (Exception e)
{
Logger.Error(e, "Error occured while shutting down GrpcEnvironment.");
}
});
}
}
}

@ -43,7 +43,7 @@ namespace Grpc.Core
/// A stream of messages to be read.
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IAsyncStreamReader<TResponse> : IAsyncEnumerator<TResponse>
public interface IAsyncStreamReader<T> : IAsyncEnumerator<T>
{
// TODO(jtattermusch): consider just using IAsyncEnumerator instead of this interface.
}

@ -50,5 +50,13 @@ namespace Grpc.Core
/// </summary>
/// <param name="message">the message to be written. Cannot be null.</param>
Task WriteAsync(T message);
/// <summary>
/// Write options that will be used for the next write.
/// If null, default options will be used.
/// Once set, this property maintains its value across subsequent
/// writes.
/// <value>The write options.</value>
WriteOptions WriteOptions { get; set; }
}
}

@ -50,7 +50,7 @@ namespace Grpc.Core.Internal
{
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCall<TRequest, TResponse>>();
readonly CallInvocationDetails<TRequest, TResponse> callDetails;
readonly CallInvocationDetails<TRequest, TResponse> details;
// Completion of a pending unary response if not null.
TaskCompletionSource<TResponse> unaryResponseTcs;
@ -63,7 +63,8 @@ namespace Grpc.Core.Internal
public AsyncCall(CallInvocationDetails<TRequest, TResponse> callDetails)
: base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer)
{
this.callDetails = callDetails;
this.details = callDetails.WithOptions(callDetails.Options.Normalize());
this.initialMetadataSent = true; // we always send metadata at the very beginning of the call.
}
// TODO: this method is not Async, so it shouldn't be in AsyncCall class, but
@ -89,11 +90,11 @@ namespace Grpc.Core.Internal
readingDone = true;
}
using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
using (var ctx = BatchContextSafeHandle.Create())
{
call.StartUnary(payload, ctx, metadataArray);
call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall());
var ev = cq.Pluck(ctx.Handle);
bool success = (ev.success != 0);
@ -108,15 +109,9 @@ namespace Grpc.Core.Internal
}
}
try
{
// Once the blocking call returns, the result should be available synchronously.
return unaryResponseTcs.Task.Result;
}
catch (AggregateException ae)
{
throw ExceptionHelper.UnwrapRpcException(ae);
}
// Once the blocking call returns, the result should be available synchronously.
// Note that GetAwaiter().GetResult() doesn't wrap exceptions in AggregateException.
return unaryResponseTcs.Task.GetAwaiter().GetResult();
}
}
@ -130,7 +125,7 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
Initialize(callDetails.Channel.Environment.CompletionQueue);
Initialize(details.Channel.Environment.CompletionQueue);
halfcloseRequested = true;
readingDone = true;
@ -138,9 +133,9 @@ namespace Grpc.Core.Internal
byte[] payload = UnsafeSerialize(msg);
unaryResponseTcs = new TaskCompletionSource<TResponse>();
using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
call.StartUnary(payload, HandleUnaryResponse, metadataArray);
call.StartUnary(HandleUnaryResponse, payload, metadataArray, GetWriteFlagsForCall());
}
return unaryResponseTcs.Task;
}
@ -157,12 +152,12 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
Initialize(callDetails.Channel.Environment.CompletionQueue);
Initialize(details.Channel.Environment.CompletionQueue);
readingDone = true;
unaryResponseTcs = new TaskCompletionSource<TResponse>();
using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
call.StartClientStreaming(HandleUnaryResponse, metadataArray);
}
@ -181,16 +176,16 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
Initialize(callDetails.Channel.Environment.CompletionQueue);
Initialize(details.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(callDetails.Options.Headers))
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
call.StartServerStreaming(payload, HandleFinished, metadataArray);
call.StartServerStreaming(HandleFinished, payload, metadataArray, GetWriteFlagsForCall());
}
}
}
@ -206,9 +201,9 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
Initialize(callDetails.Channel.Environment.CompletionQueue);
Initialize(details.Channel.Environment.CompletionQueue);
using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers))
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
call.StartDuplexStreaming(HandleFinished, metadataArray);
}
@ -219,9 +214,9 @@ namespace Grpc.Core.Internal
/// Sends a streaming request. Only one pending send action is allowed at any given time.
/// completionDelegate is called when the operation finishes.
/// </summary>
public void StartSendMessage(TRequest msg, AsyncCompletionDelegate<object> completionDelegate)
public void StartSendMessage(TRequest msg, WriteFlags writeFlags, AsyncCompletionDelegate<object> completionDelegate)
{
StartSendMessageInternal(msg, completionDelegate);
StartSendMessageInternal(msg, writeFlags, completionDelegate);
}
/// <summary>
@ -278,6 +273,14 @@ namespace Grpc.Core.Internal
}
}
public CallInvocationDetails<TRequest, TResponse> Details
{
get
{
return this.details;
}
}
/// <summary>
/// On client-side, we only fire readCompletionDelegate once all messages have been read
/// and status has been received.
@ -310,14 +313,17 @@ namespace Grpc.Core.Internal
protected override void OnReleaseResources()
{
callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Decrement();
details.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();
var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance;
var call = details.Channel.Handle.CreateCall(details.Channel.Environment.CompletionRegistry,
parentCall, ContextPropagationToken.DefaultMask, cq,
details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value));
details.Channel.Environment.DebugStats.ActiveClientCalls.Increment();
InitializeInternal(call);
RegisterCancellationCallback();
}
@ -325,13 +331,22 @@ namespace Grpc.Core.Internal
// Make sure that once cancellationToken for this call is cancelled, Cancel() will be called.
private void RegisterCancellationCallback()
{
var token = callDetails.Options.CancellationToken;
var token = details.Options.CancellationToken;
if (token.CanBeCanceled)
{
token.Register(() => this.Cancel());
}
}
/// <summary>
/// Gets WriteFlags set in callDetails.Options.WriteOptions
/// </summary>
private WriteFlags GetWriteFlagsForCall()
{
var writeOptions = details.Options.WriteOptions;
return writeOptions != null ? writeOptions.Flags : default(WriteFlags);
}
/// <summary>
/// Handler for unary response completion.
/// </summary>

@ -71,6 +71,9 @@ namespace Grpc.Core.Internal
protected bool halfclosed;
protected bool finished; // True if close has been received from the peer.
protected bool initialMetadataSent;
protected long streamingWritesCounter;
public AsyncCallBase(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
{
this.serializer = Preconditions.CheckNotNull(serializer);
@ -123,7 +126,7 @@ namespace Grpc.Core.Internal
/// Initiates sending a message. Only one send operation can be active at a time.
/// completionDelegate is invoked upon completion.
/// </summary>
protected void StartSendMessageInternal(TWrite msg, AsyncCompletionDelegate<object> completionDelegate)
protected void StartSendMessageInternal(TWrite msg, WriteFlags writeFlags, AsyncCompletionDelegate<object> completionDelegate)
{
byte[] payload = UnsafeSerialize(msg);
@ -132,8 +135,11 @@ namespace Grpc.Core.Internal
Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
CheckSendingAllowed();
call.StartSendMessage(payload, HandleSendFinished);
call.StartSendMessage(HandleSendFinished, payload, writeFlags, !initialMetadataSent);
sendCompletionDelegate = completionDelegate;
initialMetadataSent = true;
streamingWritesCounter++;
}
}
@ -287,7 +293,7 @@ namespace Grpc.Core.Internal
if (!success)
{
FireCompletion(origCompletionDelegate, null, new OperationFailedException("Send failed"));
FireCompletion(origCompletionDelegate, null, new InvalidOperationException("Send failed"));
}
else
{
@ -312,7 +318,7 @@ namespace Grpc.Core.Internal
if (!success)
{
FireCompletion(origCompletionDelegate, null, new OperationFailedException("Halfclose failed"));
FireCompletion(origCompletionDelegate, null, new InvalidOperationException("Halfclose failed"));
}
else
{

@ -83,9 +83,9 @@ namespace Grpc.Core.Internal
/// Sends a streaming response. Only one pending send action is allowed at any given time.
/// completionDelegate is called when the operation finishes.
/// </summary>
public void StartSendMessage(TResponse msg, AsyncCompletionDelegate<object> completionDelegate)
public void StartSendMessage(TResponse msg, WriteFlags writeFlags, AsyncCompletionDelegate<object> completionDelegate)
{
StartSendMessageInternal(msg, completionDelegate);
StartSendMessageInternal(msg, writeFlags, completionDelegate);
}
/// <summary>
@ -97,6 +97,35 @@ namespace Grpc.Core.Internal
StartReadMessageInternal(completionDelegate);
}
/// <summary>
/// Initiates sending a initial metadata.
/// Even though C-core allows sending metadata in parallel to sending messages, we will treat sending metadata as a send message operation
/// to make things simpler.
/// completionDelegate is invoked upon completion.
/// </summary>
public void StartSendInitialMetadata(Metadata headers, AsyncCompletionDelegate<object> completionDelegate)
{
lock (myLock)
{
Preconditions.CheckNotNull(headers, "metadata");
Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
Preconditions.CheckState(!initialMetadataSent, "Response headers can only be sent once per call.");
Preconditions.CheckState(streamingWritesCounter == 0, "Response headers can only be sent before the first write starts.");
CheckSendingAllowed();
Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
using (var metadataArray = MetadataArraySafeHandle.Create(headers))
{
call.StartSendInitialMetadata(HandleSendFinished, metadataArray);
}
this.initialMetadataSent = true;
sendCompletionDelegate = completionDelegate;
}
}
/// <summary>
/// Sends call result status, also indicating server is done with streaming responses.
/// Only one pending send action is allowed at any given time.
@ -111,7 +140,7 @@ namespace Grpc.Core.Internal
using (var metadataArray = MetadataArraySafeHandle.Create(trailers))
{
call.StartSendStatusFromServer(status, HandleHalfclosed, metadataArray);
call.StartSendStatusFromServer(HandleHalfclosed, status, metadataArray, !initialMetadataSent);
}
halfcloseRequested = true;
readingDone = true;

@ -42,6 +42,8 @@ namespace Grpc.Core.Internal
/// </summary>
internal class CallSafeHandle : SafeHandleZeroIsInvalid
{
public static readonly CallSafeHandle NullInstance = new CallSafeHandle();
const uint GRPC_WRITE_BUFFER_HINT = 1;
CompletionRegistry completionRegistry;
@ -53,7 +55,7 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_unary(CallSafeHandle call,
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray);
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call,
@ -62,7 +64,7 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call,
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len,
MetadataArraySafeHandle metadataArray);
MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call,
@ -70,7 +72,7 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call,
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len);
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_send_close_from_client(CallSafeHandle call,
@ -78,7 +80,7 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call,
BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray);
BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_recv_message(CallSafeHandle call,
@ -88,6 +90,10 @@ namespace Grpc.Core.Internal
static extern GRPCCallError grpcsharp_call_start_serverside(CallSafeHandle call,
BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_send_initial_metadata(CallSafeHandle call,
BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
[DllImport("grpc_csharp_ext.dll")]
static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call);
@ -103,17 +109,17 @@ namespace Grpc.Core.Internal
this.completionRegistry = completionRegistry;
}
public void StartUnary(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartUnary(BatchCompletionDelegate callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
{
var ctx = BatchContextSafeHandle.Create();
completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray)
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
.CheckOk();
}
public void StartUnary(byte[] payload, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray)
public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
{
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray)
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
.CheckOk();
}
@ -124,11 +130,11 @@ namespace Grpc.Core.Internal
grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk();
}
public void StartServerStreaming(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartServerStreaming(BatchCompletionDelegate callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
{
var ctx = BatchContextSafeHandle.Create();
completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray).CheckOk();
grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags).CheckOk();
}
public void StartDuplexStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
@ -138,11 +144,11 @@ namespace Grpc.Core.Internal
grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk();
}
public void StartSendMessage(byte[] payload, BatchCompletionDelegate callback)
public void StartSendMessage(BatchCompletionDelegate callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
{
var ctx = BatchContextSafeHandle.Create();
completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length)).CheckOk();
grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata).CheckOk();
}
public void StartSendCloseFromClient(BatchCompletionDelegate callback)
@ -152,11 +158,11 @@ namespace Grpc.Core.Internal
grpcsharp_call_send_close_from_client(this, ctx).CheckOk();
}
public void StartSendStatusFromServer(Status status, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartSendStatusFromServer(BatchCompletionDelegate callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata)
{
var ctx = BatchContextSafeHandle.Create();
completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray).CheckOk();
grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray, sendEmptyInitialMetadata).CheckOk();
}
public void StartReceiveMessage(BatchCompletionDelegate callback)
@ -173,6 +179,13 @@ namespace Grpc.Core.Internal
grpcsharp_call_start_serverside(this, ctx).CheckOk();
}
public void StartSendInitialMetadata(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
completionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk();
}
public void Cancel()
{
grpcsharp_call_cancel(this).CheckOk();

@ -47,7 +47,7 @@ namespace Grpc.Core.Internal
static extern ChannelSafeHandle grpcsharp_secure_channel_create(CredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs);
[DllImport("grpc_csharp_ext.dll")]
static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline);
static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline);
[DllImport("grpc_csharp_ext.dll")]
static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect);
@ -76,9 +76,9 @@ namespace Grpc.Core.Internal
return grpcsharp_secure_channel_create(credentials, target, channelArgs);
}
public CallSafeHandle CreateCall(CompletionRegistry registry, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline)
public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline)
{
var result = grpcsharp_channel_create_call(this, cq, method, host, deadline);
var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
result.SetCompletionRegistry(registry);
return result;
}

@ -40,16 +40,18 @@ namespace Grpc.Core.Internal
internal class ClientRequestStream<TRequest, TResponse> : IClientStreamWriter<TRequest>
{
readonly AsyncCall<TRequest, TResponse> call;
WriteOptions writeOptions;
public ClientRequestStream(AsyncCall<TRequest, TResponse> call)
{
this.call = call;
this.writeOptions = call.Details.Options.WriteOptions;
}
public Task WriteAsync(TRequest message)
{
var taskSource = new AsyncCompletionTaskSource<object>();
call.StartSendMessage(message, taskSource.CompletionDelegate);
call.StartSendMessage(message, GetWriteFlags(), taskSource.CompletionDelegate);
return taskSource.Task;
}
@ -59,5 +61,24 @@ namespace Grpc.Core.Internal
call.StartSendCloseFromClient(taskSource.CompletionDelegate);
return taskSource.Task;
}
public WriteOptions WriteOptions
{
get
{
return this.writeOptions;
}
set
{
writeOptions = value;
}
}
private WriteFlags GetWriteFlags()
{
var options = writeOptions;
return options != null ? options.Flags : default(WriteFlags);
}
}
}

@ -75,7 +75,7 @@ namespace Grpc.Core.Internal
var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall);
Status status;
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken);
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken);
try
{
Preconditions.CheckArgument(await requestStream.MoveNext());
@ -131,7 +131,7 @@ namespace Grpc.Core.Internal
var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall);
Status status;
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken);
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken);
try
{
Preconditions.CheckArgument(await requestStream.MoveNext());
@ -187,7 +187,7 @@ namespace Grpc.Core.Internal
var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall);
Status status;
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken);
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken);
try
{
var result = await handler(requestStream, context);
@ -247,7 +247,7 @@ namespace Grpc.Core.Internal
var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall);
Status status;
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken);
var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken);
try
{
await handler(requestStream, responseStream, context);
@ -304,13 +304,14 @@ namespace Grpc.Core.Internal
return new Status(StatusCode.Unknown, "Exception was thrown by handler.");
}
public static ServerCallContext NewContext(ServerRpcNew newRpc, string peer, CancellationToken cancellationToken)
public static ServerCallContext NewContext<TRequest, TResponse>(ServerRpcNew newRpc, string peer, ServerResponseStream<TRequest, TResponse> serverResponseStream, CancellationToken cancellationToken)
where TRequest : class
where TResponse : class
{
DateTime realtimeDeadline = newRpc.Deadline.ToClockType(GPRClockType.Realtime).ToDateTime();
return new ServerCallContext(
newRpc.Method, newRpc.Host, peer, realtimeDeadline,
newRpc.RequestMetadata, cancellationToken);
return new ServerCallContext(newRpc.Call, newRpc.Method, newRpc.Host, peer, realtimeDeadline,
newRpc.RequestMetadata, cancellationToken, serverResponseStream.WriteResponseHeadersAsync, serverResponseStream);
}
}
}

@ -38,11 +38,12 @@ namespace Grpc.Core.Internal
/// <summary>
/// Writes responses asynchronously to an underlying AsyncCallServer object.
/// </summary>
internal class ServerResponseStream<TRequest, TResponse> : IServerStreamWriter<TResponse>
internal class ServerResponseStream<TRequest, TResponse> : IServerStreamWriter<TResponse>, IHasWriteOptions
where TRequest : class
where TResponse : class
{
readonly AsyncCallServer<TRequest, TResponse> call;
WriteOptions writeOptions;
public ServerResponseStream(AsyncCallServer<TRequest, TResponse> call)
{
@ -52,7 +53,7 @@ namespace Grpc.Core.Internal
public Task WriteAsync(TResponse message)
{
var taskSource = new AsyncCompletionTaskSource<object>();
call.StartSendMessage(message, taskSource.CompletionDelegate);
call.StartSendMessage(message, GetWriteFlags(), taskSource.CompletionDelegate);
return taskSource.Task;
}
@ -62,5 +63,31 @@ namespace Grpc.Core.Internal
call.StartSendStatusFromServer(status, trailers, taskSource.CompletionDelegate);
return taskSource.Task;
}
public Task WriteResponseHeadersAsync(Metadata responseHeaders)
{
var taskSource = new AsyncCompletionTaskSource<object>();
call.StartSendInitialMetadata(responseHeaders, taskSource.CompletionDelegate);
return taskSource.Task;
}
public WriteOptions WriteOptions
{
get
{
return writeOptions;
}
set
{
writeOptions = value;
}
}
private WriteFlags GetWriteFlags()
{
var options = writeOptions;
return options != null ? options.Flags : default(WriteFlags);
}
}
}

@ -211,7 +211,7 @@ namespace Grpc.Core.Internal
return Timespec.InfPast;
}
Preconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime");
Preconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime needs of kind DateTimeKind.Utc or be equal to DateTime.MaxValue or DateTime.MinValue.");
try
{

@ -54,8 +54,8 @@ namespace Grpc.Core
/// <param name="privateKey">PEM encoded private key.</param>
public KeyCertificatePair(string certificateChain, string privateKey)
{
this.certificateChain = Preconditions.CheckNotNull(certificateChain);
this.privateKey = Preconditions.CheckNotNull(privateKey);
this.certificateChain = Preconditions.CheckNotNull(certificateChain, "certificateChain");
this.privateKey = Preconditions.CheckNotNull(privateKey, "privateKey");
}
/// <summary>

@ -42,16 +42,21 @@ namespace Grpc.Core.Logging
readonly Type forType;
readonly string forTypeString;
/// <summary>Creates a console logger not associated to any specific type.</summary>
public ConsoleLogger() : this(null)
{
}
/// <summary>Creates a console logger that logs messsage specific for given type.</summary>
private ConsoleLogger(Type forType)
{
this.forType = forType;
this.forTypeString = forType != null ? forType.FullName + " " : "";
}
/// <summary>
/// Returns a logger associated with the specified type.
/// </summary>
public ILogger ForType<T>()
{
if (typeof(T) == forType)
@ -61,31 +66,37 @@ namespace Grpc.Core.Logging
return new ConsoleLogger(typeof(T));
}
/// <summary>Logs a message with severity Debug.</summary>
public void Debug(string message, params object[] formatArgs)
{
Log("D", message, formatArgs);
}
/// <summary>Logs a message with severity Info.</summary>
public void Info(string message, params object[] formatArgs)
{
Log("I", message, formatArgs);
}
/// <summary>Logs a message with severity Warning.</summary>
public void Warning(string message, params object[] formatArgs)
{
Log("W", message, formatArgs);
}
/// <summary>Logs a message and an associated exception with severity Warning.</summary>
public void Warning(Exception exception, string message, params object[] formatArgs)
{
Log("W", message + " " + exception, formatArgs);
}
/// <summary>Logs a message with severity Error.</summary>
public void Error(string message, params object[] formatArgs)
{
Log("E", message, formatArgs);
}
/// <summary>Logs a message and an associated exception with severity Error.</summary>
public void Error(Exception exception, string message, params object[] formatArgs)
{
Log("E", message + " " + exception, formatArgs);

@ -42,16 +42,22 @@ namespace Grpc.Core.Logging
/// <summary>Returns a logger associated with the specified type.</summary>
ILogger ForType<T>();
/// <summary>Logs a message with severity Debug.</summary>
void Debug(string message, params object[] formatArgs);
/// <summary>Logs a message with severity Info.</summary>
void Info(string message, params object[] formatArgs);
/// <summary>Logs a message with severity Warning.</summary>
void Warning(string message, params object[] formatArgs);
/// <summary>Logs a message and an associated exception with severity Warning.</summary>
void Warning(Exception exception, string message, params object[] formatArgs);
/// <summary>Logs a message with severity Error.</summary>
void Error(string message, params object[] formatArgs);
/// <summary>Logs a message and an associated exception with severity Error.</summary>
void Error(Exception exception, string message, params object[] formatArgs);
}
}

@ -37,19 +37,27 @@ using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// For serializing and deserializing messages.
/// Encapsulates the logic for serializing and deserializing messages.
/// </summary>
public struct Marshaller<T>
{
readonly Func<T, byte[]> serializer;
readonly Func<byte[], T> deserializer;
/// <summary>
/// Initializes a new marshaller.
/// </summary>
/// <param name="serializer">Function that will be used to serialize messages.</param>
/// <param name="deserializer">Function that will be used to deserialize messages.</param>
public Marshaller(Func<T, byte[]> serializer, Func<byte[], T> deserializer)
{
this.serializer = Preconditions.CheckNotNull(serializer);
this.deserializer = Preconditions.CheckNotNull(deserializer);
this.serializer = Preconditions.CheckNotNull(serializer, "serializer");
this.deserializer = Preconditions.CheckNotNull(deserializer, "deserializer");
}
/// <summary>
/// Gets the serializer function.
/// </summary>
public Func<T, byte[]> Serializer
{
get
@ -58,6 +66,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets the deserializer function.
/// </summary>
public Func<byte[], T> Deserializer
{
get
@ -72,11 +83,17 @@ namespace Grpc.Core
/// </summary>
public static class Marshallers
{
/// <summary>
/// Creates a marshaller from specified serializer and deserializer.
/// </summary>
public static Marshaller<T> Create<T>(Func<T, byte[]> serializer, Func<byte[], T> deserializer)
{
return new Marshaller<T>(serializer, deserializer);
}
/// <summary>
/// Returns a marshaller for <c>string</c> type. This is useful for testing.
/// </summary>
public static Marshaller<string> StringMarshaller
{
get

@ -114,6 +114,16 @@ namespace Grpc.Core
entries.Add(item);
}
public void Add(string key, string value)
{
Add(new Entry(key, value));
}
public void Add(string key, byte[] valueBytes)
{
Add(new Entry(key, valueBytes));
}
public void Clear()
{
CheckWriteable();
@ -176,15 +186,15 @@ namespace Grpc.Core
public Entry(string key, byte[] valueBytes)
{
this.key = Preconditions.CheckNotNull(key);
this.key = Preconditions.CheckNotNull(key, "key");
this.value = null;
this.valueBytes = Preconditions.CheckNotNull(valueBytes);
this.valueBytes = Preconditions.CheckNotNull(valueBytes, "valueBytes");
}
public Entry(string key, string value)
{
this.key = Preconditions.CheckNotNull(key);
this.value = Preconditions.CheckNotNull(value);
this.key = Preconditions.CheckNotNull(key, "key");
this.value = Preconditions.CheckNotNull(value, "value");
this.valueBytes = null;
}

@ -41,14 +41,21 @@ namespace Grpc.Core
/// </summary>
public enum MethodType
{
Unary, // Unary request, unary response.
ClientStreaming, // Streaming request, unary response.
ServerStreaming, // Unary request, streaming response.
DuplexStreaming // Streaming request, streaming response.
/// <summary>Single request sent from client, single response received from server.</summary>
Unary,
/// <summary>Stream of request sent from client, single response received from server.</summary>
ClientStreaming,
/// <summary>Single request sent from client, stream of responses received from server.</summary>
ServerStreaming,
/// <summary>Both server and client can stream arbitrary number of requests and responses simultaneously.</summary>
DuplexStreaming
}
/// <summary>
/// A description of a service method.
/// A description of a remote method.
/// </summary>
public class Method<TRequest, TResponse>
{
@ -59,16 +66,27 @@ namespace Grpc.Core
readonly Marshaller<TResponse> responseMarshaller;
readonly string fullName;
/// <summary>
/// Initializes a new instance of the <c>Method</c> class.
/// </summary>
/// <param name="type">Type of method.</param>
/// <param name="serviceName">Name of service this method belongs to.</param>
/// <param name="name">Unqualified name of the method.</param>
/// <param name="requestMarshaller">Marshaller used for request messages.</param>
/// <param name="responseMarshaller">Marshaller used for response messages.</param>
public Method(MethodType type, string serviceName, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller)
{
this.type = type;
this.serviceName = Preconditions.CheckNotNull(serviceName);
this.name = Preconditions.CheckNotNull(name);
this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller);
this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller);
this.fullName = GetFullName(serviceName);
this.serviceName = Preconditions.CheckNotNull(serviceName, "serviceName");
this.name = Preconditions.CheckNotNull(name, "name");
this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller, "requestMarshaller");
this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller, "responseMarshaller");
this.fullName = GetFullName(serviceName, name);
}
/// <summary>
/// Gets the type of the method.
/// </summary>
public MethodType Type
{
get
@ -77,6 +95,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets the name of the service to which this method belongs.
/// </summary>
public string ServiceName
{
get
@ -85,6 +106,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets the unqualified name of the method.
/// </summary>
public string Name
{
get
@ -93,6 +117,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets the marshaller used for request messages.
/// </summary>
public Marshaller<TRequest> RequestMarshaller
{
get
@ -101,6 +128,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Gets the marshaller used for response messages.
/// </summary>
public Marshaller<TResponse> ResponseMarshaller
{
get
@ -108,7 +138,11 @@ namespace Grpc.Core
return this.responseMarshaller;
}
}
/// <summary>
/// Gets the fully qualified name of the method. On the server side, methods are dispatched
/// based on this name.
/// </summary>
public string FullName
{
get
@ -120,9 +154,9 @@ namespace Grpc.Core
/// <summary>
/// Gets full name of the method including the service name.
/// </summary>
internal string GetFullName(string serviceName)
internal static string GetFullName(string serviceName, string methodName)
{
return "/" + Preconditions.CheckNotNull(serviceName) + "/" + this.Name;
return "/" + serviceName + "/" + methodName;
}
}
}

@ -36,22 +36,34 @@ using System;
namespace Grpc.Core
{
/// <summary>
/// Thrown when remote procedure call fails.
/// Thrown when remote procedure call fails. Every <c>RpcException</c> is associated with a resulting <see cref="Status"/> of the call.
/// </summary>
public class RpcException : Exception
{
private readonly Status status;
/// <summary>
/// Creates a new <c>RpcException</c> associated with given status.
/// </summary>
/// <param name="status">Resulting status of a call.</param>
public RpcException(Status status) : base(status.ToString())
{
this.status = status;
}
/// <summary>
/// Creates a new <c>RpcException</c> associated with given status and message.
/// </summary>
/// <param name="status">Resulting status of a call.</param>
/// <param name="message">The exception message.</param>
public RpcException(Status status, string message) : base(message)
{
this.status = status;
}
/// <summary>
/// Resulting status of the call.
/// </summary>
public Status Status
{
get

@ -192,7 +192,7 @@ namespace Grpc.Core
{
lock (myLock)
{
Preconditions.CheckNotNull(serverPort.Credentials);
Preconditions.CheckNotNull(serverPort.Credentials, "serverPort");
Preconditions.CheckState(!startRequested);
var address = string.Format("{0}:{1}", serverPort.Host, serverPort.Port);
int boundPort;

@ -36,15 +36,16 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Internal;
namespace Grpc.Core
{
/// <summary>
/// Context for a server-side call.
/// </summary>
public sealed class ServerCallContext
public class ServerCallContext
{
// TODO(jtattermusch): expose method to send initial metadata back to client
private readonly CallSafeHandle callHandle;
private readonly string method;
private readonly string host;
private readonly string peer;
@ -54,15 +55,34 @@ namespace Grpc.Core
private readonly Metadata responseTrailers = new Metadata();
private Status status = Status.DefaultSuccess;
private Func<Metadata, Task> writeHeadersFunc;
private IHasWriteOptions writeOptionsHolder;
public ServerCallContext(string method, string host, string peer, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken)
internal ServerCallContext(CallSafeHandle callHandle, string method, string host, string peer, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken,
Func<Metadata, Task> writeHeadersFunc, IHasWriteOptions writeOptionsHolder)
{
this.callHandle = callHandle;
this.method = method;
this.host = host;
this.peer = peer;
this.deadline = deadline;
this.requestHeaders = requestHeaders;
this.cancellationToken = cancellationToken;
this.writeHeadersFunc = writeHeadersFunc;
this.writeOptionsHolder = writeOptionsHolder;
}
public Task WriteResponseHeadersAsync(Metadata responseHeaders)
{
return writeHeadersFunc(responseHeaders);
}
/// <summary>
/// Creates a propagation token to be used to propagate call context to a child call.
/// </summary>
public ContextPropagationToken CreatePropagationToken(ContextPropagationOptions options = null)
{
return new ContextPropagationToken(callHandle, deadline, cancellationToken, options);
}
/// <summary>Name of method called in this RPC.</summary>
@ -110,7 +130,7 @@ namespace Grpc.Core
}
}
///<summary>Cancellation token signals when call is cancelled.</summary>
/// <summary>Cancellation token signals when call is cancelled.</summary>
public CancellationToken CancellationToken
{
get
@ -141,5 +161,31 @@ namespace Grpc.Core
status = value;
}
}
/// <summary>
/// Allows setting write options for the following write.
/// For streaming response calls, this property is also exposed as on IServerStreamWriter for convenience.
/// Both properties are backed by the same underlying value.
/// </summary>
public WriteOptions WriteOptions
{
get
{
return writeOptionsHolder.WriteOptions;
}
set
{
writeOptionsHolder.WriteOptions = value;
}
}
}
/// <summary>
/// Allows sharing write options between ServerCallContext and other objects.
/// </summary>
public interface IHasWriteOptions
{
WriteOptions WriteOptions { get; set; }
}
}

@ -91,7 +91,7 @@ namespace Grpc.Core
{
this.keyCertificatePairs = new List<KeyCertificatePair>(keyCertificatePairs).AsReadOnly();
Preconditions.CheckArgument(this.keyCertificatePairs.Count > 0,
"At least one KeyCertificatePair needs to be provided");
"At least one KeyCertificatePair needs to be provided.");
if (forceClientAuth)
{
Preconditions.CheckNotNull(rootCertificates,

@ -31,12 +31,8 @@
#endregion
using System;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Internal;
namespace Grpc.Core
{
/// <summary>

@ -62,9 +62,9 @@ namespace Grpc.Core
/// <param name="credentials">credentials to use to secure this port.</param>
public ServerPort(string host, int port, ServerCredentials credentials)
{
this.host = Preconditions.CheckNotNull(host);
this.host = Preconditions.CheckNotNull(host, "host");
this.port = port;
this.credentials = Preconditions.CheckNotNull(credentials);
this.credentials = Preconditions.CheckNotNull(credentials, "credentials");
}
/// <summary>

@ -79,7 +79,7 @@ namespace Grpc.Core
where TRequest : class
where TResponse : class
{
callHandlers.Add(method.GetFullName(serviceName), ServerCalls.UnaryCall(method, handler));
callHandlers.Add(method.FullName, ServerCalls.UnaryCall(method, handler));
return this;
}
@ -89,7 +89,7 @@ namespace Grpc.Core
where TRequest : class
where TResponse : class
{
callHandlers.Add(method.GetFullName(serviceName), ServerCalls.ClientStreamingCall(method, handler));
callHandlers.Add(method.FullName, ServerCalls.ClientStreamingCall(method, handler));
return this;
}
@ -99,7 +99,7 @@ namespace Grpc.Core
where TRequest : class
where TResponse : class
{
callHandlers.Add(method.GetFullName(serviceName), ServerCalls.ServerStreamingCall(method, handler));
callHandlers.Add(method.FullName, ServerCalls.ServerStreamingCall(method, handler));
return this;
}
@ -109,7 +109,7 @@ namespace Grpc.Core
where TRequest : class
where TResponse : class
{
callHandlers.Add(method.GetFullName(serviceName), ServerCalls.DuplexStreamingCall(method, handler));
callHandlers.Add(method.FullName, ServerCalls.DuplexStreamingCall(method, handler));
return this;
}

@ -29,13 +29,12 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Runtime.InteropServices;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Represents RPC result.
/// Represents RPC result, which consists of <see cref="StatusCode"/> and an optional detail string.
/// </summary>
public struct Status
{
@ -52,6 +51,11 @@ namespace Grpc.Core
readonly StatusCode statusCode;
readonly string detail;
/// <summary>
/// Creates a new instance of <c>Status</c>.
/// </summary>
/// <param name="statusCode">Status code.</param>
/// <param name="detail">Detail.</param>
public Status(StatusCode statusCode, string detail)
{
this.statusCode = statusCode;
@ -80,6 +84,9 @@ namespace Grpc.Core
}
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Status"/>.
/// </summary>
public override string ToString()
{
return string.Format("Status(StatusCode={0}, Detail=\"{1}\")", statusCode, detail);

@ -31,8 +31,6 @@
#endregion
using System;
namespace Grpc.Core
{
/// <summary>
@ -41,101 +39,101 @@ namespace Grpc.Core
/// </summary>
public enum StatusCode
{
/* Not an error; returned on success */
/// <summary>Not an error; returned on success.</summary>
OK = 0,
/* The operation was cancelled (typically by the caller). */
/// <summary>The operation was cancelled (typically by the caller).</summary>
Cancelled = 1,
/* Unknown error. An example of where this error may be returned is
if a Status value received from another address space belongs to
an error-space that is not known in this address space. Also
errors raised by APIs that do not return enough error information
may be converted to this error. */
/// <summary>
/// Unknown error. An example of where this error may be returned is
/// if a Status value received from another address space belongs to
/// an error-space that is not known in this address space. Also
/// errors raised by APIs that do not return enough error information
/// may be converted to this error.
/// </summary>
Unknown = 2,
/* Client specified an invalid argument. Note that this differs
from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
that are problematic regardless of the state of the system
(e.g., a malformed file name). */
/// <summary>
/// Client specified an invalid argument. Note that this differs
/// from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
/// that are problematic regardless of the state of the system
/// (e.g., a malformed file name).
/// </summary>
InvalidArgument = 3,
/* Deadline expired before operation could complete. For operations
that change the state of the system, this error may be returned
even if the operation has completed successfully. For example, a
successful response from a server could have been delayed long
enough for the deadline to expire. */
/// <summary>
/// Deadline expired before operation could complete. For operations
/// that change the state of the system, this error may be returned
/// even if the operation has completed successfully. For example, a
/// successful response from a server could have been delayed long
/// enough for the deadline to expire.
/// </summary>
DeadlineExceeded = 4,
/* Some requested entity (e.g., file or directory) was not found. */
/// <summary>Some requested entity (e.g., file or directory) was not found.</summary>
NotFound = 5,
/* Some entity that we attempted to create (e.g., file or directory)
already exists. */
/// <summary>Some entity that we attempted to create (e.g., file or directory) already exists.</summary>
AlreadyExists = 6,
/* The caller does not have permission to execute the specified
operation. PERMISSION_DENIED must not be used for rejections
caused by exhausting some resource (use RESOURCE_EXHAUSTED
instead for those errors). PERMISSION_DENIED must not be
used if the caller can not be identified (use UNAUTHENTICATED
instead for those errors). */
/// <summary>
/// The caller does not have permission to execute the specified
/// operation. PERMISSION_DENIED must not be used for rejections
/// caused by exhausting some resource (use RESOURCE_EXHAUSTED
/// instead for those errors). PERMISSION_DENIED must not be
/// used if the caller can not be identified (use UNAUTHENTICATED
/// instead for those errors).
/// </summary>
PermissionDenied = 7,
/* The request does not have valid authentication credentials for the
operation. */
/// <summary>The request does not have valid authentication credentials for the operation.</summary>
Unauthenticated = 16,
/* Some resource has been exhausted, perhaps a per-user quota, or
perhaps the entire file system is out of space. */
/// <summary>
/// Some resource has been exhausted, perhaps a per-user quota, or
/// perhaps the entire file system is out of space.
/// </summary>
ResourceExhausted = 8,
/* Operation was rejected because the system is not in a state
required for the operation's execution. For example, directory
to be deleted may be non-empty, an rmdir operation is applied to
a non-directory, etc.
A litmus test that may help a service implementor in deciding
between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
(a) Use UNAVAILABLE if the client can retry just the failing call.
(b) Use ABORTED if the client should retry at a higher-level
(e.g., restarting a read-modify-write sequence).
(c) Use FAILED_PRECONDITION if the client should not retry until
the system state has been explicitly fixed. E.g., if an "rmdir"
fails because the directory is non-empty, FAILED_PRECONDITION
should be returned since the client should not retry unless
they have first fixed up the directory by deleting files from it.
(d) Use FAILED_PRECONDITION if the client performs conditional
REST Get/Update/Delete on a resource and the resource on the
server does not match the condition. E.g., conflicting
read-modify-write on the same resource. */
/// <summary>
/// Operation was rejected because the system is not in a state
/// required for the operation's execution. For example, directory
/// to be deleted may be non-empty, an rmdir operation is applied to
/// a non-directory, etc.
/// </summary>
FailedPrecondition = 9,
/* The operation was aborted, typically due to a concurrency issue
like sequencer check failures, transaction aborts, etc.
See litmus test above for deciding between FAILED_PRECONDITION,
ABORTED, and UNAVAILABLE. */
/// <summary>
/// The operation was aborted, typically due to a concurrency issue
/// like sequencer check failures, transaction aborts, etc.
/// </summary>
Aborted = 10,
/* Operation was attempted past the valid range. E.g., seeking or
reading past end of file.
Unlike INVALID_ARGUMENT, this error indicates a problem that may
be fixed if the system state changes. For example, a 32-bit file
system will generate INVALID_ARGUMENT if asked to read at an
offset that is not in the range [0,2^32-1], but it will generate
OUT_OF_RANGE if asked to read from an offset past the current
file size.
There is a fair bit of overlap between FAILED_PRECONDITION and
OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific
error) when it applies so that callers who are iterating through
a space can easily look for an OUT_OF_RANGE error to detect when
they are done. */
/// <summary>
/// Operation was attempted past the valid range. E.g., seeking or
/// reading past end of file.
/// </summary>
OutOfRange = 11,
/* Operation is not implemented or not supported/enabled in this service. */
/// <summary>Operation is not implemented or not supported/enabled in this service.</summary>
Unimplemented = 12,
/* Internal errors. Means some invariants expected by underlying
system has been broken. If you see one of these errors,
something is very broken. */
/// <summary>
/// Internal errors. Means some invariants expected by underlying
/// system has been broken. If you see one of these errors,
/// something is very broken.
/// </summary>
Internal = 13,
/* The service is currently unavailable. This is a most likely a
transient condition and may be corrected by retrying with
a backoff.
See litmus test above for deciding between FAILED_PRECONDITION,
ABORTED, and UNAVAILABLE. */
/// <summary>
/// The service is currently unavailable. This is a most likely a
/// transient condition and may be corrected by retrying with
/// a backoff.
/// </summary>
Unavailable = 14,
/* Unrecoverable data loss or corruption. */
/// <summary>Unrecoverable data loss or corruption.</summary>
DataLoss = 15
}
}

@ -33,7 +33,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Grpc.Core.Utils
@ -46,7 +45,7 @@ namespace Grpc.Core.Utils
/// <summary>
/// Reads the entire stream and executes an async action for each element.
/// </summary>
public static async Task ForEach<T>(this IAsyncStreamReader<T> streamReader, Func<T, Task> asyncAction)
public static async Task ForEachAsync<T>(this IAsyncStreamReader<T> streamReader, Func<T, Task> asyncAction)
where T : class
{
while (await streamReader.MoveNext())
@ -58,7 +57,7 @@ namespace Grpc.Core.Utils
/// <summary>
/// Reads the entire stream and creates a list containing all the elements read.
/// </summary>
public static async Task<List<T>> ToList<T>(this IAsyncStreamReader<T> streamReader)
public static async Task<List<T>> ToListAsync<T>(this IAsyncStreamReader<T> streamReader)
where T : class
{
var result = new List<T>();
@ -73,7 +72,7 @@ namespace Grpc.Core.Utils
/// Writes all elements from given enumerable to the stream.
/// Completes the stream afterwards unless close = false.
/// </summary>
public static async Task WriteAll<T>(this IClientStreamWriter<T> streamWriter, IEnumerable<T> elements, bool complete = true)
public static async Task WriteAllAsync<T>(this IClientStreamWriter<T> streamWriter, IEnumerable<T> elements, bool complete = true)
where T : class
{
foreach (var element in elements)
@ -89,7 +88,7 @@ namespace Grpc.Core.Utils
/// <summary>
/// Writes all elements from given enumerable to the stream.
/// </summary>
public static async Task WriteAll<T>(this IServerStreamWriter<T> streamWriter, IEnumerable<T> elements)
public static async Task WriteAllAsync<T>(this IServerStreamWriter<T> streamWriter, IEnumerable<T> elements)
where T : class
{
foreach (var element in elements)

@ -39,6 +39,9 @@ using System.Threading.Tasks;
namespace Grpc.Core.Utils
{
/// <summary>
/// Utility methods to run microbenchmarks.
/// </summary>
public static class BenchmarkUtil
{
/// <summary>

@ -32,17 +32,16 @@
#endregion
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace Grpc.Core.Utils
{
/// <summary>
/// Utility methods to simplify checking preconditions in the code.
/// </summary>
public static class Preconditions
{
/// <summary>
/// Throws ArgumentException if condition is false.
/// Throws <see cref="ArgumentException"/> if condition is false.
/// </summary>
public static void CheckArgument(bool condition)
{
@ -53,7 +52,7 @@ namespace Grpc.Core.Utils
}
/// <summary>
/// Throws ArgumentException with given message if condition is false.
/// Throws <see cref="ArgumentException"/> with given message if condition is false.
/// </summary>
public static void CheckArgument(bool condition, string errorMessage)
{
@ -64,31 +63,31 @@ namespace Grpc.Core.Utils
}
/// <summary>
/// Throws NullReferenceException if reference is null.
/// Throws <see cref="ArgumentNullException"/> if reference is null.
/// </summary>
public static T CheckNotNull<T>(T reference)
{
if (reference == null)
{
throw new NullReferenceException();
throw new ArgumentNullException();
}
return reference;
}
/// <summary>
/// Throws NullReferenceException with given message if reference is null.
/// Throws <see cref="ArgumentNullException"/> if reference is null.
/// </summary>
public static T CheckNotNull<T>(T reference, string errorMessage)
public static T CheckNotNull<T>(T reference, string paramName)
{
if (reference == null)
{
throw new NullReferenceException(errorMessage);
throw new ArgumentNullException(paramName);
}
return reference;
}
/// <summary>
/// Throws InvalidOperationException if condition is false.
/// Throws <see cref="InvalidOperationException"/> if condition is false.
/// </summary>
public static void CheckState(bool condition)
{
@ -99,7 +98,7 @@ namespace Grpc.Core.Utils
}
/// <summary>
/// Throws InvalidOperationException with given message if condition is false.
/// Throws <see cref="InvalidOperationException"/> with given message if condition is false.
/// </summary>
public static void CheckState(bool condition, string errorMessage)
{

@ -1,5 +1,37 @@
#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.Reflection;
using System.Runtime.CompilerServices;
// The current version of gRPC C#.
[assembly: AssemblyVersion(Grpc.Core.VersionInfo.CurrentVersion + ".*")]
[assembly: AssemblyVersion(Grpc.Core.VersionInfo.CurrentVersion + ".0")]

@ -1,13 +1,46 @@
using System.Reflection;
using System.Runtime.CompilerServices;
#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
namespace Grpc.Core
{
/// <summary>
/// Provides info about current version of gRPC.
/// </summary>
public static class VersionInfo
{
/// <summary>
/// Current version of gRPC
/// </summary>
public const string CurrentVersion = "0.6.0";
public const string CurrentVersion = "0.6.1";
}
}

@ -0,0 +1,82 @@
#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;
namespace Grpc.Core
{
/// <summary>
/// Flags for write operations.
/// </summary>
[Flags]
public enum WriteFlags
{
/// <summary>
/// Hint that the write may be buffered and need not go out on the wire immediately.
/// gRPC is free to buffer the message until the next non-buffered
/// write, or until write stream completion, but it need not buffer completely or at all.
/// </summary>
BufferHint = 0x1,
/// <summary>
/// Force compression to be disabled for a particular write.
/// </summary>
NoCompress = 0x2
}
/// <summary>
/// Options for write operations.
/// </summary>
public class WriteOptions
{
/// <summary>
/// Default write options.
/// </summary>
public static readonly WriteOptions Default = new WriteOptions();
private WriteFlags flags;
public WriteOptions(WriteFlags flags = default(WriteFlags))
{
this.flags = flags;
}
public WriteFlags Flags
{
get
{
return this.flags;
}
}
}
}

@ -92,15 +92,8 @@ namespace math.Tests
[Test]
public void DivByZero()
{
try
{
DivReply response = client.Div(new DivArgs.Builder { Dividend = 0, Divisor = 0 }.Build());
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
}
var ex = Assert.Throws<RpcException>(() => client.Div(new DivArgs.Builder { Dividend = 0, Divisor = 0 }.Build()));
Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
}
[Test]
@ -116,7 +109,7 @@ namespace math.Tests
{
using (var call = client.Fib(new FibArgs.Builder { Limit = 6 }.Build()))
{
var responses = await call.ResponseStream.ToList();
var responses = await call.ResponseStream.ToListAsync();
CollectionAssert.AreEqual(new List<long> { 1, 1, 2, 3, 5, 8 },
responses.ConvertAll((n) => n.Num_));
}
@ -158,15 +151,10 @@ namespace math.Tests
using (var call = client.Fib(new FibArgs.Builder { Limit = 0 }.Build(),
deadline: DateTime.UtcNow.AddMilliseconds(500)))
{
try
{
await call.ResponseStream.ToList();
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.DeadlineExceeded, e.Status.StatusCode);
}
var ex = Assert.Throws<RpcException>(async () => await call.ResponseStream.ToListAsync());
// We can't guarantee the status code always DeadlineExceeded. See issue #2685.
Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
}
}
@ -179,7 +167,7 @@ namespace math.Tests
var numbers = new List<long> { 10, 20, 30 }.ConvertAll(
n => Num.CreateBuilder().SetNum_(n).Build());
await call.RequestStream.WriteAll(numbers);
await call.RequestStream.WriteAllAsync(numbers);
var result = await call.ResponseAsync;
Assert.AreEqual(60, result.Num_);
}
@ -197,8 +185,8 @@ namespace math.Tests
using (var call = client.DivMany())
{
await call.RequestStream.WriteAll(divArgsList);
var result = await call.ResponseStream.ToList();
await call.RequestStream.WriteAllAsync(divArgsList);
var result = await call.ResponseStream.ToListAsync();
CollectionAssert.AreEqual(new long[] { 3, 4, 3 }, result.ConvertAll((divReply) => divReply.Quotient));
CollectionAssert.AreEqual(new long[] { 1, 16, 1 }, result.ConvertAll((divReply) => divReply.Remainder));

@ -54,7 +54,7 @@ namespace math
{
using (var call = client.Fib(new FibArgs.Builder { Limit = 5 }.Build()))
{
List<Num> result = await call.ResponseStream.ToList();
List<Num> result = await call.ResponseStream.ToListAsync();
Console.WriteLine("Fib Result: " + string.Join("|", result));
}
}
@ -70,7 +70,7 @@ namespace math
using (var call = client.Sum())
{
await call.RequestStream.WriteAll(numbers);
await call.RequestStream.WriteAllAsync(numbers);
Console.WriteLine("Sum Result: " + await call.ResponseAsync);
}
}
@ -85,8 +85,8 @@ namespace math
};
using (var call = client.DivMany())
{
await call.RequestStream.WriteAll(divArgsList);
Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList()));
await call.RequestStream.WriteAllAsync(divArgsList);
Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToListAsync()));
}
}
@ -102,7 +102,7 @@ namespace math
Num sum;
using (var sumCall = client.Sum())
{
await sumCall.RequestStream.WriteAll(numbers);
await sumCall.RequestStream.WriteAllAsync(numbers);
sum = await sumCall.ResponseAsync;
}

@ -75,7 +75,7 @@ namespace math
public async Task<Num> Sum(IAsyncStreamReader<Num> requestStream, ServerCallContext context)
{
long sum = 0;
await requestStream.ForEach(async num =>
await requestStream.ForEachAsync(async num =>
{
sum += num.Num_;
});
@ -84,10 +84,7 @@ namespace math
public async Task DivMany(IAsyncStreamReader<DivArgs> requestStream, IServerStreamWriter<DivReply> responseStream, ServerCallContext context)
{
await requestStream.ForEach(async divArgs =>
{
await responseStream.WriteAsync(DivInternal(divArgs));
});
await requestStream.ForEachAsync(async divArgs => await responseStream.WriteAsync(DivInternal(divArgs)));
}
static DivReply DivInternal(DivArgs args)

@ -92,11 +92,11 @@ namespace Grpc.HealthCheck.Tests
public void NullsRejected()
{
var impl = new HealthServiceImpl();
Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus(null, "", HealthCheckResponse.Types.ServingStatus.SERVING));
Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus("", null, HealthCheckResponse.Types.ServingStatus.SERVING));
Assert.Throws(typeof(ArgumentNullException), () => impl.SetStatus(null, "", HealthCheckResponse.Types.ServingStatus.SERVING));
Assert.Throws(typeof(ArgumentNullException), () => impl.SetStatus("", null, HealthCheckResponse.Types.ServingStatus.SERVING));
Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus(null, ""));
Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus("", null));
Assert.Throws(typeof(ArgumentNullException), () => impl.ClearStatus(null, ""));
Assert.Throws(typeof(ArgumentNullException), () => impl.ClearStatus("", null));
}
private static HealthCheckResponse.Types.ServingStatus GetStatusHelper(HealthServiceImpl impl, string host, string service)

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

@ -8,7 +8,7 @@
<RootNamespace>Grpc.IntegrationTesting</RootNamespace>
<AssemblyName>Grpc.IntegrationTesting</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<NuGetPackageImportStamp>041c163e</NuGetPackageImportStamp>
<NuGetPackageImportStamp>6566287f</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -38,20 +38,33 @@
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto">
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.9.2.27816, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.2\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@ -78,15 +91,6 @@
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs">

@ -43,6 +43,7 @@ using Grpc.Auth;
using Grpc.Core;
using Grpc.Core.Utils;
using NUnit.Framework;
using Google.Apis.Auth.OAuth2;
namespace Grpc.IntegrationTesting
{
@ -97,10 +98,10 @@ namespace Grpc.IntegrationTesting
}
var interopClient = new InteropClient(options);
interopClient.Run();
interopClient.Run().Wait();
}
private void Run()
private async Task Run()
{
Credentials credentials = null;
if (options.useTls)
@ -120,17 +121,7 @@ namespace Grpc.IntegrationTesting
using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions))
{
TestService.TestServiceClient client = new TestService.TestServiceClient(channel);
if (options.testCase == "service_account_creds" || options.testCase == "compute_engine_creds")
{
var credential = GoogleCredential.GetApplicationDefault();
if (credential.IsCreateScopedRequired)
{
credential = credential.CreateScoped(new[] { AuthScope });
}
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
}
RunTestCaseAsync(options.testCase, client).Wait();
await RunTestCaseAsync(options.testCase, client);
}
GrpcEnvironment.Shutdown();
}
@ -158,16 +149,19 @@ namespace Grpc.IntegrationTesting
await RunEmptyStreamAsync(client);
break;
case "service_account_creds":
RunServiceAccountCreds(client);
await RunServiceAccountCredsAsync(client);
break;
case "compute_engine_creds":
RunComputeEngineCreds(client);
await RunComputeEngineCredsAsync(client);
break;
case "jwt_token_creds":
await RunJwtTokenCredsAsync(client);
break;
case "oauth2_auth_token":
RunOAuth2AuthToken(client);
await RunOAuth2AuthTokenAsync(client);
break;
case "per_rpc_creds":
RunPerRpcCreds(client);
await RunPerRpcCredsAsync(client);
break;
case "cancel_after_begin":
await RunCancelAfterBeginAsync(client);
@ -215,7 +209,7 @@ namespace Grpc.IntegrationTesting
using (var call = client.StreamingInputCall())
{
await call.RequestStream.WriteAll(bodySizes);
await call.RequestStream.WriteAllAsync(bodySizes);
var response = await call.ResponseAsync;
Assert.AreEqual(74922, response.AggregatedPayloadSize);
@ -237,7 +231,7 @@ namespace Grpc.IntegrationTesting
using (var call = client.StreamingOutputCall(request))
{
var responseList = await call.ResponseStream.ToList();
var responseList = await call.ResponseStream.ToListAsync();
foreach (var res in responseList)
{
Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type);
@ -303,15 +297,19 @@ namespace Grpc.IntegrationTesting
{
await call.RequestStream.CompleteAsync();
var responseList = await call.ResponseStream.ToList();
var responseList = await call.ResponseStream.ToListAsync();
Assert.AreEqual(0, responseList.Count);
}
Console.WriteLine("Passed!");
}
public static void RunServiceAccountCreds(TestService.ITestServiceClient client)
public static async Task RunServiceAccountCredsAsync(TestService.TestServiceClient client)
{
Console.WriteLine("running service_account_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
credential = credential.CreateScoped(new[] { AuthScope });
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
var request = SimpleRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.SetResponseSize(314159)
@ -329,9 +327,13 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!");
}
public static void RunComputeEngineCreds(TestService.ITestServiceClient client)
public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client)
{
Console.WriteLine("running compute_engine_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsFalse(credential.IsCreateScopedRequired);
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
var request = SimpleRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.SetResponseSize(314159)
@ -349,12 +351,35 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!");
}
public static void RunOAuth2AuthToken(TestService.TestServiceClient client)
public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client)
{
Console.WriteLine("running jwt_token_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
// check this a credential with scope support, but don't add the scope.
Assert.IsTrue(credential.IsCreateScopedRequired);
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
var request = SimpleRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.SetResponseSize(314159)
.SetPayload(CreateZerosPayload(271828))
.SetFillUsername(true)
.SetFillOauthScope(true)
.Build();
var response = client.UnaryCall(request);
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(314159, response.Payload.Body.Length);
Assert.AreEqual(ServiceAccountUser, response.Username);
Console.WriteLine("Passed!");
}
public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client)
{
Console.WriteLine("running oauth2_auth_token");
var credential = GoogleCredential.GetApplicationDefault().CreateScoped(new[] { AuthScope });
Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result);
string oauth2Token = credential.Token.AccessToken;
ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
string oauth2Token = await credential.GetAccessTokenForRequestAsync();
client.HeaderInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token);
@ -370,13 +395,12 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!");
}
public static void RunPerRpcCreds(TestService.TestServiceClient client)
public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client)
{
Console.WriteLine("running per_rpc_creds");
var credential = GoogleCredential.GetApplicationDefault().CreateScoped(new[] { AuthScope });
Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result);
string oauth2Token = credential.Token.AccessToken;
ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
string oauth2Token = await credential.GetAccessTokenForRequestAsync();
var headerInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token);
var request = SimpleRequest.CreateBuilder()
@ -385,7 +409,7 @@ namespace Grpc.IntegrationTesting
.Build();
var headers = new Metadata();
headerInterceptor(headers);
headerInterceptor("", headers);
var response = client.UnaryCall(request, headers: headers);
Assert.AreEqual(AuthScopeResponse, response.OauthScope);
@ -404,15 +428,8 @@ namespace Grpc.IntegrationTesting
await Task.Delay(1000);
cts.Cancel();
try
{
var response = await call.ResponseAsync;
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode);
}
var ex = Assert.Throws<RpcException>(async () => await call.ResponseAsync);
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
Console.WriteLine("Passed!");
}
@ -435,15 +452,8 @@ namespace Grpc.IntegrationTesting
cts.Cancel();
try
{
await call.ResponseStream.MoveNext();
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode);
}
var ex = Assert.Throws<RpcException>(async () => await call.ResponseStream.MoveNext());
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
Console.WriteLine("Passed!");
}

@ -71,7 +71,7 @@ namespace grpc.testing
public async Task<StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<StreamingInputCallRequest> requestStream, ServerCallContext context)
{
int sum = 0;
await requestStream.ForEach(async request =>
await requestStream.ForEachAsync(async request =>
{
sum += request.Payload.Body.Length;
});
@ -80,7 +80,7 @@ namespace grpc.testing
public async Task FullDuplexCall(IAsyncStreamReader<StreamingOutputCallRequest> requestStream, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context)
{
await requestStream.ForEach(async request =>
await requestStream.ForEachAsync(async request =>
{
foreach (var responseParam in request.ResponseParametersList)
{

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.2" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.2" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="Ix-Async" version="1.2.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />

@ -1,8 +1,8 @@
@rem Builds gRPC NuGet packages
@rem Current package versions
set VERSION=0.6.0
set CORE_VERSION=0.10.0
set VERSION=0.6.1
set CORE_VERSION=0.10.1
@rem Adjust the location of nuget.exe
set NUGET=C:\nuget\nuget.exe

@ -0,0 +1,2 @@
SandCastle project files to generate HTML reference documentation.

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- The configuration and platform will be used to determine which assemblies to include from solution and
project documentation sources -->
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{77e3da09-fc92-486f-a90a-99ca788e8b59}</ProjectGuid>
<SHFBSchemaVersion>2015.6.5.0</SHFBSchemaVersion>
<!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual Studio adds them anyway -->
<AssemblyName>Documentation</AssemblyName>
<RootNamespace>Documentation</RootNamespace>
<Name>Documentation</Name>
<!-- SHFB properties -->
<FrameworkVersion>.NET Framework 4.5</FrameworkVersion>
<OutputPath>..\..\..\doc\ref\csharp\html</OutputPath>
<Language>en-US</Language>
<DocumentationSources>
<DocumentationSource sourceFile="..\Grpc.Auth\Grpc.Auth.csproj" />
<DocumentationSource sourceFile="..\Grpc.Core\Grpc.Core.csproj" /></DocumentationSources>
<BuildAssemblerVerbosity>OnlyWarningsAndErrors</BuildAssemblerVerbosity>
<HelpFileFormat>Website</HelpFileFormat>
<IndentHtml>False</IndentHtml>
<KeepLogFile>True</KeepLogFile>
<DisableCodeBlockComponent>False</DisableCodeBlockComponent>
<CleanIntermediates>True</CleanIntermediates>
<HelpFileVersion>1.0.0.0</HelpFileVersion>
<MaximumGroupParts>2</MaximumGroupParts>
<NamespaceGrouping>False</NamespaceGrouping>
<SyntaxFilters>Standard</SyntaxFilters>
<SdkLinkTarget>Blank</SdkLinkTarget>
<RootNamespaceContainer>True</RootNamespaceContainer>
<PresentationStyle>VS2013</PresentationStyle>
<Preliminary>False</Preliminary>
<NamingMethod>MemberName</NamingMethod>
<HelpTitle>gRPC C#</HelpTitle>
<ContentPlacement>AboveNamespaces</ContentPlacement>
<HtmlHelpName>Documentation</HtmlHelpName>
</PropertyGroup>
<!-- There are no properties for these groups. AnyCPU needs to appear in order for Visual Studio to perform
the build. The others are optional common platform types that may appear. -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Win32' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Win32' ">
</PropertyGroup>
<!-- Import the SHFB build targets -->
<Import Project="$(SHFBROOT)\SandcastleHelpFileBuilder.targets" />
<!-- The pre-build and post-build event properties must appear *after* the targets file import in order to be
evaluated correctly. -->
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
</PropertyGroup>
</Project>

@ -376,10 +376,12 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_destroy(grpc_channel *channel) {
}
GPR_EXPORT grpc_call *GPR_CALLTYPE
grpcsharp_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq,
grpcsharp_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(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
return grpc_channel_create_call(channel, parent_call, propagation_mask, cq,
method, host, deadline);
}
@ -497,7 +499,7 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call *call) {
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx,
const char *send_buffer, size_t send_buffer_len,
grpc_metadata_array *initial_metadata) {
grpc_metadata_array *initial_metadata, gpr_uint32 write_flags) {
/* TODO: don't use magic number */
grpc_op ops[6];
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
@ -511,7 +513,7 @@ grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx,
ops[1].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ops[1].data.send_message = ctx->send_message;
ops[1].flags = 0;
ops[1].flags = write_flags;
ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
ops[2].flags = 0;
@ -578,7 +580,7 @@ grpcsharp_call_start_client_streaming(grpc_call *call,
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
grpc_call *call, grpcsharp_batch_context *ctx, const char *send_buffer,
size_t send_buffer_len, grpc_metadata_array *initial_metadata) {
size_t send_buffer_len, grpc_metadata_array *initial_metadata, gpr_uint32 write_flags) {
/* TODO: don't use magic number */
grpc_op ops[5];
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
@ -592,7 +594,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
ops[1].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ops[1].data.send_message = ctx->send_message;
ops[1].flags = 0;
ops[1].flags = write_flags;
ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
ops[2].flags = 0;
@ -651,15 +653,22 @@ grpcsharp_call_start_duplex_streaming(grpc_call *call,
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_send_message(grpc_call *call, grpcsharp_batch_context *ctx,
const char *send_buffer, size_t send_buffer_len) {
const char *send_buffer, size_t send_buffer_len,
gpr_uint32 write_flags,
gpr_int32 send_empty_initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[1];
grpc_op ops[2];
size_t nops = send_empty_initial_metadata ? 2 : 1;
ops[0].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ops[0].data.send_message = ctx->send_message;
ops[0].flags = 0;
ops[0].flags = write_flags;
ops[1].op = GRPC_OP_SEND_INITIAL_METADATA;
ops[1].data.send_initial_metadata.count = 0;
ops[1].data.send_initial_metadata.metadata = NULL;
ops[1].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
return grpc_call_start_batch(call, ops, nops, ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
@ -675,9 +684,11 @@ grpcsharp_call_send_close_from_client(grpc_call *call,
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server(
grpc_call *call, grpcsharp_batch_context *ctx, grpc_status_code status_code,
const char *status_details, grpc_metadata_array *trailing_metadata) {
const char *status_details, grpc_metadata_array *trailing_metadata,
gpr_int32 send_empty_initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[1];
grpc_op ops[2];
size_t nops = send_empty_initial_metadata ? 2 : 1;
ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
ops[0].data.send_status_from_server.status = status_code;
ops[0].data.send_status_from_server.status_details =
@ -689,8 +700,12 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server(
ops[0].data.send_status_from_server.trailing_metadata =
ctx->send_status_from_server.trailing_metadata.metadata;
ops[0].flags = 0;
ops[1].op = GRPC_OP_SEND_INITIAL_METADATA;
ops[1].data.send_initial_metadata.count = 0;
ops[1].data.send_initial_metadata.metadata = NULL;
ops[1].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
return grpc_call_start_batch(call, ops, nops, ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
@ -706,16 +721,28 @@ grpcsharp_call_recv_message(grpc_call *call, grpcsharp_batch_context *ctx) {
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_start_serverside(grpc_call *call, grpcsharp_batch_context *ctx) {
/* TODO: don't use magic number */
grpc_op ops[2];
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
ops[0].data.send_initial_metadata.count = 0;
ops[0].data.send_initial_metadata.metadata = NULL;
grpc_op ops[1];
ops[0].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
ops[0].data.recv_close_on_server.cancelled =
(&ctx->recv_close_on_server_cancelled);
ops[0].flags = 0;
ops[1].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
ops[1].data.recv_close_on_server.cancelled =
(&ctx->recv_close_on_server_cancelled);
ops[1].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_send_initial_metadata(grpc_call *call,
grpcsharp_batch_context *ctx,
grpc_metadata_array *initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[1];
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
initial_metadata);
ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
ops[0].data.send_initial_metadata.metadata =
ctx->send_initial_metadata.metadata;
ops[0].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
@ -849,6 +876,11 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_redirect_log(grpcsharp_log_func func) {
typedef void(GPR_CALLTYPE *test_callback_funcptr)(gpr_int32 success);
/* Version info */
GPR_EXPORT const char *GPR_CALLTYPE grpcsharp_version_string() {
return grpc_version_string();
}
/* For testing */
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_test_callback(test_callback_funcptr callback) {

@ -11,7 +11,8 @@
'-pedantic',
'-g',
'-zdefs',
'-Werror'
'-Werror',
'-Wno-error=deprecated-declarations'
],
'ldflags': [
'-g'

@ -63,7 +63,7 @@ function runTest(iterations, callback) {
var timeDiff = process.hrtime(startTime);
intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000;
next(i+1);
}, {}, deadline);
}, {}, {deadline: deadline});
}
}
next(0);

@ -510,10 +510,21 @@ NAN_METHOD(Call::New) {
NanUtf8String method(args[1]);
double deadline = args[2]->NumberValue();
grpc_channel *wrapped_channel = channel->GetWrappedChannel();
grpc_call *wrapped_call = grpc_channel_create_call(
wrapped_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
CompletionQueueAsyncWorker::GetQueue(), *method, channel->GetHost(),
MillisecondsToTimespec(deadline));
grpc_call *wrapped_call;
if (args[3]->IsString()) {
NanUtf8String host_override(args[3]);
wrapped_call = grpc_channel_create_call(
wrapped_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
CompletionQueueAsyncWorker::GetQueue(), *method,
*host_override, MillisecondsToTimespec(deadline));
} else if (args[3]->IsUndefined() || args[3]->IsNull()) {
wrapped_call = grpc_channel_create_call(
wrapped_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
CompletionQueueAsyncWorker::GetQueue(), *method,
NULL, MillisecondsToTimespec(deadline));
} else {
return NanThrowTypeError("Call's fourth argument must be a string");
}
call = new Call(wrapped_call);
args.This()->SetHiddenValue(NanNew("channel_"), channel_object);
}

@ -59,14 +59,12 @@ using v8::Value;
NanCallback *Channel::constructor;
Persistent<FunctionTemplate> Channel::fun_tpl;
Channel::Channel(grpc_channel *channel, NanUtf8String *host)
: wrapped_channel(channel), host(host) {}
Channel::Channel(grpc_channel *channel) : wrapped_channel(channel) {}
Channel::~Channel() {
if (wrapped_channel != NULL) {
grpc_channel_destroy(wrapped_channel);
}
delete host;
}
void Channel::Init(Handle<Object> exports) {
@ -91,8 +89,6 @@ bool Channel::HasInstance(Handle<Value> val) {
grpc_channel *Channel::GetWrappedChannel() { return this->wrapped_channel; }
char *Channel::GetHost() { return **this->host; }
NAN_METHOD(Channel::New) {
NanScope();
@ -103,8 +99,7 @@ NAN_METHOD(Channel::New) {
}
grpc_channel *wrapped_channel;
// Owned by the Channel object
NanUtf8String *host = new NanUtf8String(args[0]);
NanUtf8String *host_override = NULL;
NanUtf8String host(args[0]);
grpc_credentials *creds;
if (!Credentials::HasInstance(args[1])) {
return NanThrowTypeError(
@ -116,12 +111,9 @@ NAN_METHOD(Channel::New) {
grpc_channel_args *channel_args_ptr;
if (args[2]->IsUndefined()) {
channel_args_ptr = NULL;
wrapped_channel = grpc_insecure_channel_create(**host, NULL);
wrapped_channel = grpc_insecure_channel_create(*host, NULL);
} else if (args[2]->IsObject()) {
Handle<Object> args_hash(args[2]->ToObject()->Clone());
if (args_hash->HasOwnProperty(NanNew(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG))) {
host_override = new NanUtf8String(args_hash->Get(NanNew(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)));
}
Handle<Array> keys(args_hash->GetOwnPropertyNames());
grpc_channel_args channel_args;
channel_args.num_args = keys->Length();
@ -153,20 +145,15 @@ NAN_METHOD(Channel::New) {
return NanThrowTypeError("Channel expects a string and an object");
}
if (creds == NULL) {
wrapped_channel = grpc_insecure_channel_create(**host, channel_args_ptr);
wrapped_channel = grpc_insecure_channel_create(*host, channel_args_ptr);
} else {
wrapped_channel =
grpc_secure_channel_create(creds, **host, channel_args_ptr);
grpc_secure_channel_create(creds, *host, channel_args_ptr);
}
if (channel_args_ptr != NULL) {
free(channel_args_ptr->args);
}
Channel *channel;
if (host_override == NULL) {
channel = new Channel(wrapped_channel, host);
} else {
channel = new Channel(wrapped_channel, host_override);
}
Channel *channel = new Channel(wrapped_channel);
channel->Wrap(args.This());
NanReturnValue(args.This());
} else {

@ -53,11 +53,8 @@ class Channel : public ::node::ObjectWrap {
/* Returns the grpc_channel struct that this object wraps */
grpc_channel *GetWrappedChannel();
/* Return the hostname that this channel connects to */
char *GetHost();
private:
explicit Channel(grpc_channel *channel, NanUtf8String *host);
explicit Channel(grpc_channel *channel);
~Channel();
// Prevent copying
@ -71,7 +68,6 @@ class Channel : public ::node::ObjectWrap {
static v8::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_channel *wrapped_channel;
NanUtf8String *host;
};
} // namespace node

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save