Merge branch 'master' of github.com:google/grpc into docker

pull/427/head
Donna Dionne 10 years ago
commit 573a09688a
  1. 8
      INSTALL
  2. 9272
      Makefile
  3. 2
      README.md
  4. 57
      build.json
  5. 85
      include/grpc/grpc.h
  6. 53
      include/grpc/support/log_win32.h
  7. 4
      include/grpc/support/port_platform.h
  8. 42
      src/core/channel/client_channel.c
  9. 16
      src/core/channel/http_client_filter.c
  10. 15
      src/core/httpcli/httpcli_security_context.c
  11. 50
      src/core/iomgr/fd_posix.c
  12. 22
      src/core/iomgr/fd_posix.h
  13. 200
      src/core/iomgr/iocp_windows.c
  14. 52
      src/core/iomgr/iocp_windows.h
  15. 6
      src/core/iomgr/iomgr_posix.c
  16. 67
      src/core/iomgr/iomgr_windows.c
  17. 197
      src/core/iomgr/pollset_multipoller_with_epoll.c
  18. 32
      src/core/iomgr/pollset_multipoller_with_poll_posix.c
  19. 31
      src/core/iomgr/pollset_posix.c
  20. 1
      src/core/iomgr/pollset_posix.h
  21. 37
      src/core/iomgr/pollset_windows.c
  22. 2
      src/core/iomgr/pollset_windows.h
  23. 19
      src/core/iomgr/sockaddr_utils.c
  24. 6
      src/core/iomgr/sockaddr_utils.h
  25. 4
      src/core/iomgr/sockaddr_win32.h
  26. 77
      src/core/iomgr/socket_windows.c
  27. 75
      src/core/iomgr/socket_windows.h
  28. 215
      src/core/iomgr/tcp_client_windows.c
  29. 374
      src/core/iomgr/tcp_server_windows.c
  30. 373
      src/core/iomgr/tcp_windows.c
  31. 57
      src/core/iomgr/tcp_windows.h
  32. 130
      src/core/security/auth.c
  33. 3
      src/core/security/secure_transport_setup.c
  34. 177
      src/core/security/security_context.c
  35. 38
      src/core/security/security_context.h
  36. 54
      src/core/support/cpu_windows.c
  37. 2
      src/core/support/file_win32.c
  38. 40
      src/core/support/log_win32.c
  39. 1
      src/core/surface/byte_buffer.c
  40. 7
      src/core/surface/byte_buffer_queue.c
  41. 1
      src/core/surface/byte_buffer_queue.h
  42. 240
      src/core/surface/call.c
  43. 27
      src/core/surface/call.h
  44. 13
      src/core/surface/call_details.c
  45. 16
      src/core/surface/channel.c
  46. 21
      src/core/surface/completion_queue.c
  47. 10
      src/core/surface/completion_queue.h
  48. 6
      src/core/surface/event_string.c
  49. 12
      src/core/surface/metadata_array.c
  50. 7
      src/core/surface/secure_channel_create.c
  51. 98
      src/core/surface/server.c
  52. 16
      src/core/transport/chttp2/hpack_table.c
  53. 1
      src/cpp/common/completion_queue.cc
  54. 2
      src/csharp/.gitignore
  55. 12
      src/csharp/Grpc.sln
  56. 1
      src/csharp/GrpcApi/.gitignore
  57. 282
      src/csharp/GrpcApi/Empty.cs
  58. 36
      src/csharp/GrpcApi/GrpcApi.csproj
  59. 2
      src/csharp/GrpcApi/MathExamples.cs
  60. 2922
      src/csharp/GrpcApi/Messages.cs
  61. 170
      src/csharp/GrpcApi/TestServiceGrpc.cs
  62. 11
      src/csharp/GrpcApi/packages.config
  63. 13
      src/csharp/GrpcApi/proto/empty.proto
  64. 0
      src/csharp/GrpcApi/proto/math.proto
  65. 102
      src/csharp/GrpcApi/proto/messages.proto
  66. 42
      src/csharp/GrpcApi/proto/test.proto
  67. 1
      src/csharp/GrpcApiTests/.gitignore
  68. 19
      src/csharp/GrpcApiTests/GrpcApiTests.csproj
  69. 5
      src/csharp/GrpcApiTests/packages.config
  70. 1
      src/csharp/GrpcCore/.gitignore
  71. 1
      src/csharp/GrpcCore/GrpcCore.csproj
  72. 4
      src/csharp/GrpcCore/GrpcEnvironment.cs
  73. 36
      src/csharp/GrpcCore/Internal/CallSafeHandle.cs
  74. 4
      src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs
  75. 12
      src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs
  76. 37
      src/csharp/GrpcCore/Internal/Enums.cs
  77. 40
      src/csharp/GrpcCore/Internal/Event.cs
  78. 16
      src/csharp/GrpcCore/Internal/ServerSafeHandle.cs
  79. 2
      src/csharp/GrpcCore/Internal/Timespec.cs
  80. 50
      src/csharp/GrpcCore/Utils/RecordingQueue.cs
  81. 1
      src/csharp/GrpcCoreTests/.gitignore
  82. 14
      src/csharp/GrpcCoreTests/GrpcCoreTests.csproj
  83. 4
      src/csharp/GrpcCoreTests/packages.config
  84. 1
      src/csharp/InteropClient/.gitignore
  85. 303
      src/csharp/InteropClient/Client.cs
  86. 63
      src/csharp/InteropClient/InteropClient.csproj
  87. 22
      src/csharp/InteropClient/Properties/AssemblyInfo.cs
  88. 5
      src/csharp/InteropClient/packages.config
  89. 2
      src/csharp/MathClient/.gitignore
  90. 14
      src/csharp/MathClient/MathClient.cs
  91. 2
      src/csharp/MathClient/MathClient.csproj
  92. 2
      src/csharp/MathClient/Properties/AssemblyInfo.cs
  93. 32
      src/csharp/README.md
  94. BIN
      src/csharp/lib/Google.ProtocolBuffers.dll
  95. 3
      src/node/examples/math_server.js
  96. 115
      src/node/examples/perf_test.js
  97. 14
      src/node/examples/stock.proto
  98. 3
      src/node/examples/stock_client.js
  99. 11
      src/node/examples/stock_server.js
  100. 29
      src/node/ext/event.cc
  101. Some files were not shown because too many files have changed in this diff Show More

@ -100,16 +100,16 @@ Then, you can build and install protobuf 3.0.0:
A word on OpenSSL
-----------------
Secure HTTP2 requires to have the TLS extension ALPN (see rfc 7301 and
Secure HTTP2 requires the TLS extension ALPN (see rfc 7301 and
http://http2.github.io/http2-spec/ section 3.3). Our HTTP2 implementation
relies on OpenSSL's implementation. OpenSSL 1.0.2 is the first released version
of OpenSSL that has ALPN support, and this explains our dependency on it.
Note that the Makefile supports compiling only the unsecure elements of grpc,
and if you do not have OpenSSL and do not want it, you can still proceed
with installing only the elements you require. However, it is recommended
to encrypt your network traffic, therefore we urge you to not use the unsecure
version of grpc if possible.
with installing only the elements you require. However, we strongly recommend
the use of encryption for all network traffic, and discourage the use of grpc
without TLS.
Compiling

9272
Makefile

File diff suppressed because one or more lines are too long

@ -1,4 +1,4 @@
[gRPC - An RPC library and framework](http://github.com/google/grpc)
[gRPC - An RPC library and framework](http://github.com/grpc/grpc)
===================================
Copyright 2015 Google Inc.

@ -42,6 +42,7 @@
"src/core/iomgr/endpoint.h",
"src/core/iomgr/endpoint_pair.h",
"src/core/iomgr/fd_posix.h",
"src/core/iomgr/iocp_windows.h",
"src/core/iomgr/iomgr.h",
"src/core/iomgr/iomgr_internal.h",
"src/core/iomgr/iomgr_posix.h",
@ -57,9 +58,11 @@
"src/core/iomgr/sockaddr_utils.h",
"src/core/iomgr/sockaddr_win32.h",
"src/core/iomgr/socket_utils_posix.h",
"src/core/iomgr/socket_windows.h",
"src/core/iomgr/tcp_client.h",
"src/core/iomgr/tcp_posix.h",
"src/core/iomgr/tcp_server.h",
"src/core/iomgr/tcp_windows.h",
"src/core/iomgr/time_averaged_stats.h",
"src/core/iomgr/wakeup_fd_pipe.h",
"src/core/iomgr/wakeup_fd_posix.h",
@ -130,10 +133,13 @@
"src/core/iomgr/endpoint.c",
"src/core/iomgr/endpoint_pair_posix.c",
"src/core/iomgr/fd_posix.c",
"src/core/iomgr/iocp_windows.c",
"src/core/iomgr/iomgr.c",
"src/core/iomgr/iomgr_posix.c",
"src/core/iomgr/iomgr_windows.c",
"src/core/iomgr/pollset_kick.c",
"src/core/iomgr/pollset_multipoller_with_poll_posix.c",
"src/core/iomgr/pollset_multipoller_with_epoll.c",
"src/core/iomgr/pollset_posix.c",
"src/core/iomgr/pollset_windows.c",
"src/core/iomgr/resolve_address.c",
@ -141,9 +147,13 @@
"src/core/iomgr/socket_utils_common_posix.c",
"src/core/iomgr/socket_utils_linux.c",
"src/core/iomgr/socket_utils_posix.c",
"src/core/iomgr/socket_windows.c",
"src/core/iomgr/tcp_client_posix.c",
"src/core/iomgr/tcp_client_windows.c",
"src/core/iomgr/tcp_posix.c",
"src/core/iomgr/tcp_server_posix.c",
"src/core/iomgr/tcp_server_windows.c",
"src/core/iomgr/tcp_windows.c",
"src/core/iomgr/time_averaged_stats.c",
"src/core/iomgr/wakeup_fd_eventfd.c",
"src/core/iomgr/wakeup_fd_nospecial.c",
@ -163,6 +173,7 @@
"src/core/surface/byte_buffer_queue.c",
"src/core/surface/byte_buffer_reader.c",
"src/core/surface/call.c",
"src/core/surface/call_details.c",
"src/core/surface/channel.c",
"src/core/surface/channel_create.c",
"src/core/surface/client.c",
@ -170,6 +181,7 @@
"src/core/surface/event_string.c",
"src/core/surface/init.c",
"src/core/surface/lame_client.c",
"src/core/surface/metadata_array.c",
"src/core/surface/secure_channel_create.c",
"src/core/surface/secure_server_create.c",
"src/core/surface/server.c",
@ -214,6 +226,7 @@
"include/grpc/support/histogram.h",
"include/grpc/support/host_port.h",
"include/grpc/support/log.h",
"include/grpc/support/log_win32.h",
"include/grpc/support/port_platform.h",
"include/grpc/support/slice.h",
"include/grpc/support/slice_buffer.h",
@ -240,6 +253,7 @@
"src/core/support/cmdline.c",
"src/core/support/cpu_linux.c",
"src/core/support/cpu_posix.c",
"src/core/support/cpu_windows.c",
"src/core/support/env_linux.c",
"src/core/support/env_posix.c",
"src/core/support/env_win32.c",
@ -335,7 +349,8 @@
"deps": [
"gpr",
"grpc"
]
],
"vs_project_guid": "{D64C6D63-4458-4A88-AB38-35678384A7E4}"
},
{
"name": "grpc_test_util",
@ -411,11 +426,11 @@
"src/cpp/client/create_channel.cc",
"src/cpp/client/credentials.cc",
"src/cpp/client/internal_stub.cc",
"src/cpp/common/completion_queue.cc",
"src/cpp/common/rpc_method.cc",
"src/cpp/proto/proto_utils.cc",
"src/cpp/server/async_server.cc",
"src/cpp/server/async_server_context.cc",
"src/cpp/server/completion_queue.cc",
"src/cpp/server/server.cc",
"src/cpp/server/server_builder.cc",
"src/cpp/server/server_context_impl.cc",
@ -746,6 +761,7 @@
{
"name": "echo_client",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/echo/client.c"
@ -755,12 +771,12 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "echo_server",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/echo/server.c"
@ -770,8 +786,7 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "echo_test",
@ -804,6 +819,7 @@
{
"name": "fling_client",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/fling/client.c"
@ -813,12 +829,12 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "fling_server",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/fling/server.c"
@ -828,8 +844,7 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "fling_stream_test",
@ -1239,6 +1254,7 @@
{
"name": "json_rewrite",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/json/json_rewrite.c"
@ -1246,12 +1262,12 @@
"deps": [
"grpc",
"gpr"
],
"run": false
]
},
{
"name": "json_rewrite_test",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/json/json_rewrite_test.c"
@ -1261,8 +1277,7 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "json_test",
@ -1574,6 +1589,7 @@
{
"name": "interop_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
@ -1588,12 +1604,12 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "interop_server",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
@ -1608,12 +1624,12 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "qps_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/qps/qpstest.proto",
@ -1631,6 +1647,7 @@
{
"name": "qps_server",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/qps/qpstest.proto",
@ -1711,6 +1728,7 @@
{
"name": "tips_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"examples/tips/main.cc"
@ -1723,8 +1741,7 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "tips_publisher_test",

@ -177,14 +177,14 @@ void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
/* A single metadata element */
typedef struct grpc_metadata {
char *key;
char *value;
const char *key;
const char *value;
size_t value_length;
} grpc_metadata;
typedef enum grpc_completion_type {
GRPC_QUEUE_SHUTDOWN, /* Shutting down */
GRPC_IOREQ, /* grpc_call_ioreq completion */
GRPC_OP_COMPLETE, /* operation completion */
GRPC_READ, /* A read has completed */
GRPC_WRITE_ACCEPTED, /* A write has been accepted by
flow control */
@ -212,7 +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;
grpc_op_error op_complete;
struct {
size_t count;
grpc_metadata *elements;
@ -239,23 +239,45 @@ typedef struct {
grpc_metadata *metadata;
} grpc_metadata_array;
void grpc_metadata_array_init(grpc_metadata_array *array);
void grpc_metadata_array_destroy(grpc_metadata_array *array);
typedef struct {
const char *method;
const char *host;
char *method;
size_t method_capacity;
char *host;
size_t host_capacity;
gpr_timespec deadline;
} grpc_call_details;
void grpc_call_details_init(grpc_call_details *details);
void grpc_call_details_destroy(grpc_call_details *details);
typedef enum {
/* Send initial metadata: one and only one instance MUST be sent for each call,
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_INITIAL_METADATA = 0,
/* Send a message: 0 or more of these operations can occur for each call */
GRPC_OP_SEND_MESSAGE,
/* Send a close from the server: one and only one instance MUST be sent from the client,
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_CLOSE_FROM_CLIENT,
/* Send status from the server: one and only one instance MUST be sent from the server
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_STATUS_FROM_SERVER,
/* Receive initial metadata: one and only one MUST be made on the client, must
not be made on the server */
GRPC_OP_RECV_INITIAL_METADATA,
GRPC_OP_RECV_MESSAGES,
/* Receive a message: 0 or more of these operations can occur for each call */
GRPC_OP_RECV_MESSAGE,
/* Receive status on the client: one and only one must be made on the client */
GRPC_OP_RECV_STATUS_ON_CLIENT,
/* Receive status on the server: one and only one must be made on the server */
GRPC_OP_RECV_CLOSE_ON_SERVER
} grpc_op_type;
/* Operation data: one field for each op type (except SEND_CLOSE_FROM_CLIENT which has
no arguments) */
typedef struct grpc_op {
grpc_op_type op;
union {
@ -270,15 +292,46 @@ typedef struct grpc_op {
grpc_status_code status;
const char *status_details;
} send_status_from_server;
/* ownership of the array is with the caller, but ownership of the elements
stays with the call object (ie key, value members are owned by the call
object, recv_initial_metadata->array is owned by the caller).
After the operation completes, call grpc_metadata_array_destroy on this
value, or reuse it in a future op. */
grpc_metadata_array *recv_initial_metadata;
grpc_byte_buffer **recv_message;
struct {
/* ownership of the array is with the caller, but ownership of the elements
stays with the call object (ie key, value members are owned by the call
object, trailing_metadata->array is owned by the caller).
After the operation completes, call grpc_metadata_array_destroy on this
value, or reuse it in a future op. */
grpc_metadata_array *trailing_metadata;
grpc_status_code *status;
/* status_details is a buffer owned by the application before the op completes
and after the op has completed. During the operation status_details may be
reallocated to a size larger than *status_details_capacity, in which case
*status_details_capacity will be updated with the new array capacity.
Pre-allocating space:
size_t my_capacity = 8;
char *my_details = gpr_malloc(my_capacity);
x.status_details = &my_details;
x.status_details_capacity = &my_capacity;
Not pre-allocating space:
size_t my_capacity = 0;
char *my_details = NULL;
x.status_details = &my_details;
x.status_details_capacity = &my_capacity;
After the call:
gpr_free(my_details); */
char **status_details;
size_t *status_details_capacity;
} recv_status_on_client;
struct {
/* out argument, set to 1 if the call failed in any way (seen as a cancellation
on the server), or 0 if the call succeeded */
int *cancelled;
} recv_close_on_server;
} data;
@ -330,6 +383,19 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec deadline);
/* Create a call given a grpc_channel, in order to call 'method'. The request
is not sent until grpc_call_invoke is called. All completions are sent to
'completion_queue'. */
grpc_call *grpc_channel_create_call(grpc_channel *channel,
grpc_completion_queue *completion_queue,
const char *method, const char *host,
gpr_timespec deadline);
/* Start a batch of operations defined in the array ops; when complete, post a
completion of type 'tag' to the completion queue bound to the call.
The order of ops specified in the batch has no significance.
Only one operation of each type can be active at once in any given
batch. */
grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
size_t nops, void *tag);
@ -473,6 +539,11 @@ void grpc_call_destroy(grpc_call *call);
grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new);
grpc_call_error grpc_server_request_call(
grpc_server *server, grpc_call **call, grpc_call_details *details,
grpc_metadata_array *request_metadata,
grpc_completion_queue *completion_queue, void *tag_new);
/* Create a server */
grpc_server *grpc_server_create(grpc_completion_queue *cq,
const grpc_channel_args *args);

@ -0,0 +1,53 @@
/*
*
* 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_LOG_WIN32_H__
#define __GRPC_SUPPORT_LOG_WIN32_H__
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Returns a string allocated with gpr_malloc that contains a UTF-8
* formatted error message, corresponding to the error messageid.
* Use in conjunction with GetLastError() et al.
*/
char *gpr_format_message(DWORD messageid);
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_SUPPORT_LOG_H__ */

@ -46,10 +46,12 @@
#define GPR_WIN32 1
#define GPR_ARCH_64 1
#define GPR_GETPID_IN_PROCESS_H 1
#define GPR_WINSOCK_SOCKET 1
#elif defined(_WIN32) || defined(WIN32)
#define GPR_ARCH_32 1
#define GPR_WIN32 1
#define GPR_GETPID_IN_PROCESS_H 1
#define GPR_WINSOCK_SOCKET 1
#elif defined(ANDROID) || defined(__ANDROID__)
#define GPR_ANDROID 1
#define GPR_ARCH_32 1
@ -71,7 +73,7 @@
#define GPR_CPU_LINUX 1
#define GPR_GCC_ATOMIC 1
#define GPR_LINUX 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_LINUX_MULTIPOLL_WITH_EPOLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1

@ -210,11 +210,30 @@ static void remove_waiting_child(channel_data *chand, call_data *calld) {
chand->waiting_child_count = new_count;
}
static void send_up_cancelled_ops(grpc_call_element *elem) {
grpc_call_op finish_op;
channel_data *chand = elem->channel_data;
/* send up a synthesized status */
finish_op.type = GRPC_RECV_METADATA;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.data.metadata = grpc_mdelem_ref(chand->cancel_status);
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
/* send up a finish */
finish_op.type = GRPC_RECV_FINISH;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
}
static void cancel_rpc(grpc_call_element *elem, grpc_call_op *op) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
grpc_call_element *child_elem;
grpc_call_op finish_op;
gpr_mu_lock(&chand->mu);
switch (calld->state) {
@ -225,27 +244,16 @@ static void cancel_rpc(grpc_call_element *elem, grpc_call_op *op) {
return; /* early out */
case CALL_WAITING:
remove_waiting_child(chand, calld);
calld->state = CALL_CANCELLED;
gpr_mu_unlock(&chand->mu);
send_up_cancelled_ops(elem);
calld->s.waiting.on_complete(calld->s.waiting.on_complete_user_data,
GRPC_OP_ERROR);
/* fallthrough intended */
return; /* early out */
case CALL_CREATED:
calld->state = CALL_CANCELLED;
gpr_mu_unlock(&chand->mu);
/* send up a synthesized status */
finish_op.type = GRPC_RECV_METADATA;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.data.metadata = grpc_mdelem_ref(chand->cancel_status);
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
/* send up a finish */
finish_op.type = GRPC_RECV_FINISH;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
send_up_cancelled_ops(elem);
return; /* early out */
case CALL_CANCELLED:
gpr_mu_unlock(&chand->mu);

@ -1,5 +1,4 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
@ -44,6 +43,7 @@ typedef struct channel_data {
grpc_mdelem *method;
grpc_mdelem *scheme;
grpc_mdelem *content_type;
grpc_mdelem *status;
} channel_data;
/* used to silence 'variable not used' warnings */
@ -86,6 +86,18 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->content_type));
grpc_call_next_op(elem, op);
break;
case GRPC_RECV_METADATA:
if (op->data.metadata == channeld->status) {
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
} else if (op->data.metadata->key == channeld->status->key) {
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
grpc_call_element_send_cancel(elem);
} else {
grpc_call_next_op(elem, op);
}
break;
default:
/* pass control up or down the stack depending on op->dir */
grpc_call_next_op(elem, op);
@ -166,6 +178,7 @@ static void init_channel_elem(grpc_channel_element *elem,
grpc_mdelem_from_strings(mdctx, ":scheme", scheme_from_args(args));
channeld->content_type =
grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
}
/* Destructor for channel data */
@ -177,6 +190,7 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
grpc_mdelem_unref(channeld->method);
grpc_mdelem_unref(channeld->scheme);
grpc_mdelem_unref(channeld->content_type);
grpc_mdelem_unref(channeld->status);
}
const grpc_channel_filter grpc_http_client_filter = {

@ -73,20 +73,23 @@ static grpc_security_status httpcli_ssl_create_handshaker(
return GRPC_SECURITY_OK;
}
static grpc_security_status httpcli_ssl_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
static grpc_security_status httpcli_ssl_check_peer(grpc_security_context *ctx,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
grpc_httpcli_ssl_channel_security_context *c =
(grpc_httpcli_ssl_channel_security_context *)ctx;
grpc_security_status status = GRPC_SECURITY_OK;
/* Check the peer name. */
if (c->secure_peer_name != NULL &&
!tsi_ssl_peer_matches_name(peer, c->secure_peer_name)) {
!tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) {
gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate",
c->secure_peer_name);
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
}
return GRPC_SECURITY_OK;
tsi_peer_destruct(&peer);
return status;
}
static grpc_security_context_vtable httpcli_ssl_vtable = {

@ -68,7 +68,6 @@ static grpc_fd *fd_freelist = NULL;
static gpr_mu fd_freelist_mu;
static void freelist_fd(grpc_fd *fd) {
gpr_free(fd->watchers);
gpr_mu_lock(&fd_freelist_mu);
fd->freelist_next = fd_freelist;
fd_freelist = fd;
@ -93,9 +92,7 @@ static grpc_fd *alloc_fd(int fd) {
gpr_atm_rel_store(&r->writest.state, NOT_READY);
gpr_atm_rel_store(&r->shutdown, 0);
r->fd = fd;
r->watchers = NULL;
r->watcher_count = 0;
r->watcher_capacity = 0;
r->watcher_root.next = r->watcher_root.prev = &r->watcher_root;
r->freelist_next = NULL;
return r;
}
@ -118,9 +115,7 @@ static void unref_by(grpc_fd *fd, int n) {
}
}
void grpc_fd_global_init(void) {
gpr_mu_init(&fd_freelist_mu);
}
void grpc_fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
void grpc_fd_global_shutdown(void) {
while (fd_freelist != NULL) {
@ -145,11 +140,11 @@ int grpc_fd_is_orphaned(grpc_fd *fd) {
}
static void wake_watchers(grpc_fd *fd) {
size_t i, n;
grpc_fd_watcher *watcher;
gpr_mu_lock(&fd->watcher_mu);
n = fd->watcher_count;
for (i = 0; i < n; i++) {
grpc_pollset_force_kick(fd->watchers[i]);
for (watcher = fd->watcher_root.next; watcher != &fd->watcher_root;
watcher = watcher->next) {
grpc_pollset_force_kick(watcher->pollset);
}
gpr_mu_unlock(&fd->watcher_mu);
}
@ -293,36 +288,27 @@ void grpc_fd_notify_on_write(grpc_fd *fd, grpc_iomgr_cb_func write_cb,
}
gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset,
gpr_uint32 read_mask, gpr_uint32 write_mask) {
gpr_uint32 read_mask, gpr_uint32 write_mask,
grpc_fd_watcher *watcher) {
/* keep track of pollers that have requested our events, in case they change
*/
gpr_mu_lock(&fd->watcher_mu);
if (fd->watcher_capacity == fd->watcher_count) {
fd->watcher_capacity =
GPR_MAX(fd->watcher_capacity + 8, fd->watcher_capacity * 3 / 2);
fd->watchers = gpr_realloc(fd->watchers,
fd->watcher_capacity * sizeof(grpc_pollset *));
}
fd->watchers[fd->watcher_count++] = pollset;
watcher->next = &fd->watcher_root;
watcher->prev = watcher->next->prev;
watcher->next->prev = watcher->prev->next = watcher;
watcher->pollset = pollset;
watcher->fd = fd;
gpr_mu_unlock(&fd->watcher_mu);
return (gpr_atm_acq_load(&fd->readst.state) != READY ? read_mask : 0) |
(gpr_atm_acq_load(&fd->writest.state) != READY ? write_mask : 0);
}
void grpc_fd_end_poll(grpc_fd *fd, grpc_pollset *pollset) {
size_t r, w, n;
gpr_mu_lock(&fd->watcher_mu);
n = fd->watcher_count;
for (r = 0, w = 0; r < n; r++) {
if (fd->watchers[r] == pollset) {
fd->watcher_count--;
continue;
}
fd->watchers[w++] = fd->watchers[r];
}
gpr_mu_unlock(&fd->watcher_mu);
void grpc_fd_end_poll(grpc_fd_watcher *watcher) {
gpr_mu_lock(&watcher->fd->watcher_mu);
watcher->next->prev = watcher->prev;
watcher->prev->next = watcher->next;
gpr_mu_unlock(&watcher->fd->watcher_mu);
}
void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback) {

@ -47,7 +47,16 @@ typedef struct {
gpr_atm state;
} grpc_fd_state;
typedef struct grpc_fd {
typedef struct grpc_fd grpc_fd;
typedef struct grpc_fd_watcher {
struct grpc_fd_watcher *next;
struct grpc_fd_watcher *prev;
grpc_pollset *pollset;
grpc_fd *fd;
} grpc_fd_watcher;
struct grpc_fd {
int fd;
/* refst format:
bit0: 1=active/0=orphaned
@ -60,9 +69,7 @@ typedef struct grpc_fd {
gpr_atm shutdown;
gpr_mu watcher_mu;
grpc_pollset **watchers;
size_t watcher_count;
size_t watcher_capacity;
grpc_fd_watcher watcher_root;
grpc_fd_state readst;
grpc_fd_state writest;
@ -70,7 +77,7 @@ typedef struct grpc_fd {
grpc_iomgr_cb_func on_done;
void *on_done_user_data;
struct grpc_fd *freelist_next;
} grpc_fd;
};
/* Create a wrapped file descriptor.
Requires fd is a non-blocking file descriptor.
@ -95,9 +102,10 @@ void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_cb_func on_done, void *user_data);
Polling strategies that do not need to alter their behavior depending on the
fd's current interest (such as epoll) do not need to call this function. */
gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset,
gpr_uint32 read_mask, gpr_uint32 write_mask);
gpr_uint32 read_mask, gpr_uint32 write_mask,
grpc_fd_watcher *rec);
/* Complete polling previously started with grpc_fd_begin_poll */
void grpc_fd_end_poll(grpc_fd *fd, grpc_pollset *pollset);
void grpc_fd_end_poll(grpc_fd_watcher *rec);
/* Return 1 if this fd is orphaned, 0 otherwise */
int grpc_fd_is_orphaned(grpc_fd *fd);

@ -0,0 +1,200 @@
/*
*
* 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_WINSOCK_SOCKET
#include <winsock2.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/alloc.h>
#include <grpc/support/thd.h>
#include "src/core/iomgr/alarm_internal.h"
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr_internal.h"
#include "src/core/iomgr/socket_windows.h"
static ULONG g_iocp_kick_token;
static OVERLAPPED g_iocp_custom_overlap;
static gpr_event g_shutdown_iocp;
static gpr_event g_iocp_done;
static HANDLE g_iocp;
static int do_iocp_work() {
BOOL success;
DWORD bytes = 0;
DWORD flags = 0;
ULONG_PTR completion_key;
LPOVERLAPPED overlapped;
gpr_timespec wait_time = gpr_inf_future;
grpc_winsocket *socket;
grpc_winsocket_callback_info *info;
void(*f)(void *, int) = NULL;
void *opaque = NULL;
success = GetQueuedCompletionStatus(g_iocp, &bytes,
&completion_key, &overlapped,
gpr_time_to_millis(wait_time));
if (!success && !overlapped) {
/* The deadline got attained. */
return 0;
}
GPR_ASSERT(completion_key && overlapped);
if (overlapped == &g_iocp_custom_overlap) {
if (completion_key == (ULONG_PTR) &g_iocp_kick_token) {
/* We were awoken from a kick. */
gpr_log(GPR_DEBUG, "do_iocp_work - got a kick");
return 1;
}
gpr_log(GPR_ERROR, "Unknown custom completion key.");
abort();
}
socket = (grpc_winsocket*) completion_key;
if (overlapped == &socket->write_info.overlapped) {
gpr_log(GPR_DEBUG, "do_iocp_work - got write packet");
info = &socket->write_info;
} else if (overlapped == &socket->read_info.overlapped) {
gpr_log(GPR_DEBUG, "do_iocp_work - got read packet");
info = &socket->read_info;
} else {
gpr_log(GPR_ERROR, "Unknown IOCP operation");
abort();
}
success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes,
FALSE, &flags);
gpr_log(GPR_DEBUG, "bytes: %u, flags: %u - op %s", bytes, flags,
success ? "succeeded" : "failed");
info->bytes_transfered = bytes;
info->wsa_error = success ? 0 : WSAGetLastError();
GPR_ASSERT(overlapped == &info->overlapped);
gpr_mu_lock(&socket->state_mu);
GPR_ASSERT(!info->has_pending_iocp);
if (info->cb) {
f = info->cb;
opaque = info->opaque;
info->cb = NULL;
} else {
info->has_pending_iocp = 1;
}
gpr_mu_unlock(&socket->state_mu);
if (f) f(opaque, 1);
return 1;
}
static void iocp_loop(void *p) {
while (!gpr_event_get(&g_shutdown_iocp)) {
grpc_maybe_call_delayed_callbacks(NULL, 1);
do_iocp_work();
}
gpr_event_set(&g_iocp_done, (void *)1);
}
void grpc_iocp_init(void) {
gpr_thd_id id;
g_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL,
(ULONG_PTR)NULL, 0);
GPR_ASSERT(g_iocp);
gpr_event_init(&g_iocp_done);
gpr_event_init(&g_shutdown_iocp);
gpr_thd_new(&id, iocp_loop, NULL, NULL);
}
void grpc_iocp_shutdown(void) {
BOOL success;
gpr_event_set(&g_shutdown_iocp, (void *)1);
success = PostQueuedCompletionStatus(g_iocp, 0,
(ULONG_PTR) &g_iocp_kick_token,
&g_iocp_custom_overlap);
GPR_ASSERT(success);
gpr_event_wait(&g_iocp_done, gpr_inf_future);
success = CloseHandle(g_iocp);
GPR_ASSERT(success);
}
void grpc_iocp_add_socket(grpc_winsocket *socket) {
HANDLE ret;
if (socket->added_to_iocp) return;
ret = CreateIoCompletionPort((HANDLE)socket->socket,
g_iocp, (gpr_uintptr) socket, 0);
if (!ret) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message);
gpr_free(utf8_message);
__debugbreak();
abort();
}
socket->added_to_iocp = 1;
GPR_ASSERT(ret == g_iocp);
}
static void socket_notify_on_iocp(grpc_winsocket *socket,
void(*cb)(void *, int), void *opaque,
grpc_winsocket_callback_info *info) {
int run_now = 0;
GPR_ASSERT(!info->cb);
gpr_mu_lock(&socket->state_mu);
if (info->has_pending_iocp) {
run_now = 1;
info->has_pending_iocp = 0;
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - runs now");
} else {
info->cb = cb;
info->opaque = opaque;
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - queued");
}
gpr_mu_unlock(&socket->state_mu);
if (run_now) cb(opaque, 1);
}
void grpc_socket_notify_on_write(grpc_winsocket *socket,
void(*cb)(void *, int), void *opaque) {
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_write");
socket_notify_on_iocp(socket, cb, opaque, &socket->write_info);
}
void grpc_socket_notify_on_read(grpc_winsocket *socket,
void(*cb)(void *, int), void *opaque) {
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_read");
socket_notify_on_iocp(socket, cb, opaque, &socket->read_info);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,52 @@
/*
*
* 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_IOMGR_IOCP_WINDOWS_H_
#define __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_
#include <windows.h>
#include <grpc/support/sync.h>
#include "src/core/iomgr/socket_windows.h"
void grpc_iocp_init(void);
void grpc_iocp_shutdown(void);
void grpc_iocp_add_socket(grpc_winsocket *);
void grpc_socket_notify_on_write(grpc_winsocket *, void(*cb)(void *, int success),
void *opaque);
void grpc_socket_notify_on_read(grpc_winsocket *, void(*cb)(void *, int success),
void *opaque);
#endif /* __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ */

@ -31,6 +31,10 @@
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET
#include "src/core/iomgr/iomgr_posix.h"
#include "src/core/iomgr/fd_posix.h"
@ -43,3 +47,5 @@ void grpc_iomgr_platform_shutdown(void) {
grpc_pollset_global_shutdown();
grpc_fd_global_shutdown();
}
#endif /* GRPC_POSIX_SOCKET */

@ -0,0 +1,67 @@
/*
*
* 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_WINSOCK_SOCKET
#include "src/core/iomgr/sockaddr_win32.h"
#include <grpc/support/log.h>
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr.h"
static void winsock_init(void) {
WSADATA wsaData;
int status = WSAStartup(MAKEWORD(2, 0), &wsaData);
GPR_ASSERT(status == 0);
}
static void winsock_shutdown(void) {
int status = WSACleanup();
GPR_ASSERT(status == 0);
}
void grpc_iomgr_platform_init(void) {
winsock_init();
grpc_iocp_init();
}
void grpc_iomgr_platform_shutdown(void) {
grpc_iocp_shutdown();
winsock_shutdown();
}
#endif /* GRPC_WINSOCK_SOCKET */

@ -0,0 +1,197 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL
#include <errno.h>
#include <string.h>
#include <sys/epoll.h>
#include <unistd.h>
#include "src/core/iomgr/fd_posix.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
typedef struct {
int epoll_fd;
grpc_wakeup_fd_info wakeup_fd;
} pollset_hdr;
static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset,
grpc_fd *fd) {
pollset_hdr *h = pollset->data.ptr;
struct epoll_event ev;
int err;
ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
ev.data.ptr = fd;
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev);
if (err < 0) {
/* FDs may be added to a pollset multiple times, so EEXIST is normal. */
if (errno != EEXIST) {
gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd,
strerror(errno));
}
}
}
static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset,
grpc_fd *fd) {
pollset_hdr *h = pollset->data.ptr;
int err;
/* Note that this can race with concurrent poll, but that should be fine since
* at worst it creates a spurious read event on a reused grpc_fd object. */
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL);
if (err < 0) {
gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd->fd,
strerror(errno));
}
}
/* TODO(klempner): We probably want to turn this down a bit */
#define GRPC_EPOLL_MAX_EVENTS 1000
static int multipoll_with_epoll_pollset_maybe_work(
grpc_pollset *pollset, gpr_timespec deadline, gpr_timespec now,
int allow_synchronous_callback) {
struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS];
int ep_rv;
pollset_hdr *h = pollset->data.ptr;
int timeout_ms;
/* If you want to ignore epoll's ability to sanely handle parallel pollers,
* for a more apples-to-apples performance comparison with poll, add a
* if (pollset->counter == 0) { return 0 }
* here.
*/
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) {
timeout_ms = -1;
} else {
timeout_ms = gpr_time_to_millis(gpr_time_sub(deadline, now));
if (timeout_ms <= 0) {
return 1;
}
}
pollset->counter += 1;
gpr_mu_unlock(&pollset->mu);
do {
ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms);
if (ep_rv < 0) {
if (errno != EINTR) {
gpr_log(GPR_ERROR, "epoll_wait() failed: %s", strerror(errno));
}
} else {
int i;
for (i = 0; i < ep_rv; ++i) {
if (ep_ev[i].data.ptr == 0) {
grpc_wakeup_fd_consume_wakeup(&h->wakeup_fd);
} else {
grpc_fd *fd = ep_ev[i].data.ptr;
/* TODO(klempner): We might want to consider making err and pri
* separate events */
int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP);
int read = ep_ev[i].events & (EPOLLIN | EPOLLPRI);
int write = ep_ev[i].events & EPOLLOUT;
if (read || cancel) {
grpc_fd_become_readable(fd, allow_synchronous_callback);
}
if (write || cancel) {
grpc_fd_become_writable(fd, allow_synchronous_callback);
}
}
}
}
timeout_ms = 0;
} while (ep_rv == GRPC_EPOLL_MAX_EVENTS);
gpr_mu_lock(&pollset->mu);
pollset->counter -= 1;
/* TODO(klempner): This should signal once per event rather than broadcast,
* although it probably doesn't matter because threads will generally be
* blocked in epoll_wait rather than being blocked on the cv. */
gpr_cv_broadcast(&pollset->cv);
return 1;
}
static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) {
pollset_hdr *h = pollset->data.ptr;
grpc_wakeup_fd_destroy(&h->wakeup_fd);
close(h->epoll_fd);
gpr_free(h);
}
static void epoll_kick(grpc_pollset *pollset) {
pollset_hdr *h = pollset->data.ptr;
grpc_wakeup_fd_wakeup(&h->wakeup_fd);
}
static const grpc_pollset_vtable multipoll_with_epoll_pollset = {
multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_del_fd,
multipoll_with_epoll_pollset_maybe_work, epoll_kick,
multipoll_with_epoll_pollset_destroy};
void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds,
size_t nfds) {
size_t i;
pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr));
struct epoll_event ev;
int err;
pollset->vtable = &multipoll_with_epoll_pollset;
pollset->data.ptr = h;
h->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (h->epoll_fd < 0) {
/* TODO(klempner): Fall back to poll here, especially on ENOSYS */
gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno));
abort();
}
for (i = 0; i < nfds; i++) {
multipoll_with_epoll_pollset_add_fd(pollset, fds[i]);
}
grpc_wakeup_fd_create(&h->wakeup_fd);
ev.events = EPOLLIN;
ev.data.ptr = 0;
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD,
GRPC_WAKEUP_FD_GET_READ_FD(&h->wakeup_fd), &ev);
if (err < 0) {
gpr_log(GPR_ERROR, "Wakeup fd epoll_ctl failed: %s", strerror(errno));
abort();
}
}
#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */

@ -53,11 +53,11 @@ typedef struct {
size_t fd_count;
size_t fd_capacity;
grpc_fd **fds;
/* fds being polled by the current poller: parallel arrays of pollfd and the
* grpc_fd* that the pollfd was constructed from */
/* fds being polled by the current poller: parallel arrays of pollfd, and
a grpc_fd_watcher */
size_t pfd_count;
size_t pfd_capacity;
grpc_fd **selfds;
grpc_fd_watcher *watchers;
struct pollfd *pfds;
/* fds that have been removed from the pollset explicitly */
size_t del_count;
@ -98,7 +98,7 @@ static void end_polling(grpc_pollset *pollset) {
pollset_hdr *h;
h = pollset->data.ptr;
for (i = 1; i < h->pfd_count; i++) {
grpc_fd_end_poll(h->selfds[i], pollset);
grpc_fd_end_poll(&h->watchers[i]);
}
}
@ -125,9 +125,9 @@ static int multipoll_with_poll_pollset_maybe_work(
if (h->pfd_capacity < h->fd_count + 1) {
h->pfd_capacity = GPR_MAX(h->pfd_capacity * 3 / 2, h->fd_count + 1);
gpr_free(h->pfds);
gpr_free(h->selfds);
gpr_free(h->watchers);
h->pfds = gpr_malloc(sizeof(struct pollfd) * h->pfd_capacity);
h->selfds = gpr_malloc(sizeof(grpc_fd *) * h->pfd_capacity);
h->watchers = gpr_malloc(sizeof(grpc_fd_watcher) * h->pfd_capacity);
}
nf = 0;
np = 1;
@ -147,7 +147,7 @@ static int multipoll_with_poll_pollset_maybe_work(
grpc_fd_unref(h->fds[i]);
} else {
h->fds[nf++] = h->fds[i];
h->selfds[np] = h->fds[i];
h->watchers[np].fd = h->fds[i];
h->pfds[np].fd = h->fds[i]->fd;
h->pfds[np].revents = 0;
np++;
@ -167,8 +167,8 @@ static int multipoll_with_poll_pollset_maybe_work(
gpr_mu_unlock(&pollset->mu);
for (i = 1; i < np; i++) {
h->pfds[i].events =
grpc_fd_begin_poll(h->selfds[i], pollset, POLLIN, POLLOUT);
h->pfds[i].events = grpc_fd_begin_poll(h->watchers[i].fd, pollset, POLLIN,
POLLOUT, &h->watchers[i]);
}
r = poll(h->pfds, h->pfd_count, timeout);
@ -184,10 +184,10 @@ static int multipoll_with_poll_pollset_maybe_work(
}
for (i = 1; i < np; i++) {
if (h->pfds[i].revents & POLLIN) {
grpc_fd_become_readable(h->selfds[i], allow_synchronous_callback);
grpc_fd_become_readable(h->watchers[i].fd, allow_synchronous_callback);
}
if (h->pfds[i].revents & POLLOUT) {
grpc_fd_become_writable(h->selfds[i], allow_synchronous_callback);
grpc_fd_become_writable(h->watchers[i].fd, allow_synchronous_callback);
}
}
}
@ -200,6 +200,10 @@ static int multipoll_with_poll_pollset_maybe_work(
return 1;
}
static void multipoll_with_poll_pollset_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) {
size_t i;
pollset_hdr *h = pollset->data.ptr;
@ -211,7 +215,7 @@ static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) {
grpc_fd_unref(h->dels[i]);
}
gpr_free(h->pfds);
gpr_free(h->selfds);
gpr_free(h->watchers);
gpr_free(h->fds);
gpr_free(h->dels);
gpr_free(h);
@ -219,7 +223,7 @@ static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) {
static const grpc_pollset_vtable multipoll_with_poll_pollset = {
multipoll_with_poll_pollset_add_fd, multipoll_with_poll_pollset_del_fd,
multipoll_with_poll_pollset_maybe_work,
multipoll_with_poll_pollset_maybe_work, multipoll_with_poll_pollset_kick,
multipoll_with_poll_pollset_destroy};
void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds,
@ -234,7 +238,7 @@ void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds,
h->pfd_count = 0;
h->pfd_capacity = 0;
h->pfds = NULL;
h->selfds = NULL;
h->watchers = NULL;
h->del_count = 0;
h->del_capacity = 0;
h->dels = NULL;

@ -76,11 +76,17 @@ static void backup_poller(void *p) {
void grpc_pollset_kick(grpc_pollset *p) {
if (p->counter) {
grpc_pollset_kick_kick(&p->kick_state);
p->vtable->kick(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);
}
static void kick_using_pollset_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
/* global state management */
@ -184,7 +190,7 @@ static void empty_pollset_destroy(grpc_pollset *pollset) {}
static const grpc_pollset_vtable empty_pollset = {
empty_pollset_add_fd, empty_pollset_del_fd, empty_pollset_maybe_work,
empty_pollset_destroy};
kick_using_pollset_kick, empty_pollset_destroy};
static void become_empty_pollset(grpc_pollset *pollset) {
pollset->vtable = &empty_pollset;
@ -200,8 +206,15 @@ static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) {
if (fd == pollset->data.ptr) return;
fds[0] = pollset->data.ptr;
fds[1] = fd;
grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds));
grpc_fd_unref(fds[0]);
if (!grpc_fd_is_orphaned(fds[0])) {
grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds));
grpc_fd_unref(fds[0]);
} else {
/* old fd is orphaned and we haven't cleaned it up until now, so remain a
* unary poller */
grpc_fd_unref(fds[0]);
pollset->data.ptr = fd;
}
}
static void unary_poll_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) {
@ -217,6 +230,7 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
int allow_synchronous_callback) {
struct pollfd pfd[2];
grpc_fd *fd;
grpc_fd_watcher fd_watcher;
int timeout;
int r;
@ -249,7 +263,7 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
pollset->counter = 1;
gpr_mu_unlock(&pollset->mu);
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT);
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT, &fd_watcher);
r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout);
if (r < 0) {
@ -271,7 +285,7 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
}
grpc_pollset_kick_post_poll(&pollset->kick_state);
grpc_fd_end_poll(fd, pollset);
grpc_fd_end_poll(&fd_watcher);
gpr_mu_lock(&pollset->mu);
pollset->counter = 0;
@ -286,7 +300,8 @@ static void unary_poll_pollset_destroy(grpc_pollset *pollset) {
static const grpc_pollset_vtable unary_poll_pollset = {
unary_poll_pollset_add_fd, unary_poll_pollset_del_fd,
unary_poll_pollset_maybe_work, unary_poll_pollset_destroy};
unary_poll_pollset_maybe_work, kick_using_pollset_kick,
unary_poll_pollset_destroy};
static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd) {
pollset->vtable = &unary_poll_pollset;

@ -66,6 +66,7 @@ struct grpc_pollset_vtable {
void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd);
int (*maybe_work)(grpc_pollset *pollset, gpr_timespec deadline,
gpr_timespec now, int allow_synchronous_callback);
void (*kick)(grpc_pollset *pollset);
void (*destroy)(grpc_pollset *pollset);
};

@ -33,6 +33,39 @@
#include <grpc/support/port_platform.h>
#ifdef GPR_WIN32
#ifdef GPR_WINSOCK_SOCKET
#endif /* GPR_WIN32 */
#include <grpc/support/thd.h>
#include "src/core/iomgr/alarm_internal.h"
#include "src/core/iomgr/iomgr_internal.h"
#include "src/core/iomgr/pollset_windows.h"
void grpc_pollset_init(grpc_pollset *pollset) {
gpr_mu_init(&pollset->mu);
gpr_cv_init(&pollset->cv);
}
void grpc_pollset_destroy(grpc_pollset *pollset) {
gpr_mu_destroy(&pollset->mu);
gpr_cv_destroy(&pollset->cv);
}
int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
gpr_timespec now;
now = gpr_now();
if (gpr_time_cmp(now, deadline) > 0) {
return 0;
}
if (grpc_maybe_call_delayed_callbacks(NULL, 1)) {
return 1;
}
if (grpc_alarm_check(NULL, now, &deadline)) {
return 1;
}
return 0;
}
void grpc_pollset_kick(grpc_pollset *p) { }
#endif /* GPR_WINSOCK_SOCKET */

@ -34,9 +34,11 @@
#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_
#define __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_
#include <windows.h>
#include <grpc/support/sync.h>
#include "src/core/iomgr/pollset_kick.h"
#include "src/core/iomgr/socket_windows.h"
/* forward declare only in this file to avoid leaking impl details via
pollset.h; real users of grpc_fd should always include 'fd_posix.h' and not

@ -111,13 +111,20 @@ int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) {
void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
struct sockaddr_in6 *wild6_out) {
memset(wild4_out, 0, sizeof(*wild4_out));
wild4_out->sin_family = AF_INET;
wild4_out->sin_port = htons(port);
grpc_sockaddr_make_wildcard4(port, wild4_out);
grpc_sockaddr_make_wildcard6(port, wild6_out);
}
void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out) {
memset(wild_out, 0, sizeof(*wild_out));
wild_out->sin_family = AF_INET;
wild_out->sin_port = htons(port);
}
memset(wild6_out, 0, sizeof(*wild6_out));
wild6_out->sin6_family = AF_INET6;
wild6_out->sin6_port = htons(port);
void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out) {
memset(wild_out, 0, sizeof(*wild_out));
wild_out->sin6_family = AF_INET6;
wild_out->sin6_port = htons(port);
}
int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,

@ -57,6 +57,12 @@ int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out);
void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
struct sockaddr_in6 *wild6_out);
/* Writes 0.0.0.0:port. */
void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out);
/* Writes [::]:port. */
void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out);
/* Return the IP port number of a sockaddr */
int grpc_sockaddr_get_port(const struct sockaddr *addr);

@ -35,5 +35,7 @@
#define __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_
#include <ws2tcpip.h>
#include <winsock2.h>
#include <mswsock.h>
#endif // __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_
#endif /* __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_ */

@ -0,0 +1,77 @@
/*
*
* 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>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#ifdef GPR_WINSOCK_SOCKET
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr.h"
#include "src/core/iomgr/iomgr_internal.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/pollset.h"
#include "src/core/iomgr/pollset_windows.h"
grpc_winsocket *grpc_winsocket_create(SOCKET socket) {
grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket));
gpr_log(GPR_DEBUG, "grpc_winsocket_create");
memset(r, 0, sizeof(grpc_winsocket));
r->socket = socket;
gpr_mu_init(&r->state_mu);
grpc_iomgr_ref();
grpc_iocp_add_socket(r);
return r;
}
void shutdown_op(grpc_winsocket_callback_info *info) {
if (!info->cb) return;
grpc_iomgr_add_delayed_callback(info->cb, info->opaque, 0);
}
void grpc_winsocket_shutdown(grpc_winsocket *socket) {
gpr_log(GPR_DEBUG, "grpc_winsocket_shutdown");
shutdown_op(&socket->read_info);
shutdown_op(&socket->write_info);
}
void grpc_winsocket_orphan(grpc_winsocket *socket) {
gpr_log(GPR_DEBUG, "grpc_winsocket_orphan");
grpc_iomgr_unref();
closesocket(socket->socket);
gpr_mu_destroy(&socket->state_mu);
gpr_free(socket);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,75 @@
/*
*
* 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_IOMGR_HANDLE_WINDOWS_H__
#define __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__
#include <windows.h>
#include <grpc/support/sync.h>
#include <grpc/support/atm.h>
typedef struct grpc_winsocket_callback_info {
/* This is supposed to be a WSAOVERLAPPED, but in order to get that
* definition, we need to include ws2tcpip.h, which needs to be included
* from the top, otherwise it'll clash with a previous inclusion of
* windows.h that in turns includes winsock.h. If anyone knows a way
* to do it properly, feel free to send a patch.
*/
OVERLAPPED overlapped;
void(*cb)(void *opaque, int success);
void *opaque;
int has_pending_iocp;
DWORD bytes_transfered;
int wsa_error;
} grpc_winsocket_callback_info;
typedef struct grpc_winsocket {
SOCKET socket;
int added_to_iocp;
grpc_winsocket_callback_info write_info;
grpc_winsocket_callback_info read_info;
gpr_mu state_mu;
} grpc_winsocket;
/* Create a wrapped windows handle.
This takes ownership of closing it. */
grpc_winsocket *grpc_winsocket_create(SOCKET socket);
void grpc_winsocket_shutdown(grpc_winsocket *socket);
void grpc_winsocket_orphan(grpc_winsocket *socket);
#endif /* __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__ */

@ -0,0 +1,215 @@
/*
*
* 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_WINSOCK_SOCKET
#include "src/core/iomgr/sockaddr_win32.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/useful.h>
#include "src/core/iomgr/tcp_client.h"
#include "src/core/iomgr/tcp_windows.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/alarm.h"
#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/sockaddr_utils.h"
typedef struct {
void(*cb)(void *arg, grpc_endpoint *tcp);
void *cb_arg;
gpr_mu mu;
grpc_winsocket *socket;
gpr_timespec deadline;
grpc_alarm alarm;
int refs;
} async_connect;
static void async_connect_cleanup(async_connect *ac) {
int done = (--ac->refs == 0);
gpr_mu_unlock(&ac->mu);
if (done) {
gpr_mu_destroy(&ac->mu);
gpr_free(ac);
}
}
static void on_alarm(void *acp, int success) {
async_connect *ac = acp;
gpr_mu_lock(&ac->mu);
if (ac->socket != NULL && success) {
grpc_winsocket_shutdown(ac->socket);
}
async_connect_cleanup(ac);
}
static void on_connect(void *acp, int success) {
async_connect *ac = acp;
SOCKET sock = ac->socket->socket;
grpc_endpoint *ep = NULL;
grpc_winsocket_callback_info *info = &ac->socket->write_info;
void(*cb)(void *arg, grpc_endpoint *tcp) = ac->cb;
void *cb_arg = ac->cb_arg;
grpc_alarm_cancel(&ac->alarm);
if (success) {
DWORD transfered_bytes = 0;
DWORD flags;
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
&transfered_bytes, FALSE,
&flags);
GPR_ASSERT(transfered_bytes == 0);
if (!wsa_success) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
gpr_free(utf8_message);
goto finish;
} else {
gpr_log(GPR_DEBUG, "on_connect: connection established");
ep = grpc_tcp_create(ac->socket);
goto finish;
}
} else {
gpr_log(GPR_ERROR, "on_connect is shutting down");
goto finish;
}
abort();
finish:
gpr_mu_lock(&ac->mu);
if (!ep) {
grpc_winsocket_orphan(ac->socket);
}
async_connect_cleanup(ac);
cb(cb_arg, ep);
}
void grpc_tcp_client_connect(void(*cb)(void *arg, grpc_endpoint *tcp),
void *arg, const struct sockaddr *addr,
int addr_len, gpr_timespec deadline) {
SOCKET sock = INVALID_SOCKET;
BOOL success;
int status;
struct sockaddr_in6 addr6_v4mapped;
struct sockaddr_in6 local_address;
async_connect *ac;
grpc_winsocket *socket = NULL;
LPFN_CONNECTEX ConnectEx;
GUID guid = WSAID_CONNECTEX;
DWORD ioctl_num_bytes;
const char *message = NULL;
char *utf8_message;
grpc_winsocket_callback_info *info;
/* Use dualstack sockets where available. */
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
addr = (const struct sockaddr *)&addr6_v4mapped;
addr_len = sizeof(addr6_v4mapped);
}
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
message = "Unable to create socket: %s";
goto failure;
}
if (!grpc_tcp_prepare_socket(sock)) {
message = "Unable to set socket options: %s";
goto failure;
}
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx),
&ioctl_num_bytes, NULL, NULL);
if (status != 0) {
message = "Unable to retreive ConnectEx pointer: %s";
goto failure;
}
grpc_sockaddr_make_wildcard6(0, &local_address);
status = bind(sock, (struct sockaddr *) &local_address,
sizeof(local_address));
if (status != 0) {
message = "Unable to bind socket: %s";
goto failure;
}
socket = grpc_winsocket_create(sock);
info = &socket->write_info;
success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped);
if (success) {
gpr_log(GPR_DEBUG, "connected immediately - but we still go to sleep");
} else {
int error = WSAGetLastError();
if (error != ERROR_IO_PENDING) {
message = "ConnectEx failed: %s";
goto failure;
}
}
gpr_log(GPR_DEBUG, "grpc_tcp_client_connect: connection pending");
ac = gpr_malloc(sizeof(async_connect));
ac->cb = cb;
ac->cb_arg = arg;
ac->socket = socket;
gpr_mu_init(&ac->mu);
ac->refs = 2;
grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now());
grpc_socket_notify_on_write(socket, on_connect, ac);
return;
failure:
utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, message, utf8_message);
gpr_free(utf8_message);
if (socket) {
grpc_winsocket_orphan(socket);
} else if (sock != INVALID_SOCKET) {
closesocket(sock);
}
cb(arg, NULL);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,374 @@
/*
*
* 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_WINSOCK_SOCKET
#define _GNU_SOURCE
#include "src/core/iomgr/sockaddr_utils.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/pollset_windows.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/tcp_server.h"
#include "src/core/iomgr/tcp_windows.h"
#define INIT_PORT_CAP 2
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
static gpr_once s_init_max_accept_queue_size;
static int s_max_accept_queue_size;
/* one listening port */
typedef struct server_port {
gpr_uint8 addresses[sizeof(struct sockaddr_in6) * 2 + 32];
SOCKET new_socket;
grpc_winsocket *socket;
grpc_tcp_server *server;
LPFN_ACCEPTEX AcceptEx;
} server_port;
/* the overall server */
struct grpc_tcp_server {
grpc_tcp_server_cb cb;
void *cb_arg;
gpr_mu mu;
gpr_cv cv;
/* active port count: how many ports are actually still listening */
int active_ports;
/* all listening ports */
server_port *ports;
size_t nports;
size_t port_capacity;
};
grpc_tcp_server *grpc_tcp_server_create(void) {
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
gpr_mu_init(&s->mu);
gpr_cv_init(&s->cv);
s->active_ports = 0;
s->cb = NULL;
s->cb_arg = NULL;
s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP);
s->nports = 0;
s->port_capacity = INIT_PORT_CAP;
return s;
}
void grpc_tcp_server_destroy(grpc_tcp_server *s) {
size_t i;
gpr_mu_lock(&s->mu);
/* shutdown all fd's */
for (i = 0; i < s->nports; i++) {
grpc_winsocket_shutdown(s->ports[i].socket);
}
/* wait while that happens */
while (s->active_ports) {
gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future);
}
gpr_mu_unlock(&s->mu);
/* delete ALL the things */
for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i];
grpc_winsocket_orphan(sp->socket);
}
gpr_free(s->ports);
gpr_free(s);
}
/* Prepare a recently-created socket for listening. */
static int prepare_socket(SOCKET sock,
const struct sockaddr *addr, int addr_len) {
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
if (sock == INVALID_SOCKET) goto error;
if (!grpc_tcp_prepare_socket(sock)) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "Unable to prepare socket: %s", utf8_message);
gpr_free(utf8_message);
goto error;
}
if (bind(sock, addr, addr_len) == SOCKET_ERROR) {
char *addr_str;
char *utf8_message = gpr_format_message(WSAGetLastError());
grpc_sockaddr_to_string(&addr_str, addr, 0);
gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, utf8_message);
gpr_free(utf8_message);
gpr_free(addr_str);
goto error;
}
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "listen: %s", utf8_message);
gpr_free(utf8_message);
goto error;
}
sockname_len = sizeof(sockname_temp);
if (getsockname(sock, (struct sockaddr *) &sockname_temp, &sockname_len)
== SOCKET_ERROR) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "getsockname: %s", utf8_message);
gpr_free(utf8_message);
goto error;
}
return grpc_sockaddr_get_port((struct sockaddr *) &sockname_temp);
error:
if (sock != INVALID_SOCKET) closesocket(sock);
return -1;
}
static void on_accept(void *arg, int success);
static void start_accept(server_port *port) {
SOCKET sock = INVALID_SOCKET;
char *message;
char *utf8_message;
BOOL success;
DWORD addrlen = sizeof(struct sockaddr_in6) + 16;
DWORD bytes_received = 0;
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
message = "Unable to create socket: %s";
goto failure;
}
if (!grpc_tcp_prepare_socket(sock)) {
message = "Unable to prepare socket: %s";
goto failure;
}
success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0,
addrlen, addrlen, &bytes_received,
&port->socket->read_info.overlapped);
if (success) {
gpr_log(GPR_DEBUG, "accepted immediately - but we still go to sleep");
} else {
int error = WSAGetLastError();
if (error != ERROR_IO_PENDING) {
message = "AcceptEx failed: %s";
goto failure;
}
}
port->new_socket = sock;
grpc_socket_notify_on_read(port->socket, on_accept, port);
return;
failure:
utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, message, utf8_message);
gpr_free(utf8_message);
if (sock != INVALID_SOCKET) closesocket(sock);
}
/* event manager callback when reads are ready */
static void on_accept(void *arg, int success) {
server_port *sp = arg;
SOCKET sock = sp->new_socket;
grpc_winsocket_callback_info *info = &sp->socket->read_info;
grpc_endpoint *ep = NULL;
if (success) {
DWORD transfered_bytes = 0;
DWORD flags;
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
&transfered_bytes, FALSE,
&flags);
if (!wsa_success) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message);
gpr_free(utf8_message);
closesocket(sock);
} else {
gpr_log(GPR_DEBUG, "on_accept: accepted connection");
ep = grpc_tcp_create(grpc_winsocket_create(sock));
}
} else {
gpr_log(GPR_DEBUG, "on_accept: shutting down");
closesocket(sock);
gpr_mu_lock(&sp->server->mu);
if (0 == --sp->server->active_ports) {
gpr_cv_broadcast(&sp->server->cv);
}
gpr_mu_unlock(&sp->server->mu);
}
if (ep) sp->server->cb(sp->server->cb_arg, ep);
start_accept(sp);
}
static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
const struct sockaddr *addr, int addr_len) {
server_port *sp;
int port;
int status;
GUID guid = WSAID_ACCEPTEX;
DWORD ioctl_num_bytes;
LPFN_ACCEPTEX AcceptEx;
if (sock == INVALID_SOCKET) return -1;
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid), &AcceptEx, sizeof(AcceptEx),
&ioctl_num_bytes, NULL, NULL);
if (status != 0) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
gpr_free(utf8_message);
closesocket(sock);
return -1;
}
port = prepare_socket(sock, addr, addr_len);
if (port >= 0) {
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->cb && "must add ports before starting server");
/* append it to the list under a lock */
if (s->nports == s->port_capacity) {
s->port_capacity *= 2;
s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity);
}
sp = &s->ports[s->nports++];
sp->server = s;
sp->socket = grpc_winsocket_create(sock);
sp->AcceptEx = AcceptEx;
GPR_ASSERT(sp->socket);
gpr_mu_unlock(&s->mu);
}
return port;
}
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
int addr_len) {
int allocated_port = -1;
unsigned i;
SOCKET sock;
struct sockaddr_in6 addr6_v4mapped;
struct sockaddr_in6 wildcard;
struct sockaddr *allocated_addr = NULL;
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
int port;
/* Check if this is a wildcard port, and if so, try to keep the port the same
as some previously created listener. */
if (grpc_sockaddr_get_port(addr) == 0) {
for (i = 0; i < s->nports; i++) {
sockname_len = sizeof(sockname_temp);
if (0 == getsockname(s->ports[i].socket->socket,
(struct sockaddr *) &sockname_temp,
&sockname_len)) {
port = grpc_sockaddr_get_port((struct sockaddr *) &sockname_temp);
if (port > 0) {
allocated_addr = malloc(addr_len);
memcpy(allocated_addr, addr, addr_len);
grpc_sockaddr_set_port(allocated_addr, port);
addr = allocated_addr;
break;
}
}
}
}
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
addr = (const struct sockaddr *)&addr6_v4mapped;
addr_len = sizeof(addr6_v4mapped);
}
/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
if (grpc_sockaddr_is_wildcard(addr, &port)) {
grpc_sockaddr_make_wildcard6(port, &wildcard);
addr = (struct sockaddr *) &wildcard;
addr_len = sizeof(wildcard);
}
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "unable to create socket: %s", utf8_message);
gpr_free(utf8_message);
}
allocated_port = add_socket_to_server(s, sock, addr, addr_len);
gpr_free(allocated_addr);
return allocated_port;
}
SOCKET grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) {
return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET;
}
void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset,
grpc_tcp_server_cb cb, void *cb_arg) {
size_t i;
GPR_ASSERT(cb);
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->cb);
GPR_ASSERT(s->active_ports == 0);
s->cb = cb;
s->cb_arg = cb_arg;
for (i = 0; i < s->nports; i++) {
start_accept(s->ports + i);
s->active_ports++;
}
gpr_mu_unlock(&s->mu);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,373 @@
/*
*
* 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_WINSOCK_SOCKET
#include "src/core/iomgr/sockaddr_win32.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/useful.h>
#include "src/core/iomgr/alarm.h"
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/sockaddr_utils.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/tcp_client.h"
static int set_non_block(SOCKET sock) {
int status;
unsigned long param = 1;
DWORD ret;
status = WSAIoctl(sock, FIONBIO, &param, sizeof(param), NULL, 0, &ret,
NULL, NULL);
return status == 0;
}
static int set_dualstack(SOCKET sock) {
int status;
unsigned long param = 0;
status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(const char *) &param, sizeof(param));
return status == 0;
}
int grpc_tcp_prepare_socket(SOCKET sock) {
if (!set_non_block(sock))
return 0;
if (!set_dualstack(sock))
return 0;
return 1;
}
typedef struct grpc_tcp {
grpc_endpoint base;
grpc_winsocket *socket;
gpr_refcount refcount;
grpc_endpoint_read_cb read_cb;
void *read_user_data;
gpr_slice read_slice;
int outstanding_read;
grpc_endpoint_write_cb write_cb;
void *write_user_data;
gpr_slice_buffer write_slices;
int outstanding_write;
} grpc_tcp;
static void tcp_ref(grpc_tcp *tcp) {
gpr_log(GPR_DEBUG, "tcp_ref");
gpr_ref(&tcp->refcount);
}
static void tcp_unref(grpc_tcp *tcp) {
gpr_log(GPR_DEBUG, "tcp_unref");
if (gpr_unref(&tcp->refcount)) {
gpr_log(GPR_DEBUG, "tcp_unref: destroying");
gpr_slice_buffer_destroy(&tcp->write_slices);
grpc_winsocket_orphan(tcp->socket);
gpr_free(tcp);
}
}
static void on_read(void *tcpp, int success) {
grpc_tcp *tcp = (grpc_tcp *) tcpp;
grpc_winsocket *socket = tcp->socket;
gpr_slice sub;
gpr_slice *slice = NULL;
size_t nslices = 0;
grpc_endpoint_cb_status status;
grpc_endpoint_read_cb cb = tcp->read_cb;
grpc_winsocket_callback_info *info = &socket->read_info;
void *opaque = tcp->read_user_data;
GPR_ASSERT(tcp->outstanding_read);
if (!success) {
tcp_unref(tcp);
cb(opaque, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN);
return;
}
gpr_log(GPR_DEBUG, "on_read");
tcp->outstanding_read = 0;
if (socket->read_info.wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error);
__debugbreak();
gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
gpr_free(utf8_message);
status = GRPC_ENDPOINT_CB_ERROR;
} else {
if (info->bytes_transfered != 0) {
sub = gpr_slice_sub(tcp->read_slice, 0, info->bytes_transfered);
gpr_log(GPR_DEBUG, "on_read: calling callback");
status = GRPC_ENDPOINT_CB_OK;
slice = &sub;
nslices = 1;
} else {
gpr_log(GPR_DEBUG, "on_read: closed socket");
gpr_slice_unref(tcp->read_slice);
status = GRPC_ENDPOINT_CB_EOF;
}
}
tcp_unref(tcp);
cb(opaque, slice, nslices, status);
}
static void win_notify_on_read(grpc_endpoint *ep,
grpc_endpoint_read_cb cb, void *arg) {
grpc_tcp *tcp = (grpc_tcp *) ep;
grpc_winsocket *handle = tcp->socket;
grpc_winsocket_callback_info *info = &handle->read_info;
int status;
DWORD bytes_read = 0;
DWORD flags = 0;
int error;
WSABUF buffer;
GPR_ASSERT(!tcp->outstanding_read);
tcp_ref(tcp);
tcp->outstanding_read = 1;
tcp->read_cb = cb;
tcp->read_user_data = arg;
tcp->read_slice = gpr_slice_malloc(8192);
buffer.len = GPR_SLICE_LENGTH(tcp->read_slice);
buffer.buf = GPR_SLICE_START_PTR(tcp->read_slice);
gpr_log(GPR_DEBUG, "win_notify_on_read: calling WSARecv without overlap");
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
NULL, NULL);
info->wsa_error = status == 0 ? 0 : WSAGetLastError();
if (info->wsa_error != WSAEWOULDBLOCK) {
gpr_log(GPR_DEBUG, "got response immediately, calling on_read");
info->bytes_transfered = bytes_read;
/* This might heavily recurse. */
on_read(tcp, 1);
return;
}
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSARecv with overlap");
memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED));
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
&info->overlapped, NULL);
if (status == 0) {
gpr_log(GPR_DEBUG, "got response immediately, but we're going to sleep");
grpc_socket_notify_on_read(tcp->socket, on_read, tcp);
return;
}
error = WSAGetLastError();
if (error != WSA_IO_PENDING) {
char *utf8_message = gpr_format_message(WSAGetLastError());
__debugbreak();
gpr_log(GPR_ERROR, "WSARecv error: %s", utf8_message);
gpr_free(utf8_message);
/* would the IO completion port be called anyway... ? Let's assume not. */
tcp->outstanding_read = 0;
tcp_unref(tcp);
cb(arg, NULL, 0, GRPC_ENDPOINT_CB_ERROR);
return;
}
gpr_log(GPR_DEBUG, "waiting on the IO completion port now");
grpc_socket_notify_on_read(tcp->socket, on_read, tcp);
}
static void on_write(void *tcpp, int success) {
grpc_tcp *tcp = (grpc_tcp *) tcpp;
grpc_winsocket *handle = tcp->socket;
grpc_winsocket_callback_info *info = &handle->write_info;
grpc_endpoint_cb_status status = GRPC_ENDPOINT_CB_OK;
grpc_endpoint_write_cb cb = tcp->write_cb;
void *opaque = tcp->write_user_data;
GPR_ASSERT(tcp->outstanding_write);
gpr_log(GPR_DEBUG, "on_write");
if (!success) {
tcp_unref(tcp);
cb(opaque, GRPC_ENDPOINT_CB_SHUTDOWN);
return;
}
if (info->wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
gpr_free(utf8_message);
status = GRPC_ENDPOINT_CB_ERROR;
} else {
GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length);
}
gpr_slice_buffer_reset_and_unref(&tcp->write_slices);
tcp->outstanding_write = 0;
tcp_unref(tcp);
cb(opaque, status);
}
static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
gpr_slice *slices, size_t nslices,
grpc_endpoint_write_cb cb,
void *arg) {
grpc_tcp *tcp = (grpc_tcp *) ep;
grpc_winsocket *socket = tcp->socket;
grpc_winsocket_callback_info *info = &socket->write_info;
unsigned i;
DWORD bytes_sent;
int status;
WSABUF local_buffers[16];
WSABUF *allocated = NULL;
WSABUF *buffers = local_buffers;
GPR_ASSERT(nslices != 0);
GPR_ASSERT(GPR_SLICE_LENGTH(slices[0]) != 0);
GPR_ASSERT(!tcp->outstanding_write);
tcp_ref(tcp);
gpr_log(GPR_DEBUG, "win_write");
tcp->outstanding_write = 1;
tcp->write_cb = cb;
tcp->write_user_data = arg;
gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices);
if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) {
buffers = (WSABUF *) gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count);
allocated = buffers;
}
for (i = 0; i < tcp->write_slices.count; i++) {
buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]);
buffers[i].buf = GPR_SLICE_START_PTR(tcp->write_slices.slices[i]);
}
gpr_log(GPR_DEBUG, "win_write: calling WSASend without overlap");
status = WSASend(socket->socket, buffers, tcp->write_slices.count,
&bytes_sent, 0, NULL, NULL);
info->wsa_error = status == 0 ? 0 : WSAGetLastError();
if (info->wsa_error != WSAEWOULDBLOCK) {
grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR;
gpr_log(GPR_DEBUG, "got response immediately, cleaning up and leaving");
if (status == 0) {
ret = GRPC_ENDPOINT_WRITE_DONE;
GPR_ASSERT(bytes_sent == tcp->write_slices.length);
} else {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
gpr_free(utf8_message);
}
if (allocated) gpr_free(allocated);
gpr_slice_buffer_reset_and_unref(&tcp->write_slices);
tcp->outstanding_write = 0;
tcp_unref(tcp);
return ret;
}
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSASend with overlap");
memset(&socket->write_info, 0, sizeof(OVERLAPPED));
status = WSASend(socket->socket, buffers, tcp->write_slices.count,
&bytes_sent, 0, &socket->write_info.overlapped, NULL);
if (allocated) gpr_free(allocated);
if (status != 0) {
int error = WSAGetLastError();
if (error != WSA_IO_PENDING) {
char *utf8_message = gpr_format_message(WSAGetLastError());
__debugbreak();
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
gpr_free(utf8_message);
/* would the IO completion port be called anyway ? Let's assume not. */
tcp->outstanding_write = 0;
tcp_unref(tcp);
return GRPC_ENDPOINT_WRITE_ERROR;
}
gpr_log(GPR_DEBUG, "win_write: got pending op");
} else {
gpr_log(GPR_DEBUG, "wrote data immediately - but we're going to sleep");
}
grpc_socket_notify_on_write(socket, on_write, tcp);
return GRPC_ENDPOINT_WRITE_PENDING;
}
static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) {
grpc_tcp *tcp = (grpc_tcp *) ep;
gpr_log(GPR_DEBUG, "win_add_to_pollset");
grpc_iocp_add_socket(tcp->socket);
}
static void win_shutdown(grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *) ep;
gpr_log(GPR_DEBUG, "win_shutdown");
grpc_winsocket_shutdown(tcp->socket);
}
static void win_destroy(grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *) ep;
gpr_log(GPR_DEBUG, "win_destroy");
tcp_unref(tcp);
}
static grpc_endpoint_vtable vtable = {
win_notify_on_read, win_write, win_add_to_pollset, win_shutdown, win_destroy
};
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket) {
grpc_tcp *tcp = (grpc_tcp *) gpr_malloc(sizeof(grpc_tcp));
memset(tcp, 0, sizeof(grpc_tcp));
tcp->base.vtable = &vtable;
tcp->socket = socket;
gpr_slice_buffer_init(&tcp->write_slices);
gpr_ref_init(&tcp->refcount, 1);
return &tcp->base;
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,57 @@
/*
*
* 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_IOMGR_TCP_WINDOWS_H__
#define __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__
/*
Low level TCP "bottom half" implementation, for use by transports built on
top of a TCP connection.
Note that this file does not (yet) include APIs for creating the socket in
the first place.
All calls passing slice transfer ownership of a slice refcount unless
otherwise specified.
*/
#include "src/core/iomgr/endpoint.h"
#include "src/core/iomgr/socket_windows.h"
/* Create a tcp endpoint given a winsock handle.
* Takes ownership of the handle.
*/
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket);
int grpc_tcp_prepare_socket(SOCKET sock);
#endif /* __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ */

@ -35,22 +35,49 @@
#include <string.h>
#include "src/core/security/security_context.h"
#include "src/core/security/credentials.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/support/string.h"
#include "src/core/channel/channel_stack.h"
#include "src/core/security/security_context.h"
#include "src/core/security/credentials.h"
#include "src/core/surface/call.h"
/* We can have a per-call credentials. */
typedef struct {
grpc_credentials *creds;
grpc_mdstr *host;
grpc_call_op op;
} call_data;
/* We can have a per-channel credentials. */
typedef struct {
grpc_channel_security_context *security_context;
grpc_mdctx *md_ctx;
grpc_mdstr *authority_string;
grpc_mdstr *error_msg_key;
} channel_data;
static void do_nothing(void *ignored, grpc_op_error error) {}
static void bubbleup_error(grpc_call_element *elem, const char *error_msg) {
grpc_call_op finish_op;
channel_data *channeld = elem->channel_data;
gpr_log(GPR_ERROR, "%s", error_msg);
finish_op.type = GRPC_RECV_METADATA;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.data.metadata = grpc_mdelem_from_metadata_strings(
channeld->md_ctx, channeld->error_msg_key,
grpc_mdstr_from_string(channeld->md_ctx, error_msg));
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
grpc_call_element_send_cancel(elem);
}
static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
size_t num_md,
grpc_credentials_status status) {
@ -62,6 +89,46 @@ static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
grpc_call_next_op(elem, &((call_data *)elem->call_data)->op);
}
static void send_security_metadata(grpc_call_element *elem, grpc_call_op *op) {
/* grab pointers to our data from the call element */
call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data;
grpc_credentials *channel_creds =
channeld->security_context->request_metadata_creds;
/* TODO(jboeuf):
Decide on the policy in this case:
- populate both channel and call?
- the call takes precedence over the channel?
- leave this decision up to the channel credentials? */
if (calld->creds != NULL) {
gpr_log(GPR_ERROR, "Ignoring per call credentials for now.");
}
if (channel_creds != NULL &&
grpc_credentials_has_request_metadata(channel_creds)) {
calld->op = *op; /* Copy op (originates from the caller's stack). */
grpc_credentials_get_request_metadata(channel_creds,
on_credentials_metadata, elem);
} else {
grpc_call_next_op(elem, op);
}
}
static void on_host_checked(void *user_data, grpc_security_status status) {
grpc_call_element *elem = (grpc_call_element *)user_data;
call_data *calld = elem->call_data;
if (status == GRPC_SECURITY_OK) {
send_security_metadata(elem, &calld->op);
} else {
char *error_msg;
gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
grpc_mdstr_as_c_string(calld->host));
bubbleup_error(elem, error_msg);
gpr_free(error_msg);
}
}
/* Called either:
- in response to an API call (or similar) from above, to send something
- a network event (or similar) from below, to receive something
@ -74,26 +141,36 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
channel_data *channeld = elem->channel_data;
switch (op->type) {
case GRPC_SEND_START: {
grpc_credentials *channel_creds =
channeld->security_context->request_metadata_creds;
/* TODO(jboeuf):
Decide on the policy in this case:
- populate both channel and call?
- the call takes precedence over the channel?
- leave this decision up to the channel credentials? */
if (calld->creds != NULL) {
gpr_log(GPR_ERROR, "Ignoring per call credentials for now.");
case GRPC_SEND_METADATA:
/* Pointer comparison is OK for md_elems created from the same context. */
if (op->data.metadata->key == channeld->authority_string) {
if (calld->host != NULL) grpc_mdstr_unref(calld->host);
calld->host = grpc_mdstr_ref(op->data.metadata->value);
}
if (channel_creds != NULL &&
grpc_credentials_has_request_metadata(channel_creds)) {
grpc_call_next_op(elem, op);
break;
case GRPC_SEND_START:
if (calld->host != NULL) {
grpc_security_status status;
const char *call_host = grpc_mdstr_as_c_string(calld->host);
calld->op = *op; /* Copy op (originates from the caller's stack). */
grpc_credentials_get_request_metadata(channel_creds,
on_credentials_metadata, elem);
break;
status = grpc_channel_security_context_check_call_host(
channeld->security_context, call_host, on_host_checked, elem);
if (status != GRPC_SECURITY_OK) {
if (status == GRPC_SECURITY_ERROR) {
char *error_msg;
gpr_asprintf(&error_msg,
"Invalid host %s set in :authority metadata.",
call_host);
bubbleup_error(elem, error_msg);
gpr_free(error_msg);
}
break;
}
}
/* FALLTHROUGH INTENDED. */
}
send_security_metadata(elem, op);
break;
default:
/* pass control up or down the stack depending on op->dir */
@ -116,6 +193,7 @@ static void init_call_elem(grpc_call_element *elem,
Find a way to pass-in the credentials from the caller here. */
call_data *calld = elem->call_data;
calld->creds = NULL;
calld->host = NULL;
}
/* Destructor for call_data */
@ -124,6 +202,9 @@ static void destroy_call_elem(grpc_call_element *elem) {
if (calld->creds != NULL) {
grpc_credentials_unref(calld->creds);
}
if (calld->host != NULL) {
grpc_mdstr_unref(calld->host);
}
}
/* Constructor for channel_data */
@ -146,6 +227,11 @@ static void init_channel_elem(grpc_channel_element *elem,
GPR_ASSERT(ctx->is_client_side);
channeld->security_context =
(grpc_channel_security_context *)grpc_security_context_ref(ctx);
channeld->md_ctx = metadata_context;
channeld->authority_string =
grpc_mdstr_from_string(channeld->md_ctx, ":authority");
channeld->error_msg_key =
grpc_mdstr_from_string(channeld->md_ctx, "grpc-message");
}
/* Destructor for channel data */
@ -154,6 +240,12 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
channel_data *channeld = elem->channel_data;
grpc_channel_security_context *ctx = channeld->security_context;
if (ctx != NULL) grpc_security_context_unref(&ctx->base);
if (channeld->authority_string != NULL) {
grpc_mdstr_unref(channeld->authority_string);
}
if (channeld->error_msg_key != NULL) {
grpc_mdstr_unref(channeld->error_msg_key);
}
}
const grpc_channel_filter grpc_client_auth_filter = {

@ -113,8 +113,7 @@ static void check_peer(grpc_secure_transport_setup *s) {
return;
}
peer_status =
grpc_security_context_check_peer(s->ctx, &peer, on_peer_checked, s);
tsi_peer_destruct(&peer);
grpc_security_context_check_peer(s->ctx, peer, on_peer_checked, s);
if (peer_status == GRPC_SECURITY_ERROR) {
gpr_log(GPR_ERROR, "Peer check failed.");
secure_transport_setup_done(s, 0);

@ -69,12 +69,22 @@ grpc_security_status grpc_security_context_create_handshaker(
}
grpc_security_status grpc_security_context_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
if (ctx == NULL) return GRPC_SECURITY_ERROR;
grpc_security_context *ctx, tsi_peer peer, grpc_security_check_cb cb,
void *user_data) {
if (ctx == NULL) {
tsi_peer_destruct(&peer);
return GRPC_SECURITY_ERROR;
}
return ctx->vtable->check_peer(ctx, peer, cb, user_data);
}
grpc_security_status grpc_channel_security_context_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data) {
if (ctx == NULL || ctx->check_call_host == NULL) return GRPC_SECURITY_ERROR;
return ctx->check_call_host(ctx, host, cb, user_data);
}
void grpc_security_context_unref(grpc_security_context *ctx) {
if (ctx == NULL) return;
if (gpr_unref(&ctx->refcount)) ctx->vtable->destroy(ctx);
@ -137,6 +147,11 @@ static int check_request_metadata_creds(grpc_credentials *creds) {
/* -- Fake implementation. -- */
typedef struct {
grpc_channel_security_context base;
int call_host_check_is_async;
} grpc_fake_channel_security_context;
static void fake_channel_destroy(grpc_security_context *ctx) {
grpc_channel_security_context *c = (grpc_channel_security_context *)ctx;
grpc_credentials_unref(c->request_metadata_creds);
@ -158,31 +173,51 @@ static grpc_security_status fake_server_create_handshaker(
}
static grpc_security_status fake_check_peer(grpc_security_context *ctx,
const tsi_peer *peer,
grpc_security_check_peer_cb cb,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
const char *prop_name;
if (peer->property_count != 1) {
grpc_security_status status = GRPC_SECURITY_OK;
if (peer.property_count != 1) {
gpr_log(GPR_ERROR, "Fake peers should only have 1 property.");
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
prop_name = peer->properties[0].name;
prop_name = peer.properties[0].name;
if (prop_name == NULL ||
strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) {
gpr_log(GPR_ERROR, "Unexpected property in fake peer: %s.",
prop_name == NULL ? "<EMPTY>" : prop_name);
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
if (peer->properties[0].type != TSI_PEER_PROPERTY_TYPE_STRING) {
if (peer.properties[0].type != TSI_PEER_PROPERTY_TYPE_STRING) {
gpr_log(GPR_ERROR, "Invalid type of cert type property.");
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
if (strncmp(peer->properties[0].value.string.data, TSI_FAKE_CERTIFICATE_TYPE,
peer->properties[0].value.string.length)) {
if (strncmp(peer.properties[0].value.string.data, TSI_FAKE_CERTIFICATE_TYPE,
peer.properties[0].value.string.length)) {
gpr_log(GPR_ERROR, "Invalid value for cert type property.");
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
end:
tsi_peer_destruct(&peer);
return status;
}
static grpc_security_status fake_channel_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data) {
grpc_fake_channel_security_context *c =
(grpc_fake_channel_security_context *)ctx;
if (c->call_host_check_is_async) {
cb(user_data, GRPC_SECURITY_OK);
return GRPC_SECURITY_PENDING;
} else {
return GRPC_SECURITY_OK;
}
return GRPC_SECURITY_OK;
}
static grpc_security_context_vtable fake_channel_vtable = {
@ -192,15 +227,17 @@ static grpc_security_context_vtable fake_server_vtable = {
fake_server_destroy, fake_server_create_handshaker, fake_check_peer};
grpc_channel_security_context *grpc_fake_channel_security_context_create(
grpc_credentials *request_metadata_creds) {
grpc_channel_security_context *c =
gpr_malloc(sizeof(grpc_channel_security_context));
gpr_ref_init(&c->base.refcount, 1);
c->base.is_client_side = 1;
c->base.vtable = &fake_channel_vtable;
grpc_credentials *request_metadata_creds, int call_host_check_is_async) {
grpc_fake_channel_security_context *c =
gpr_malloc(sizeof(grpc_fake_channel_security_context));
gpr_ref_init(&c->base.base.refcount, 1);
c->base.base.is_client_side = 1;
c->base.base.vtable = &fake_channel_vtable;
GPR_ASSERT(check_request_metadata_creds(request_metadata_creds));
c->request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
return c;
c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
c->base.check_call_host = fake_channel_check_call_host;
c->call_host_check_is_async = call_host_check_is_async;
return &c->base;
}
grpc_security_context *grpc_fake_server_security_context_create(void) {
@ -215,7 +252,9 @@ grpc_security_context *grpc_fake_server_security_context_create(void) {
typedef struct {
grpc_channel_security_context base;
tsi_ssl_handshaker_factory *handshaker_factory;
char *secure_peer_name;
char *target_name;
char *overridden_target_name;
tsi_peer peer;
} grpc_ssl_channel_security_context;
typedef struct {
@ -230,7 +269,9 @@ static void ssl_channel_destroy(grpc_security_context *ctx) {
if (c->handshaker_factory != NULL) {
tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
}
if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name);
if (c->target_name != NULL) gpr_free(c->target_name);
if (c->overridden_target_name != NULL) gpr_free(c->overridden_target_name);
tsi_peer_destruct(&c->peer);
gpr_free(ctx);
}
@ -244,11 +285,11 @@ static void ssl_server_destroy(grpc_security_context *ctx) {
static grpc_security_status ssl_create_handshaker(
tsi_ssl_handshaker_factory *handshaker_factory, int is_client,
const char *secure_peer_name, tsi_handshaker **handshaker) {
const char *peer_name, tsi_handshaker **handshaker) {
tsi_result result = TSI_OK;
if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR;
result = tsi_ssl_handshaker_factory_create_handshaker(
handshaker_factory, is_client ? secure_peer_name : NULL, handshaker);
handshaker_factory, is_client ? peer_name : NULL, handshaker);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
tsi_result_to_string(result));
@ -261,7 +302,10 @@ static grpc_security_status ssl_channel_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
return ssl_create_handshaker(c->handshaker_factory, 1, c->secure_peer_name,
return ssl_create_handshaker(c->handshaker_factory, 1,
c->overridden_target_name != NULL
? c->overridden_target_name
: c->target_name,
handshaker);
}
@ -271,7 +315,7 @@ static grpc_security_status ssl_server_create_handshaker(
return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker);
}
static grpc_security_status ssl_check_peer(const char *secure_peer_name,
static grpc_security_status ssl_check_peer(const char *peer_name,
const tsi_peer *peer) {
/* Check the ALPN. */
const tsi_peer_property *p =
@ -291,28 +335,54 @@ static grpc_security_status ssl_check_peer(const char *secure_peer_name,
}
/* Check the peer name if specified. */
if (secure_peer_name != NULL &&
!tsi_ssl_peer_matches_name(peer, secure_peer_name)) {
gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate",
secure_peer_name);
if (peer_name != NULL &&
!tsi_ssl_peer_matches_name(peer, peer_name)) {
gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", peer_name);
return GRPC_SECURITY_ERROR;
}
return GRPC_SECURITY_OK;
}
static grpc_security_status ssl_channel_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
static grpc_security_status ssl_channel_check_peer(grpc_security_context *ctx,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
return ssl_check_peer(c->secure_peer_name, peer);
grpc_security_status status = ssl_check_peer(c->overridden_target_name != NULL
? c->overridden_target_name
: c->target_name,
&peer);
c->peer = peer;
return status;
}
static grpc_security_status ssl_server_check_peer(grpc_security_context *ctx,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
/* TODO(jboeuf): Find a way to expose the peer to the authorization layer. */
grpc_security_status status = ssl_check_peer(NULL, &peer);
tsi_peer_destruct(&peer);
return status;
}
static grpc_security_status ssl_server_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
/* TODO(jboeuf): Find a way to expose the peer to the authorization layer. */
return ssl_check_peer(NULL, peer);
static grpc_security_status ssl_channel_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
if (tsi_ssl_peer_matches_name(&c->peer, host)) return GRPC_SECURITY_OK;
/* If the target name was overridden, then the original target_name was
'checked' transitively during the previous peer check at the end of the
handshake. */
if (c->overridden_target_name != NULL && !strcmp(host, c->target_name)) {
return GRPC_SECURITY_OK;
} else {
return GRPC_SECURITY_ERROR;
}
}
static grpc_security_context_vtable ssl_channel_vtable = {
@ -345,7 +415,8 @@ static size_t get_default_pem_roots(const unsigned char **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) {
const char *target_name, const char *overridden_target_name,
grpc_channel_security_context **ctx) {
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const unsigned char **alpn_protocol_strings =
gpr_malloc(sizeof(const char *) * num_alpn_protocols);
@ -364,8 +435,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) {
gpr_log(GPR_ERROR, "An ssl channel needs a config and a secure name.");
if (config == NULL || target_name == NULL) {
gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name.");
goto error;
}
if (!check_request_metadata_creds(request_metadata_creds)) {
@ -379,8 +450,12 @@ grpc_security_status grpc_ssl_channel_security_context_create(
c->base.base.vtable = &ssl_channel_vtable;
c->base.base.is_client_side = 1;
c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
if (secure_peer_name != NULL) {
c->secure_peer_name = gpr_strdup(secure_peer_name);
c->base.check_call_host = ssl_channel_check_call_host;
if (target_name != NULL) {
c->target_name = gpr_strdup(target_name);
}
if (overridden_target_name != NULL) {
c->overridden_target_name = gpr_strdup(overridden_target_name);
}
if (config->pem_root_certs == NULL) {
pem_root_certs_size = get_default_pem_roots(&pem_root_certs);
@ -478,7 +553,7 @@ grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds,
grpc_channel *channel = NULL;
grpc_security_status status = GRPC_SECURITY_OK;
size_t i = 0;
const char *secure_peer_name = target;
const char *overridden_target_name = NULL;
grpc_arg arg;
grpc_channel_args *new_args;
@ -486,13 +561,13 @@ grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds,
grpc_arg *arg = &args->args[i];
if (!strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) &&
arg->type == GRPC_ARG_STRING) {
secure_peer_name = arg->value.string;
overridden_target_name = arg->value.string;
break;
}
}
status = grpc_ssl_channel_security_context_create(
request_metadata_creds, grpc_ssl_credentials_get_config(ssl_creds),
secure_peer_name, &ctx);
target, overridden_target_name, &ctx);
if (status != GRPC_SECURITY_OK) {
return grpc_lame_client_channel_create();
}
@ -510,7 +585,7 @@ grpc_channel *grpc_fake_transport_security_channel_create(
grpc_credentials *fake_creds, grpc_credentials *request_metadata_creds,
const char *target, const grpc_channel_args *args) {
grpc_channel_security_context *ctx =
grpc_fake_channel_security_context_create(request_metadata_creds);
grpc_fake_channel_security_context_create(request_metadata_creds, 1);
grpc_channel *channel =
grpc_secure_channel_create_internal(target, args, ctx);
grpc_security_context_unref(&ctx->base);

@ -56,16 +56,15 @@ typedef struct grpc_security_context grpc_security_context;
#define GRPC_SECURITY_CONTEXT_ARG "grpc.security_context"
typedef void (*grpc_security_check_peer_cb)(void *user_data,
grpc_security_status status);
typedef void (*grpc_security_check_cb)(void *user_data,
grpc_security_status status);
typedef struct {
void (*destroy)(grpc_security_context *ctx);
grpc_security_status (*create_handshaker)(grpc_security_context *ctx,
tsi_handshaker **handshaker);
grpc_security_status (*check_peer)(grpc_security_context *ctx,
const tsi_peer *peer,
grpc_security_check_peer_cb,
grpc_security_status (*check_peer)(grpc_security_context *ctx, tsi_peer peer,
grpc_security_check_cb cb,
void *user_data);
} grpc_security_context_vtable;
@ -87,18 +86,14 @@ grpc_security_status grpc_security_context_create_handshaker(
/* Check the peer.
Implementations can choose to check the peer either synchronously or
asynchronously. In the first case, a successful will return
asynchronously. In the first case, a successful call will return
GRPC_SECURITY_OK. In the asynchronous case, the call will return
GRPC_SECURITY_PENDING unless an error is detected early on.
Note:
Asynchronous implementations of this interface should make a copy of the
fields of the peer they want to check as there is no guarantee on the
lifetime of the peer object beyond this call.
Ownership of the peer is transfered.
*/
grpc_security_status grpc_security_context_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data);
grpc_security_context *ctx, tsi_peer peer,
grpc_security_check_cb cb, void *user_data);
/* Util to encapsulate the context in a channel arg. */
grpc_arg grpc_security_context_to_arg(grpc_security_context *ctx);
@ -120,14 +115,26 @@ typedef struct grpc_channel_security_context grpc_channel_security_context;
struct grpc_channel_security_context {
grpc_security_context base; /* requires is_client_side to be non 0. */
grpc_credentials *request_metadata_creds;
grpc_security_status (*check_call_host)(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data);
};
/* Checks that the host that will be set for a call is acceptable.
Implementations can choose do the check either synchronously or
asynchronously. In the first case, a successful call will return
GRPC_SECURITY_OK. In the asynchronous case, the call will return
GRPC_SECURITY_PENDING unless an error is detected early on. */
grpc_security_status grpc_channel_security_context_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data);
/* --- Creation security contexts. --- */
/* For TESTING ONLY!
Creates a fake context that emulates real channel security. */
grpc_channel_security_context *grpc_fake_channel_security_context_create(
grpc_credentials *request_metadata_creds);
grpc_credentials *request_metadata_creds, int call_host_check_is_async);
/* For TESTING ONLY!
Creates a fake context that emulates real server security. */
@ -148,7 +155,8 @@ grpc_security_context *grpc_fake_server_security_context_create(void);
*/
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);
const char *target_name, const char *overridden_target_name,
grpc_channel_security_context **ctx);
/* Creates an SSL server_security_context.
- config is the SSL config to be used for the SSL channel establishment.

@ -0,0 +1,54 @@
/*
*
* 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/cpu.h"
#include <grpc/support/log.h>
unsigned gpr_cpu_num_cores(void) {
/* TODO(jtattermusch): implement */
gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1");
return 1;
}
unsigned gpr_cpu_current_cpu(void) {
/* TODO(jtattermusch): implement */
gpr_log(GPR_ERROR, "Cannot determine current CPU");
return 0;
}
#endif /* GPR_WIN32 */

@ -76,7 +76,7 @@ end:
*tmp_filename_out = gpr_tchar_to_char(tmp_filename);
}
gpr_free(tmp_filename);
gpr_free(template_string);
return result;
}

@ -35,11 +35,16 @@
#ifdef GPR_WIN32
#include <grpc/support/log.h>
#include <grpc/support/alloc.h>
#include <stdio.h>
#include <stdarg.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/support/string_win32.h"
void gpr_log(const char *file, int line, gpr_log_severity severity,
const char *format, ...) {
char *message = NULL;
@ -74,8 +79,35 @@ void gpr_log(const char *file, int line, gpr_log_severity severity,
/* Simple starter implementation */
void gpr_default_log(gpr_log_func_args *args) {
fprintf(stderr, "%s %s:%d: %s\n", gpr_log_severity_string(args->severity),
char time_buffer[64];
gpr_timespec now = gpr_now();
struct tm tm;
if (localtime_s(&tm, &now.tv_sec)) {
strcpy(time_buffer, "error:localtime");
} else if (0 ==
strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) {
strcpy(time_buffer, "error:strftime");
}
fprintf(stderr, "%s%s.%09u %5u %s:%d: %s\n",
gpr_log_severity_string(args->severity), time_buffer,
(int)(now.tv_nsec), GetCurrentThreadId(),
args->file, args->line, args->message);
}
#endif
char *gpr_format_message(DWORD messageid) {
LPTSTR tmessage;
char *message;
DWORD status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, messageid,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)(&tmessage), 0, NULL);
message = gpr_tchar_to_char(tmessage);
LocalFree(tmessage);
return message;
}
#endif /* GPR_WIN32 */

@ -61,6 +61,7 @@ grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) {
}
void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) {
if (!bb) return;
switch (bb->type) {
case GRPC_BB_SLICE_BUFFER:
gpr_slice_buffer_destroy(&bb->data.slice_buffer);

@ -65,6 +65,13 @@ void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) {
bba_push(&q->filling, buffer);
}
void grpc_bbq_flush(grpc_byte_buffer_queue *q) {
grpc_byte_buffer *bb;
while ((bb = grpc_bbq_pop(q))) {
grpc_byte_buffer_destroy(bb);
}
}
grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) {
grpc_bbq_array temp_array;

@ -53,6 +53,7 @@ typedef struct {
void grpc_bbq_destroy(grpc_byte_buffer_queue *q);
grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q);
void grpc_bbq_flush(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);

@ -225,7 +225,7 @@ static void do_nothing(void *ignored, grpc_op_error also_ignored) {}
static send_action choose_send_action(grpc_call *call);
static void enact_send_action(grpc_call *call, send_action sa);
grpc_call *grpc_call_create(grpc_channel *channel,
grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
const void *server_transport_data) {
size_t i;
grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel);
@ -234,6 +234,7 @@ grpc_call *grpc_call_create(grpc_channel *channel,
memset(call, 0, sizeof(grpc_call));
gpr_mu_init(&call->mu);
call->channel = channel;
call->cq = cq;
call->is_client = server_transport_data == NULL;
for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) {
call->request_set[i] = REQSET_EMPTY;
@ -252,6 +253,11 @@ grpc_call *grpc_call_create(grpc_channel *channel,
return call;
}
void grpc_call_set_completion_queue(grpc_call *call,
grpc_completion_queue *cq) {
call->cq = cq;
}
void grpc_call_internal_ref(grpc_call *c) { gpr_ref(&c->internal_refcount); }
static void destroy_call(void *call, int ignored_success) {
@ -291,8 +297,21 @@ void grpc_call_internal_unref(grpc_call *c, int allow_immediate_deletion) {
static void set_status_code(grpc_call *call, status_source source,
gpr_uint32 status) {
int flush;
call->status[source].is_set = 1;
call->status[source].code = status;
if (call->is_client) {
flush = status == GRPC_STATUS_CANCELLED;
} else {
flush = status != GRPC_STATUS_OK;
}
if (flush && !grpc_bbq_empty(&call->incoming_queue)) {
gpr_log(GPR_ERROR, "Flushing unread messages due to error status %d", status);
grpc_bbq_flush(&call->incoming_queue);
}
}
static void set_status_details(grpc_call *call, status_source source,
@ -376,37 +395,53 @@ static void unlock(grpc_call *call) {
}
}
static void get_final_status(grpc_call *call, grpc_recv_status_args args) {
static void get_final_status(grpc_call *call, grpc_ioreq_data out) {
int i;
for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
if (call->status[i].is_set) {
out.recv_status.set_value(call->status[i].code,
out.recv_status.user_data);
return;
}
}
if (call->is_client) {
out.recv_status.set_value(GRPC_STATUS_UNKNOWN, out.recv_status.user_data);
} else {
out.recv_status.set_value(GRPC_STATUS_OK, out.recv_status.user_data);
}
}
static void get_final_details(grpc_call *call, grpc_ioreq_data out) {
int i;
for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
if (call->status[i].is_set) {
*args.code = call->status[i].code;
if (!args.details) return;
if (call->status[i].details) {
gpr_slice details = call->status[i].details->slice;
size_t len = GPR_SLICE_LENGTH(details);
if (len + 1 > *args.details_capacity) {
*args.details_capacity =
GPR_MAX(len + 1, *args.details_capacity * 3 / 2);
*args.details = gpr_realloc(*args.details, *args.details_capacity);
if (len + 1 > *out.recv_status_details.details_capacity) {
*out.recv_status_details.details_capacity = GPR_MAX(
len + 1, *out.recv_status_details.details_capacity * 3 / 2);
*out.recv_status_details.details =
gpr_realloc(*out.recv_status_details.details,
*out.recv_status_details.details_capacity);
}
memcpy(*args.details, GPR_SLICE_START_PTR(details), len);
(*args.details)[len] = 0;
memcpy(*out.recv_status_details.details, GPR_SLICE_START_PTR(details),
len);
(*out.recv_status_details.details)[len] = 0;
} else {
goto no_details;
}
return;
}
}
*args.code = GRPC_STATUS_UNKNOWN;
if (!args.details) return;
no_details:
if (0 == *args.details_capacity) {
*args.details_capacity = 8;
*args.details = gpr_malloc(*args.details_capacity);
if (0 == *out.recv_status_details.details_capacity) {
*out.recv_status_details.details_capacity = 8;
*out.recv_status_details.details =
gpr_malloc(*out.recv_status_details.details_capacity);
}
**args.details = 0;
**out.recv_status_details.details = 0;
}
static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
@ -444,8 +479,11 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
case GRPC_IOREQ_SEND_CLOSE:
break;
case GRPC_IOREQ_RECV_STATUS:
get_final_status(
call, call->request_data[GRPC_IOREQ_RECV_STATUS].recv_status);
get_final_status(call, call->request_data[GRPC_IOREQ_RECV_STATUS]);
break;
case GRPC_IOREQ_RECV_STATUS_DETAILS:
get_final_details(call,
call->request_data[GRPC_IOREQ_RECV_STATUS_DETAILS]);
break;
case GRPC_IOREQ_RECV_INITIAL_METADATA:
SWAP(grpc_metadata_array, call->buffered_metadata[0],
@ -669,6 +707,7 @@ static void finish_read_ops(grpc_call *call) {
finish_ioreq_op(call, GRPC_IOREQ_RECV_MESSAGE, GRPC_OP_OK);
}
finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS, GRPC_OP_OK);
finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS_DETAILS, GRPC_OP_OK);
finish_ioreq_op(call, GRPC_IOREQ_RECV_TRAILING_METADATA, GRPC_OP_OK);
/* fallthrough */
case READ_STATE_GOT_INITIAL_METADATA:
@ -746,20 +785,6 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
return GRPC_CALL_OK;
}
static void call_start_ioreq_done(grpc_call *call, grpc_op_error status,
void *user_data) {
grpc_cq_end_ioreq(call->cq, user_data, call, do_nothing, NULL, status);
}
grpc_call_error grpc_call_start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
size_t nreqs, void *tag) {
grpc_call_error err;
lock(call);
err = start_ioreq(call, reqs, nreqs, call_start_ioreq_done, tag);
unlock(call);
return err;
}
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) {
@ -919,8 +944,8 @@ void grpc_call_recv_metadata(grpc_call_element *elem, grpc_mdelem *md) {
gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity);
}
mdusr = &dest->metadata[dest->count++];
mdusr->key = (char *)grpc_mdstr_as_c_string(md->key);
mdusr->value = (char *)grpc_mdstr_as_c_string(md->value);
mdusr->key = grpc_mdstr_as_c_string(md->key);
mdusr->value = grpc_mdstr_as_c_string(md->value);
mdusr->value_length = GPR_SLICE_LENGTH(md->value->slice);
if (call->owned_metadata_count == call->owned_metadata_capacity) {
call->owned_metadata_capacity = GPR_MAX(
@ -943,6 +968,123 @@ void grpc_call_initial_metadata_complete(grpc_call_element *surface_element) {
set_read_state(call, READ_STATE_GOT_INITIAL_METADATA);
}
/*
* BATCH API IMPLEMENTATION
*/
static void set_status_value_directly(grpc_status_code status, void *dest) {
*(grpc_status_code *)dest = status;
}
static void set_cancelled_value(grpc_status_code status, void *dest) {
*(grpc_status_code *)dest = (status != GRPC_STATUS_OK);
}
static void finish_batch(grpc_call *call, grpc_op_error result, void *tag) {
grpc_cq_end_op_complete(call->cq, tag, call, do_nothing, NULL, GRPC_OP_OK);
}
grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
size_t nops, void *tag) {
grpc_ioreq reqs[GRPC_IOREQ_OP_COUNT];
size_t in;
size_t out;
const grpc_op *op;
grpc_ioreq *req;
/* rewrite batch ops into ioreq ops */
for (in = 0, out = 0; in < nops; in++) {
op = &ops[in];
switch (op->op) {
case GRPC_OP_SEND_INITIAL_METADATA:
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_INITIAL_METADATA;
req->data.send_metadata.count = op->data.send_initial_metadata.count;
req->data.send_metadata.metadata =
op->data.send_initial_metadata.metadata;
break;
case GRPC_OP_SEND_MESSAGE:
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_MESSAGE;
req->data.send_message = op->data.send_message;
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
if (!call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_CLOSE;
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
if (call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_CLIENT;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_TRAILING_METADATA;
req->data.send_metadata.count =
op->data.send_status_from_server.trailing_metadata_count;
req->data.send_metadata.metadata =
op->data.send_status_from_server.trailing_metadata;
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_STATUS;
req->data.send_status.code = op->data.send_status_from_server.status;
req->data.send_status.details =
op->data.send_status_from_server.status_details;
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_CLOSE;
break;
case GRPC_OP_RECV_INITIAL_METADATA:
if (!call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_INITIAL_METADATA;
req->data.recv_metadata = op->data.recv_initial_metadata;
break;
case GRPC_OP_RECV_MESSAGE:
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_MESSAGE;
req->data.recv_message = op->data.recv_message;
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
if (!call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_STATUS;
req->data.recv_status.set_value = set_status_value_directly;
req->data.recv_status.user_data = op->data.recv_status_on_client.status;
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_STATUS_DETAILS;
req->data.recv_status_details.details =
op->data.recv_status_on_client.status_details;
req->data.recv_status_details.details_capacity =
op->data.recv_status_on_client.status_details_capacity;
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_TRAILING_METADATA;
req->data.recv_metadata =
op->data.recv_status_on_client.trailing_metadata;
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_CLOSE;
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_STATUS;
req->data.recv_status.set_value = set_cancelled_value;
req->data.recv_status.user_data =
op->data.recv_close_on_server.cancelled;
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_CLOSE;
break;
}
}
grpc_cq_begin_op(call->cq, call, GRPC_OP_COMPLETE);
return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_batch,
tag);
}
/*
* LEGACY API IMPLEMENTATION
* All this code will disappear as soon as wrappings are updated
@ -983,8 +1125,8 @@ static void destroy_legacy_state(legacy_state *ls) {
size_t i, j;
for (i = 0; i < 2; i++) {
for (j = 0; j < ls->md_out_count[i]; j++) {
gpr_free(ls->md_out[i][j].key);
gpr_free(ls->md_out[i][j].value);
gpr_free((char *)ls->md_out[i][j].key);
gpr_free((char *)ls->md_out[i][j].value);
}
gpr_free(ls->md_out[i]);
}
@ -1017,7 +1159,7 @@ grpc_call_error grpc_call_add_metadata_old(grpc_call *call,
mdout->key = gpr_strdup(metadata->key);
mdout->value = gpr_malloc(metadata->value_length);
mdout->value_length = metadata->value_length;
memcpy(mdout->value, metadata->value, metadata->value_length);
memcpy((char *)mdout->value, metadata->value, metadata->value_length);
unlock(call);
@ -1060,7 +1202,7 @@ static void finish_send_metadata(grpc_call *call, grpc_op_error status,
grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag,
void *finished_tag, gpr_uint32 flags) {
grpc_ioreq reqs[3];
grpc_ioreq reqs[4];
legacy_state *ls;
grpc_call_error err;
@ -1089,11 +1231,13 @@ grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq,
reqs[0].op = GRPC_IOREQ_RECV_TRAILING_METADATA;
reqs[0].data.recv_metadata = &ls->trailing_md_in;
reqs[1].op = GRPC_IOREQ_RECV_STATUS;
reqs[1].data.recv_status.details = &ls->details;
reqs[1].data.recv_status.details_capacity = &ls->details_capacity;
reqs[1].data.recv_status.code = &ls->status;
reqs[2].op = GRPC_IOREQ_RECV_CLOSE;
err = start_ioreq(call, reqs, 3, finish_status, NULL);
reqs[1].data.recv_status.user_data = &ls->status;
reqs[1].data.recv_status.set_value = set_status_value_directly;
reqs[2].op = GRPC_IOREQ_RECV_STATUS_DETAILS;
reqs[2].data.recv_status_details.details = &ls->details;
reqs[2].data.recv_status_details.details_capacity = &ls->details_capacity;
reqs[3].op = GRPC_IOREQ_RECV_CLOSE;
err = start_ioreq(call, reqs, 4, finish_status, NULL);
if (err != GRPC_CALL_OK) goto done;
done:
@ -1116,14 +1260,16 @@ grpc_call_error grpc_call_server_accept_old(grpc_call *call,
ls = get_legacy_state(call);
err = bind_cq(call, cq);
if (err != GRPC_CALL_OK) return err;
if (err != GRPC_CALL_OK) {
unlock(call);
return err;
}
ls->finished_tag = finished_tag;
reqs[0].op = GRPC_IOREQ_RECV_STATUS;
reqs[0].data.recv_status.details = NULL;
reqs[0].data.recv_status.details_capacity = 0;
reqs[0].data.recv_status.code = &ls->status;
reqs[0].data.recv_status.user_data = &ls->status;
reqs[0].data.recv_status.set_value = set_status_value_directly;
reqs[1].op = GRPC_IOREQ_RECV_CLOSE;
err = start_ioreq(call, reqs, 2, finish_status, NULL);
unlock(call);

@ -44,6 +44,7 @@ typedef enum {
GRPC_IOREQ_RECV_MESSAGE,
GRPC_IOREQ_RECV_TRAILING_METADATA,
GRPC_IOREQ_RECV_STATUS,
GRPC_IOREQ_RECV_STATUS_DETAILS,
GRPC_IOREQ_RECV_CLOSE,
GRPC_IOREQ_SEND_INITIAL_METADATA,
GRPC_IOREQ_SEND_MESSAGE,
@ -53,24 +54,25 @@ typedef enum {
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 {
void (*set_value)(grpc_status_code status, void *user_data);
void *user_data;
} recv_status;
struct {
char **details;
size_t *details_capacity;
} recv_status_details;
struct {
size_t count;
grpc_metadata *metadata;
const grpc_metadata *metadata;
} send_metadata;
grpc_byte_buffer *send_message;
struct {
grpc_status_code code;
char *details;
const char *details;
} send_status;
} grpc_ioreq_data;
@ -83,9 +85,11 @@ typedef void (*grpc_ioreq_completion_func)(grpc_call *call,
grpc_op_error status,
void *user_data);
grpc_call *grpc_call_create(grpc_channel *channel,
grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
const void *server_transport_data);
void grpc_call_set_completion_queue(grpc_call *call, grpc_completion_queue *cq);
void grpc_call_internal_ref(grpc_call *call);
void grpc_call_internal_unref(grpc_call *call, int allow_immediate_deletion);
@ -104,8 +108,7 @@ grpc_call_error grpc_call_start_ioreq_and_call_back(
grpc_ioreq_completion_func on_complete, void *user_data);
/* 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_initial_metadata_complete(grpc_call_element *surface_element);
void grpc_call_set_deadline(grpc_call_element *surface_element,
gpr_timespec deadline);

@ -0,0 +1,13 @@
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <string.h>
void grpc_call_details_init(grpc_call_details *cd) {
memset(cd, 0, sizeof(*cd));
}
void grpc_call_details_destroy(grpc_call_details *cd) {
gpr_free(cd->method);
gpr_free(cd->host);
}

@ -77,9 +77,10 @@ grpc_channel *grpc_channel_create_from_filters(
static void do_nothing(void *ignored, grpc_op_error error) {}
grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec absolute_deadline) {
grpc_call *grpc_channel_create_call(grpc_channel *channel,
grpc_completion_queue *cq,
const char *method, const char *host,
gpr_timespec absolute_deadline) {
grpc_call *call;
grpc_mdelem *path_mdelem;
grpc_mdelem *authority_mdelem;
@ -90,7 +91,7 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
return NULL;
}
call = grpc_call_create(channel, NULL);
call = grpc_call_create(channel, cq, NULL);
/* Add :path and :authority headers. */
/* TODO(klempner): Consider optimizing this by stashing mdelems for common
@ -126,6 +127,13 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
return call;
}
grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec absolute_deadline) {
return grpc_channel_create_call(channel, NULL, method, host,
absolute_deadline);
}
void grpc_channel_internal_ref(grpc_channel *channel) {
gpr_ref(&channel->refs);
}

@ -185,14 +185,25 @@ 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) {
void grpc_cq_end_op_complete(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 = add_locked(cc, GRPC_OP_COMPLETE, tag, call, on_finish, user_data);
ev->base.data.write_accepted = error;
end_op_locked(cc, GRPC_IOREQ);
end_op_locked(cc, GRPC_OP_COMPLETE);
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}
void grpc_cq_end_op(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_OP_COMPLETE, tag, call, on_finish, user_data);
ev->base.data.write_accepted = error;
end_op_locked(cc, GRPC_OP_COMPLETE);
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}

@ -78,6 +78,10 @@ void grpc_cq_end_finish_accepted(grpc_completion_queue *cc, void *tag,
grpc_call *call,
grpc_event_finish_func on_finish,
void *user_data, grpc_op_error error);
/* Queue a GRPC_OP_COMPLETED operation */
void grpc_cq_end_op_complete(grpc_completion_queue *cc, void *tag,
grpc_call *call, grpc_event_finish_func on_finish,
void *user_data, grpc_op_error error);
/* Queue a GRPC_CLIENT_METADATA_READ operation */
void grpc_cq_end_client_metadata_read(grpc_completion_queue *cc, void *tag,
grpc_call *call,
@ -97,9 +101,9 @@ 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_op(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);

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

@ -0,0 +1,12 @@
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <string.h>
void grpc_metadata_array_init(grpc_metadata_array *array) {
memset(array, 0, sizeof(*array));
}
void grpc_metadata_array_destroy(grpc_metadata_array *array) {
gpr_free(array->metadata);
}

@ -189,8 +189,8 @@ static void done_setup(void *sp) {
static grpc_transport_setup_result complete_setup(void *channel_stack,
grpc_transport *transport,
grpc_mdctx *mdctx) {
static grpc_channel_filter const *extra_filters[] = {&grpc_http_client_filter,
&grpc_http_filter};
static grpc_channel_filter const *extra_filters[] = {
&grpc_client_auth_filter, &grpc_http_client_filter, &grpc_http_filter};
return grpc_client_channel_transport_setup_complete(
channel_stack, transport, extra_filters, GPR_ARRAY_SIZE(extra_filters),
mdctx);
@ -208,7 +208,7 @@ grpc_channel *grpc_secure_channel_create_internal(
grpc_arg context_arg;
grpc_channel_args *args_copy;
grpc_mdctx *mdctx = grpc_mdctx_create();
#define MAX_FILTERS 4
#define MAX_FILTERS 3
const grpc_channel_filter *filters[MAX_FILTERS];
int n = 0;
if (grpc_find_security_context_in_args(args) != NULL) {
@ -222,7 +222,6 @@ grpc_channel *grpc_secure_channel_create_internal(
if (grpc_channel_args_is_census_enabled(args)) {
filters[n++] = &grpc_client_census_filter;
}
filters[n++] = &grpc_client_auth_filter;
filters[n++] = &grpc_client_channel_filter;
GPR_ASSERT(n <= MAX_FILTERS);
channel = grpc_channel_create_from_filters(filters, n, args_copy, mdctx, 1);

@ -72,12 +72,15 @@ struct channel_data {
};
typedef void (*new_call_cb)(grpc_server *server, grpc_completion_queue *cq,
grpc_call **call, grpc_call_details *details,
grpc_metadata_array *initial_metadata,
call_data *calld, void *user_data);
typedef struct {
void *user_data;
grpc_completion_queue *cq;
grpc_call **call;
grpc_call_details *details;
grpc_metadata_array *initial_metadata;
new_call_cb cb;
} requested_call;
@ -121,7 +124,9 @@ typedef enum {
ZOMBIED
} call_state;
typedef struct legacy_data { grpc_metadata_array *initial_metadata; } legacy_data;
typedef struct legacy_data {
grpc_metadata_array *initial_metadata;
} legacy_data;
struct call_data {
grpc_call *call;
@ -132,6 +137,7 @@ struct call_data {
grpc_mdstr *host;
legacy_data *legacy;
grpc_call_details *details;
gpr_uint8 included[CALL_LIST_COUNT];
call_link links[CALL_LIST_COUNT];
@ -240,7 +246,8 @@ static void start_new_rpc(grpc_call_element *elem) {
requested_call rc = server->requested_calls[--server->requested_call_count];
calld->state = ACTIVATED;
gpr_mu_unlock(&server->mu);
rc.cb(server, rc.cq, rc.initial_metadata, calld, rc.user_data);
rc.cb(server, rc.cq, rc.call, rc.details, rc.initial_metadata, calld,
rc.user_data);
} else {
calld->state = PENDING;
call_list_join(server, calld, PENDING_START);
@ -339,21 +346,22 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elemn,
static void channel_op(grpc_channel_element *elem,
grpc_channel_element *from_elem, grpc_channel_op *op) {
channel_data *chand = elem->channel_data;
grpc_server *server = chand->server;
switch (op->type) {
case GRPC_ACCEPT_CALL:
/* create a call */
grpc_call_create(chand->channel,
grpc_call_create(chand->channel, NULL,
op->data.accept_call.transport_server_data);
break;
case GRPC_TRANSPORT_CLOSED:
/* if the transport is closed for a server channel, we destroy the
channel */
gpr_mu_lock(&chand->server->mu);
server_ref(chand->server);
gpr_mu_lock(&server->mu);
server_ref(server);
destroy_channel(chand);
gpr_mu_unlock(&chand->server->mu);
server_unref(chand->server);
gpr_mu_unlock(&server->mu);
server_unref(server);
break;
case GRPC_TRANSPORT_GOAWAY:
gpr_slice_unref(op->data.goaway.message);
@ -617,6 +625,7 @@ void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
/* terminate all the requested calls */
for (i = 0; i < requested_call_count; i++) {
requested_calls[i].cb(server, requested_calls[i].cq,
requested_calls[i].call, requested_calls[i].details,
requested_calls[i].initial_metadata, NULL,
requested_calls[i].user_data);
}
@ -667,6 +676,8 @@ void grpc_server_add_listener(grpc_server *server, void *arg,
static grpc_call_error queue_call_request(grpc_server *server,
grpc_completion_queue *cq,
grpc_call **call,
grpc_call_details *details,
grpc_metadata_array *initial_metadata,
new_call_cb cb, void *user_data) {
call_data *calld;
@ -674,7 +685,7 @@ static grpc_call_error queue_call_request(grpc_server *server,
gpr_mu_lock(&server->mu);
if (server->shutdown) {
gpr_mu_unlock(&server->mu);
cb(server, cq, initial_metadata, NULL, user_data);
cb(server, cq, call, details, initial_metadata, NULL, user_data);
return GRPC_CALL_OK;
}
calld = call_list_remove_head(server, PENDING_START);
@ -682,7 +693,7 @@ static grpc_call_error queue_call_request(grpc_server *server,
GPR_ASSERT(calld->state == PENDING);
calld->state = ACTIVATED;
gpr_mu_unlock(&server->mu);
cb(server, cq, initial_metadata, calld, user_data);
cb(server, cq, call, details, initial_metadata, calld, user_data);
return GRPC_CALL_OK;
} else {
if (server->requested_call_count == server->requested_call_capacity) {
@ -696,6 +707,8 @@ static grpc_call_error queue_call_request(grpc_server *server,
rc = &server->requested_calls[server->requested_call_count++];
rc->cb = cb;
rc->cq = cq;
rc->call = call;
rc->details = details;
rc->user_data = user_data;
rc->initial_metadata = initial_metadata;
gpr_mu_unlock(&server->mu);
@ -703,18 +716,64 @@ static grpc_call_error queue_call_request(grpc_server *server,
}
}
static void cpstr(char **dest, size_t *capacity, grpc_mdstr *value) {
gpr_slice slice = value->slice;
size_t len = GPR_SLICE_LENGTH(slice);
if (len + 1 > *capacity) {
*capacity = GPR_MAX(len + 1, *capacity * 2);
*dest = gpr_realloc(*dest, *capacity);
}
memcpy(*dest, grpc_mdstr_as_c_string(value), len + 1);
}
static void publish_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) {
cpstr(&calld->details->host, &calld->details->host_capacity, calld->host);
cpstr(&calld->details->method, &calld->details->method_capacity,
calld->path);
calld->details->deadline = calld->deadline;
grpc_cq_end_op_complete(server->cq, tag, call, do_nothing, NULL,
GRPC_OP_OK);
} else {
abort();
}
}
static void begin_request(grpc_server *server, grpc_completion_queue *cq,
grpc_call **call, grpc_call_details *details,
grpc_metadata_array *initial_metadata,
call_data *call_data, void *tag) {
abort();
call_data *calld, void *tag) {
grpc_ioreq req;
if (!calld) {
*call = NULL;
initial_metadata->count = 0;
grpc_cq_end_op_complete(cq, tag, NULL, do_nothing, NULL, GRPC_OP_ERROR);
return;
}
calld->details = details;
grpc_call_set_completion_queue(calld->call, cq);
*call = calld->call;
req.op = GRPC_IOREQ_RECV_INITIAL_METADATA;
req.data.recv_metadata = initial_metadata;
grpc_call_internal_ref(calld->call);
grpc_call_start_ioreq_and_call_back(calld->call, &req, 1, publish_request,
tag);
}
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);
grpc_call_error grpc_server_request_call(grpc_server *server, grpc_call **call,
grpc_call_details *details,
grpc_metadata_array *initial_metadata,
grpc_completion_queue *cq, void *tag) {
grpc_cq_begin_op(cq, NULL, GRPC_OP_COMPLETE);
return queue_call_request(server, cq, call, details, initial_metadata,
begin_request, tag);
}
static void publish_legacy_request(grpc_call *call, grpc_op_error status,
@ -737,9 +796,12 @@ static void publish_legacy_request(grpc_call *call, grpc_op_error status,
}
static void begin_legacy_request(grpc_server *server, grpc_completion_queue *cq,
grpc_call **call, grpc_call_details *details,
grpc_metadata_array *initial_metadata,
call_data *calld, void *tag) {
grpc_ioreq req;
GPR_ASSERT(call == NULL);
GPR_ASSERT(details == NULL);
if (!calld) {
gpr_free(initial_metadata);
grpc_cq_end_new_rpc(cq, tag, NULL, do_nothing, NULL, NULL, NULL,
@ -762,7 +824,7 @@ grpc_call_error grpc_server_request_call_old(grpc_server *server,
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,
return queue_call_request(server, server->cq, NULL, NULL, client_metadata,
begin_legacy_request, tag_new);
}

@ -164,7 +164,21 @@ void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
/* we can't add elements bigger than the max table size */
assert(elem_bytes <= tbl->max_bytes);
if (elem_bytes > tbl->max_bytes) {
/* HPACK draft 10 section 4.4 states:
* If the size of the new entry is less than or equal to the maximum
* size, that entry is added to the table. It is not an error to
* attempt to add an entry that is larger than the maximum size; an
* attempt to add an entry larger than the entire table causes
* the table
* to be emptied of all existing entries, and results in an
* empty table.
*/
while (tbl->num_ents) {
evict1(tbl);
}
return;
}
/* evict entries to ensure no overflow */
while (elem_bytes > tbl->max_bytes - tbl->mem_used) {

@ -31,7 +31,6 @@
*
*/
// TODO(yangg) maybe move to internal/common
#include <grpc++/completion_queue.h>
#include <grpc/grpc.h>

@ -1,2 +1,4 @@
*.userprefs
test-results
packages
Grpc.v12.suo

@ -1,8 +1,6 @@

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}"
@ -11,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCoreTests", "GrpcCoreTe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcApiTests", "GrpcApiTests\GrpcApiTests.csproj", "{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathClient", "MathClient\MathClient.csproj", "{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InteropClient", "InteropClient\InteropClient.csproj", "{C61154BA-DD4A-4838-8420-0162A28925E0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
@ -33,12 +35,16 @@ Global
{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
{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.ActiveCfg = Debug|x86
{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.Build.0 = Debug|x86
{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.ActiveCfg = Release|x86
{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.Build.0 = Release|x86
{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
StartupItem = InteropClient\InteropClient.csproj
EndGlobalSection
EndGlobal

@ -1,2 +1,3 @@
test-results
bin
obj

@ -0,0 +1,282 @@
// Generated by ProtoGen, Version=2.4.1.521, Culture=neutral, PublicKeyToken=17b3b1f090c3ea48. DO NOT EDIT!
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
using pb = global::Google.ProtocolBuffers;
using pbc = global::Google.ProtocolBuffers.Collections;
using pbd = global::Google.ProtocolBuffers.Descriptors;
using scg = global::System.Collections.Generic;
namespace grpc.testing {
namespace Proto {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial class Empty {
#region Extension registration
public static void RegisterAllExtensions(pb::ExtensionRegistry registry) {
}
#endregion
#region Static variables
internal static pbd::MessageDescriptor internal__static_grpc_testing_Empty__Descriptor;
internal static pb::FieldAccess.FieldAccessorTable<global::grpc.testing.Empty, global::grpc.testing.Empty.Builder> internal__static_grpc_testing_Empty__FieldAccessorTable;
#endregion
#region Descriptor
public static pbd::FileDescriptor Descriptor {
get { return descriptor; }
}
private static pbd::FileDescriptor descriptor;
static Empty() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CgtlbXB0eS5wcm90bxIMZ3JwYy50ZXN0aW5nIgcKBUVtcHR5"));
pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
descriptor = root;
internal__static_grpc_testing_Empty__Descriptor = Descriptor.MessageTypes[0];
internal__static_grpc_testing_Empty__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable<global::grpc.testing.Empty, global::grpc.testing.Empty.Builder>(internal__static_grpc_testing_Empty__Descriptor,
new string[] { });
return null;
};
pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbd::FileDescriptor[] {
}, assigner);
}
#endregion
}
}
#region Messages
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Empty : pb::GeneratedMessage<Empty, Empty.Builder> {
private Empty() { }
private static readonly Empty defaultInstance = new Empty().MakeReadOnly();
private static readonly string[] _emptyFieldNames = new string[] { };
private static readonly uint[] _emptyFieldTags = new uint[] { };
public static Empty DefaultInstance {
get { return defaultInstance; }
}
public override Empty DefaultInstanceForType {
get { return DefaultInstance; }
}
protected override Empty ThisMessage {
get { return this; }
}
public static pbd::MessageDescriptor Descriptor {
get { return global::grpc.testing.Proto.Empty.internal__static_grpc_testing_Empty__Descriptor; }
}
protected override pb::FieldAccess.FieldAccessorTable<Empty, Empty.Builder> InternalFieldAccessors {
get { return global::grpc.testing.Proto.Empty.internal__static_grpc_testing_Empty__FieldAccessorTable; }
}
public override bool IsInitialized {
get {
return true;
}
}
public override void WriteTo(pb::ICodedOutputStream output) {
int size = SerializedSize;
string[] field_names = _emptyFieldNames;
UnknownFields.WriteTo(output);
}
private int memoizedSerializedSize = -1;
public override int SerializedSize {
get {
int size = memoizedSerializedSize;
if (size != -1) return size;
size = 0;
size += UnknownFields.SerializedSize;
memoizedSerializedSize = size;
return size;
}
}
public static Empty ParseFrom(pb::ByteString data) {
return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
}
public static Empty ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
}
public static Empty ParseFrom(byte[] data) {
return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
}
public static Empty ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
}
public static Empty ParseFrom(global::System.IO.Stream input) {
return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
}
public static Empty ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
}
public static Empty ParseDelimitedFrom(global::System.IO.Stream input) {
return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
}
public static Empty ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
}
public static Empty ParseFrom(pb::ICodedInputStream input) {
return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
}
public static Empty ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
}
private Empty MakeReadOnly() {
return this;
}
public static Builder CreateBuilder() { return new Builder(); }
public override Builder ToBuilder() { return CreateBuilder(this); }
public override Builder CreateBuilderForType() { return new Builder(); }
public static Builder CreateBuilder(Empty prototype) {
return new Builder(prototype);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Builder : pb::GeneratedBuilder<Empty, Builder> {
protected override Builder ThisBuilder {
get { return this; }
}
public Builder() {
result = DefaultInstance;
resultIsReadOnly = true;
}
internal Builder(Empty cloneFrom) {
result = cloneFrom;
resultIsReadOnly = true;
}
private bool resultIsReadOnly;
private Empty result;
private Empty PrepareBuilder() {
if (resultIsReadOnly) {
Empty original = result;
result = new Empty();
resultIsReadOnly = false;
MergeFrom(original);
}
return result;
}
public override bool IsInitialized {
get { return result.IsInitialized; }
}
protected override Empty MessageBeingBuilt {
get { return PrepareBuilder(); }
}
public override Builder Clear() {
result = DefaultInstance;
resultIsReadOnly = true;
return this;
}
public override Builder Clone() {
if (resultIsReadOnly) {
return new Builder(result);
} else {
return new Builder().MergeFrom(result);
}
}
public override pbd::MessageDescriptor DescriptorForType {
get { return global::grpc.testing.Empty.Descriptor; }
}
public override Empty DefaultInstanceForType {
get { return global::grpc.testing.Empty.DefaultInstance; }
}
public override Empty BuildPartial() {
if (resultIsReadOnly) {
return result;
}
resultIsReadOnly = true;
return result.MakeReadOnly();
}
public override Builder MergeFrom(pb::IMessage other) {
if (other is Empty) {
return MergeFrom((Empty) other);
} else {
base.MergeFrom(other);
return this;
}
}
public override Builder MergeFrom(Empty other) {
if (other == global::grpc.testing.Empty.DefaultInstance) return this;
PrepareBuilder();
this.MergeUnknownFields(other.UnknownFields);
return this;
}
public override Builder MergeFrom(pb::ICodedInputStream input) {
return MergeFrom(input, pb::ExtensionRegistry.Empty);
}
public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
PrepareBuilder();
pb::UnknownFieldSet.Builder unknownFields = null;
uint tag;
string field_name;
while (input.ReadTag(out tag, out field_name)) {
if(tag == 0 && field_name != null) {
int field_ordinal = global::System.Array.BinarySearch(_emptyFieldNames, field_name, global::System.StringComparer.Ordinal);
if(field_ordinal >= 0)
tag = _emptyFieldTags[field_ordinal];
else {
if (unknownFields == null) {
unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
}
ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
continue;
}
}
switch (tag) {
case 0: {
throw pb::InvalidProtocolBufferException.InvalidTag();
}
default: {
if (pb::WireFormat.IsEndGroupTag(tag)) {
if (unknownFields != null) {
this.UnknownFields = unknownFields.Build();
}
return this;
}
if (unknownFields == null) {
unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
}
ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
break;
}
}
}
if (unknownFields != null) {
this.UnknownFields = unknownFields.Build();
}
return this;
}
}
static Empty() {
object.ReferenceEquals(global::grpc.testing.Proto.Empty.Descriptor, null);
}
}
#endregion
}
#endregion Designer generated code

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?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>
@ -30,27 +30,34 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="Google.ProtocolBuffers, Version=2.4.1.521, Culture=neutral, PublicKeyToken=55f7125234beb589, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Reactive.Linq, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<Private>False</Private>
<Reference Include="System.Reactive.Core">
<HintPath>..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
</Reference>
<Reference Include="System.Data.Linq" />
<Reference Include="System.Reactive.Interfaces, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<Private>False</Private>
<Reference Include="System.Reactive.Interfaces">
<HintPath>..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<Private>False</Private>
<Reference Include="System.Data.Linq" />
<Reference Include="System.Reactive.Linq">
<HintPath>..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
</Reference>
<Reference Include="Google.ProtocolBuffers">
<HintPath>..\lib\Google.ProtocolBuffers.dll</HintPath>
<Reference Include="System.Reactive.PlatformServices">
<HintPath>..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Examples.cs" />
<Compile Include="Math.cs" />
<Compile Include="MathGrpc.cs" />
<Compile Include="MathServiceImpl.cs" />
<Compile Include="Empty.cs" />
<Compile Include="Messages.cs" />
<Compile Include="TestServiceGrpc.cs" />
<Compile Include="MathExamples.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@ -59,4 +66,11 @@
<Name>GrpcCore</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="proto\math.proto" />
<None Include="proto\empty.proto" />
<None Include="proto\messages.proto" />
<None Include="proto\test.proto" />
</ItemGroup>
</Project>

@ -6,7 +6,7 @@ using Google.GRPC.Core.Utils;
namespace math
{
public class Examples
public static class MathExamples
{
public static void DivExample(MathGrpc.IMathServiceClient stub)
{

File diff suppressed because it is too large Load Diff

@ -0,0 +1,170 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reactive.Linq;
using Google.GRPC.Core;
namespace grpc.testing
{
/// <summary>
/// TestService (this is handwritten version of code that will normally be generated).
/// </summary>
public class TestServiceGrpc
{
readonly static Marshaller<Empty> emptyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Empty.ParseFrom);
readonly static Marshaller<SimpleRequest> simpleRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleRequest.ParseFrom);
readonly static Marshaller<SimpleResponse> simpleResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleResponse.ParseFrom);
readonly static Marshaller<StreamingOutputCallRequest> streamingOutputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallRequest.ParseFrom);
readonly static Marshaller<StreamingOutputCallResponse> streamingOutputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallResponse.ParseFrom);
readonly static Marshaller<StreamingInputCallRequest> streamingInputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallRequest.ParseFrom);
readonly static Marshaller<StreamingInputCallResponse> streamingInputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallResponse.ParseFrom);
readonly static Method<Empty, Empty> emptyCallMethod = new Method<Empty, Empty>(
MethodType.Unary,
"/grpc.testing.TestService/EmptyCall",
emptyMarshaller,
emptyMarshaller
);
readonly static Method<SimpleRequest, SimpleResponse> unaryCallMethod = new Method<SimpleRequest, SimpleResponse>(
MethodType.Unary,
"/grpc.testing.TestService/UnaryCall",
simpleRequestMarshaller,
simpleResponseMarshaller
);
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> streamingOutputCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>(
MethodType.ServerStreaming,
"/grpc.testing.TestService/StreamingOutputCall",
streamingOutputCallRequestMarshaller,
streamingOutputCallResponseMarshaller
);
readonly static Method<StreamingInputCallRequest, StreamingInputCallResponse> streamingInputCallMethod = new Method<StreamingInputCallRequest, StreamingInputCallResponse>(
MethodType.ClientStreaming,
"/grpc.testing.TestService/StreamingInputCall",
streamingInputCallRequestMarshaller,
streamingInputCallResponseMarshaller
);
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> fullDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>(
MethodType.DuplexStreaming,
"/grpc.testing.TestService/FullDuplexCall",
streamingOutputCallRequestMarshaller,
streamingOutputCallResponseMarshaller
);
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> halfDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>(
MethodType.DuplexStreaming,
"/grpc.testing.TestService/HalfDuplexCall",
streamingOutputCallRequestMarshaller,
streamingOutputCallResponseMarshaller
);
public interface ITestServiceClient
{
Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken));
Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken));
SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken));
Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken));
Task StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken));
ClientStreamingAsyncResult<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken));
IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken));
IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken));
}
public class TestServiceClientStub : ITestServiceClient
{
readonly Channel channel;
public TestServiceClientStub(Channel channel)
{
this.channel = channel;
}
public Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<Empty, Empty>(emptyCallMethod, channel);
return Calls.BlockingUnaryCall(call, request, token);
}
public Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<Empty, Empty>(emptyCallMethod, channel);
return Calls.AsyncUnaryCall(call, request, token);
}
public SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel);
return Calls.BlockingUnaryCall(call, request, token);
}
public Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel);
return Calls.AsyncUnaryCall(call, request, token);
}
public Task StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) {
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(streamingOutputCallMethod, channel);
return Calls.AsyncServerStreamingCall(call, request, responseObserver, token);
}
public ClientStreamingAsyncResult<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<StreamingInputCallRequest, StreamingInputCallResponse>(streamingInputCallMethod, channel);
return Calls.AsyncClientStreamingCall(call, token);
}
public IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(fullDuplexCallMethod, channel);
return Calls.DuplexStreamingCall(call, responseObserver, token);
}
public IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken))
{
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(halfDuplexCallMethod, channel);
return Calls.DuplexStreamingCall(call, responseObserver, token);
}
}
// server-side interface
public interface ITestService
{
void EmptyCall(Empty request, IObserver<Empty> responseObserver);
void UnaryCall(SimpleRequest request, IObserver<SimpleResponse> responseObserver);
void StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver);
IObserver<StreamingInputCallRequest> StreamingInputCall(IObserver<StreamingInputCallResponse> responseObserver);
IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver);
IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver);
}
public static ServerServiceDefinition BindService(ITestService serviceImpl)
{
return ServerServiceDefinition.CreateBuilder("/grpc.testing.TestService/")
.AddMethod(emptyCallMethod, serviceImpl.EmptyCall)
.AddMethod(unaryCallMethod, serviceImpl.UnaryCall)
.AddMethod(streamingOutputCallMethod, serviceImpl.StreamingOutputCall)
.AddMethod(streamingInputCallMethod, serviceImpl.StreamingInputCall)
.AddMethod(fullDuplexCallMethod, serviceImpl.FullDuplexCall)
.AddMethod(halfDuplexCallMethod, serviceImpl.HalfDuplexCall)
.Build();
}
public static ITestServiceClient NewStub(Channel channel)
{
return new TestServiceClientStub(channel);
}
}
}

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="Ix-Main" version="1.2.3" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
<package id="Rx-Main" version="2.2.5" targetFramework="net45" />
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
</packages>

@ -0,0 +1,13 @@
syntax = "proto2";
package grpc.testing;
// An empty message that you can re-use to avoid defining duplicated empty
// messages in your project. A typical example is to use it as argument or the
// return value of a service API. For instance:
//
// service Foo {
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
// };
//
message Empty {}

@ -0,0 +1,102 @@
// Message definitions to be used by integration test service definitions.
syntax = "proto2";
package grpc.testing;
// The type of payload that should be returned.
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
// A block of data, to simply increase gRPC message size.
message Payload {
// The type of data in body.
optional PayloadType type = 1;
// Primary contents of payload.
optional bytes body = 2;
}
// Unary request.
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
optional PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
optional int32 response_size = 2;
// Optional input payload sent along with the request.
optional Payload payload = 3;
// Whether SimpleResponse should include username.
optional bool fill_username = 4;
// Whether SimpleResponse should include OAuth scope.
optional bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
message SimpleResponse {
// Payload to increase message size.
optional Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
optional string username = 2;
// OAuth scope.
optional string oauth_scope = 3;
}
// Client-streaming request.
message StreamingInputCallRequest {
// Optional input payload sent along with the request.
optional Payload payload = 1;
// Not expecting any payload from the response.
}
// Client-streaming response.
message StreamingInputCallResponse {
// Aggregated size of payloads received from the client.
optional int32 aggregated_payload_size = 1;
}
// Configuration for a particular response.
message ResponseParameters {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
optional int32 size = 1;
// Desired interval between consecutive responses in the response stream in
// microseconds.
optional int32 interval_us = 2;
}
// Server-streaming request.
message StreamingOutputCallRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
optional PayloadType response_type = 1;
// Configuration for each expected response message.
repeated ResponseParameters response_parameters = 2;
// Optional input payload sent along with the request.
optional Payload payload = 3;
}
// Server-streaming response, as configured by the request and parameters.
message StreamingOutputCallResponse {
// Payload to increase response size.
optional Payload payload = 1;
}

@ -0,0 +1,42 @@
// An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto2";
import "empty.proto";
import "messages.proto";
package grpc.testing;
// A simple service to test the various types of RPCs and experiment with
// performance with various types of payload.
service TestService {
// One empty request followed by one empty response.
rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty);
// One request followed by one response.
// The server returns the client payload as-is.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
rpc FullDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}

@ -1,2 +1,3 @@
test-results
bin
obj

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?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>
@ -30,13 +30,14 @@
<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 Include="Google.ProtocolBuffers, Version=2.4.1.521, Culture=neutral, PublicKeyToken=55f7125234beb589, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference>
<Reference Include="Google.ProtocolBuffers">
<HintPath>..\lib\Google.ProtocolBuffers.dll</HintPath>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
@ -53,4 +54,10 @@
<Name>GrpcCore</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
</Project>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages>

@ -1 +1,2 @@
bin
obj

@ -62,6 +62,7 @@
<Compile Include="ServerServiceDefinition.cs" />
<Compile Include="Utils\RecordingObserver.cs" />
<Compile Include="Utils\PortPicker.cs" />
<Compile Include="Utils\RecordingQueue.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

@ -13,10 +13,10 @@ namespace Google.GRPC.Core
{
const int THREAD_POOL_SIZE = 1;
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_init();
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_shutdown();
static object staticLock = new object();

@ -15,66 +15,66 @@ namespace Google.GRPC.Core.Internal
{
const UInt32 GRPC_WRITE_BUFFER_HINT = 1;
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
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")]
[DllImport("grpc.dll", 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")]
[DllImport("grpc.dll")]
static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")]
[DllImport("grpc.dll", 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")]
[DllImport("grpc.dll")]
static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern GRPCCallError grpc_call_cancel(CallSafeHandle call);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
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")]
[DllImport("grpc.dll", 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")]
[DllImport("grpc.dll")]
static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")]
[DllImport("grpc.dll", 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")]
[DllImport("grpc.dll")]
static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag);
[DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")]
[DllImport("grpc.dll", 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")]
[DllImport("grpc_csharp_ext.dll")]
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")]
[DllImport("grpc_csharp_ext.dll", 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")]
[DllImport("grpc.dll")]
static extern void grpc_call_destroy(IntPtr call);
private CallSafeHandle()

@ -10,10 +10,10 @@ namespace Google.GRPC.Core.Internal
/// </summary>
internal class ChannelSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_channel_destroy(IntPtr channel);
private ChannelSafeHandle()

@ -9,22 +9,22 @@ namespace Google.GRPC.Core.Internal
/// </summary>
internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern CompletionQueueSafeHandle grpc_completion_queue_create();
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_completion_queue_destroy(IntPtr cq);
private CompletionQueueSafeHandle()

@ -36,29 +36,36 @@ namespace Google.GRPC.Core.Internal
/// </summary>
internal enum GRPCCompletionType
{
GRPC_QUEUE_SHUTDOWN,
/* Shutting down */
GRPC_READ,
GRPC_QUEUE_SHUTDOWN,
/* operation completion */
GRPC_OP_COMPLETE,
/* A read has completed */
GRPC_INVOKE_ACCEPTED,
/* An invoke call has been accepted by flow
control */
GRPC_READ,
/* A write 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_FINISH_ACCEPTED,
/* The metadata array sent by server received at client */
GRPC_CLIENT_METADATA_READ,
/* The metadata array sent by server received at
client */
/* 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_RPC_NEW,
/* The server has finished shutting down */
GRPC_SERVER_SHUTDOWN,
/* must be last, forces users to include a default: case */
GRPC_COMPLETION_DO_NOT_USE
/* must be last, forces users to include
a default: case */
}
/// <summary>

@ -9,34 +9,34 @@ namespace Google.GRPC.Core.Internal
/// </summary>
internal class EventSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_event_finish(IntPtr ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern CallSafeHandle grpc_event_call(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern StatusCode grpc_event_finished_status(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char*
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpc_event_read_length(EventSafeHandle ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char*
public GRPCCompletionType GetCompletionType()
@ -98,34 +98,34 @@ namespace Google.GRPC.Core.Internal
/// </summary>
internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_event_finish(IntPtr ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char*
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen);
[DllImport("libgrpc_csharp_ext.so")]
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char*
public EventSafeHandleNotOwned() : base(false)

@ -10,30 +10,30 @@ namespace Google.GRPC.Core.Internal
/// </summary>
internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("libgrpc.so", EntryPoint = "grpc_server_request_call_old")]
[DllImport("grpc.dll", 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")]
[DllImport("grpc.dll")]
static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args);
// TODO: check int representation size
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr);
// TODO: check int representation size
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_server_start(ServerSafeHandle server);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_server_shutdown(ServerSafeHandle server);
[DllImport("libgrpc.so", EntryPoint = "grpc_server_shutdown_and_notify")]
[DllImport("grpc.dll", EntryPoint = "grpc_server_shutdown_and_notify")]
static extern void grpc_server_shutdown_and_notify_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
[DllImport("libgrpc.so")]
[DllImport("grpc.dll")]
static extern void grpc_server_destroy(IntPtr server);
private ServerSafeHandle()

@ -13,7 +13,7 @@ namespace Google.GRPC.Core.Internal
const int nanosPerSecond = 1000 * 1000 * 1000;
const int nanosPerTick = 100;
[DllImport("libgpr.so")]
[DllImport("gpr.dll")]
static extern Timespec gpr_now();
// TODO: this only works on 64bit linux, can we autoselect the right size of ints?

@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.Concurrent;
namespace Google.GRPC.Core.Utils
{
/// <summary>
/// Observer that allows us to await incoming messages one-by-one.
/// The implementation is not ideal and class will be probably replaced
/// by something more versatile in the future.
/// </summary>
public class RecordingQueue<T> : IObserver<T>
{
readonly BlockingCollection<T> queue = new BlockingCollection<T>();
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
public void OnCompleted()
{
tcs.SetResult(null);
}
public void OnError(Exception error)
{
tcs.SetException(error);
}
public void OnNext(T value)
{
queue.Add(value);
}
public BlockingCollection<T> Queue
{
get
{
return queue;
}
}
public Task Finished
{
get
{
return tcs.Task;
}
}
}
}

@ -1,2 +1,3 @@
test-results
bin
obj

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?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>
@ -30,10 +30,10 @@
<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 Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
@ -49,4 +49,10 @@
<Name>GrpcCore</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
</Project>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages>

@ -0,0 +1,303 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using System.Text.RegularExpressions;
using Google.GRPC.Core;
using Google.GRPC.Core.Utils;
using Google.ProtocolBuffers;
using grpc.testing;
namespace Google.GRPC.Interop
{
class Client
{
private class ClientOptions
{
public bool help;
public string serverHost;
public string serverHostOverride;
public int? serverPort;
public string testCase;
public bool useTls;
public bool useTestCa;
}
ClientOptions options;
private Client(ClientOptions options)
{
this.options = options;
}
public static void Main(string[] args)
{
Console.WriteLine("gRPC C# interop testing client");
ClientOptions options = ParseArguments(args);
if (options.serverHost == null || !options.serverPort.HasValue || options.testCase == null)
{
Console.WriteLine("Missing required argument.");
Console.WriteLine();
options.help = true;
}
if (options.help)
{
Console.WriteLine("Usage:");
Console.WriteLine(" --server_host=HOSTNAME");
Console.WriteLine(" --server_host_override=HOSTNAME");
Console.WriteLine(" --server_port=PORT");
Console.WriteLine(" --test_case=TESTCASE");
Console.WriteLine(" --use_tls=BOOLEAN");
Console.WriteLine(" --use_test_ca=BOOLEAN");
Console.WriteLine();
Environment.Exit(1);
}
var interopClient = new Client(options);
interopClient.Run();
}
private void Run()
{
string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort);
using (Channel channel = new Channel(addr))
{
TestServiceGrpc.ITestServiceClient client = new TestServiceGrpc.TestServiceClientStub(channel);
RunTestCase(options.testCase, client);
}
GrpcEnvironment.Shutdown();
}
private void RunTestCase(string testCase, TestServiceGrpc.ITestServiceClient client)
{
switch (testCase)
{
case "empty_unary":
RunEmptyUnary(client);
break;
case "large_unary":
RunLargeUnary(client);
break;
case "client_streaming":
RunClientStreaming(client);
break;
case "server_streaming":
RunServerStreaming(client);
break;
case "ping_pong":
RunPingPong(client);
break;
case "empty_stream":
RunEmptyStream(client);
break;
default:
throw new ArgumentException("Unknown test case " + testCase);
}
}
private void RunEmptyUnary(TestServiceGrpc.ITestServiceClient client)
{
Console.WriteLine("running empty_unary");
var response = client.EmptyCall(Empty.DefaultInstance);
Assert.IsNotNull(response);
Console.WriteLine("Passed!");
}
private void RunLargeUnary(TestServiceGrpc.ITestServiceClient client)
{
Console.WriteLine("running large_unary");
var request = SimpleRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.SetResponseSize(314159)
.SetPayload(CreateZerosPayload(271828))
.Build();
var response = client.UnaryCall(request);
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(314159, response.Payload.Body.Length);
Console.WriteLine("Passed!");
}
private void RunClientStreaming(TestServiceGrpc.ITestServiceClient client)
{
Console.WriteLine("running client_streaming");
var bodySizes = new List<int>{27182, 8, 1828, 45904};
var context = client.StreamingInputCall();
foreach (var size in bodySizes)
{
context.Inputs.OnNext(
StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build());
}
context.Inputs.OnCompleted();
var response = context.Task.Result;
Assert.AreEqual(74922, response.AggregatedPayloadSize);
Console.WriteLine("Passed!");
}
private void RunServerStreaming(TestServiceGrpc.ITestServiceClient client)
{
Console.WriteLine("running server_streaming");
var bodySizes = new List<int>{31415, 9, 2653, 58979};
var request = StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.AddRangeResponseParameters(bodySizes.ConvertAll(
(size) => ResponseParameters.CreateBuilder().SetSize(size).Build()))
.Build();
var recorder = new RecordingObserver<StreamingOutputCallResponse>();
client.StreamingOutputCall(request, recorder);
var responseList = recorder.ToList().Result;
foreach (var res in responseList)
{
Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type);
}
CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length));
Console.WriteLine("Passed!");
}
private void RunPingPong(TestServiceGrpc.ITestServiceClient client)
{
Console.WriteLine("running ping_pong");
var recorder = new RecordingQueue<StreamingOutputCallResponse>();
var inputs = client.FullDuplexCall(recorder);
StreamingOutputCallResponse response;
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415))
.SetPayload(CreateZerosPayload(27182)).Build());
response = recorder.Queue.Take();
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(31415, response.Payload.Body.Length);
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9))
.SetPayload(CreateZerosPayload(8)).Build());
response = recorder.Queue.Take();
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(9, response.Payload.Body.Length);
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2635))
.SetPayload(CreateZerosPayload(1828)).Build());
response = recorder.Queue.Take();
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(2653, response.Payload.Body.Length);
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979))
.SetPayload(CreateZerosPayload(45904)).Build());
response = recorder.Queue.Take();
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(58979, response.Payload.Body.Length);
recorder.Finished.Wait();
Assert.AreEqual(0, recorder.Queue.Count);
Console.WriteLine("Passed!");
}
private void RunEmptyStream(TestServiceGrpc.ITestServiceClient client)
{
Console.WriteLine("running empty_stream");
var recorder = new RecordingObserver<StreamingOutputCallResponse>();
var inputs = client.FullDuplexCall(recorder);
inputs.OnCompleted();
var responseList = recorder.ToList().Result;
Assert.AreEqual(0, responseList.Count);
Console.WriteLine("Passed!");
}
private Payload CreateZerosPayload(int size) {
return Payload.CreateBuilder().SetBody(ByteString.CopyFrom(new byte[size])).Build();
}
private static ClientOptions ParseArguments(string[] args)
{
var options = new ClientOptions();
foreach(string arg in args)
{
ParseArgument(arg, options);
if (options.help)
{
break;
}
}
return options;
}
private static void ParseArgument(string arg, ClientOptions options)
{
Match match;
match = Regex.Match(arg, "--server_host=(.*)");
if (match.Success)
{
options.serverHost = match.Groups[1].Value.Trim();
return;
}
match = Regex.Match(arg, "--server_host_override=(.*)");
if (match.Success)
{
options.serverHostOverride = match.Groups[1].Value.Trim();
return;
}
match = Regex.Match(arg, "--server_port=(.*)");
if (match.Success)
{
options.serverPort = int.Parse(match.Groups[1].Value.Trim());
return;
}
match = Regex.Match(arg, "--test_case=(.*)");
if (match.Success)
{
options.testCase = match.Groups[1].Value.Trim();
return;
}
match = Regex.Match(arg, "--use_tls=(.*)");
if (match.Success)
{
options.useTls = bool.Parse(match.Groups[1].Value.Trim());
return;
}
match = Regex.Match(arg, "--use_test_ca=(.*)");
if (match.Success)
{
options.useTestCa = bool.Parse(match.Groups[1].Value.Trim());
return;
}
Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg));
options.help = true;
}
}
}

@ -0,0 +1,63 @@
<?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>{C61154BA-DD4A-4838-8420-0162A28925E0}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>InteropClient</RootNamespace>
<AssemblyName>InteropClient</AssemblyName>
<StartupObject>Google.GRPC.Interop.Client</StartupObject>
<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="Google.ProtocolBuffers, Version=2.4.1.521, Culture=neutral, PublicKeyToken=55f7125234beb589, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Client.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>
<ProjectReference Include="..\GrpcApi\GrpcApi.csproj">
<Project>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</Project>
<Name>GrpcApi</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

@ -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("InteropClient")]
[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,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages>

@ -0,0 +1,2 @@
bin
obj

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

@ -35,8 +35,8 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MathClient.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

@ -3,7 +3,7 @@ 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: AssemblyTitle ("MathClient")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]

@ -15,8 +15,15 @@ EXPERIMENTAL ONLY
completely rewritten.
INSTALLATION AND USAGE
----------------------
INSTALLATION AND USAGE: WINDOWS
-------------------------------
- Open Grpc.sln using Visual Studio 2013. NuGet dependencies will be restored
upon build.
INSTALLATION AND USAGE: LINUX & MONO
------------------------------------
- Compile and install the gRPC C Core library
```
@ -31,6 +38,18 @@ sudo apt-get install monodevelop monodevelop-nunit
sudo apt-get install nunit nunit-console
```
- NuGet is used to manage project's dependencies. Prior opening Grpc.sln,
download dependencies using NuGet restore command:
```
# Import needed certicates into Mono certificate store:
mozroots --import --sync
# Download NuGet.exe http://nuget.codeplex.com/releases/
# Restore the nuget packages with Grpc C# dependencies
mono ~/Downloads/NuGet.exe restore Grpc.sln
```
- Use MonoDevelop to open the solution Grpc.sln (you can also run unit tests
from there).
@ -47,8 +66,11 @@ CONTENTS
- ext:
The extension library that wraps C API to be more digestible by C#.
- GrpcApi:
API examples for math.proto and testservice.proto
- GrpcCore:
The main gRPC C# library.
- GrpcApi:
API examples for math.proto.
- InteropClient:
Client for interop testing.
- MathClient:
An example client that sends some requests to math server.

@ -128,7 +128,8 @@ var server = new Server({
});
if (require.main === module) {
server.bind('localhost:7070').listen();
server.bind('0.0.0.0:7070');
server.listen();
}
/**

@ -0,0 +1,115 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
var grpc = require('..');
var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing;
var _ = require('underscore');
var interop_server = require('../interop/interop_server.js');
function runTest(iterations, callback) {
var testServer = interop_server.getServer(0, false);
testServer.server.listen();
var client = new testProto.TestService('localhost:' + testServer.port);
function runIterations(finish) {
var start = process.hrtime();
var intervals = [];
var pending = iterations;
function next(i) {
if (i >= iterations) {
testServer.server.shutdown();
var totalDiff = process.hrtime(start);
finish({
total: totalDiff[0] * 1000000 + totalDiff[1] / 1000,
intervals: intervals
});
} else{
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var startTime = process.hrtime();
client.emptyCall({}, function(err, resp) {
var timeDiff = process.hrtime(startTime);
intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000;
next(i+1);
}, {}, deadline);
}
}
next(0);
}
function warmUp(num) {
var pending = num;
for (var i = 0; i < num; i++) {
(function(i) {
client.emptyCall({}, function(err, resp) {
pending--;
if (pending === 0) {
runIterations(callback);
}
});
})(i);
}
}
warmUp(100);
}
function percentile(arr, percentile) {
if (percentile > 99) {
percentile = 99;
}
if (percentile < 0) {
percentile = 0;
}
return arr[(arr.length * percentile / 100)|0];
}
if (require.main === module) {
var count;
if (process.argv.length >= 3) {
count = process.argv[2];
} else {
count = 100;
}
runTest(count, function(results) {
var sorted_intervals = _.sortBy(results.intervals, _.identity);
console.log('count:', count);
console.log('total time:', results.total, 'us');
console.log('median:', percentile(sorted_intervals, 50), 'us');
console.log('90th percentile:', percentile(sorted_intervals, 90), 'us');
console.log('95th percentile:', percentile(sorted_intervals, 95), 'us');
console.log('99th percentile:', percentile(sorted_intervals, 99), 'us');
console.log('QPS:', (count / results.total) * 1000000);
});
}
module.exports = runTest;

@ -35,28 +35,28 @@ package examples;
message StockRequest {
optional string symbol = 1;
optional int32 num_trades_to_watch = 2 [default=0];
};
}
message StockReply {
optional float price = 1;
optional string symbol = 2;
};
}
// Interface exported by the server
service Stock {
// Simple blocking RPC
rpc GetLastTradePrice(StockRequest) returns (StockReply) {
};
}
// Bidirectional streaming RPC
rpc GetLastTradePriceMultiple(stream StockRequest) returns
(stream StockReply) {
};
}
// Unidirectional server-to-client streaming RPC
rpc WatchFutureTrades(StockRequest) returns (stream StockReply) {
};
}
// Unidirectional client-to-server streaming RPC
rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) {
};
}
};
}

@ -39,5 +39,8 @@ var examples = grpc.load(__dirname + '/stock.proto').examples;
*
* var StockClient = require('stock_client.js');
* var stockClient = new StockClient(server_address);
* stockClient.getLastTradePrice({symbol: 'GOOG'}, function(error, response) {
* console.log(error || response);
* });
*/
module.exports = examples.Stock;

@ -35,10 +35,10 @@ var _ = require('underscore');
var grpc = require('..');
var examples = grpc.load(__dirname + '/stock.proto').examples;
var StockServer = grpc.makeServerConstructor([examples.Stock.service]);
var StockServer = grpc.buildServer([examples.Stock.service]);
function getLastTradePrice(call, callback) {
callback(null, {price: 88});
callback(null, {symbol: call.request.symbol, price: 88});
}
function watchFutureTrades(call) {
@ -80,4 +80,9 @@ var stockServer = new StockServer({
}
});
exports.module = stockServer;
if (require.main === module) {
stockServer.bind('0.0.0.0:8080');
stockServer.listen();
}
module.exports = stockServer;

@ -1,6 +1,6 @@
/*
*
* Copyright 2014, Google Inc.
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -58,11 +58,11 @@ using v8::Value;
Handle<Value> ParseMetadata(grpc_metadata *metadata_elements, size_t length) {
NanEscapableScope();
std::map<char*, size_t> size_map;
std::map<char*, size_t> index_map;
std::map<const char*, size_t> size_map;
std::map<const char*, size_t> index_map;
for (unsigned int i = 0; i < length; i++) {
char *key = metadata_elements[i].key;
const char *key = metadata_elements[i].key;
if (size_map.count(key)) {
size_map[key] += 1;
}
@ -97,8 +97,6 @@ Handle<Value> GetEventData(grpc_event *event) {
switch (event->type) {
case GRPC_READ:
return NanEscapeScope(ByteBufferToBuffer(event->data.read));
case GRPC_INVOKE_ACCEPTED:
return NanEscapeScope(NanNew<Number>(event->data.invoke_accepted));
case GRPC_WRITE_ACCEPTED:
return NanEscapeScope(NanNew<Number>(event->data.write_accepted));
case GRPC_FINISH_ACCEPTED:
@ -124,12 +122,12 @@ Handle<Value> GetEventData(grpc_event *event) {
return NanEscapeScope(NanNull());
}
rpc_new->Set(
NanNew<String, const char *>("method"),
NanNew<String, const char *>(event->data.server_rpc_new.method));
NanNew("method"),
NanNew(event->data.server_rpc_new.method));
rpc_new->Set(
NanNew<String, const char *>("host"),
NanNew<String, const char *>(event->data.server_rpc_new.host));
rpc_new->Set(NanNew<String, const char *>("absolute_deadline"),
NanNew("host"),
NanNew(event->data.server_rpc_new.host));
rpc_new->Set(NanNew("absolute_deadline"),
NanNew<Date>(TimespecToMilliseconds(
event->data.server_rpc_new.deadline)));
count = event->data.server_rpc_new.metadata_count;
@ -137,12 +135,11 @@ Handle<Value> GetEventData(grpc_event *event) {
metadata = NanNew<Array>(static_cast<int>(count));
for (unsigned int i = 0; i < count; i++) {
Handle<Object> item_obj = Object::New();
item_obj->Set(NanNew<String, const char *>("key"),
NanNew<String, char *>(items[i].key));
item_obj->Set(NanNew("key"),
NanNew(items[i].key));
item_obj->Set(
NanNew<String, const char *>("value"),
NanNew<String, char *>(items[i].value,
static_cast<int>(items[i].value_length)));
NanNew("value"),
NanNew(items[i].value, static_cast<int>(items[i].value_length)));
metadata->Set(i, item_obj);
}
rpc_new->Set(NanNew("metadata"), ParseMetadata(items, count));

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

Loading…
Cancel
Save