Merge branch 'master' into node_new_core_api

pull/504/head
murgatroid99 10 years ago
commit 2c810a4c4b
  1. 91
      Makefile
  2. 36
      build.json
  3. 3
      include/grpc++/credentials.h
  4. 70
      include/grpc/grpc.h
  5. 6
      include/grpc/grpc_security.h
  6. 6
      include/grpc/support/port_platform.h
  7. 16
      src/core/channel/channel_stack.c
  8. 5
      src/core/channel/client_channel.c
  9. 8
      src/core/channel/connected_channel.c
  10. 4
      src/core/iomgr/alarm.c
  11. 9
      src/core/iomgr/alarm_internal.h
  12. 41
      src/core/iomgr/pollset_kick.c
  13. 4
      src/core/iomgr/pollset_posix.c
  14. 2
      src/core/iomgr/resolve_address.c
  15. 2
      src/core/iomgr/socket_utils_linux.c
  16. 1
      src/core/iomgr/socket_utils_posix.c
  17. 6
      src/core/iomgr/tcp_server_posix.c
  18. 6
      src/core/security/credentials.c
  19. 46
      src/core/security/security_context.c
  20. 60
      src/core/support/env.h
  21. 61
      src/core/support/env_linux.c
  22. 56
      src/core/support/env_posix.c
  23. 60
      src/core/support/env_win32.c
  24. 89
      src/core/support/file.c
  25. 61
      src/core/support/file.h
  26. 97
      src/core/support/file_posix.c
  27. 78
      src/core/support/file_win32.c
  28. 6
      src/core/support/log_linux.c
  29. 7
      src/core/support/log_posix.c
  30. 3
      src/core/support/string_posix.c
  31. 84
      src/core/surface/byte_buffer_queue.c
  32. 59
      src/core/surface/byte_buffer_queue.h
  33. 1677
      src/core/surface/call.c
  34. 72
      src/core/surface/call.h
  35. 29
      src/core/surface/channel.c
  36. 2
      src/core/surface/channel.h
  37. 15
      src/core/surface/client.c
  38. 25
      src/core/surface/completion_queue.c
  39. 4
      src/core/surface/completion_queue.h
  40. 6
      src/core/surface/event_string.c
  41. 19
      src/core/surface/lame_client.c
  42. 261
      src/core/surface/server.c
  43. 2
      src/core/transport/chttp2/stream_encoder.c
  44. 36
      src/core/transport/chttp2_transport.c
  45. 1
      src/cpp/client/channel.cc
  46. 38
      src/csharp/Grpc.sln
  47. 2
      src/csharp/GrpcApi/.gitignore
  48. 74
      src/csharp/GrpcApi/DummyMathServiceClient.cs
  49. 97
      src/csharp/GrpcApi/Examples.cs
  50. 64
      src/csharp/GrpcApi/GrpcApi.csproj
  51. 26
      src/csharp/GrpcApi/IMathServiceClient.cs
  52. 1531
      src/csharp/GrpcApi/Math.cs
  53. 75
      src/csharp/GrpcApi/MathServiceClientStub.cs
  54. 35
      src/csharp/GrpcApi/Messages.cs
  55. 22
      src/csharp/GrpcApi/Properties/AssemblyInfo.cs
  56. 32
      src/csharp/GrpcApi/RecordingObserver.cs
  57. 50
      src/csharp/GrpcApi/math.proto
  58. 1
      src/csharp/GrpcCore/.gitignore
  59. 69
      src/csharp/GrpcCore/Call.cs
  60. 85
      src/csharp/GrpcCore/Calls.cs
  61. 59
      src/csharp/GrpcCore/Channel.cs
  62. 37
      src/csharp/GrpcCore/ClientStreamingAsyncResult.cs
  63. 62
      src/csharp/GrpcCore/GrpcCore.csproj
  64. 91
      src/csharp/GrpcCore/GrpcEnvironment.cs
  65. 485
      src/csharp/GrpcCore/Internal/AsyncCall.cs
  66. 182
      src/csharp/GrpcCore/Internal/CallSafeHandle.cs
  67. 34
      src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs
  68. 66
      src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs
  69. 75
      src/csharp/GrpcCore/Internal/Enums.cs
  70. 191
      src/csharp/GrpcCore/Internal/Event.cs
  71. 129
      src/csharp/GrpcCore/Internal/GrpcThreadPool.cs
  72. 28
      src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs
  73. 76
      src/csharp/GrpcCore/Internal/ServerSafeHandle.cs
  74. 33
      src/csharp/GrpcCore/Internal/StreamingInputObserver.cs
  75. 67
      src/csharp/GrpcCore/Internal/Timespec.cs
  76. 24
      src/csharp/GrpcCore/Properties/AssemblyInfo.cs
  77. 27
      src/csharp/GrpcCore/RpcException.cs
  78. 141
      src/csharp/GrpcCore/Server.cs
  79. 36
      src/csharp/GrpcCore/Status.cs
  80. 150
      src/csharp/GrpcCore/StatusCode.cs
  81. 2
      src/csharp/GrpcCoreTests/.gitignore
  82. 48
      src/csharp/GrpcCoreTests/ClientServerTest.cs
  83. 53
      src/csharp/GrpcCoreTests/GrpcCoreTests.csproj
  84. 18
      src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs
  85. 22
      src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs
  86. 21
      src/csharp/GrpcCoreTests/ServerTest.cs
  87. 41
      src/csharp/GrpcCoreTests/TestResult.xml
  88. 43
      src/csharp/GrpcCoreTests/TimespecTest.cs
  89. 51
      src/csharp/GrpcCoreTests/Utils.cs
  90. 1
      src/csharp/GrpcDemo/.gitignore
  91. 52
      src/csharp/GrpcDemo/GrpcDemo.csproj
  92. 28
      src/csharp/GrpcDemo/Program.cs
  93. 22
      src/csharp/GrpcDemo/Properties/AssemblyInfo.cs
  94. 32
      src/csharp/README.md
  95. BIN
      src/csharp/lib/Google.ProtocolBuffers.dll
  96. 72
      src/node/README.md
  97. 2
      templates/Makefile.template
  98. 3
      test/core/echo/echo_test.c
  99. 16
      test/core/end2end/cq_verifier.c
  100. 1
      test/core/end2end/dualstack_socket_test.c
  101. Some files were not shown because too many files have changed in this diff Show More

File diff suppressed because one or more lines are too long

@ -73,6 +73,7 @@
"src/core/statistics/census_tracing.h",
"src/core/statistics/hash_table.h",
"src/core/statistics/window_stats.h",
"src/core/surface/byte_buffer_queue.h",
"src/core/surface/call.h",
"src/core/surface/channel.h",
"src/core/surface/client.h",
@ -159,6 +160,7 @@
"src/core/statistics/hash_table.c",
"src/core/statistics/window_stats.c",
"src/core/surface/byte_buffer.c",
"src/core/surface/byte_buffer_queue.c",
"src/core/surface/byte_buffer_reader.c",
"src/core/surface/call.c",
"src/core/surface/channel.c",
@ -221,12 +223,12 @@
"include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h",
"include/grpc/support/time.h",
"include/grpc/support/time_posix.h",
"include/grpc/support/time_win32.h",
"include/grpc/support/useful.h"
],
"headers": [
"src/core/support/cpu.h",
"src/core/support/env.h",
"src/core/support/file.h",
"src/core/support/murmur_hash.h",
"src/core/support/string.h",
"src/core/support/thd_internal.h"
@ -237,6 +239,12 @@
"src/core/support/cmdline.c",
"src/core/support/cpu_linux.c",
"src/core/support/cpu_posix.c",
"src/core/support/env_linux.c",
"src/core/support/env_posix.c",
"src/core/support/env_win32.c",
"src/core/support/file.c",
"src/core/support/file_posix.c",
"src/core/support/file_win32.c",
"src/core/support/histogram.c",
"src/core/support/host_port.c",
"src/core/support/log.c",
@ -923,6 +931,30 @@
"gpr"
]
},
{
"name": "gpr_file_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/file_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{
"name": "gpr_env_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/env_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{
"name": "gpr_slice_buffer_test",
"build": "test",

@ -66,14 +66,13 @@ class Credentials final {
// Options used to build SslCredentials
// pem_roots_cert is the buffer containing the PEM encoding of the server root
// certificates. This parameter cannot be empty.
// certificates. If this parameter is empty, the default roots will be used.
// pem_private_key is the buffer containing the PEM encoding of the client's
// private key. This parameter can be empty if the client does not have a
// private key.
// pem_cert_chain is the buffer containing the PEM encoding of the client's
// certificate chain. This parameter can be empty if the client does not have
// a certificate chain.
// TODO(jboeuf) Change it to point to a file.
struct SslCredentialsOptions {
grpc::string pem_root_certs;
grpc::string pem_private_key;

@ -183,17 +183,16 @@ typedef struct grpc_metadata {
} grpc_metadata;
typedef enum grpc_completion_type {
GRPC_QUEUE_SHUTDOWN, /* Shutting down */
GRPC_READ, /* A read has completed */
GRPC_INVOKE_ACCEPTED, /* An invoke call has been accepted by flow
control */
GRPC_WRITE_ACCEPTED, /* A write has been accepted by
flow control */
GRPC_QUEUE_SHUTDOWN, /* Shutting down */
GRPC_IOREQ, /* grpc_call_ioreq completion */
GRPC_READ, /* A read has completed */
GRPC_WRITE_ACCEPTED, /* A write has been accepted by
flow control */
GRPC_FINISH_ACCEPTED, /* writes_done or write_status has been accepted */
GRPC_CLIENT_METADATA_READ, /* The metadata array sent by server received at
client */
GRPC_FINISHED, /* An RPC has finished. The event contains status.
On the server this will be OK or Cancelled. */
GRPC_FINISHED, /* An RPC has finished. The event contains status.
On the server this will be OK or Cancelled. */
GRPC_SERVER_RPC_NEW, /* A new RPC has arrived at the server */
GRPC_SERVER_SHUTDOWN, /* The server has finished shutting down */
GRPC_COMPLETION_DO_NOT_USE /* must be last, forces users to include
@ -213,6 +212,7 @@ typedef struct grpc_event {
grpc_op_error write_accepted;
grpc_op_error finish_accepted;
grpc_op_error invoke_accepted;
grpc_op_error ioreq;
struct {
size_t count;
grpc_metadata *elements;
@ -233,6 +233,57 @@ typedef struct grpc_event {
} data;
} grpc_event;
typedef struct {
size_t count;
size_t capacity;
grpc_metadata *metadata;
} grpc_metadata_array;
typedef struct {
const char *method;
const char *host;
gpr_timespec deadline;
} grpc_call_details;
typedef enum {
GRPC_OP_SEND_INITIAL_METADATA = 0,
GRPC_OP_SEND_MESSAGE,
GRPC_OP_SEND_CLOSE_FROM_CLIENT,
GRPC_OP_SEND_STATUS_FROM_SERVER,
GRPC_OP_RECV_INITIAL_METADATA,
GRPC_OP_RECV_MESSAGES,
GRPC_OP_RECV_STATUS_ON_CLIENT,
GRPC_OP_RECV_CLOSE_ON_SERVER
} grpc_op_type;
typedef struct grpc_op {
grpc_op_type op;
union {
struct {
size_t count;
const grpc_metadata *metadata;
} send_initial_metadata;
grpc_byte_buffer *send_message;
struct {
size_t trailing_metadata_count;
grpc_metadata *trailing_metadata;
grpc_status_code status;
const char *status_details;
} send_status_from_server;
grpc_metadata_array *recv_initial_metadata;
grpc_byte_buffer **recv_message;
struct {
grpc_metadata_array *trailing_metadata;
grpc_status_code *status;
char **status_details;
size_t *status_details_capacity;
} recv_status_on_client;
struct {
int *cancelled;
} recv_close_on_server;
} data;
} grpc_op;
/* Initialize the grpc library */
void grpc_init(void);
@ -279,6 +330,9 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec deadline);
grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
size_t nops, void *tag);
/* Create a client channel */
grpc_channel *grpc_channel_create(const char *target,
const grpc_channel_args *args);

@ -54,6 +54,12 @@ void grpc_credentials_release(grpc_credentials *creds);
/* Creates default credentials. */
grpc_credentials *grpc_default_credentials_create(void);
/* Environment variable that points to the default SSL roots file. This file
must be a PEM encoded file with all the roots such as the one that can be
downloaded from https://pki.google.com/roots.pem. */
#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \
"GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"
/* Object that holds a private key / certificate chain pair in PEM format. */
typedef struct {
/* private_key is the NULL-terminated string containing the PEM encoding of

@ -61,6 +61,8 @@
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
#define GPR_POSIX_ENV 1
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1
@ -74,6 +76,8 @@
#define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_LINUX_ENV 1
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1
@ -93,6 +97,8 @@
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
#define GPR_POSIX_ENV 1
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1

@ -210,6 +210,7 @@ void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
metadata_op.dir = GRPC_CALL_UP;
metadata_op.done_cb = do_nothing;
metadata_op.user_data = NULL;
metadata_op.flags = 0;
metadata_op.data.metadata = mdelem;
grpc_call_next_op(cur_elem, &metadata_op);
}
@ -221,6 +222,7 @@ void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
metadata_op.dir = GRPC_CALL_DOWN;
metadata_op.done_cb = do_nothing;
metadata_op.user_data = NULL;
metadata_op.flags = 0;
metadata_op.data.metadata = mdelem;
grpc_call_next_op(cur_elem, &metadata_op);
}
@ -231,14 +233,16 @@ void grpc_call_element_send_cancel(grpc_call_element *cur_elem) {
cancel_op.dir = GRPC_CALL_DOWN;
cancel_op.done_cb = do_nothing;
cancel_op.user_data = NULL;
cancel_op.flags = 0;
grpc_call_next_op(cur_elem, &cancel_op);
}
void grpc_call_element_send_finish(grpc_call_element *cur_elem) {
grpc_call_op cancel_op;
cancel_op.type = GRPC_SEND_FINISH;
cancel_op.dir = GRPC_CALL_DOWN;
cancel_op.done_cb = do_nothing;
cancel_op.user_data = NULL;
grpc_call_next_op(cur_elem, &cancel_op);
grpc_call_op finish_op;
finish_op.type = GRPC_SEND_FINISH;
finish_op.dir = GRPC_CALL_DOWN;
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
finish_op.flags = 0;
grpc_call_next_op(cur_elem, &finish_op);
}

@ -298,6 +298,7 @@ static void channel_op(grpc_channel_element *elem,
grpc_channel_element *from_elem, grpc_channel_op *op) {
channel_data *chand = elem->channel_data;
grpc_child_channel *child_channel;
grpc_channel_op rop;
GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
switch (op->type) {
@ -323,6 +324,10 @@ static void channel_op(grpc_channel_element *elem,
if (child_channel) {
grpc_child_channel_destroy(child_channel, 1);
}
/* fake a transport closed to satisfy the refcounting in client */
rop.type = GRPC_TRANSPORT_CLOSED;
rop.dir = GRPC_CALL_UP;
grpc_channel_next_op(elem, &rop);
break;
case GRPC_TRANSPORT_GOAWAY:
/* receiving goaway: if it's from our active child, drop the active child;

@ -298,10 +298,6 @@ static void recv_error(channel_data *chand, call_data *calld, int line,
static void do_nothing(void *calldata, grpc_op_error error) {}
static void done_message(void *user_data, grpc_op_error error) {
grpc_byte_buffer_destroy(user_data);
}
static void finish_message(channel_data *chand, call_data *calld) {
grpc_call_element *elem = calld->elem;
grpc_call_op call_op;
@ -309,9 +305,9 @@ static void finish_message(channel_data *chand, call_data *calld) {
call_op.flags = 0;
/* if we got all the bytes for this message, call up the stack */
call_op.type = GRPC_RECV_MESSAGE;
call_op.done_cb = done_message;
call_op.done_cb = do_nothing;
/* TODO(ctiller): this could be a lot faster if coded directly */
call_op.user_data = call_op.data.message = grpc_byte_buffer_create(
call_op.data.message = grpc_byte_buffer_create(
calld->incoming_message.slices, calld->incoming_message.count);
gpr_slice_buffer_reset_and_unref(&calld->incoming_message);

@ -335,10 +335,6 @@ static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now,
gpr_mu_unlock(&g_mu);
gpr_mu_unlock(&g_checker_mu);
} else if (next) {
gpr_mu_lock(&g_mu);
*next = gpr_time_min(*next, g_shard_queue[0]->min_deadline);
gpr_mu_unlock(&g_mu);
}
if (n && drop_mu) {

@ -39,6 +39,15 @@
/* iomgr internal api for dealing with alarms */
/* Check for alarms to be run, and run them.
Return non zero if alarm callbacks were executed.
Drops drop_mu if it is non-null before executing callbacks.
If next is non-null, TRY to update *next with the next running alarm
IF that alarm occurs before *next current value.
*next is never guaranteed to be updated on any given execution; however,
with high probability at least one thread in the system will see an update
at any time slice. */
int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next);
void grpc_alarm_list_init(gpr_timespec now);

@ -48,49 +48,49 @@
/* This implementation is based on a freelist of wakeup fds, with extra logic to
* handle kicks while there is no attached fd. */
/* TODO(klempner): Autosize this, and consider providing a way to disable the
* cap entirely on systems with large fd limits */
#define GRPC_MAX_CACHED_WFDS 50
#define GRPC_WFD_LOW_WATERMARK 25
static grpc_kick_fd_info *fd_freelist = NULL;
static int fd_freelist_count = 0;
static gpr_mu fd_freelist_mu;
static grpc_kick_fd_info *allocate_wfd(void) {
grpc_kick_fd_info *info;
grpc_kick_fd_info *info = NULL;
gpr_mu_lock(&fd_freelist_mu);
if (fd_freelist != NULL) {
info = fd_freelist;
fd_freelist = fd_freelist->next;
--fd_freelist_count;
} else {
}
gpr_mu_unlock(&fd_freelist_mu);
if (info == NULL) {
info = gpr_malloc(sizeof(*info));
grpc_wakeup_fd_create(&info->wakeup_fd);
info->next = NULL;
}
gpr_mu_unlock(&fd_freelist_mu);
return info;
}
static void destroy_wfd(void) {
/* assumes fd_freelist_mu is held */
grpc_kick_fd_info *current = fd_freelist;
fd_freelist = fd_freelist->next;
fd_freelist_count--;
grpc_wakeup_fd_destroy(&current->wakeup_fd);
gpr_free(current);
static void destroy_wfd(grpc_kick_fd_info* wfd) {
grpc_wakeup_fd_destroy(&wfd->wakeup_fd);
gpr_free(wfd);
}
static void free_wfd(grpc_kick_fd_info *fd_info) {
gpr_mu_lock(&fd_freelist_mu);
fd_info->next = fd_freelist;
fd_freelist = fd_info;
fd_freelist_count++;
if (fd_freelist_count > GRPC_MAX_CACHED_WFDS) {
while (fd_freelist_count > GRPC_WFD_LOW_WATERMARK) {
destroy_wfd();
}
if (fd_freelist_count < GRPC_MAX_CACHED_WFDS) {
fd_info->next = fd_freelist;
fd_freelist = fd_info;
fd_freelist_count++;
fd_info = NULL;
}
gpr_mu_unlock(&fd_freelist_mu);
if (fd_info) {
destroy_wfd(fd_info);
}
}
void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state) {
@ -148,6 +148,11 @@ void grpc_pollset_kick_global_init(void) {
}
void grpc_pollset_kick_global_destroy(void) {
while (fd_freelist != NULL) {
grpc_kick_fd_info *current = fd_freelist;
fd_freelist = fd_freelist->next;
destroy_wfd(current);
}
grpc_wakeup_fd_global_destroy();
gpr_mu_destroy(&fd_freelist_mu);
}

@ -80,9 +80,7 @@ void grpc_pollset_kick(grpc_pollset *p) {
}
}
void grpc_pollset_force_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick_kick(&p->kick_state); }
/* global state management */

@ -31,7 +31,9 @@
*
*/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/resolve_address.h"

@ -31,7 +31,9 @@
*
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX

@ -35,7 +35,6 @@
#ifdef GPR_POSIX_SOCKETUTILS
#define _BSD_SOURCE
#include "src/core/iomgr/socket_utils_posix.h"
#include <fcntl.h>

@ -31,11 +31,15 @@
*
*/
/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET
#define _GNU_SOURCE
#include "src/core/iomgr/tcp_server.h"
#include <limits.h>

@ -216,14 +216,10 @@ static void ssl_copy_key_material(const char *input, unsigned char **output,
static void ssl_build_config(const char *pem_root_certs,
grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
grpc_ssl_config *config) {
if (pem_root_certs == NULL) {
/* TODO(jboeuf): Get them from the environment. */
gpr_log(GPR_ERROR, "Default SSL roots not yet implemented.");
} else {
if (pem_root_certs != NULL) {
ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
&config->pem_root_certs_size);
}
if (pem_key_cert_pair != NULL) {
GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);

@ -39,6 +39,8 @@
#include "src/core/channel/http_client_filter.h"
#include "src/core/security/credentials.h"
#include "src/core/security/secure_endpoint.h"
#include "src/core/support/env.h"
#include "src/core/support/file.h"
#include "src/core/support/string.h"
#include "src/core/surface/lame_client.h"
#include "src/core/transport/chttp2/alpn.h"
@ -319,6 +321,28 @@ static grpc_security_context_vtable ssl_channel_vtable = {
static grpc_security_context_vtable ssl_server_vtable = {
ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer};
static gpr_slice default_pem_root_certs;
static void init_default_pem_root_certs(void) {
char *default_root_certs_path =
gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
if (default_root_certs_path == NULL) {
default_pem_root_certs = gpr_empty_slice();
} else {
default_pem_root_certs = gpr_load_file(default_root_certs_path, NULL);
gpr_free(default_root_certs_path);
}
}
static size_t get_default_pem_roots(const unsigned char **pem_root_certs) {
/* TODO(jboeuf@google.com): Maybe revisit the approach which consists in
loading all the roots once for the lifetime of the process. */
static gpr_once once = GPR_ONCE_INIT;
gpr_once_init(&once, init_default_pem_root_certs);
*pem_root_certs = GPR_SLICE_START_PTR(default_pem_root_certs);
return GPR_SLICE_LENGTH(default_pem_root_certs);
}
grpc_security_status grpc_ssl_channel_security_context_create(
grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
const char *secure_peer_name, grpc_channel_security_context **ctx) {
@ -330,6 +354,8 @@ grpc_security_status grpc_ssl_channel_security_context_create(
tsi_result result = TSI_OK;
grpc_ssl_channel_security_context *c;
size_t i;
const unsigned char *pem_root_certs;
size_t pem_root_certs_size;
for (i = 0; i < num_alpn_protocols; i++) {
alpn_protocol_strings[i] =
@ -338,9 +364,8 @@ grpc_security_status grpc_ssl_channel_security_context_create(
strlen(grpc_chttp2_get_alpn_version_index(i));
}
if (config == NULL || secure_peer_name == NULL ||
config->pem_root_certs == NULL) {
gpr_log(GPR_ERROR, "An ssl channel needs a secure name and root certs.");
if (config == NULL || secure_peer_name == NULL) {
gpr_log(GPR_ERROR, "An ssl channel needs a config and a secure name.");
goto error;
}
if (!check_request_metadata_creds(request_metadata_creds)) {
@ -357,11 +382,20 @@ grpc_security_status grpc_ssl_channel_security_context_create(
if (secure_peer_name != NULL) {
c->secure_peer_name = gpr_strdup(secure_peer_name);
}
if (config->pem_root_certs == NULL) {
pem_root_certs_size = get_default_pem_roots(&pem_root_certs);
if (pem_root_certs == NULL || pem_root_certs_size == 0) {
gpr_log(GPR_ERROR, "Could not get default pem root certs.");
goto error;
}
} else {
pem_root_certs = config->pem_root_certs;
pem_root_certs_size = config->pem_root_certs_size;
}
result = tsi_create_ssl_client_handshaker_factory(
config->pem_private_key, config->pem_private_key_size,
config->pem_cert_chain, config->pem_cert_chain_size,
config->pem_root_certs, config->pem_root_certs_size,
GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings,
config->pem_cert_chain, config->pem_cert_chain_size, pem_root_certs,
pem_root_certs_size, GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings,
alpn_protocol_string_lengths, num_alpn_protocols, &c->handshaker_factory);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",

@ -0,0 +1,60 @@
/*
*
* Copyright 2014, 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.
*
*/
#ifndef __GRPC_SUPPORT_ENV_H__
#define __GRPC_SUPPORT_ENV_H__
#include <stdio.h>
#include <grpc/support/slice.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Env utility functions */
/* Gets the environment variable value with the specified name.
Returns a newly allocated string. It is the responsability of the caller to
gpr_free the return value if not NULL (which means that the environment
variable exists). */
char *gpr_getenv(const char *name);
/* Sets the the environment with the specified name to the specified value. */
void gpr_setenv(const char *name, const char *value);
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_SUPPORT_ENV_H__ */

@ -0,0 +1,61 @@
/*
*
* Copyright 2014, 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.
*
*/
/* for secure_getenv. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX_ENV
#include "src/core/support/env.h"
#include <stdlib.h>
#include <grpc/support/log.h>
#include "src/core/support/string.h"
char *gpr_getenv(const char *name) {
char *result = secure_getenv(name);
return result == NULL ? result : gpr_strdup(result);
}
void gpr_setenv(const char *name, const char *value) {
int res = setenv(name, value, 1);
GPR_ASSERT(res == 0);
}
#endif /* GPR_LINUX_ENV */

@ -0,0 +1,56 @@
/*
*
* Copyright 2014, 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.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_ENV
#include "src/core/support/env.h"
#include <stdlib.h>
#include <grpc/support/log.h>
#include "src/core/support/string.h"
char *gpr_getenv(const char *name) {
char *result = getenv(name);
return result == NULL ? result : gpr_strdup(result);
}
void gpr_setenv(const char *name, const char *value) {
int res = setenv(name, value, 1);
GPR_ASSERT(res == 0);
}
#endif /* GPR_POSIX_ENV */

@ -0,0 +1,60 @@
/*
*
* Copyright 2014, 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.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_WIN32
#include "src/core/support/env.h"
#include <stdlib.h>
#include <grpc/support/log.h>
char *gpr_getenv(const char *name) {
size_t required_size;
char *result = NULL;
getenv_s(&required_size, NULL, 0, name);
if (required_size == 0) return NULL;
result = gpr_malloc(required_size);
getenv_s(&required_size, result, required_size, name);
return result;
}
void gpr_setenv(const char *name, const char *value) {
errno_t res = _putenv_s(name, value);
GPR_ASSERT(res == 0);
}
#endif /* GPR_WIN32 */

@ -0,0 +1,89 @@
/*
*
* Copyright 2014, 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.
*
*/
#include "src/core/support/file.h"
#include <errno.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/support/string.h"
gpr_slice gpr_load_file(const char *filename, int *success) {
unsigned char *contents = NULL;
size_t contents_size = 0;
unsigned char buf[4096];
char *error_msg = NULL;
gpr_slice result = gpr_empty_slice();
FILE *file = fopen(filename, "rb");
if (file == NULL) {
gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename,
strerror(errno));
GPR_ASSERT(error_msg != NULL);
goto end;
}
while (1) {
size_t bytes_read = fread(buf, 1, sizeof(buf), file);
if (bytes_read > 0) {
contents = gpr_realloc(contents, contents_size + bytes_read);
memcpy(contents + contents_size, buf, bytes_read);
contents_size += bytes_read;
}
if (bytes_read < sizeof(buf)) {
if (ferror(file)) {
gpr_asprintf(&error_msg, "Error %s occured while reading file %s.",
strerror(errno), filename);
GPR_ASSERT(error_msg != NULL);
goto end;
} else {
GPR_ASSERT(feof(file));
break;
}
}
}
if (success != NULL) *success = 1;
result = gpr_slice_new(contents, contents_size, gpr_free);
end:
if (error_msg != NULL) {
gpr_log(GPR_ERROR, "%s", error_msg);
gpr_free(error_msg);
if (success != NULL) *success = 0;
}
if (file != NULL) fclose(file);
return result;
}

@ -0,0 +1,61 @@
/*
*
* Copyright 2014, 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.
*
*/
#ifndef __GRPC_SUPPORT_FILE_H__
#define __GRPC_SUPPORT_FILE_H__
#include <stdio.h>
#include <grpc/support/slice.h>
#ifdef __cplusplus
extern "C" {
#endif
/* File utility functions */
/* Loads the content of a file into a slice. The success parameter, if not NULL,
will be set to 1 in case of success and 0 in case of failure. */
gpr_slice gpr_load_file(const char *filename, int *success);
/* Creates a temporary file from a prefix.
If tmp_filename is not NULL, *tmp_filename is assigned the name of the
created file and it is the responsibility of the caller to gpr_free it
unless an error occurs in which case it will be set to NULL. */
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename);
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_SUPPORT_FILE_H__ */

@ -0,0 +1,97 @@
/*
*
* Copyright 2014, 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.
*
*/
/* Posix code for gpr fdopen and mkstemp support. */
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
/* Don't know why I have to do this for mkstemp, looks like _POSIX_C_SOURCE
should be enough... */
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_FILE
#include "src/core/support/file.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/support/string.h"
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) {
FILE *result = NULL;
char *template;
int fd;
if (tmp_filename != NULL) *tmp_filename = NULL;
gpr_asprintf(&template, "%s_XXXXXX", prefix);
GPR_ASSERT(template != NULL);
fd = mkstemp(template);
if (fd == -1) {
gpr_log(GPR_ERROR, "mkstemp failed for template %s with error %s.",
template, strerror(errno));
goto end;
}
result = fdopen(fd, "w+");
if (result == NULL) {
gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).",
template, fd, strerror(errno));
unlink(template);
close(fd);
goto end;
}
end:
if (result != NULL && tmp_filename != NULL) {
*tmp_filename = template;
} else {
gpr_free(template);
}
return result;
}
#endif /* GPR_POSIX_FILE */

@ -0,0 +1,78 @@
/*
*
* Copyright 2014, 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.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_WIN32
#include "src/core/support/file.h"
#include <io.h>
#include <stdio.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) {
FILE *result = NULL;
char *template;
if (tmp_filename != NULL) *tmp_filename = NULL;
gpr_asprintf(&template, "%s_XXXXXX", prefix);
GPR_ASSERT(template != NULL);
/* _mktemp_s can only create a maximum of 26 file names for any combination of
base and template values which is kind of sad... We may revisit this
function later to have something better... */
if (_mktemp_s(template, strlen(template) + 1) != 0) {
gpr_log(LOG_ERROR, "Could not create tmp file.");
goto end;
}
if (fopen_s(&result, template, "wb+") != 0) {
gpr_log(GPR_ERROR, "Could not open file %s", template);
result = NULL;
goto end;
}
end:
if (result != NULL && tmp_filename != NULL) {
*tmp_filename = template;
} else {
gpr_free(template);
}
return result;
}
#endif /* GPR_WIN32 */

@ -31,8 +31,14 @@
*
*/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX

@ -31,11 +31,16 @@
*
*/
#ifndef _POSIX_C_SOURCE
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
/* FIXME: "posix" files probably shouldn't depend on _GNU_SOURCE */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <grpc/support/port_platform.h>
#if defined(GPR_POSIX_LOG)

@ -33,7 +33,8 @@
/* Posix code for gpr snprintf support. */
#ifndef _POSIX_C_SOURCE
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif

@ -0,0 +1,84 @@
/*
*
* Copyright 2014, 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.
*
*/
#include "src/core/surface/byte_buffer_queue.h"
#include <grpc/support/alloc.h>
#include <grpc/support/useful.h>
static void bba_destroy(grpc_bbq_array *array, size_t start_pos) {
size_t i;
for (i = start_pos; i < array->count; i++) {
grpc_byte_buffer_destroy(array->data[i]);
}
gpr_free(array->data);
}
/* Append an operation to an array, expanding as needed */
static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) {
if (a->count == a->capacity) {
a->capacity = GPR_MAX(a->capacity * 2, 8);
a->data = gpr_realloc(a->data, sizeof(grpc_byte_buffer *) * a->capacity);
}
a->data[a->count++] = buffer;
}
void grpc_bbq_destroy(grpc_byte_buffer_queue *q) {
bba_destroy(&q->filling, 0);
bba_destroy(&q->draining, q->drain_pos);
}
int grpc_bbq_empty(grpc_byte_buffer_queue *q) {
return (q->drain_pos == q->draining.count && q->filling.count == 0);
}
void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) {
bba_push(&q->filling, buffer);
}
grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) {
grpc_bbq_array temp_array;
if (q->drain_pos == q->draining.count) {
if (q->filling.count == 0) {
return NULL;
}
q->draining.count = 0;
q->drain_pos = 0;
/* swap arrays */
temp_array = q->filling;
q->filling = q->draining;
q->draining = temp_array;
}
return q->draining.data[q->drain_pos++];
}

@ -0,0 +1,59 @@
/*
*
* Copyright 2014, 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.
*
*/
#ifndef __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__
#define __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__
#include <grpc/byte_buffer.h>
/* TODO(ctiller): inline an element or two into this struct to avoid per-call
allocations */
typedef struct {
grpc_byte_buffer **data;
size_t count;
size_t capacity;
} grpc_bbq_array;
/* should be initialized by zeroing memory */
typedef struct {
size_t drain_pos;
grpc_bbq_array filling;
grpc_bbq_array draining;
} grpc_byte_buffer_queue;
void grpc_bbq_destroy(grpc_byte_buffer_queue *q);
grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q);
int grpc_bbq_empty(grpc_byte_buffer_queue *q);
void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *bb);
#endif /* __GRPC_INTERNAL_SURFACE_BYTE_BUFFER_QUEUE_H__ */

File diff suppressed because it is too large Load Diff

@ -38,27 +38,73 @@
#include "src/core/channel/metadata_buffer.h"
#include <grpc/grpc.h>
/* Primitive operation types - grpc_op's get rewritten into these */
typedef enum {
GRPC_IOREQ_RECV_INITIAL_METADATA,
GRPC_IOREQ_RECV_MESSAGE,
GRPC_IOREQ_RECV_TRAILING_METADATA,
GRPC_IOREQ_RECV_STATUS,
GRPC_IOREQ_RECV_CLOSE,
GRPC_IOREQ_SEND_INITIAL_METADATA,
GRPC_IOREQ_SEND_MESSAGE,
GRPC_IOREQ_SEND_TRAILING_METADATA,
GRPC_IOREQ_SEND_STATUS,
GRPC_IOREQ_SEND_CLOSE,
GRPC_IOREQ_OP_COUNT
} grpc_ioreq_op;
typedef struct {
grpc_status_code *code;
char **details;
size_t *details_capacity;
} grpc_recv_status_args;
typedef union {
grpc_metadata_array *recv_metadata;
grpc_byte_buffer **recv_message;
grpc_recv_status_args recv_status;
struct {
size_t count;
grpc_metadata *metadata;
} send_metadata;
grpc_byte_buffer *send_message;
struct {
grpc_status_code code;
char *details;
} send_status;
} grpc_ioreq_data;
typedef struct {
grpc_ioreq_op op;
grpc_ioreq_data data;
} grpc_ioreq;
typedef void (*grpc_ioreq_completion_func)(grpc_call *call,
grpc_op_error status,
void *user_data);
grpc_call *grpc_call_create(grpc_channel *channel,
const void *server_transport_data);
void grpc_call_internal_ref(grpc_call *call);
void grpc_call_internal_unref(grpc_call *call);
void grpc_call_internal_unref(grpc_call *call, int allow_immediate_deletion);
/* Helpers for grpc_client, grpc_server filters to publish received data to
the completion queue/surface layer */
void grpc_call_recv_metadata(grpc_call_element *surface_element,
grpc_call_op *op);
void grpc_call_recv_message(
grpc_call_element *surface_element, grpc_byte_buffer *message,
void (*on_finish)(void *user_data, grpc_op_error error), void *user_data);
void grpc_call_recv_finish(grpc_call_element *surface_element,
int is_full_close);
grpc_mdelem *md);
void grpc_call_recv_message(grpc_call_element *surface_element,
grpc_byte_buffer *message);
void grpc_call_read_closed(grpc_call_element *surface_element);
void grpc_call_stream_closed(grpc_call_element *surface_element);
void grpc_call_execute_op(grpc_call *call, grpc_call_op *op);
grpc_call_error grpc_call_start_ioreq_and_call_back(
grpc_call *call, const grpc_ioreq *reqs, size_t nreqs,
grpc_ioreq_completion_func on_complete, void *user_data);
/* Called when it's known that the initial batch of metadata is complete on the
client side (must not be called on the server) */
void grpc_call_client_initial_metadata_complete(
/* Called when it's known that the initial batch of metadata is complete */
void grpc_call_initial_metadata_complete(
grpc_call_element *surface_element);
void grpc_call_set_deadline(grpc_call_element *surface_element,
@ -69,10 +115,4 @@ grpc_call_stack *grpc_call_get_call_stack(grpc_call *call);
/* Given the top call_element, get the call object. */
grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element);
/* Get the metadata buffer. */
grpc_metadata_buffer *grpc_call_get_metadata_buffer(grpc_call *call);
void grpc_call_add_mdelem(grpc_call *call, grpc_mdelem *mdelem,
gpr_uint32 flags);
#endif /* __GRPC_INTERNAL_SURFACE_CALL_H__ */

@ -51,7 +51,10 @@ struct grpc_channel {
grpc_mdstr *authority_string;
};
#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1))
#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c)+1))
#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) (((grpc_channel *)(channel_stack)) - 1)
#define CHANNEL_FROM_TOP_ELEM(top_elem) \
CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem))
grpc_channel *grpc_channel_create_from_filters(
const grpc_channel_filter **filters, size_t num_filters,
@ -60,8 +63,8 @@ grpc_channel *grpc_channel_create_from_filters(
sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
grpc_channel *channel = gpr_malloc(size);
channel->is_client = is_client;
/* decremented by grpc_channel_destroy */
gpr_ref_init(&channel->refs, 1);
/* decremented by grpc_channel_destroy, and grpc_client_channel_closed if is_client */
gpr_ref_init(&channel->refs, 1 + is_client);
channel->metadata_context = mdctx;
channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status");
channel->grpc_message_string = grpc_mdstr_from_string(mdctx, "grpc-message");
@ -80,6 +83,7 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
grpc_call *call;
grpc_mdelem *path_mdelem;
grpc_mdelem *authority_mdelem;
grpc_call_op op;
if (!channel->is_client) {
gpr_log(GPR_ERROR, "Cannot create a call on the server.");
@ -91,20 +95,25 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
/* Add :path and :authority headers. */
/* TODO(klempner): Consider optimizing this by stashing mdelems for common
values of method and host. */
grpc_mdstr_ref(channel->path_string);
path_mdelem = grpc_mdelem_from_metadata_strings(
channel->metadata_context, channel->path_string,
channel->metadata_context, grpc_mdstr_ref(channel->path_string),
grpc_mdstr_from_string(channel->metadata_context, method));
grpc_call_add_mdelem(call, path_mdelem, 0);
op.type = GRPC_SEND_METADATA;
op.dir = GRPC_CALL_DOWN;
op.flags = 0;
op.data.metadata = path_mdelem;
op.done_cb = do_nothing;
op.user_data = NULL;
grpc_call_execute_op(call, &op);
grpc_mdstr_ref(channel->authority_string);
authority_mdelem = grpc_mdelem_from_metadata_strings(
channel->metadata_context, channel->authority_string,
grpc_mdstr_from_string(channel->metadata_context, host));
grpc_call_add_mdelem(call, authority_mdelem, 0);
op.data.metadata = authority_mdelem;
grpc_call_execute_op(call, &op);
if (0 != gpr_time_cmp(absolute_deadline, gpr_inf_future)) {
grpc_call_op op;
op.type = GRPC_SEND_DEADLINE;
op.dir = GRPC_CALL_DOWN;
op.flags = 0;
@ -152,6 +161,10 @@ void grpc_channel_destroy(grpc_channel *channel) {
grpc_channel_internal_unref(channel);
}
void grpc_client_channel_closed(grpc_channel_element *elem) {
grpc_channel_internal_unref(CHANNEL_FROM_TOP_ELEM(elem));
}
grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) {
return CHANNEL_STACK_FROM_CHANNEL(channel);
}

@ -45,6 +45,8 @@ grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel);
grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel);
grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel);
void grpc_client_channel_closed(grpc_channel_element *elem);
void grpc_channel_internal_ref(grpc_channel *channel);
void grpc_channel_internal_unref(grpc_channel *channel);

@ -34,6 +34,7 @@
#include "src/core/surface/client.h"
#include "src/core/surface/call.h"
#include "src/core/surface/channel.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@ -56,23 +57,23 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_call_next_op(elem, op);
break;
case GRPC_RECV_METADATA:
grpc_call_recv_metadata(elem, op);
grpc_call_recv_metadata(elem, op->data.metadata);
break;
case GRPC_RECV_DEADLINE:
gpr_log(GPR_ERROR, "Deadline received by client (ignored)");
break;
case GRPC_RECV_MESSAGE:
grpc_call_recv_message(elem, op->data.message, op->done_cb,
op->user_data);
grpc_call_recv_message(elem, op->data.message);
op->done_cb(op->user_data, GRPC_OP_OK);
break;
case GRPC_RECV_HALF_CLOSE:
grpc_call_recv_finish(elem, 0);
grpc_call_read_closed(elem);
break;
case GRPC_RECV_FINISH:
grpc_call_recv_finish(elem, 1);
grpc_call_stream_closed(elem);
break;
case GRPC_RECV_END_OF_INITIAL_METADATA:
grpc_call_client_initial_metadata_complete(elem);
grpc_call_initial_metadata_complete(elem);
break;
default:
GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
@ -87,7 +88,7 @@ static void channel_op(grpc_channel_element *elem,
gpr_log(GPR_ERROR, "Client cannot accept new calls");
break;
case GRPC_TRANSPORT_CLOSED:
gpr_log(GPR_ERROR, "Transport closed");
grpc_client_channel_closed(elem);
break;
case GRPC_TRANSPORT_GOAWAY:
gpr_slice_unref(op->data.goaway.message);

@ -173,18 +173,6 @@ void grpc_cq_end_read(grpc_completion_queue *cc, void *tag, grpc_call *call,
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}
void grpc_cq_end_invoke_accepted(grpc_completion_queue *cc, void *tag,
grpc_call *call,
grpc_event_finish_func on_finish,
void *user_data, grpc_op_error error) {
event *ev;
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
ev = add_locked(cc, GRPC_INVOKE_ACCEPTED, tag, call, on_finish, user_data);
ev->base.data.invoke_accepted = error;
end_op_locked(cc, GRPC_INVOKE_ACCEPTED);
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}
void grpc_cq_end_write_accepted(grpc_completion_queue *cc, void *tag,
grpc_call *call,
grpc_event_finish_func on_finish,
@ -197,6 +185,17 @@ void grpc_cq_end_write_accepted(grpc_completion_queue *cc, void *tag,
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}
void grpc_cq_end_ioreq(grpc_completion_queue *cc, void *tag, grpc_call *call,
grpc_event_finish_func on_finish, void *user_data,
grpc_op_error error) {
event *ev;
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
ev = add_locked(cc, GRPC_IOREQ, tag, call, on_finish, user_data);
ev->base.data.write_accepted = error;
end_op_locked(cc, GRPC_IOREQ);
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}
void grpc_cq_end_finish_accepted(grpc_completion_queue *cc, void *tag,
grpc_call *call,
grpc_event_finish_func on_finish,
@ -389,7 +388,7 @@ void grpc_event_finish(grpc_event *base) {
event *ev = (event *)base;
ev->on_finish(ev->on_finish_user_data, GRPC_OP_OK);
if (ev->base.call) {
grpc_call_internal_unref(ev->base.call);
grpc_call_internal_unref(ev->base.call, 1);
}
gpr_free(ev);
}

@ -97,6 +97,10 @@ void grpc_cq_end_new_rpc(grpc_completion_queue *cc, void *tag, grpc_call *call,
gpr_timespec deadline, size_t metadata_count,
grpc_metadata *metadata_elements);
void grpc_cq_end_ioreq(grpc_completion_queue *cc, void *tag, grpc_call *call,
grpc_event_finish_func on_finish, void *user_data,
grpc_op_error error);
void grpc_cq_end_server_shutdown(grpc_completion_queue *cc, void *tag);
/* disable polling for some tests */

@ -87,10 +87,10 @@ char *grpc_event_string(grpc_event *ev) {
gpr_strvec_add(&buf, gpr_strdup(" end-of-stream"));
}
break;
case GRPC_INVOKE_ACCEPTED:
gpr_strvec_add(&buf, gpr_strdup("INVOKE_ACCEPTED: "));
case GRPC_IOREQ:
gpr_strvec_add(&buf, gpr_strdup("IOREQ: "));
addhdr(&buf, ev);
adderr(&buf, ev->data.invoke_accepted);
adderr(&buf, ev->data.ioreq);
break;
case GRPC_WRITE_ACCEPTED:
gpr_strvec_add(&buf, gpr_strdup("WRITE_ACCEPTED: "));

@ -50,26 +50,16 @@ typedef struct {
grpc_mdelem *message;
} channel_data;
static void do_nothing(void *data, grpc_op_error error) {}
static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_call_op *op) {
channel_data *channeld = elem->channel_data;
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
switch (op->type) {
case GRPC_SEND_START: {
grpc_call_op set_status_op;
grpc_mdelem_ref(channeld->message);
memset(&set_status_op, 0, sizeof(grpc_call_op));
set_status_op.dir = GRPC_CALL_UP;
set_status_op.type = GRPC_RECV_METADATA;
set_status_op.done_cb = do_nothing;
set_status_op.data.metadata = channeld->message;
grpc_call_recv_metadata(elem, &set_status_op);
grpc_call_recv_finish(elem, 1);
case GRPC_SEND_START:
grpc_call_recv_metadata(elem, grpc_mdelem_ref(channeld->message));
grpc_call_stream_closed(elem);
break;
}
case GRPC_SEND_METADATA:
grpc_mdelem_unref(op->data.metadata);
break;
@ -86,6 +76,9 @@ static void channel_op(grpc_channel_element *elem,
case GRPC_CHANNEL_GOAWAY:
gpr_slice_unref(op->data.goaway.message);
break;
case GRPC_CHANNEL_DISCONNECT:
grpc_client_channel_closed(elem);
break;
default:
break;
}

@ -44,6 +44,7 @@
#include "src/core/surface/call.h"
#include "src/core/surface/channel.h"
#include "src/core/surface/completion_queue.h"
#include "src/core/transport/metadata.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
@ -63,11 +64,24 @@ typedef struct channel_data channel_data;
struct channel_data {
grpc_server *server;
grpc_channel *channel;
grpc_mdstr *path_key;
grpc_mdstr *authority_key;
/* linked list of all channels on a server */
channel_data *next;
channel_data *prev;
};
typedef void (*new_call_cb)(grpc_server *server, grpc_completion_queue *cq,
grpc_metadata_array *initial_metadata,
call_data *calld, void *user_data);
typedef struct {
void *user_data;
grpc_completion_queue *cq;
grpc_metadata_array *initial_metadata;
new_call_cb cb;
} requested_call;
struct grpc_server {
size_t channel_filter_count;
const grpc_channel_filter **channel_filters;
@ -76,9 +90,9 @@ struct grpc_server {
gpr_mu mu;
void **tags;
size_t ntags;
size_t tag_cap;
requested_call *requested_calls;
size_t requested_call_count;
size_t requested_call_capacity;
gpr_uint8 shutdown;
gpr_uint8 have_shutdown_tag;
@ -107,11 +121,17 @@ typedef enum {
ZOMBIED
} call_state;
typedef struct legacy_data { grpc_metadata_array *initial_metadata; } legacy_data;
struct call_data {
grpc_call *call;
call_state state;
gpr_timespec deadline;
grpc_mdstr *path;
grpc_mdstr *host;
legacy_data *legacy;
gpr_uint8 included[CALL_LIST_COUNT];
call_link links[CALL_LIST_COUNT];
@ -179,7 +199,7 @@ static void server_unref(grpc_server *server) {
grpc_channel_args_destroy(server->channel_args);
gpr_mu_destroy(&server->mu);
gpr_free(server->channel_filters);
gpr_free(server->tags);
gpr_free(server->requested_calls);
gpr_free(server);
}
}
@ -210,62 +230,36 @@ static void destroy_channel(channel_data *chand) {
grpc_iomgr_add_callback(finish_destroy_channel, chand);
}
static void queue_new_rpc(grpc_server *server, call_data *calld, void *tag) {
grpc_call *call = calld->call;
grpc_metadata_buffer *mdbuf = grpc_call_get_metadata_buffer(call);
size_t count = grpc_metadata_buffer_count(mdbuf);
grpc_metadata *elements = grpc_metadata_buffer_extract_elements(mdbuf);
const char *host = NULL;
const char *method = NULL;
size_t i;
for (i = 0; i < count; i++) {
if (0 == strcmp(elements[i].key, ":authority")) {
host = elements[i].value;
} else if (0 == strcmp(elements[i].key, ":path")) {
method = elements[i].value;
}
}
grpc_call_internal_ref(call);
grpc_cq_end_new_rpc(server->cq, tag, call,
grpc_metadata_buffer_cleanup_elements, elements, method,
host, calld->deadline, count, elements);
}
static void start_new_rpc(grpc_call_element *elem) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
grpc_server *server = chand->server;
gpr_mu_lock(&server->mu);
if (server->ntags) {
if (server->requested_call_count > 0) {
requested_call rc = server->requested_calls[--server->requested_call_count];
calld->state = ACTIVATED;
queue_new_rpc(server, calld, server->tags[--server->ntags]);
gpr_mu_unlock(&server->mu);
rc.cb(server, rc.cq, rc.initial_metadata, calld, rc.user_data);
} else {
calld->state = PENDING;
call_list_join(server, calld, PENDING_START);
gpr_mu_unlock(&server->mu);
}
gpr_mu_unlock(&server->mu);
}
static void kill_zombie(void *elem, int success) {
grpc_call_destroy(grpc_call_from_top_element(elem));
}
static void finish_rpc(grpc_call_element *elem, int is_full_close) {
static void stream_closed(grpc_call_element *elem) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
gpr_mu_lock(&chand->server->mu);
switch (calld->state) {
case ACTIVATED:
grpc_call_recv_finish(elem, is_full_close);
break;
case PENDING:
if (!is_full_close) {
grpc_call_recv_finish(elem, is_full_close);
break;
}
call_list_remove(chand->server, calld, PENDING_START);
/* fallthrough intended */
case NOT_STARTED:
@ -276,27 +270,60 @@ static void finish_rpc(grpc_call_element *elem, int is_full_close) {
break;
}
gpr_mu_unlock(&chand->server->mu);
grpc_call_stream_closed(elem);
}
static void read_closed(grpc_call_element *elem) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
gpr_mu_lock(&chand->server->mu);
switch (calld->state) {
case ACTIVATED:
case PENDING:
grpc_call_read_closed(elem);
break;
case NOT_STARTED:
calld->state = ZOMBIED;
grpc_iomgr_add_callback(kill_zombie, elem);
break;
case ZOMBIED:
break;
}
gpr_mu_unlock(&chand->server->mu);
}
static void call_op(grpc_call_element *elem, grpc_call_element *from_elemn,
grpc_call_op *op) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
grpc_mdelem *md;
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
switch (op->type) {
case GRPC_RECV_METADATA:
grpc_call_recv_metadata(elem, op);
md = op->data.metadata;
if (md->key == chand->path_key) {
calld->path = grpc_mdstr_ref(md->value);
grpc_mdelem_unref(md);
} else if (md->key == chand->authority_key) {
calld->host = grpc_mdstr_ref(md->value);
grpc_mdelem_unref(md);
} else {
grpc_call_recv_metadata(elem, md);
}
break;
case GRPC_RECV_END_OF_INITIAL_METADATA:
start_new_rpc(elem);
grpc_call_initial_metadata_complete(elem);
break;
case GRPC_RECV_MESSAGE:
grpc_call_recv_message(elem, op->data.message, op->done_cb,
op->user_data);
grpc_call_recv_message(elem, op->data.message);
op->done_cb(op->user_data, GRPC_OP_OK);
break;
case GRPC_RECV_HALF_CLOSE:
finish_rpc(elem, 0);
read_closed(elem);
break;
case GRPC_RECV_FINISH:
finish_rpc(elem, 1);
stream_closed(elem);
break;
case GRPC_RECV_DEADLINE:
grpc_call_set_deadline(elem, op->data.deadline);
@ -371,6 +398,7 @@ static void init_call_elem(grpc_call_element *elem,
static void destroy_call_elem(grpc_call_element *elem) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
int i;
gpr_mu_lock(&chand->server->mu);
@ -383,6 +411,19 @@ static void destroy_call_elem(grpc_call_element *elem) {
}
gpr_mu_unlock(&chand->server->mu);
if (calld->host) {
grpc_mdstr_unref(calld->host);
}
if (calld->path) {
grpc_mdstr_unref(calld->path);
}
if (calld->legacy) {
gpr_free(calld->legacy->initial_metadata->metadata);
gpr_free(calld->legacy->initial_metadata);
gpr_free(calld->legacy);
}
server_unref(chand->server);
}
@ -395,6 +436,8 @@ static void init_channel_elem(grpc_channel_element *elem,
GPR_ASSERT(!is_last);
chand->server = NULL;
chand->channel = NULL;
chand->path_key = grpc_mdstr_from_string(metadata_context, ":path");
chand->authority_key = grpc_mdstr_from_string(metadata_context, ":authority");
chand->next = chand->prev = chand;
}
@ -406,6 +449,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
chand->prev->next = chand->next;
chand->next = chand->prev = chand;
gpr_mu_unlock(&chand->server->mu);
grpc_mdstr_unref(chand->path_key);
grpc_mdstr_unref(chand->authority_key);
server_unref(chand->server);
}
}
@ -413,17 +458,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
static const grpc_channel_filter server_surface_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "server", };
static void early_terminate_requested_calls(grpc_completion_queue *cq,
void **tags, size_t ntags) {
size_t i;
for (i = 0; i < ntags; i++) {
grpc_cq_end_new_rpc(cq, tags[i], NULL, do_nothing, NULL, NULL, NULL,
gpr_inf_past, 0, NULL);
}
}
init_channel_elem, destroy_channel_elem, "server",
};
grpc_server *grpc_server_create_from_filters(grpc_completion_queue *cq,
grpc_channel_filter **filters,
@ -517,8 +553,8 @@ grpc_transport_setup_result grpc_server_setup_transport(
void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
void *shutdown_tag) {
listener *l;
void **tags;
size_t ntags;
requested_call *requested_calls;
size_t requested_call_count;
channel_data **channels;
channel_data *c;
size_t nchannels;
@ -547,10 +583,10 @@ void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
i++;
}
tags = server->tags;
ntags = server->ntags;
server->tags = NULL;
server->ntags = 0;
requested_calls = server->requested_calls;
requested_call_count = server->requested_call_count;
server->requested_calls = NULL;
server->requested_call_count = 0;
server->shutdown = 1;
server->have_shutdown_tag = have_shutdown_tag;
@ -579,8 +615,12 @@ void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
gpr_free(channels);
/* terminate all the requested calls */
early_terminate_requested_calls(server->cq, tags, ntags);
gpr_free(tags);
for (i = 0; i < requested_call_count; i++) {
requested_calls[i].cb(server, requested_calls[i].cq,
requested_calls[i].initial_metadata, NULL,
requested_calls[i].user_data);
}
gpr_free(requested_calls);
/* Shutdown listeners */
for (l = server->listeners; l; l = l->next) {
@ -625,36 +665,105 @@ void grpc_server_add_listener(grpc_server *server, void *arg,
server->listeners = l;
}
grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new) {
static grpc_call_error queue_call_request(grpc_server *server,
grpc_completion_queue *cq,
grpc_metadata_array *initial_metadata,
new_call_cb cb, void *user_data) {
call_data *calld;
grpc_cq_begin_op(server->cq, NULL, GRPC_SERVER_RPC_NEW);
requested_call *rc;
gpr_mu_lock(&server->mu);
if (server->shutdown) {
gpr_mu_unlock(&server->mu);
early_terminate_requested_calls(server->cq, &tag_new, 1);
cb(server, cq, initial_metadata, NULL, user_data);
return GRPC_CALL_OK;
}
calld = call_list_remove_head(server, PENDING_START);
if (calld) {
GPR_ASSERT(calld->state == PENDING);
calld->state = ACTIVATED;
queue_new_rpc(server, calld, tag_new);
gpr_mu_unlock(&server->mu);
cb(server, cq, initial_metadata, calld, user_data);
return GRPC_CALL_OK;
} else {
if (server->tag_cap == server->ntags) {
server->tag_cap = GPR_MAX(3 * server->tag_cap / 2, server->tag_cap + 1);
server->tags =
gpr_realloc(server->tags, sizeof(void *) * server->tag_cap);
if (server->requested_call_count == server->requested_call_capacity) {
server->requested_call_capacity =
GPR_MAX(server->requested_call_capacity + 8,
server->requested_call_capacity * 2);
server->requested_calls =
gpr_realloc(server->requested_calls,
sizeof(requested_call) * server->requested_call_capacity);
}
server->tags[server->ntags++] = tag_new;
rc = &server->requested_calls[server->requested_call_count++];
rc->cb = cb;
rc->cq = cq;
rc->user_data = user_data;
rc->initial_metadata = initial_metadata;
gpr_mu_unlock(&server->mu);
return GRPC_CALL_OK;
}
gpr_mu_unlock(&server->mu);
}
static void begin_request(grpc_server *server, grpc_completion_queue *cq,
grpc_metadata_array *initial_metadata,
call_data *call_data, void *tag) {
abort();
}
return GRPC_CALL_OK;
grpc_call_error grpc_server_request_call(
grpc_server *server, grpc_call_details *details,
grpc_metadata_array *initial_metadata, grpc_completion_queue *cq,
void *tag) {
grpc_cq_begin_op(cq, NULL, GRPC_IOREQ);
return queue_call_request(server, cq, initial_metadata, begin_request, tag);
}
static void publish_legacy_request(grpc_call *call, grpc_op_error status,
void *tag) {
grpc_call_element *elem =
grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
grpc_server *server = chand->server;
if (status == GRPC_OP_OK) {
grpc_cq_end_new_rpc(server->cq, tag, call, do_nothing, NULL,
grpc_mdstr_as_c_string(calld->path),
grpc_mdstr_as_c_string(calld->host), calld->deadline,
calld->legacy->initial_metadata->count,
calld->legacy->initial_metadata->metadata);
} else {
abort();
}
}
static void begin_legacy_request(grpc_server *server, grpc_completion_queue *cq,
grpc_metadata_array *initial_metadata,
call_data *calld, void *tag) {
grpc_ioreq req;
if (!calld) {
gpr_free(initial_metadata);
grpc_cq_end_new_rpc(cq, tag, NULL, do_nothing, NULL, NULL, NULL,
gpr_inf_past, 0, NULL);
return;
}
req.op = GRPC_IOREQ_RECV_INITIAL_METADATA;
req.data.recv_metadata = initial_metadata;
calld->legacy = gpr_malloc(sizeof(legacy_data));
memset(calld->legacy, 0, sizeof(legacy_data));
calld->legacy->initial_metadata = initial_metadata;
grpc_call_internal_ref(calld->call);
grpc_call_start_ioreq_and_call_back(calld->call, &req, 1,
publish_legacy_request, tag);
}
grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new) {
grpc_metadata_array *client_metadata =
gpr_malloc(sizeof(grpc_metadata_array));
memset(client_metadata, 0, sizeof(*client_metadata));
grpc_cq_begin_op(server->cq, NULL, GRPC_SERVER_RPC_NEW);
return queue_call_request(server, server->cq, client_metadata,
begin_legacy_request, tag_new);
}
const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) {

@ -432,7 +432,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
framer_state *st) {
char timeout_str[32];
char timeout_str[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
grpc_chttp2_encode_timeout(gpr_time_sub(deadline, gpr_now()), timeout_str);
hpack_enc(c, grpc_mdelem_from_metadata_strings(
c->mdctx, grpc_mdstr_ref(c->timeout_key_str),

@ -184,7 +184,6 @@ struct transport {
gpr_uint8 is_client;
gpr_mu mu;
gpr_cv cv;
/* basic state management - what are we doing at the moment? */
gpr_uint8 reading;
@ -328,6 +327,9 @@ static void maybe_start_some_streams(transport *t);
static void become_skip_parser(transport *t);
static void recv_data(void *tp, gpr_slice *slices, size_t nslices,
grpc_endpoint_cb_status error);
/*
* CONSTRUCTION/DESTRUCTION/REFCOUNTING
*/
@ -382,8 +384,8 @@ static void ref_transport(transport *t) { gpr_ref(&t->refs); }
static void init_transport(transport *t, grpc_transport_setup_callback setup,
void *arg, const grpc_channel_args *channel_args,
grpc_endpoint *ep, grpc_mdctx *mdctx,
int is_client) {
grpc_endpoint *ep, gpr_slice *slices, size_t nslices,
grpc_mdctx *mdctx, int is_client) {
size_t i;
int j;
grpc_transport_setup_result sr;
@ -395,7 +397,6 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
/* one ref is for destroy, the other for when ep becomes NULL */
gpr_ref_init(&t->refs, 2);
gpr_mu_init(&t->mu);
gpr_cv_init(&t->cv);
t->metadata_context = mdctx;
t->str_grpc_timeout =
grpc_mdstr_from_string(t->metadata_context, "grpc-timeout");
@ -422,6 +423,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
gpr_slice_buffer_init(&t->outbuf);
gpr_slice_buffer_init(&t->qbuf);
grpc_sopb_init(&t->nuke_later_sopb);
grpc_chttp2_hpack_parser_init(&t->hpack_parser, t->metadata_context);
if (is_client) {
gpr_slice_buffer_add(&t->qbuf,
gpr_slice_from_copied_string(CLIENT_CONNECT_STRING));
@ -476,14 +478,15 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
ref_transport(t);
gpr_mu_unlock(&t->mu);
ref_transport(t);
recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK);
sr = setup(arg, &t->base, t->metadata_context);
lock(t);
t->cb = sr.callbacks;
t->cb_user_data = sr.user_data;
grpc_chttp2_hpack_parser_init(&t->hpack_parser, t->metadata_context);
t->calling_back = 0;
gpr_cv_broadcast(&t->cv);
unlock(t);
unref_transport(t);
}
@ -492,9 +495,6 @@ static void destroy_transport(grpc_transport *gt) {
transport *t = (transport *)gt;
gpr_mu_lock(&t->mu);
while (t->calling_back) {
gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future);
}
t->cb = NULL;
gpr_mu_unlock(&t->mu);
@ -573,13 +573,6 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) {
gpr_mu_lock(&t->mu);
/* await pending callbacks
TODO(ctiller): this could be optimized to check if this stream is getting
callbacks */
while (t->calling_back) {
gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future);
}
/* stop parsing if we're currently parsing this stream */
if (t->deframe_state == DTS_FRAME && t->incoming_stream_id == s->id &&
s->id != 0) {
@ -591,7 +584,6 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) {
}
remove_from_stream_map(t, s);
gpr_cv_broadcast(&t->cv);
gpr_mu_unlock(&t->mu);
grpc_sopb_destroy(&s->outgoing_sopb);
@ -761,7 +753,6 @@ static void unlock(transport *t) {
if (perform_callbacks || call_closed || num_goaways) {
lock(t);
t->calling_back = 0;
gpr_cv_broadcast(&t->cv);
unlock(t);
unref_transport(t);
}
@ -892,7 +883,6 @@ static void finish_write_common(transport *t, int success) {
if (!t->reading) {
grpc_endpoint_destroy(t->ep);
t->ep = NULL;
gpr_cv_broadcast(&t->cv);
unref_transport(t); /* safe because we'll still have the ref for write */
}
unlock(t);
@ -957,7 +947,7 @@ static void send_batch(grpc_transport *gt, grpc_stream *gs, grpc_stream_op *ops,
stream_list_join(t, s, WRITABLE);
}
} else {
grpc_stream_ops_unref_owned_objects(ops, ops_count);
grpc_sopb_append(&t->nuke_later_sopb, ops, ops_count);
}
if (is_last && s->outgoing_sopb.nops == 0 && s->read_closed) {
stream_list_join(t, s, PENDING_CALLBACKS);
@ -1673,7 +1663,6 @@ static void recv_data(void *tp, gpr_slice *slices, size_t nslices,
if (!t->writing && t->ep) {
grpc_endpoint_destroy(t->ep);
t->ep = NULL;
gpr_cv_broadcast(&t->cv);
unref_transport(t); /* safe as we still have a ref for read */
}
unlock(t);
@ -1769,7 +1758,6 @@ void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
size_t nslices, grpc_mdctx *mdctx,
int is_client) {
transport *t = gpr_malloc(sizeof(transport));
init_transport(t, setup, arg, channel_args, ep, mdctx, is_client);
ref_transport(t);
recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK);
init_transport(t, setup, arg, channel_args, ep, slices, nslices, mdctx,
is_client);
}

@ -102,6 +102,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
grpc_call *call = grpc_channel_create_call_old(
c_channel_, method.name(), target_.c_str(), context->RawDeadline());
context->set_call(call);
grpc_event *ev;
void *finished_tag = reinterpret_cast<char *>(call);
void *metadata_read_tag = reinterpret_cast<char *>(call) + 2;

@ -0,0 +1,38 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcDemo", "GrpcDemo\GrpcDemo.csproj", "{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcApi", "GrpcApi\GrpcApi.csproj", "{7DC1433E-3225-42C7-B7EA-546D56E27A4B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCore", "GrpcCore\GrpcCore.csproj", "{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCoreTests", "GrpcCoreTests\GrpcCoreTests.csproj", "{86EC5CB4-4EA2-40A2-8057-86542A0353BB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = GrpcDemo\GrpcDemo.csproj
EndGlobalSection
EndGlobal

@ -0,0 +1,2 @@
test-results
bin

@ -0,0 +1,74 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reactive.Linq;
namespace math
{
// /// <summary>
// /// Dummy local implementation of math service.
// /// </summary>
// public class DummyMathServiceClient : IMathServiceClient
// {
// public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken))
// {
// // TODO: cancellation...
// return DivInternal(args);
// }
//
// public Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken))
// {
// return Task.Factory.StartNew(() => DivInternal(args), token);
// }
//
// public IObservable<Num> Fib(FibArgs args, CancellationToken token = default(CancellationToken))
// {
// if (args.Limit > 0)
// {
// // TODO: cancellation
// return FibInternal(args.Limit).ToObservable();
// }
//
// throw new NotImplementedException("Not implemented yet");
// }
//
// public Task<Num> Sum(IObservable<Num> inputs, CancellationToken token = default(CancellationToken))
// {
// // TODO: implement
// inputs = null;
// return Task.Factory.StartNew(() => Num.CreateBuilder().Build(), token);
// }
//
// public IObservable<DivReply> DivMany(IObservable<DivArgs> inputs, CancellationToken token = default(CancellationToken))
// {
// // TODO: implement
// inputs = null;
// return new List<DivReply> { }.ToObservable ();
// }
//
//
// DivReply DivInternal(DivArgs args)
// {
// long quotient = args.Dividend / args.Divisor;
// long remainder = args.Dividend % args.Divisor;
// return new DivReply.Builder{ Quotient = quotient, Remainder = remainder }.Build();
// }
//
// IEnumerable<Num> FibInternal(long n)
// {
// long a = 0;
// yield return new Num.Builder{Num_=a}.Build();
//
// long b = 1;
// for (long i = 0; i < n - 1; i++)
// {
// long temp = a;
// a = b;
// b = temp + b;
// yield return new Num.Builder{Num_=a}.Build();
// }
// }
// }
}

@ -0,0 +1,97 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reactive.Linq;
namespace math
{
public class Examples
{
public static void DivExample(IMathServiceClient stub)
{
DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build());
Console.WriteLine("Div Result: " + result);
}
public static void DivAsyncExample(IMathServiceClient stub)
{
Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
DivReply result = call.Result;
Console.WriteLine(result);
}
public static void DivAsyncWithCancellationExample(IMathServiceClient stub)
{
Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
DivReply result = call.Result;
Console.WriteLine(result);
}
public static void FibExample(IMathServiceClient stub)
{
var recorder = new RecordingObserver<Num>();
stub.Fib(new FibArgs.Builder { Limit = 5 }.Build(), recorder);
List<Num> numbers = recorder.ToList().Result;
Console.WriteLine("Fib Result: " + string.Join("|", recorder.ToList().Result));
}
public static void SumExample(IMathServiceClient stub)
{
List<Num> numbers = new List<Num>{new Num.Builder { Num_ = 1 }.Build(),
new Num.Builder { Num_ = 2 }.Build(),
new Num.Builder { Num_ = 3 }.Build()};
var res = stub.Sum();
foreach (var num in numbers) {
res.Inputs.OnNext(num);
}
res.Inputs.OnCompleted();
Console.WriteLine("Sum Result: " + res.Task.Result);
}
public static void DivManyExample(IMathServiceClient stub)
{
List<DivArgs> divArgsList = new List<DivArgs>{
new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(),
new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(),
new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
};
var recorder = new RecordingObserver<DivReply>();
var inputs = stub.DivMany(recorder);
foreach (var input in divArgsList)
{
inputs.OnNext(input);
}
inputs.OnCompleted();
Console.WriteLine("DivMany Result: " + string.Join("|", recorder.ToList().Result));
}
public static void DependendRequestsExample(IMathServiceClient stub)
{
var numberList = new List<Num>
{ new Num.Builder{ Num_ = 1 }.Build(),
new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build()
};
numberList.ToObservable();
//IObserver<Num> numbers;
//Task<Num> call = stub.Sum(out numbers);
//foreach (var num in numberList)
//{
// numbers.OnNext(num);
//}
//numbers.OnCompleted();
//Num sum = call.Result;
//DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build());
}
}
}

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>GrpcApi</RootNamespace>
<AssemblyName>GrpcApi</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Reactive.Linq, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<Private>False</Private>
</Reference>
<Reference Include="System.Data.Linq" />
<Reference Include="System.Reactive.Interfaces, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<Private>False</Private>
</Reference>
<Reference Include="System.Reactive.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<Private>False</Private>
</Reference>
<Reference Include="Google.ProtocolBuffers">
<HintPath>..\lib\Google.ProtocolBuffers.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Examples.cs" />
<Compile Include="IMathServiceClient.cs" />
<Compile Include="Math.cs" />
<Compile Include="DummyMathServiceClient.cs" />
<Compile Include="MathServiceClientStub.cs" />
<Compile Include="RecordingObserver.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
<Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
<Name>GrpcCore</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -0,0 +1,26 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reactive.Linq;
using Google.GRPC.Core;
namespace math
{
/// <summary>
/// Hand-written stub for MathService defined in math.proto.
/// This code will be generated by gRPC codegen in the future.
/// </summary>
public interface IMathServiceClient
{
DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken));
Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken));
Task Fib(FibArgs args, IObserver<Num> outputs, CancellationToken token = default(CancellationToken));
ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken));
IObserver<DivArgs> DivMany(IObserver<DivReply> outputs, CancellationToken token = default(CancellationToken));
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,75 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reactive.Linq;
using Google.GRPC.Core;
namespace math
{
/// <summary>
/// Implementation of math service stub (this is handwritten version of code
/// that will normally be generated).
/// </summary>
public class MathServiceClientStub : IMathServiceClient
{
readonly Channel channel;
readonly TimeSpan methodTimeout;
public MathServiceClientStub(Channel channel, TimeSpan methodTimeout)
{
this.channel = channel;
this.methodTimeout = methodTimeout;
}
public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
return Calls.BlockingUnaryCall(call, args, token);
}
public Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
return Calls.AsyncUnaryCall(call, args, token);
}
public Task Fib(FibArgs args, IObserver<Num> outputs, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<FibArgs, Num>("/math.Math/Fib", Serialize_FibArgs, Deserialize_Num, methodTimeout, channel);
return Calls.AsyncServerStreamingCall(call, args, outputs, token);
}
public ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<Num, Num>("/math.Math/Sum", Serialize_Num, Deserialize_Num, methodTimeout, channel);
return Calls.AsyncClientStreamingCall(call, token);
}
public IObserver<DivArgs> DivMany(IObserver<DivReply> outputs, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/math.Math/DivMany", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
return Calls.DuplexStreamingCall(call, outputs, token);
}
private static byte[] Serialize_DivArgs(DivArgs arg) {
return arg.ToByteArray();
}
private static byte[] Serialize_FibArgs(FibArgs arg) {
return arg.ToByteArray();
}
private static byte[] Serialize_Num(Num arg) {
return arg.ToByteArray();
}
private static DivReply Deserialize_DivReply(byte[] payload) {
return DivReply.CreateBuilder().MergeFrom(payload).Build();
}
private static Num Deserialize_Num(byte[] payload) {
return Num.CreateBuilder().MergeFrom(payload).Build();
}
}
}

@ -0,0 +1,35 @@
//using System;
//namespace Google.GRPC.Examples.Math
//{
// // Messages in this file are placeholders for actual protobuf message classes
// // that will be generated from math.proto file.
//
// public class DivArgs
// {
// public long Dividend{ get; set; }
// public long Divisor { get; set; }
// }
//
// public class DivReply
// {
// public long Quotient { get; set; }
// public long Remainder { get; set; }
// }
//
// public class FibArgs
// {
// public long Limit { get; set; }
// }
//
// public class Number
// {
// public long Num { get; set; }
// }
//
// public class FibReply
// {
// public long Count { get; set; }
// }
//}

@ -0,0 +1,22 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("GrpcApi")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("jtattermusch")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace math
{
public class RecordingObserver<T> : IObserver<T>
{
TaskCompletionSource<List<T>> tcs = new TaskCompletionSource<List<T>>();
List<T> data = new List<T>();
public void OnCompleted()
{
tcs.SetResult(data);
}
public void OnError(Exception error)
{
tcs.SetException(error);
}
public void OnNext(T value)
{
data.Add(value);
}
public Task<List<T>> ToList() {
return tcs.Task;
}
}
}

@ -0,0 +1,50 @@
syntax = "proto2";
package math;
message DivArgs {
optional int64 dividend = 1;
optional int64 divisor = 2;
}
message DivReply {
optional int64 quotient = 1;
optional int64 remainder = 2;
}
message FibArgs {
optional int64 limit = 1;
}
message Num {
optional int64 num = 1;
}
message FibReply {
optional int64 count = 1;
}
service Math {
// Div divides args.dividend by args.divisor and returns the quotient and
// remainder.
rpc Div (DivArgs) returns (DivReply) {
}
// DivMany accepts an arbitrary number of division args from the client stream
// and sends back the results in the reply stream. The stream continues until
// the client closes its end; the server does the same after sending all the
// replies. The stream ends immediately if either end aborts.
rpc DivMany (stream DivArgs) returns (stream DivReply) {
}
// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
// generates up to limit numbers; otherwise it continues until the call is
// canceled. Unlike Fib above, Fib has no final FibReply.
rpc Fib (FibArgs) returns (stream Num) {
}
// Sum sums a stream of numbers, returning the final result once the stream
// is closed.
rpc Sum (stream Num) returns (Num) {
}
}

@ -0,0 +1,69 @@
using System;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core
{
public class Call<TRequest, TResponse>
{
readonly string methodName;
readonly Func<TRequest, byte[]> requestSerializer;
readonly Func<byte[], TResponse> responseDeserializer;
readonly TimeSpan timeout;
readonly Channel channel;
// TODO: channel param should be removed in the future.
public Call(string methodName,
Func<TRequest, byte[]> requestSerializer,
Func<byte[], TResponse> responseDeserializer,
TimeSpan timeout,
Channel channel) {
this.methodName = methodName;
this.requestSerializer = requestSerializer;
this.responseDeserializer = responseDeserializer;
this.timeout = timeout;
this.channel = channel;
}
public Channel Channel
{
get
{
return this.channel;
}
}
public TimeSpan Timeout
{
get
{
return this.timeout;
}
}
public string MethodName
{
get
{
return this.methodName;
}
}
public Func<TRequest, byte[]> RequestSerializer
{
get
{
return this.requestSerializer;
}
}
public Func<byte[], TResponse> ResponseDeserializer
{
get
{
return this.responseDeserializer;
}
}
}
}

@ -0,0 +1,85 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core
{
// NOTE: this class is work-in-progress
/// <summary>
/// Helper methods for generated stubs to make RPC calls.
/// </summary>
public static class Calls
{
public static TResponse BlockingUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
{
//TODO: implement this in real synchronous style once new GRPC C core API is available.
return AsyncUnaryCall(call, req, token).Result;
}
public static async Task<TResponse> AsyncUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
asyncCall.Initialize(call.Channel, call.MethodName);
asyncCall.Start(false, GetCompletionQueue());
await asyncCall.WriteAsync(req);
await asyncCall.WritesCompletedAsync();
TResponse response = await asyncCall.ReadAsync();
Status status = await asyncCall.Finished;
if (status.StatusCode != StatusCode.GRPC_STATUS_OK)
{
throw new RpcException(status);
}
return response;
}
public static async Task AsyncServerStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, IObserver<TResponse> outputs, CancellationToken token)
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
asyncCall.Initialize(call.Channel, call.MethodName);
asyncCall.Start(false, GetCompletionQueue());
asyncCall.StartReadingToStream(outputs);
await asyncCall.WriteAsync(req);
await asyncCall.WritesCompletedAsync();
}
public static ClientStreamingAsyncResult<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token)
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
asyncCall.Initialize(call.Channel, call.MethodName);
asyncCall.Start(false, GetCompletionQueue());
var task = asyncCall.ReadAsync();
var inputs = new StreamingInputObserver<TRequest, TResponse>(asyncCall);
return new ClientStreamingAsyncResult<TRequest, TResponse>(task, inputs);
}
public static TResponse BlockingClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObservable<TRequest> inputs, CancellationToken token)
{
throw new NotImplementedException();
}
public static IObserver<TRequest> DuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObserver<TResponse> outputs, CancellationToken token)
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
asyncCall.Initialize(call.Channel, call.MethodName);
asyncCall.Start(false, GetCompletionQueue());
asyncCall.StartReadingToStream(outputs);
var inputs = new StreamingInputObserver<TRequest, TResponse>(asyncCall);
return inputs;
}
private static CompletionQueueSafeHandle GetCompletionQueue() {
return GrpcEnvironment.ThreadPool.CompletionQueue;
}
}
}

@ -0,0 +1,59 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core
{
public class Channel : IDisposable
{
/// <summary>
/// Make sure GPRC environment is initialized before any channels get used.
/// </summary>
static Channel() {
GrpcEnvironment.EnsureInitialized();
}
readonly ChannelSafeHandle handle;
readonly String target;
// TODO: add way how to create grpc_secure_channel....
// TODO: add support for channel args...
public Channel(string target)
{
this.handle = ChannelSafeHandle.Create(target, IntPtr.Zero);
this.target = target;
}
internal ChannelSafeHandle Handle
{
get
{
return this.handle;
}
}
public string Target
{
get
{
return this.target;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (handle != null && !handle.IsInvalid)
{
handle.Dispose();
}
}
}
}

@ -0,0 +1,37 @@
using System;
using System.Threading.Tasks;
namespace Google.GRPC.Core
{
/// <summary>
/// Return type for client streaming async method.
/// </summary>
public struct ClientStreamingAsyncResult<TRequest, TResponse>
{
readonly Task<TResponse> task;
readonly IObserver<TRequest> inputs;
public ClientStreamingAsyncResult(Task<TResponse> task, IObserver<TRequest> inputs)
{
this.task = task;
this.inputs = inputs;
}
public Task<TResponse> Task
{
get
{
return this.task;
}
}
public IObserver<TRequest> Inputs
{
get
{
return this.inputs;
}
}
}
}

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>GrpcCore</RootNamespace>
<AssemblyName>GrpcCore</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RpcException.cs" />
<Compile Include="Calls.cs" />
<Compile Include="Call.cs" />
<Compile Include="ClientStreamingAsyncResult.cs" />
<Compile Include="GrpcEnvironment.cs" />
<Compile Include="Status.cs" />
<Compile Include="StatusCode.cs" />
<Compile Include="Server.cs" />
<Compile Include="Channel.cs" />
<Compile Include="Internal\CallSafeHandle.cs" />
<Compile Include="Internal\ChannelSafeHandle.cs" />
<Compile Include="Internal\CompletionQueueSafeHandle.cs" />
<Compile Include="Internal\Enums.cs" />
<Compile Include="Internal\Event.cs" />
<Compile Include="Internal\SafeHandleZeroIsInvalid.cs" />
<Compile Include="Internal\Timespec.cs" />
<Compile Include="Internal\GrpcThreadPool.cs" />
<Compile Include="Internal\AsyncCall.cs" />
<Compile Include="Internal\ServerSafeHandle.cs" />
<Compile Include="Internal\StreamingInputObserver.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<Folder Include="Internal\" />
</ItemGroup>
</Project>

@ -0,0 +1,91 @@
using System;
using Google.GRPC.Core.Internal;
using System.Runtime.InteropServices;
namespace Google.GRPC.Core
{
/// <summary>
/// Encapsulates initialization and shutdown of GRPC C core library.
/// You should not need to initialize it manually, as static constructors
/// should load the library when needed.
/// </summary>
public static class GrpcEnvironment
{
const int THREAD_POOL_SIZE = 1;
[DllImport("libgrpc.so")]
static extern void grpc_init();
[DllImport("libgrpc.so")]
static extern void grpc_shutdown();
static object staticLock = new object();
static bool initCalled = false;
static bool shutdownCalled = false;
static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE);
/// <summary>
/// Makes sure GRPC environment is initialized.
/// </summary>
public static void EnsureInitialized() {
lock(staticLock)
{
if (!initCalled)
{
initCalled = true;
GrpcInit();
}
}
}
/// <summary>
/// Shuts down the GRPC environment if it was initialized before.
/// Repeated invocations have no effect.
/// </summary>
public static void Shutdown()
{
lock(staticLock)
{
if (initCalled && !shutdownCalled)
{
shutdownCalled = true;
GrpcShutdown();
}
}
}
/// <summary>
/// Initializes GRPC C Core library.
/// </summary>
private static void GrpcInit()
{
grpc_init();
threadPool.Start();
// TODO: use proper logging here
Console.WriteLine("GRPC initialized.");
}
/// <summary>
/// Shutdown GRPC C Core library.
/// </summary>
private static void GrpcShutdown()
{
threadPool.Stop();
grpc_shutdown();
// TODO: use proper logging here
Console.WriteLine("GRPC shutdown.");
}
internal static GrpcThreadPool ThreadPool
{
get
{
return threadPool;
}
}
}
}

@ -0,0 +1,485 @@
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// Listener for call events that can be delivered from a completion queue.
/// </summary>
internal interface ICallEventListener {
void OnClientMetadata();
void OnRead(byte[] payload);
void OnWriteAccepted(GRPCOpError error);
void OnFinishAccepted(GRPCOpError error);
// ignore the status on server
void OnFinished(Status status);
}
/// <summary>
/// Handle native call lifecycle and provides convenience methods.
/// </summary>
internal class AsyncCall<TWrite, TRead>: ICallEventListener, IDisposable
{
readonly Func<TWrite, byte[]> serializer;
readonly Func<byte[], TRead> deserializer;
// TODO: make sure the delegate doesn't get garbage collected while
// native callbacks are in the completion queue.
readonly EventCallbackDelegate callbackHandler;
object myLock = new object();
bool disposed;
CallSafeHandle call;
bool started;
bool errorOccured;
bool cancelRequested;
bool halfcloseRequested;
bool halfclosed;
bool doneWithReading;
Nullable<Status> finishedStatus;
TaskCompletionSource<object> writeTcs;
TaskCompletionSource<TRead> readTcs;
TaskCompletionSource<object> halfcloseTcs = new TaskCompletionSource<object>();
TaskCompletionSource<Status> finishedTcs = new TaskCompletionSource<Status>();
IObserver<TRead> readObserver;
public AsyncCall(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
{
this.serializer = serializer;
this.deserializer = deserializer;
this.callbackHandler = HandleEvent;
}
public Task WriteAsync(TWrite msg)
{
return StartWrite(msg, false).Task;
}
public Task WritesCompletedAsync()
{
WritesDone();
return halfcloseTcs.Task;
}
public Task WriteStatusAsync(Status status)
{
WriteStatus(status);
return halfcloseTcs.Task;
}
public Task<TRead> ReadAsync()
{
return StartRead().Task;
}
public Task<Status> Finished
{
get
{
return finishedTcs.Task;
}
}
/// <summary>
/// Initiates reading to given observer.
/// </summary>
public void StartReadingToStream(IObserver<TRead> readObserver) {
lock (myLock)
{
CheckStarted();
if (this.readObserver != null)
{
throw new InvalidOperationException("Already registered an observer.");
}
this.readObserver = readObserver;
StartRead();
}
}
public void Initialize(Channel channel, String methodName) {
lock (myLock)
{
this.call = CallSafeHandle.Create(channel.Handle, methodName, channel.Target, Timespec.InfFuture);
}
}
public void InitializeServer(CallSafeHandle call)
{
lock(myLock)
{
this.call = call;
}
}
// Client only
public void Start(bool buffered, CompletionQueueSafeHandle cq)
{
lock (myLock)
{
if (started)
{
throw new InvalidOperationException("Already started.");
}
call.Invoke(cq, buffered, callbackHandler, callbackHandler);
started = true;
}
}
// Server only
public void Accept(CompletionQueueSafeHandle cq)
{
lock (myLock)
{
if (started)
{
throw new InvalidOperationException("Already started.");
}
call.ServerAccept(cq, callbackHandler);
call.ServerEndInitialMetadata(0);
started = true;
}
}
public TaskCompletionSource<object> StartWrite(TWrite msg, bool buffered)
{
lock (myLock)
{
CheckStarted();
CheckNotFinished();
CheckNoError();
CheckCancelNotRequested();
if (halfcloseRequested || halfclosed)
{
throw new InvalidOperationException("Already halfclosed.");
}
if (writeTcs != null)
{
throw new InvalidOperationException("Only one write can be pending at a time");
}
// TODO: wrap serialization...
byte[] payload = serializer(msg);
call.StartWrite(payload, buffered, callbackHandler);
writeTcs = new TaskCompletionSource<object>();
return writeTcs;
}
}
// client only
public void WritesDone()
{
lock (myLock)
{
CheckStarted();
CheckNotFinished();
CheckNoError();
CheckCancelNotRequested();
if (halfcloseRequested || halfclosed)
{
throw new InvalidOperationException("Already halfclosed.");
}
call.WritesDone(callbackHandler);
halfcloseRequested = true;
}
}
// server only
public void WriteStatus(Status status)
{
lock (myLock)
{
CheckStarted();
CheckNotFinished();
CheckNoError();
CheckCancelNotRequested();
if (halfcloseRequested || halfclosed)
{
throw new InvalidOperationException("Already halfclosed.");
}
call.StartWriteStatus(status, callbackHandler);
halfcloseRequested = true;
}
}
public TaskCompletionSource<TRead> StartRead()
{
lock (myLock)
{
CheckStarted();
CheckNotFinished();
CheckNoError();
// TODO: add check for not cancelled?
if (doneWithReading)
{
throw new InvalidOperationException("Already read the last message.");
}
if (readTcs != null)
{
throw new InvalidOperationException("Only one read can be pending at a time");
}
call.StartRead(callbackHandler);
readTcs = new TaskCompletionSource<TRead>();
return readTcs;
}
}
public void Cancel()
{
lock (myLock)
{
CheckStarted();
CheckNotFinished();
cancelRequested = true;
}
// grpc_call_cancel is threadsafe
call.Cancel();
}
public void CancelWithStatus(Status status)
{
lock (myLock)
{
CheckStarted();
CheckNotFinished();
cancelRequested = true;
}
// grpc_call_cancel_with_status is threadsafe
call.CancelWithStatus(status);
}
public void OnClientMetadata()
{
// TODO: implement....
}
public void OnRead(byte[] payload)
{
TaskCompletionSource<TRead> oldTcs = null;
IObserver<TRead> observer = null;
lock (myLock)
{
oldTcs = readTcs;
readTcs = null;
if (payload == null)
{
doneWithReading = true;
}
observer = readObserver;
}
// TODO: wrap deserialization...
TRead msg = payload != null ? deserializer(payload) : default(TRead);
oldTcs.SetResult(msg);
// TODO: make sure we deliver reads in the right order.
if (observer != null)
{
if (payload != null)
{
// TODO: wrap to handle exceptions
observer.OnNext(msg);
// start a new read
StartRead();
}
else
{
// TODO: wrap to handle exceptions;
observer.OnCompleted();
}
}
}
public void OnWriteAccepted(GRPCOpError error)
{
TaskCompletionSource<object> oldTcs = null;
lock (myLock)
{
UpdateErrorOccured(error);
oldTcs = writeTcs;
writeTcs = null;
}
if (errorOccured)
{
// TODO: use the right type of exception...
oldTcs.SetException(new Exception("Write failed"));
}
else
{
// TODO: where does the continuation run?
oldTcs.SetResult(null);
}
}
public void OnFinishAccepted(GRPCOpError error)
{
lock (myLock)
{
UpdateErrorOccured(error);
halfclosed = true;
}
if (errorOccured)
{
halfcloseTcs.SetException(new Exception("Halfclose failed"));
}
else
{
halfcloseTcs.SetResult(null);
}
}
public void OnFinished(Status status)
{
lock (myLock)
{
finishedStatus = status;
DisposeResourcesIfNeeded();
}
finishedTcs.SetResult(status);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if (call != null)
{
call.Dispose();
}
}
disposed = true;
}
}
private void UpdateErrorOccured(GRPCOpError error)
{
if (error == GRPCOpError.GRPC_OP_ERROR)
{
errorOccured = true;
}
}
private void CheckStarted()
{
if (!started)
{
throw new InvalidOperationException("Call not started");
}
}
private void CheckNoError()
{
if (errorOccured)
{
throw new InvalidOperationException("Error occured when processing call.");
}
}
private void CheckNotFinished()
{
if (finishedStatus.HasValue)
{
throw new InvalidOperationException("Already finished.");
}
}
private void CheckCancelNotRequested()
{
if (cancelRequested)
{
throw new InvalidOperationException("Cancel has been requested.");
}
}
private void DisposeResourcesIfNeeded()
{
if (call != null && started && finishedStatus.HasValue)
{
// TODO: should we also wait for all the pending events to finish?
call.Dispose();
}
}
private void HandleEvent(IntPtr eventPtr) {
try {
var ev = new EventSafeHandleNotOwned(eventPtr);
switch (ev.GetCompletionType())
{
case GRPCCompletionType.GRPC_CLIENT_METADATA_READ:
OnClientMetadata();
break;
case GRPCCompletionType.GRPC_READ:
byte[] payload = ev.GetReadData();
OnRead(payload);
break;
case GRPCCompletionType.GRPC_WRITE_ACCEPTED:
OnWriteAccepted(ev.GetWriteAccepted());
break;
case GRPCCompletionType.GRPC_FINISH_ACCEPTED:
OnFinishAccepted(ev.GetFinishAccepted());
break;
case GRPCCompletionType.GRPC_FINISHED:
OnFinished(ev.GetFinished());
break;
default:
throw new ArgumentException("Unexpected completion type");
}
} catch(Exception e) {
Console.WriteLine("Caught exception in a native handler: " + e);
}
}
}
}

@ -0,0 +1,182 @@
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Google.GRPC.Core;
namespace Google.GRPC.Core.Internal
{
// TODO: we need to make sure that the delegates are not collected before invoked.
internal delegate void EventCallbackDelegate(IntPtr eventPtr);
/// <summary>
/// grpc_call from <grpc/grpc.h>
/// </summary>
internal class CallSafeHandle : SafeHandleZeroIsInvalid
{
const UInt32 GRPC_WRITE_BUFFER_HINT = 1;
[DllImport("libgrpc.so")]
static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_invoke_old")]
static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq,
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback,
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback,
UInt32 flags);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")]
static extern GRPCCallError grpc_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_cancel(CallSafeHandle call);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_start_write_status_old")]
static extern GRPCCallError grpc_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")]
static extern GRPCCallError grpc_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc.so")]
static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")]
static extern GRPCCallError grpc_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc_csharp_ext.so")]
static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call,
byte[] buffer, UIntPtr length,
IntPtr tag, UInt32 flags);
[DllImport("libgrpc_csharp_ext.so", EntryPoint = "grpc_call_start_write_from_copied_buffer")]
static extern void grpc_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call,
byte[] buffer, UIntPtr length,
[MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback,
UInt32 flags);
[DllImport("libgrpc.so")]
static extern void grpc_call_destroy(IntPtr call);
private CallSafeHandle()
{
}
/// <summary>
/// Creates a client call.
/// </summary>
public static CallSafeHandle Create(ChannelSafeHandle channel, string method, string host, Timespec deadline)
{
return grpc_channel_create_call_old(channel, method, host, deadline);
}
public void Invoke(CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, bool buffered)
{
AssertCallOk(grpc_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered)));
}
public void Invoke(CompletionQueueSafeHandle cq, bool buffered, EventCallbackDelegate metadataReadCallback, EventCallbackDelegate finishedCallback)
{
AssertCallOk(grpc_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered)));
}
public void ServerAccept(CompletionQueueSafeHandle cq, IntPtr finishedTag)
{
AssertCallOk(grpc_call_server_accept_old(this, cq, finishedTag));
}
public void ServerAccept(CompletionQueueSafeHandle cq, EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_server_accept_old_CALLBACK(this, cq, callback));
}
public void ServerEndInitialMetadata(UInt32 flags)
{
AssertCallOk(grpc_call_server_end_initial_metadata_old(this, flags));
}
public void StartWrite(byte[] payload, IntPtr tag, bool buffered)
{
grpc_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered));
}
public void StartWrite(byte[] payload, bool buffered, EventCallbackDelegate callback)
{
grpc_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered));
}
public void StartWriteStatus(Status status, IntPtr tag)
{
AssertCallOk(grpc_call_start_write_status_old(this, status.StatusCode, status.Detail, tag));
}
public void StartWriteStatus(Status status, EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback));
}
public void WritesDone(IntPtr tag)
{
AssertCallOk(grpc_call_writes_done_old(this, tag));
}
public void WritesDone(EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_writes_done_old_CALLBACK(this, callback));
}
public void StartRead(IntPtr tag)
{
AssertCallOk(grpc_call_start_read_old(this, tag));
}
public void StartRead(EventCallbackDelegate callback)
{
AssertCallOk(grpc_call_start_read_old_CALLBACK(this, callback));
}
public void Cancel()
{
AssertCallOk(grpc_call_cancel(this));
}
public void CancelWithStatus(Status status)
{
AssertCallOk(grpc_call_cancel_with_status(this, status.StatusCode, status.Detail));
}
protected override bool ReleaseHandle()
{
grpc_call_destroy(handle);
return true;
}
private static void AssertCallOk(GRPCCallError callError)
{
Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
}
private static UInt32 GetFlags(bool buffered) {
return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;
}
}
}

@ -0,0 +1,34 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// grpc_channel from <grpc/grpc.h>
/// </summary>
internal class ChannelSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs);
[DllImport("libgrpc.so")]
static extern void grpc_channel_destroy(IntPtr channel);
private ChannelSafeHandle()
{
}
public static ChannelSafeHandle Create(string target, IntPtr channelArgs)
{
return grpc_channel_create(target, channelArgs);
}
protected override bool ReleaseHandle()
{
grpc_channel_destroy(handle);
return true;
}
}
}

@ -0,0 +1,66 @@
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// grpc_completion_queue from <grpc/grpc.h>
/// </summary>
internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
static extern CompletionQueueSafeHandle grpc_completion_queue_create();
[DllImport("libgrpc.so")]
static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline);
[DllImport("libgrpc.so")]
static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline);
[DllImport("libgrpc.so")]
static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq);
[DllImport("libgrpc_csharp_ext.so")]
static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq);
[DllImport("libgrpc.so")]
static extern void grpc_completion_queue_destroy(IntPtr cq);
private CompletionQueueSafeHandle()
{
}
public static CompletionQueueSafeHandle Create()
{
return grpc_completion_queue_create();
}
public EventSafeHandle Next(Timespec deadline)
{
return grpc_completion_queue_next(this, deadline);
}
public GRPCCompletionType NextWithCallback()
{
return grpc_completion_queue_next_with_callback(this);
}
public EventSafeHandle Pluck(IntPtr tag, Timespec deadline)
{
return grpc_completion_queue_pluck(this, tag, deadline);
}
public void Shutdown()
{
grpc_completion_queue_shutdown(this);
}
protected override bool ReleaseHandle()
{
grpc_completion_queue_destroy(handle);
return true;
}
}
}

@ -0,0 +1,75 @@
using System;
using System.Runtime.InteropServices;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// from grpc/grpc.h
/// </summary>
internal enum GRPCCallError
{
/* everything went ok */
GRPC_CALL_OK = 0,
/* something failed, we don't know what */
GRPC_CALL_ERROR,
/* this method is not available on the server */
GRPC_CALL_ERROR_NOT_ON_SERVER,
/* this method is not available on the client */
GRPC_CALL_ERROR_NOT_ON_CLIENT,
/* this method must be called before server_accept */
GRPC_CALL_ERROR_ALREADY_ACCEPTED,
/* this method must be called before invoke */
GRPC_CALL_ERROR_ALREADY_INVOKED,
/* this method must be called after invoke */
GRPC_CALL_ERROR_NOT_INVOKED,
/* this call is already finished
(writes_done or write_status has already been called) */
GRPC_CALL_ERROR_ALREADY_FINISHED,
/* there is already an outstanding read/write operation on the call */
GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
/* the flags value was illegal for this call */
GRPC_CALL_ERROR_INVALID_FLAGS
}
/// <summary>
/// grpc_completion_type from grpc/grpc.h
/// </summary>
internal enum GRPCCompletionType
{
GRPC_QUEUE_SHUTDOWN,
/* Shutting down */
GRPC_READ,
/* A read has completed */
GRPC_INVOKE_ACCEPTED,
/* An invoke call has been accepted by flow
control */
GRPC_WRITE_ACCEPTED,
/* A write has been accepted by
flow control */
GRPC_FINISH_ACCEPTED,
/* writes_done or write_status has been accepted */
GRPC_CLIENT_METADATA_READ,
/* The metadata array sent by server received at
client */
GRPC_FINISHED,
/* An RPC has finished. The event contains status.
On the server this will be OK or Cancelled. */
GRPC_SERVER_RPC_NEW,
/* A new RPC has arrived at the server */
GRPC_COMPLETION_DO_NOT_USE
/* must be last, forces users to include
a default: case */
}
/// <summary>
/// grpc_op_error from grpc/grpc.h
/// </summary>
internal enum GRPCOpError
{
/* everything went ok */
GRPC_OP_OK = 0,
/* something failed, we don't know what */
GRPC_OP_ERROR
}
}

@ -0,0 +1,191 @@
using System;
using System.Runtime.InteropServices;
using Google.GRPC.Core;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// grpc_event from grpc/grpc.h
/// </summary>
internal class EventSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
static extern void grpc_event_finish(IntPtr ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern CallSafeHandle grpc_event_call(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern StatusCode grpc_event_finished_status(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char*
[DllImport("libgrpc_csharp_ext.so")]
static extern IntPtr grpc_event_read_length(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen);
[DllImport("libgrpc_csharp_ext.so")]
static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char*
public GRPCCompletionType GetCompletionType()
{
return grpc_event_type(this);
}
public GRPCOpError GetWriteAccepted()
{
return grpc_event_write_accepted(this);
}
public GRPCOpError GetFinishAccepted()
{
return grpc_event_finish_accepted(this);
}
public Status GetFinished()
{
// TODO: can the native method return string directly?
string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this));
return new Status(grpc_event_finished_status(this), details);
}
public byte[] GetReadData()
{
IntPtr len = grpc_event_read_length(this);
if (len == new IntPtr(-1))
{
return null;
}
byte[] data = new byte[(int) len];
grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length));
return data;
}
public CallSafeHandle GetCall() {
return grpc_event_call(this);
}
public string GetServerRpcNewMethod() {
// TODO: can the native method return string directly?
return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this));
}
//TODO: client_metadata_read event type
protected override bool ReleaseHandle()
{
grpc_event_finish(handle);
return true;
}
}
// TODO: this is basically c&p of EventSafeHandle. Unify!
/// <summary>
/// Not owned version of
/// grpc_event from grpc/grpc.h
/// </summary>
internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
static extern void grpc_event_finish(IntPtr ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char*
[DllImport("libgrpc_csharp_ext.so")]
static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen);
[DllImport("libgrpc_csharp_ext.so")]
static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char*
public EventSafeHandleNotOwned() : base(false)
{
}
public EventSafeHandleNotOwned(IntPtr handle) : base(false)
{
SetHandle(handle);
}
public GRPCCompletionType GetCompletionType()
{
return grpc_event_type(this);
}
public GRPCOpError GetWriteAccepted()
{
return grpc_event_write_accepted(this);
}
public GRPCOpError GetFinishAccepted()
{
return grpc_event_finish_accepted(this);
}
public Status GetFinished()
{
// TODO: can the native method return string directly?
string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this));
return new Status(grpc_event_finished_status(this), details);
}
public byte[] GetReadData()
{
IntPtr len = grpc_event_read_length(this);
if (len == new IntPtr(-1))
{
return null;
}
byte[] data = new byte[(int) len];
grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length));
return data;
}
public CallSafeHandle GetCall() {
return grpc_event_call(this);
}
public string GetServerRpcNewMethod() {
// TODO: can the native method return string directly?
return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this));
}
//TODO: client_metadata_read event type
protected override bool ReleaseHandle()
{
grpc_event_finish(handle);
return true;
}
}
}

@ -0,0 +1,129 @@
using System;
using Google.GRPC.Core.Internal;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// Pool of threads polling on the same completion queue.
/// </summary>
internal class GrpcThreadPool
{
readonly object myLock = new object();
readonly List<Thread> threads = new List<Thread>();
readonly int poolSize;
readonly Action<EventSafeHandle> eventHandler;
CompletionQueueSafeHandle cq;
public GrpcThreadPool(int poolSize) {
this.poolSize = poolSize;
}
internal GrpcThreadPool(int poolSize, Action<EventSafeHandle> eventHandler) {
this.poolSize = poolSize;
this.eventHandler = eventHandler;
}
public void Start() {
lock (myLock)
{
if (cq != null)
{
throw new InvalidOperationException("Already started.");
}
cq = CompletionQueueSafeHandle.Create();
for (int i = 0; i < poolSize; i++)
{
threads.Add(CreateAndStartThread(i));
}
}
}
public void Stop() {
lock (myLock)
{
cq.Shutdown();
Console.WriteLine("Waiting for GPRC threads to finish.");
foreach (var thread in threads)
{
thread.Join();
}
cq.Dispose();
}
}
internal CompletionQueueSafeHandle CompletionQueue
{
get
{
return cq;
}
}
private Thread CreateAndStartThread(int i) {
Action body;
if (eventHandler != null)
{
body = ThreadBodyWithHandler;
}
else
{
body = ThreadBodyNoHandler;
}
var thread = new Thread(new ThreadStart(body));
thread.IsBackground = false;
thread.Start();
if (eventHandler != null)
{
thread.Name = "grpc_server_newrpc " + i;
}
else
{
thread.Name = "grpc " + i;
}
return thread;
}
/// <summary>
/// Body of the polling thread.
/// </summary>
private void ThreadBodyNoHandler()
{
GRPCCompletionType completionType;
do
{
completionType = cq.NextWithCallback();
} while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN);
Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting.");
}
/// <summary>
/// Body of the polling thread.
/// </summary>
private void ThreadBodyWithHandler()
{
GRPCCompletionType completionType;
do
{
using (EventSafeHandle ev = cq.Next(Timespec.InfFuture)) {
completionType = ev.GetCompletionType();
eventHandler(ev);
}
} while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN);
Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting.");
}
}
}

@ -0,0 +1,28 @@
using System;
using System.Runtime.InteropServices;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// Safe handle to wrap native objects.
/// </summary>
internal abstract class SafeHandleZeroIsInvalid : SafeHandle
{
public SafeHandleZeroIsInvalid() : base(IntPtr.Zero, true)
{
}
public SafeHandleZeroIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle)
{
}
public override bool IsInvalid
{
get
{
return handle == IntPtr.Zero;
}
}
}
}

@ -0,0 +1,76 @@
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Collections.Concurrent;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// grpc_server from grpc/grpc.h
/// </summary>
internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so", EntryPoint = "grpc_server_request_call_old")]
static extern GRPCCallError grpc_server_request_call_old_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc.so")]
static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args);
// TODO: check int representation size
[DllImport("libgrpc.so")]
static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr);
// TODO: check int representation size
[DllImport("libgrpc.so")]
static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr);
[DllImport("libgrpc.so")]
static extern void grpc_server_start(ServerSafeHandle server);
[DllImport("libgrpc.so")]
static extern void grpc_server_shutdown(ServerSafeHandle server);
[DllImport("libgrpc.so")]
static extern void grpc_server_shutdown_and_notify(ServerSafeHandle server, IntPtr tag);
[DllImport("libgrpc.so")]
static extern void grpc_server_destroy(IntPtr server);
private ServerSafeHandle()
{
}
public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args)
{
// TODO: also grpc_secure_server_create...
return grpc_server_create(cq, args);
}
public int AddPort(string addr)
{
// TODO: also grpc_server_add_secure_http2_port...
return grpc_server_add_http2_port(this, addr);
}
public void Start()
{
grpc_server_start(this);
}
public void Shutdown()
{
grpc_server_shutdown(this);
}
public GRPCCallError RequestCall(EventCallbackDelegate callback)
{
return grpc_server_request_call_old_CALLBACK(this, callback);
}
protected override bool ReleaseHandle()
{
grpc_server_destroy(handle);
return true;
}
}
}

@ -0,0 +1,33 @@
using System;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core
{
internal class StreamingInputObserver<TWrite, TRead> : IObserver<TWrite>
{
readonly AsyncCall<TWrite, TRead> call;
public StreamingInputObserver(AsyncCall<TWrite, TRead> call)
{
this.call = call;
}
public void OnCompleted()
{
// TODO: how bad is the Wait here?
call.WritesCompletedAsync().Wait();
}
public void OnError(Exception error)
{
throw new InvalidOperationException("This should never be called.");
}
public void OnNext(TWrite value)
{
// TODO: how bad is the Wait here?
call.WriteAsync(value).Wait();
}
}
}

@ -0,0 +1,67 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Google.GRPC.Core.Internal
{
/// <summary>
/// gpr_timespec from grpc/support/time.h
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct Timespec
{
const int nanosPerSecond = 1000 * 1000 * 1000;
const int nanosPerTick = 100;
[DllImport("libgpr.so")]
static extern Timespec gpr_now();
// TODO: this only works on 64bit linux, can we autoselect the right size of ints?
// perhaps using IntPtr would work.
public System.Int64 tv_sec;
public System.Int64 tv_nsec;
/// <summary>
/// Timespec a long time in the future.
/// </summary>
public static Timespec InfFuture
{
get
{
// TODO: set correct value based on the length of the struct
return new Timespec { tv_sec = Int32.MaxValue, tv_nsec = 0 };
}
}
public static Timespec Now
{
get
{
return gpr_now();
}
}
/// <summary>
/// Creates a GPR deadline from current instant and given timeout.
/// </summary>
/// <returns>The from timeout.</returns>
public static Timespec DeadlineFromTimeout(TimeSpan timeout) {
if (timeout == Timeout.InfiniteTimeSpan)
{
return Timespec.InfFuture;
}
return Timespec.Now.Add(timeout);
}
public Timespec Add(TimeSpan timeSpan) {
long nanos = tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick;
long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0;
Timespec result;
result.tv_nsec = nanos % nanosPerSecond;
result.tv_sec = tv_sec + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec;
return result;
}
}
}

@ -0,0 +1,24 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("GrpcCore")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("jtattermusch")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]
[assembly: InternalsVisibleTo("GrpcCoreTests")]

@ -0,0 +1,27 @@
using System;
namespace Google.GRPC.Core
{
public class RpcException : Exception
{
private readonly Status status;
public RpcException(Status status)
{
this.status = status;
}
public RpcException(Status status, string message) : base(message)
{
this.status = status;
}
public Status Status {
get
{
return status;
}
}
}
}

@ -0,0 +1,141 @@
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Collections.Concurrent;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core
{
/// <summary>
/// Server is implemented only to be able to do
/// in-process testing.
/// </summary>
public class Server
{
// TODO: make sure the delegate doesn't get garbage collected while
// native callbacks are in the completion queue.
readonly EventCallbackDelegate newRpcHandler;
readonly BlockingCollection<NewRpcInfo> newRpcQueue = new BlockingCollection<NewRpcInfo>();
readonly ServerSafeHandle handle;
static Server() {
GrpcEnvironment.EnsureInitialized();
}
public Server()
{
// TODO: what is the tag for server shutdown?
this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero);
this.newRpcHandler = HandleNewRpc;
}
public int AddPort(string addr) {
return handle.AddPort(addr);
}
public void Start()
{
handle.Start();
}
public void RunRpc()
{
AllowOneRpc();
try {
var rpcInfo = newRpcQueue.Take();
Console.WriteLine("Server received RPC " + rpcInfo.Method);
AsyncCall<byte[], byte[]> asyncCall = new AsyncCall<byte[], byte[]>(
(payload) => payload, (payload) => payload);
asyncCall.InitializeServer(rpcInfo.Call);
asyncCall.Accept(GetCompletionQueue());
while(true) {
byte[] payload = asyncCall.ReadAsync().Result;
if (payload == null)
{
break;
}
}
asyncCall.WriteAsync(new byte[] { }).Wait();
// TODO: what should be the details?
asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait();
asyncCall.Finished.Wait();
} catch(Exception e) {
Console.WriteLine("Exception while handling RPC: " + e);
}
}
// TODO: implement disposal properly...
public void Shutdown() {
handle.Shutdown();
//handle.Dispose();
}
private void AllowOneRpc()
{
AssertCallOk(handle.RequestCall(newRpcHandler));
}
private void HandleNewRpc(IntPtr eventPtr)
{
try
{
var ev = new EventSafeHandleNotOwned(eventPtr);
newRpcQueue.Add(new NewRpcInfo(ev.GetCall(), ev.GetServerRpcNewMethod()));
}
catch (Exception e)
{
Console.WriteLine("Caught exception in a native handler: " + e);
}
}
private static void AssertCallOk(GRPCCallError callError)
{
Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
}
private static CompletionQueueSafeHandle GetCompletionQueue()
{
return GrpcEnvironment.ThreadPool.CompletionQueue;
}
private struct NewRpcInfo
{
private CallSafeHandle call;
private string method;
public NewRpcInfo(CallSafeHandle call, string method)
{
this.call = call;
this.method = method;
}
public CallSafeHandle Call
{
get
{
return this.call;
}
}
public string Method
{
get
{
return this.method;
}
}
}
}
}

@ -0,0 +1,36 @@
using System;
using System.Runtime.InteropServices;
namespace Google.GRPC.Core
{
/// <summary>
/// Represents RPC result.
/// </summary>
public struct Status
{
readonly StatusCode statusCode;
readonly string detail;
public Status(StatusCode statusCode, string detail)
{
this.statusCode = statusCode;
this.detail = detail;
}
public StatusCode StatusCode
{
get
{
return statusCode;
}
}
public string Detail
{
get
{
return detail;
}
}
}
}

@ -0,0 +1,150 @@
using System;
namespace Google.GRPC.Core
{
// TODO: element names should changed to comply with C# naming conventions.
/// <summary>
/// grpc_status_code from grpc/status.h
/// </summary>
public enum StatusCode
{
/* Not an error; returned on success
HTTP Mapping: 200 OK */
GRPC_STATUS_OK = 0,
/* The operation was cancelled (typically by the caller).
HTTP Mapping: 499 Client Closed Request */
GRPC_STATUS_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.
HTTP Mapping: 500 Internal Server Error */
GRPC_STATUS_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).
HTTP Mapping: 400 Bad Request */
GRPC_STATUS_INVALID_ARGUMENT = 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.
HTTP Mapping: 504 Gateway Timeout */
GRPC_STATUS_DEADLINE_EXCEEDED = 4,
/* Some requested entity (e.g., file or directory) was not found.
HTTP Mapping: 404 Not Found */
GRPC_STATUS_NOT_FOUND = 5,
/* Some entity that we attempted to create (e.g., file or directory)
already exists.
HTTP Mapping: 409 Conflict */
GRPC_STATUS_ALREADY_EXISTS = 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).
HTTP Mapping: 403 Forbidden */
GRPC_STATUS_PERMISSION_DENIED = 7,
/* The request does not have valid authentication credentials for the
operation.
HTTP Mapping: 401 Unauthorized */
GRPC_STATUS_UNAUTHENTICATED = 16,
/* Some resource has been exhausted, perhaps a per-user quota, or
perhaps the entire file system is out of space.
HTTP Mapping: 429 Too Many Requests */
GRPC_STATUS_RESOURCE_EXHAUSTED = 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.
HTTP Mapping: 400 Bad Request
NOTE: HTTP spec says 412 Precondition Failed should only be used if
the request contains Etag related headers. So if the server does see
Etag related headers in the request, it may choose to return 412
instead of 400 for this error code. */
GRPC_STATUS_FAILED_PRECONDITION = 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.
HTTP Mapping: 409 Conflict */
GRPC_STATUS_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.
HTTP Mapping: 400 Bad Request */
GRPC_STATUS_OUT_OF_RANGE = 11,
/* Operation is not implemented or not supported/enabled in this service.
HTTP Mapping: 501 Not Implemented */
GRPC_STATUS_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.
HTTP Mapping: 500 Internal Server Error */
GRPC_STATUS_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.
HTTP Mapping: 503 Service Unavailable */
GRPC_STATUS_UNAVAILABLE = 14,
/* Unrecoverable data loss or corruption.
HTTP Mapping: 500 Internal Server Error */
GRPC_STATUS_DATA_LOSS = 15,
/* Force users to include a default branch: */
GRPC_STATUS__DO_NOT_USE = -1
}
}

@ -0,0 +1,2 @@
test-results
bin

@ -0,0 +1,48 @@
using System;
using NUnit.Framework;
using Google.GRPC.Core.Internal;
using System.Threading;
using System.Threading.Tasks;
namespace Google.GRPC.Core.Tests
{
public class ClientServerTest
{
string request = "REQUEST";
string serverAddr = "localhost:" + Utils.PickUnusedPort();
[Test]
public void EmptyCall()
{
Server server = new Server();
server.AddPort(serverAddr);
server.Start();
Task.Factory.StartNew(
() => {
server.RunRpc();
}
);
using (Channel channel = new Channel(serverAddr))
{
CreateCall(channel);
string response = Calls.BlockingUnaryCall(CreateCall(channel), request, default(CancellationToken));
Console.WriteLine("Received response: " + response);
}
server.Shutdown();
GrpcEnvironment.Shutdown();
}
private Call<string, string> CreateCall(Channel channel)
{
return new Call<string, string>("/tests.Test/EmptyCall",
(s) => System.Text.Encoding.ASCII.GetBytes(s),
(b) => System.Text.Encoding.ASCII.GetString(b),
Timeout.InfiniteTimeSpan, channel);
}
}
}

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{86EC5CB4-4EA2-40A2-8057-86542A0353BB}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>GrpcCoreTests</RootNamespace>
<AssemblyName>GrpcCoreTests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="nunit.framework, Version=2.6.0.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77">
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ClientServerTest.cs" />
<Compile Include="ServerTest.cs" />
<Compile Include="Utils.cs" />
<Compile Include="GrpcEnvironmentTest.cs" />
<Compile Include="TimespecTest.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
<Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
<Name>GrpcCore</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -0,0 +1,18 @@
using System;
using NUnit.Framework;
using Google.GRPC.Core;
using System.Threading;
namespace Google.GRPC.Core.Tests
{
public class GrpcEnvironmentTest
{
[Test]
public void InitializeAndShutdownGrpcEnvironment() {
GrpcEnvironment.EnsureInitialized();
Thread.Sleep(500);
Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue);
GrpcEnvironment.Shutdown();
}
}
}

@ -0,0 +1,22 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("GrpcCoreTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("jtattermusch")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

@ -0,0 +1,21 @@
using System;
using NUnit.Framework;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core.Tests
{
public class ServerTest
{
[Test]
public void StartAndShutdownServer() {
Server server = new Server();
server.AddPort("localhost:" + Utils.PickUnusedPort());
server.Start();
server.Shutdown();
GrpcEnvironment.Shutdown();
}
}
}

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!--This file represents the results of running a test suite-->
<test-results name="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests/bin/Debug/GrpcCoreTests.dll" total="3" errors="0" failures="0" not-run="0" inconclusive="0" ignored="0" skipped="0" invalid="0" date="2015-01-29" time="19:40:47">
<environment nunit-version="2.6.0.0" clr-version="4.0.30319.17020" os-version="Unix 3.13.0.43" platform="Unix" cwd="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests" machine-name="jtattermusch.mtv.corp.google.com" user="jtattermusch" user-domain="jtattermusch.mtv.corp.google.com" />
<culture-info current-culture="en-US" current-uiculture="en-US" />
<test-suite type="Assembly" name="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests/bin/Debug/GrpcCoreTests.dll" executed="True" result="Success" success="True" time="0.172" asserts="0">
<results>
<test-suite type="Namespace" name="Google" executed="True" result="Success" success="True" time="0.166" asserts="0">
<results>
<test-suite type="Namespace" name="GRPC" executed="True" result="Success" success="True" time="0.166" asserts="0">
<results>
<test-suite type="Namespace" name="Core" executed="True" result="Success" success="True" time="0.166" asserts="0">
<results>
<test-suite type="Namespace" name="Tests" executed="True" result="Success" success="True" time="0.166" asserts="0">
<results>
<test-suite type="TestFixture" name="CallsTest" executed="True" result="Success" success="True" time="0.009" asserts="0">
<results>
<test-case name="Google.GRPC.Core.Tests.CallsTest.Test1" executed="True" result="Success" success="True" time="0.004" asserts="0" />
</results>
</test-suite>
<test-suite type="TestFixture" name="ClientServerTest" executed="True" result="Success" success="True" time="0.149" asserts="0">
<results>
<test-case name="Google.GRPC.Core.Tests.ClientServerTest.EmptyCall" executed="True" result="Success" success="True" time="0.111" asserts="0" />
</results>
</test-suite>
<test-suite type="TestFixture" name="ServerTest" executed="True" result="Success" success="True" time="0.001" asserts="0">
<results>
<test-case name="Google.GRPC.Core.Tests.ServerTest.StartAndShutdownServer" executed="True" result="Success" success="True" time="0.001" asserts="0" />
</results>
</test-suite>
</results>
</test-suite>
</results>
</test-suite>
</results>
</test-suite>
</results>
</test-suite>
</results>
</test-suite>
</test-results>

@ -0,0 +1,43 @@
using System;
using NUnit.Framework;
using Google.GRPC.Core.Internal;
namespace Google.GRPC.Core.Internal.Tests
{
public class TimespecTest
{
[Test]
public void Now()
{
var timespec = Timespec.Now;
}
[Test]
public void Add()
{
var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 };
var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10));
Assert.AreEqual(result.tv_sec, 12355);
Assert.AreEqual(result.tv_nsec, 123456789);
}
[Test]
public void Add_Nanos()
{
var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 };
var result = t.Add(TimeSpan.FromTicks(10));
Assert.AreEqual(result.tv_sec, 12345);
Assert.AreEqual(result.tv_nsec, 123456789 + 1000);
}
[Test]
public void Add_NanosOverflow()
{
var t = new Timespec { tv_sec = 12345, tv_nsec = 999999999 };
var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10 + 10));
Assert.AreEqual(result.tv_sec, 12356);
Assert.AreEqual(result.tv_nsec, 999);
}
}
}

@ -0,0 +1,51 @@
using System;
using System.Net;
using System.Net.Sockets;
namespace Google.GRPC.Core.Tests
{
/// <summary>
/// Testing utils.
/// </summary>
public class Utils
{
static Random random = new Random();
// TODO: cleanup this code a bit
public static int PickUnusedPort()
{
int port;
do
{
port = random.Next(2000, 50000);
} while(!IsPortAvailable(port));
return port;
}
// TODO: cleanup this code a bit
public static bool IsPortAvailable(int port)
{
bool available = true;
TcpListener server = null;
try
{
IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
server = new TcpListener(ipAddress, port);
server.Start();
}
catch (Exception ex)
{
available = false;
}
finally
{
if (server != null)
{
server.Stop();
}
}
return available;
}
}
}

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>GrpcDemo</RootNamespace>
<AssemblyName>GrpcDemo</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\GrpcApi\GrpcApi.csproj">
<Project>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</Project>
<Name>GrpcApi</Name>
</ProjectReference>
<ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
<Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
<Name>GrpcCore</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -0,0 +1,28 @@
using System;
using System.Runtime.InteropServices;
using Google.GRPC.Core;
using System.Threading;
using math;
namespace Google.GRPC.Demo
{
class MainClass
{
public static void Main (string[] args)
{
using (Channel channel = new Channel("127.0.0.1:23456"))
{
IMathServiceClient stub = new MathServiceClientStub(channel, Timeout.InfiniteTimeSpan);
Examples.DivExample(stub);
Examples.FibExample(stub);
Examples.SumExample(stub);
Examples.DivManyExample(stub);
}
GrpcEnvironment.Shutdown();
}
}
}

@ -0,0 +1,22 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("GrpcDemo")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("jtattermusch")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

@ -14,9 +14,41 @@ EXPERIMENTAL ONLY
- It is very possible that some parts of the code will be heavily refactored or
completely rewritten.
INSTALLATION AND USAGE
----------------------
- Compile and install the gRPC C Core library
```
make shared_c
sudo make install
```
- Prerequisites for development: Mono framework, MonoDevelop (IDE)
```
sudo apt-get install mono-devel
sudo apt-get install monodevelop monodevelop-nunit
sudo apt-get install nunit nunit-console
```
- Use MonoDevelop to open the solution Grpc.sln (you can also run unit tests
from there).
- After building the solution with MonoDevelop, you can use
nunit-console to run the unit tests (currently only running one by
one will make them pass.
```
nunit-console GrpcCoreTests.dll
```
CONTENTS
--------
- ext:
The extension library that wraps C API to be more digestible by C#.
- GrpcCore:
The main gRPC C# library.
- GrpcApi:
API examples for math.proto.

@ -1,12 +1,70 @@
# Node.js GRPC extension
# Node.js gRPC Library
The package is built with
## Installation
node-gyp configure
node-gyp build
First, clone this repository (NPM package coming soon). Then follow the instructions in the `INSTALL` file in the root of the repository to install the C core library that this package depends on.
or, for brevity
Then, simply run `npm install` in or referencing this directory.
node-gyp configure build
## Tests
The tests can be run with `npm test` on a dev install.
To run the test suite, simply run `npm test` in the install location.
## API
This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library
If you require this module, you will get an object with the following members
```javascript
function load(filename)
```
Takes a filename of a [Protocol Buffer](https://developers.google.com/protocol-buffers/) file, and returns an object representing the structure of the protocol buffer in the following way:
- Namespaces become maps from the names of their direct members to those member objects
- Service definitions become client constructors for clients for that service. They also have a `service` member that can be used for constructing servers.
- Message definitions become Message constructors like those that ProtoBuf.js would create
- Enum definitions become Enum objects like those that ProtoBuf.js would create
- Anything else becomes the relevant reflection object that ProtoBuf.js would create
```javascript
function loadObject(reflectionObject)
```
Returns the same structure that `load` returns, but takes a reflection object from `ProtoBuf.js` instead of a file name.
```javascript
function buildServer(serviceArray)
```
Takes an array of service objects and returns a constructor for a server that handles requests to all of those services.
```javascript
status
```
An object mapping status names to status code numbers.
```javascript
callError
```
An object mapping call error names to codes. This is primarily useful for tracking down certain kinds of internal errors.
```javascript
Credentials
```
An object with factory methods for creating credential objects for clients.
```javascript
ServerCredentials
```
An object with factory methods fro creating credential objects for servers.

@ -206,11 +206,13 @@ OPENSSL_ALPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/ope
ZLIB_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/zlib.c -lz $(LDFLAGS)
PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o /dev/null test/build/perftools.c -lprofiler $(LDFLAGS)
ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG)
HAS_SYSTEM_PERFTOOLS = $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true || echo false)
ifeq ($(HAS_SYSTEM_PERFTOOLS),true)
DEFINES += GRPC_HAVE_PERFTOOLS
LIBS += profiler
endif
endif
ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG)
HAS_SYSTEM_OPENSSL_ALPN = $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false)

@ -31,7 +31,10 @@
*
*/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <unistd.h>
#include <assert.h>
#include <stdio.h>

@ -70,6 +70,7 @@ typedef struct expectation {
union {
grpc_op_error finish_accepted;
grpc_op_error write_accepted;
grpc_op_error ioreq;
struct {
const char *method;
const char *host;
@ -180,9 +181,6 @@ static void verify_matches(expectation *e, grpc_event *ev) {
case GRPC_WRITE_ACCEPTED:
GPR_ASSERT(e->data.write_accepted == ev->data.write_accepted);
break;
case GRPC_INVOKE_ACCEPTED:
abort();
break;
case GRPC_SERVER_RPC_NEW:
GPR_ASSERT(string_equivalent(e->data.server_rpc_new.method,
ev->data.server_rpc_new.method));
@ -222,6 +220,9 @@ static void verify_matches(expectation *e, grpc_event *ev) {
GPR_ASSERT(ev->data.read == NULL);
}
break;
case GRPC_IOREQ:
GPR_ASSERT(e->data.ioreq == ev->data.ioreq);
break;
case GRPC_SERVER_SHUTDOWN:
break;
case GRPC_COMPLETION_DO_NOT_USE:
@ -242,7 +243,9 @@ static void metadata_expectation(gpr_strvec *buf, metadata *md) {
gpr_asprintf(&tmp, "%c%s:%s", i ? ',' : '{', md->keys[i], md->values[i]);
gpr_strvec_add(buf, tmp);
}
gpr_strvec_add(buf, gpr_strdup("}"));
if (md->count) {
gpr_strvec_add(buf, gpr_strdup("}"));
}
}
}
@ -261,8 +264,9 @@ static void expectation_to_strvec(gpr_strvec *buf, expectation *e) {
e->data.write_accepted);
gpr_strvec_add(buf, tmp);
break;
case GRPC_INVOKE_ACCEPTED:
gpr_strvec_add(buf, gpr_strdup("GRPC_INVOKE_ACCEPTED"));
case GRPC_IOREQ:
gpr_asprintf(&tmp, "GRPC_IOREQ result=%d", e->data.ioreq);
gpr_strvec_add(buf, tmp);
break;
case GRPC_SERVER_RPC_NEW:
timeout = gpr_time_sub(e->data.server_rpc_new.deadline, gpr_now());

@ -142,7 +142,6 @@ void test_connect(const char *server_host, const char *client_host, int port,
cq_verify(v_client);
cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
cq_verify(v_server);
cq_expect_finished(v_server, tag(102), NULL);
cq_verify(v_server);

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

Loading…
Cancel
Save