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

Conflicts:
	tools/run_tests/run_tests.py
pull/1124/head
Nicolas "Pixel" Noble 10 years ago
commit e55cd7f51f
  1. 1289
      BUILD
  2. 417
      Makefile
  3. 137
      build.json
  4. 42
      include/grpc/support/port_platform.h
  5. 8
      include/grpc/support/slice_buffer.h
  6. 2
      include/grpc/support/sync.h
  7. 77
      include/grpc/support/tls.h
  8. 52
      include/grpc/support/tls_gcc.h
  9. 52
      include/grpc/support/tls_msvc.h
  10. 53
      include/grpc/support/tls_pthread.h
  11. 59
      src/compiler/cpp_generator.cc
  12. 85
      src/core/iomgr/endpoint_pair_windows.c
  13. 4
      src/core/iomgr/pollset_multipoller_with_poll_posix.c
  14. 4
      src/core/iomgr/pollset_posix.c
  15. 3
      src/core/iomgr/tcp_server_posix.c
  16. 16
      src/core/security/factories.c
  17. 7
      src/core/security/security_context.h
  18. 11
      src/core/security/server_secure_chttp2.c
  19. 57
      src/core/support/slice_buffer.c
  20. 20
      src/core/support/time_win32.c
  21. 5
      src/core/transport/chttp2_transport.c
  22. 35
      src/cpp/client/secure_credentials.cc
  23. 61
      src/cpp/client/secure_credentials.h
  24. 31
      src/cpp/server/secure_server_credentials.cc
  25. 60
      src/cpp/server/secure_server_credentials.h
  26. 40
      src/node/examples/math_server.js
  27. 2
      src/node/package.json
  28. 8
      src/node/src/server.js
  29. 20
      src/node/test/math_client_test.js
  30. 2
      src/python/interop/interop/client.py
  31. 2
      src/python/interop/interop/methods.py
  32. 3
      src/python/src/grpc/early_adopter/implementations.py
  33. 204
      src/ruby/ext/grpc/rb_byte_buffer.c
  34. 16
      src/ruby/ext/grpc/rb_byte_buffer.h
  35. 785
      src/ruby/ext/grpc/rb_call.c
  36. 15
      src/ruby/ext/grpc/rb_call.h
  37. 69
      src/ruby/ext/grpc/rb_channel.c
  38. 4
      src/ruby/ext/grpc/rb_channel.h
  39. 4
      src/ruby/ext/grpc/rb_channel_args.c
  40. 40
      src/ruby/ext/grpc/rb_completion_queue.c
  41. 12
      src/ruby/ext/grpc/rb_completion_queue.h
  42. 25
      src/ruby/ext/grpc/rb_credentials.c
  43. 4
      src/ruby/ext/grpc/rb_credentials.h
  44. 361
      src/ruby/ext/grpc/rb_event.c
  45. 112
      src/ruby/ext/grpc/rb_grpc.c
  46. 23
      src/ruby/ext/grpc/rb_grpc.h
  47. 215
      src/ruby/ext/grpc/rb_metadata.c
  48. 112
      src/ruby/ext/grpc/rb_server.c
  49. 4
      src/ruby/ext/grpc/rb_server.h
  50. 17
      src/ruby/ext/grpc/rb_server_credentials.c
  51. 4
      src/ruby/ext/grpc/rb_server_credentials.h
  52. 1
      src/ruby/lib/grpc.rb
  53. 44
      src/ruby/lib/grpc/core/event.rb
  54. 4
      src/ruby/lib/grpc/errors.rb
  55. 183
      src/ruby/lib/grpc/generic/active_call.rb
  56. 69
      src/ruby/lib/grpc/generic/bidi_call.rb
  57. 52
      src/ruby/lib/grpc/generic/client_stub.rb
  58. 9
      src/ruby/lib/grpc/generic/rpc_desc.rb
  59. 91
      src/ruby/lib/grpc/generic/rpc_server.rb
  60. 67
      src/ruby/spec/byte_buffer_spec.rb
  61. 63
      src/ruby/spec/call_spec.rb
  62. 29
      src/ruby/spec/channel_spec.rb
  63. 356
      src/ruby/spec/client_server_spec.rb
  64. 53
      src/ruby/spec/event_spec.rb
  65. 144
      src/ruby/spec/generic/active_call_spec.rb
  66. 136
      src/ruby/spec/generic/client_stub_spec.rb
  67. 57
      src/ruby/spec/generic/rpc_desc_spec.rb
  68. 2
      src/ruby/spec/generic/rpc_server_spec.rb
  69. 64
      src/ruby/spec/metadata_spec.rb
  70. 79
      templates/BUILD.template
  71. 7
      templates/Makefile.template
  72. 3
      templates/tools/run_tests/tests.json.template
  73. 31
      templates/vsprojects/vs2013/Grpc.mak.template
  74. 2
      test/core/end2end/dualstack_socket_test.c
  75. 4
      test/core/iomgr/alarm_test.c
  76. 1
      test/core/iomgr/tcp_server_posix_test.c
  77. 82
      test/core/support/tls_test.c
  78. 2
      test/core/transport/chttp2_transport_end2end_test.c
  79. 158
      test/core/util/port_windows.c
  80. 394
      test/cpp/interop/client.cc
  81. 119
      test/cpp/interop/client_helper.cc
  82. 28
      test/cpp/interop/client_helper.h
  83. 311
      test/cpp/interop/interop_client.cc
  84. 79
      test/cpp/interop/interop_client.h
  85. 13
      test/cpp/interop/server.cc
  86. 69
      test/cpp/interop/server_helper.cc
  87. 24
      test/cpp/interop/server_helper.h
  88. 32
      test/cpp/qps/client.h
  89. 36
      test/cpp/qps/client_async.cc
  90. 13
      test/cpp/qps/client_sync.cc
  91. 4
      test/cpp/qps/histogram.h
  92. 37
      test/cpp/qps/qps_driver.cc
  93. 94
      test/cpp/qps/report.cc
  94. 57
      test/cpp/qps/report.h
  95. 20
      test/cpp/qps/server_async.cc
  96. 149
      test/cpp/qps/smoke_test.cc
  97. 28
      test/cpp/qps/smoke_test.sh
  98. 8
      tools/buildgen/generate_projects.sh
  99. 37
      tools/buildgen/plugins/expand_bin_attrs.py
  100. 3
      tools/dockerfile/grpc_python/Dockerfile
  101. Some files were not shown because too many files have changed in this diff Show More

1289
BUILD

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -29,6 +29,12 @@
"include/grpc++/impl/rpc_method.h", "include/grpc++/impl/rpc_method.h",
"include/grpc++/impl/rpc_service_method.h", "include/grpc++/impl/rpc_service_method.h",
"include/grpc++/impl/service_type.h", "include/grpc++/impl/service_type.h",
"include/grpc++/impl/sync.h",
"include/grpc++/impl/sync_cxx11.h",
"include/grpc++/impl/sync_no_cxx11.h",
"include/grpc++/impl/thd.h",
"include/grpc++/impl/thd_cxx11.h",
"include/grpc++/impl/thd_no_cxx11.h",
"include/grpc++/server.h", "include/grpc++/server.h",
"include/grpc++/server_builder.h", "include/grpc++/server_builder.h",
"include/grpc++/server_context.h", "include/grpc++/server_context.h",
@ -191,6 +197,7 @@
"src/core/iomgr/alarm_heap.c", "src/core/iomgr/alarm_heap.c",
"src/core/iomgr/endpoint.c", "src/core/iomgr/endpoint.c",
"src/core/iomgr/endpoint_pair_posix.c", "src/core/iomgr/endpoint_pair_posix.c",
"src/core/iomgr/endpoint_pair_windows.c",
"src/core/iomgr/fd_posix.c", "src/core/iomgr/fd_posix.c",
"src/core/iomgr/iocp_windows.c", "src/core/iomgr/iocp_windows.c",
"src/core/iomgr/iomgr.c", "src/core/iomgr/iomgr.c",
@ -297,6 +304,10 @@
"include/grpc/support/sync_win32.h", "include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h", "include/grpc/support/thd.h",
"include/grpc/support/time.h", "include/grpc/support/time.h",
"include/grpc/support/tls.h",
"include/grpc/support/tls_gcc.h",
"include/grpc/support/tls_msvc.h",
"include/grpc/support/tls_pthread.h",
"include/grpc/support/useful.h" "include/grpc/support/useful.h"
], ],
"headers": [ "headers": [
@ -434,6 +445,7 @@
"test/core/util/grpc_profiler.c", "test/core/util/grpc_profiler.c",
"test/core/util/parse_hexstring.c", "test/core/util/parse_hexstring.c",
"test/core/util/port_posix.c", "test/core/util/port_posix.c",
"test/core/util/port_windows.c",
"test/core/util/slice_splitter.c" "test/core/util/slice_splitter.c"
], ],
"deps": [ "deps": [
@ -464,6 +476,10 @@
"name": "grpc++", "name": "grpc++",
"build": "all", "build": "all",
"language": "c++", "language": "c++",
"headers": [
"src/cpp/client/secure_credentials.h",
"src/cpp/server/secure_server_credentials.h"
],
"src": [ "src": [
"src/cpp/client/secure_credentials.cc", "src/cpp/client/secure_credentials.cc",
"src/cpp/server/secure_server_credentials.cc" "src/cpp/server/secure_server_credentials.cc"
@ -531,6 +547,74 @@
"deps": [], "deps": [],
"secure": "no" "secure": "no"
}, },
{
"name": "interop_client_helper",
"build": "private",
"language": "c++",
"src": [
"test/cpp/interop/client_helper.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr"
]
},
{
"name": "interop_client_main",
"build": "private",
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
"test/cpp/interop/messages.proto",
"test/cpp/interop/test.proto",
"test/cpp/interop/client.cc",
"test/cpp/interop/interop_client.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "interop_server_helper",
"build": "private",
"language": "c++",
"src": [
"test/cpp/interop/server_helper.cc"
],
"deps": [
"grpc_test_util",
"grpc++",
"grpc",
"gpr"
]
},
{
"name": "interop_server_main",
"build": "private",
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
"test/cpp/interop/messages.proto",
"test/cpp/interop/test.proto",
"test/cpp/interop/server.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "pubsub_client_lib", "name": "pubsub_client_lib",
"build": "private", "build": "private",
@ -554,11 +638,13 @@
"language": "c++", "language": "c++",
"headers": [ "headers": [
"test/cpp/qps/driver.h", "test/cpp/qps/driver.h",
"test/cpp/qps/report.h",
"test/cpp/qps/timer.h" "test/cpp/qps/timer.h"
], ],
"src": [ "src": [
"test/cpp/qps/qpstest.proto", "test/cpp/qps/qpstest.proto",
"test/cpp/qps/driver.cc", "test/cpp/qps/driver.cc",
"test/cpp/qps/report.cc",
"test/cpp/qps/timer.cc" "test/cpp/qps/timer.cc"
] ]
}, },
@ -857,6 +943,9 @@
"grpc", "grpc",
"gpr_test_util", "gpr_test_util",
"gpr" "gpr"
],
"platforms": [
"posix"
] ]
}, },
{ {
@ -1144,6 +1233,18 @@
"gpr" "gpr"
] ]
}, },
{
"name": "gpr_tls_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/tls_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{ {
"name": "gpr_useful_test", "name": "gpr_useful_test",
"build": "test", "build": "test",
@ -1861,13 +1962,10 @@
"build": "test", "build": "test",
"run": false, "run": false,
"language": "c++", "language": "c++",
"src": [ "src": [],
"test/cpp/interop/empty.proto",
"test/cpp/interop/messages.proto",
"test/cpp/interop/test.proto",
"test/cpp/interop/client.cc"
],
"deps": [ "deps": [
"interop_client_main",
"interop_client_helper",
"grpc++_test_util", "grpc++_test_util",
"grpc_test_util", "grpc_test_util",
"grpc++", "grpc++",
@ -1881,13 +1979,10 @@
"build": "test", "build": "test",
"run": false, "run": false,
"language": "c++", "language": "c++",
"src": [ "src": [],
"test/cpp/interop/empty.proto",
"test/cpp/interop/messages.proto",
"test/cpp/interop/test.proto",
"test/cpp/interop/server.cc"
],
"deps": [ "deps": [
"interop_server_main",
"interop_server_helper",
"grpc++_test_util", "grpc++_test_util",
"grpc_test_util", "grpc_test_util",
"grpc++", "grpc++",
@ -1979,6 +2074,24 @@
"gpr" "gpr"
] ]
}, },
{
"name": "qps_smoke_test",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/qps/smoke_test.cc"
],
"deps": [
"qps",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "qps_worker", "name": "qps_worker",
"build": "test", "build": "test",

@ -55,14 +55,17 @@
#define GPR_WINSOCK_SOCKET 1 #define GPR_WINSOCK_SOCKET 1
#ifdef __GNUC__ #ifdef __GNUC__
#define GPR_GCC_ATOMIC 1 #define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#else #else
#define GPR_WIN32_ATOMIC 1 #define GPR_WIN32_ATOMIC 1
#define GPR_MSVC_TLS 1
#endif #endif
#elif defined(ANDROID) || defined(__ANDROID__) #elif defined(ANDROID) || defined(__ANDROID__)
#define GPR_ANDROID 1 #define GPR_ANDROID 1
#define GPR_ARCH_32 1 #define GPR_ARCH_32 1
#define GPR_CPU_LINUX 1 #define GPR_CPU_LINUX 1
#define GPR_GCC_SYNC 1 #define GPR_GCC_SYNC 1
#define GPR_GCC_TLS 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1 #define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1 #define GPR_LINUX_EVENTFD 1
@ -88,6 +91,7 @@
#include <features.h> #include <features.h>
#define GPR_CPU_LINUX 1 #define GPR_CPU_LINUX 1
#define GPR_GCC_ATOMIC 1 #define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#define GPR_LINUX 1 #define GPR_LINUX 1
#define GPR_LINUX_MULTIPOLL_WITH_EPOLL 1 #define GPR_LINUX_MULTIPOLL_WITH_EPOLL 1
#define GPR_POSIX_WAKEUP_FD 1 #define GPR_POSIX_WAKEUP_FD 1
@ -134,6 +138,32 @@
#define GPR_CPU_POSIX 1 #define GPR_CPU_POSIX 1
#endif #endif
#define GPR_GCC_ATOMIC 1 #define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#define GPR_POSIX_LOG 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
#define GPR_POSIX_ENV 1
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1
#define GPR_GETPID_IN_UNISTD_H 1
#ifdef _LP64
#define GPR_ARCH_64 1
#else /* _LP64 */
#define GPR_ARCH_32 1
#endif /* _LP64 */
#elif defined(__FreeBSD__)
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#define GPR_CPU_POSIX 1
#define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#define GPR_POSIX_LOG 1 #define GPR_POSIX_LOG 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1 #define GPR_POSIX_WAKEUP_FD 1
@ -190,16 +220,20 @@
#error Must define exactly one of GPR_ARCH_32, GPR_ARCH_64 #error Must define exactly one of GPR_ARCH_32, GPR_ARCH_64
#endif #endif
#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WIN32) + defined(GPR_CPU_IPHONE) != 1 #if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WIN32) + defined(GPR_CPU_IPHONE) + defined(GPR_CPU_CUSTOM) != 1
#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32, GPR_CPU_IPHONE #error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32, GPR_CPU_IPHONE, GPR_CPU_CUSTOM
#endif #endif
#if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_SOCKET) #if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_SOCKET)
#error Must define GPR_POSIX_SOCKET to use GPR_POSIX_MULTIPOLL_WITH_POLL #error Must define GPR_POSIX_SOCKET to use GPR_POSIX_MULTIPOLL_WITH_POLL
#endif #endif
#if defined(GPR_POSIX_SOCKET) + defined(GPR_WIN32) != 1 #if defined(GPR_POSIX_SOCKET) + defined(GPR_WINSOCK_SOCKET) + defined(GPR_CUSTOM_SOCKET) != 1
#error Must define exactly one of GPR_POSIX_SOCKET, GPR_WIN32 #error Must define exactly one of GPR_POSIX_SOCKET, GPR_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET
#endif
#if defined(GPR_MSVC_TLS) + defined(GPR_GCC_TLS) + defined(GPR_PTHREAD_TLS) + defined(GPR_CUSTOM_TLS) != 1
#error Must define exactly one of GPR_MSVC_TLS, GPR_GCC_TLS, GPR_PTHREAD_TLS, defined(GPR_CUSTOM_TLS)
#endif #endif
typedef int16_t gpr_int16; typedef int16_t gpr_int16;

@ -40,6 +40,8 @@
extern "C" { extern "C" {
#endif #endif
#define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8
/* Represents an expandable array of slices, to be interpreted as a single item /* Represents an expandable array of slices, to be interpreted as a single item
TODO(ctiller): inline some small number of elements into the struct, to TODO(ctiller): inline some small number of elements into the struct, to
avoid per-call allocations */ avoid per-call allocations */
@ -52,6 +54,8 @@ typedef struct {
size_t capacity; size_t capacity;
/* the combined length of all slices in the array */ /* the combined length of all slices in the array */
size_t length; size_t length;
/* inlined elements to avoid allocations */
gpr_slice inlined[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
} gpr_slice_buffer; } gpr_slice_buffer;
/* initialize a slice buffer */ /* initialize a slice buffer */
@ -78,9 +82,11 @@ gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned len);
void gpr_slice_buffer_pop(gpr_slice_buffer *sb); void gpr_slice_buffer_pop(gpr_slice_buffer *sb);
/* clear a slice buffer, unref all elements */ /* clear a slice buffer, unref all elements */
void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb); void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb);
/* swap the contents of two slice buffers */
void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* GRPC_SUPPORT_SLICE_BUFFER_H */ #endif /* GRPC_SUPPORT_SLICE_BUFFER_H */

@ -60,7 +60,7 @@
#include <grpc/support/sync_posix.h> #include <grpc/support/sync_posix.h>
#elif defined(GPR_WIN32) #elif defined(GPR_WIN32)
#include <grpc/support/sync_win32.h> #include <grpc/support/sync_win32.h>
#else #elif !defined(GPR_CUSTOM_SYNC)
#error Unable to determine platform for sync #error Unable to determine platform for sync
#endif #endif

@ -0,0 +1,77 @@
/*
*
* 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.
*
*/
#ifndef GRPC_SUPPORT_TLS_H
#define GRPC_SUPPORT_TLS_H
#include "port_platform.h"
/* Thread local storage.
A minimal wrapper that should be implementable across many compilers,
and implementable efficiently across most modern compilers.
Thread locals have type gpr_intptr.
Declaring a thread local variable 'foo':
GPR_TLS_DECL(foo, initial_value);
Thread locals always have static scope.
Initializing a thread local (must be done at library initialization
time):
gpr_tls_init(&foo);
Destroying a thread local:
gpr_tls_destroy(&foo);
Setting a thread local:
gpr_tls_set(&foo, new_value);
Accessing a thread local:
current_value = gpr_tls_get(&foo, value);
ALL functions here may be implemented as macros. */
#ifdef GPR_GCC_TLS
#include "tls_gcc.h"
#endif
#ifdef GPR_MSVC_TLS
#include "tls_msvc.h"
#endif
#ifdef GPR_PTHREAD_TLS
#include "tls_pthread.h"
#endif
#endif

@ -0,0 +1,52 @@
/*
*
* 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.
*
*/
#ifndef GRPC_SUPPORT_TLS_GCC_H
#define GRPC_SUPPORT_TLS_GCC_H
/* Thread local storage based on gcc compiler primitives.
#include tls.h to use this - and see that file for documentation */
struct gpr_gcc_thread_local {
gpr_intptr value;
};
#define GPR_TLS_DECL(name) \
static __thread struct gpr_gcc_thread_local name = {0}
#define gpr_tls_init(tls) do {} while (0)
#define gpr_tls_destroy(tls) do {} while (0)
#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value))
#define gpr_tls_get(tls) ((tls)->value)
#endif

@ -0,0 +1,52 @@
/*
*
* 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.
*
*/
#ifndef GRPC_SUPPORT_TLS_GCC_H
#define GRPC_SUPPORT_TLS_GCC_H
/* Thread local storage based on ms visual c compiler primitives.
#include tls.h to use this - and see that file for documentation */
struct gpr_msvc_thread_local {
gpr_intptr value;
};
#define GPR_TLS_DECL(name) \
static __thread struct gpr_msvc_thread_local name = {0}
#define gpr_tls_init(tls) do {} while (0)
#define gpr_tls_destroy(tls) do {} while (0)
#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value))
#define gpr_tls_get(tls) ((tls)->value)
#endif

@ -0,0 +1,53 @@
/*
*
* 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.
*
*/
#ifndef GRPC_SUPPORT_TLS_PTHREAD_H
#define GRPC_SUPPORT_TLS_PTHREAD_H
/* Thread local storage based on pthread library calls.
#include tls.h to use this - and see that file for documentation */
struct gpr_pthread_thread_local {
pthread_key_t key;
};
#define GPR_TLS_DECL(name) \
static struct gpr_pthread_thread_local name = {0}
#define gpr_tls_init(tls) GPR_ASSERT(0 == pthread_key_create(&(tls)->key, NULL))
#define gpr_tls_destroy(tls) pthread_key_delete((tls)->key)
#define gpr_tls_set(tls, new_value) \
GPR_ASSERT(pthread_setspecific((tls)->key, (void*)(new_value)) == 0)
#define gpr_tls_get(tls) ((gpr_intptr)pthread_getspecific((tls)->key))
#endif

@ -198,17 +198,18 @@ grpc::string GetHeaderIncludes(const grpc::protobuf::FileDescriptor *file,
temp.append("\n"); temp.append("\n");
std::vector<grpc::string> parts = if (!file->package().empty()) {
grpc_generator::tokenize(file->package(), "."); std::vector<grpc::string> parts =
grpc_generator::tokenize(file->package(), ".");
for (auto part = parts.begin(); part != parts.end(); part++) { for (auto part = parts.begin(); part != parts.end(); part++) {
temp.append("namespace "); temp.append("namespace ");
temp.append(*part); temp.append(*part);
temp.append(" {\n"); temp.append(" {\n");
}
temp.append("\n");
} }
temp.append("\n");
return temp; return temp;
} }
@ -431,15 +432,18 @@ grpc::string GetHeaderEpilogue(const grpc::protobuf::FileDescriptor *file,
vars["filename"] = file->name(); vars["filename"] = file->name();
vars["filename_identifier"] = FilenameIdentifier(file->name()); vars["filename_identifier"] = FilenameIdentifier(file->name());
std::vector<grpc::string> parts = if (!file->package().empty()) {
grpc_generator::tokenize(file->package(), "."); std::vector<grpc::string> parts =
grpc_generator::tokenize(file->package(), ".");
for (auto part = parts.rbegin(); part != parts.rend(); part++) { for (auto part = parts.rbegin(); part != parts.rend(); part++) {
vars["part"] = *part; vars["part"] = *part;
printer.Print(vars, "} // namespace $part$\n"); printer.Print(vars, "} // namespace $part$\n");
}
printer.Print(vars, "\n");
} }
printer.Print(vars, "\n\n"); printer.Print(vars, "\n");
printer.Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n"); printer.Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n");
return output; return output;
@ -480,12 +484,14 @@ grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file,
printer.Print(vars, "#include <grpc++/impl/service_type.h>\n"); printer.Print(vars, "#include <grpc++/impl/service_type.h>\n");
printer.Print(vars, "#include <grpc++/stream.h>\n"); printer.Print(vars, "#include <grpc++/stream.h>\n");
std::vector<grpc::string> parts = if (!file->package().empty()) {
grpc_generator::tokenize(file->package(), "."); std::vector<grpc::string> parts =
grpc_generator::tokenize(file->package(), ".");
for (auto part = parts.begin(); part != parts.end(); part++) { for (auto part = parts.begin(); part != parts.end(); part++) {
vars["part"] = *part; vars["part"] = *part;
printer.Print(vars, "namespace $part$ {\n"); printer.Print(vars, "namespace $part$ {\n");
}
} }
printer.Print(vars, "\n"); printer.Print(vars, "\n");
@ -860,17 +866,18 @@ grpc::string GetSourceEpilogue(const grpc::protobuf::FileDescriptor *file,
const Parameters &params) { const Parameters &params) {
grpc::string temp; grpc::string temp;
std::vector<grpc::string> parts = if (!file->package().empty()) {
grpc_generator::tokenize(file->package(), "."); std::vector<grpc::string> parts =
grpc_generator::tokenize(file->package(), ".");
for (auto part = parts.begin(); part != parts.end(); part++) { for (auto part = parts.begin(); part != parts.end(); part++) {
temp.append("} // namespace "); temp.append("} // namespace ");
temp.append(*part); temp.append(*part);
temp.append("\n");
}
temp.append("\n"); temp.append("\n");
} }
temp.append("\n");
return temp; return temp;
} }

@ -0,0 +1,85 @@
/*
*
* 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_WINSOCK_SOCKET
#include "src/core/iomgr/sockaddr_utils.h"
#include "src/core/iomgr/endpoint_pair.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include "src/core/iomgr/tcp_windows.h"
#include "src/core/iomgr/socket_windows.h"
#include <grpc/support/log.h>
static void create_sockets(SOCKET sv[2]) {
SOCKET svr_sock = INVALID_SOCKET;
SOCKET lst_sock = INVALID_SOCKET;
SOCKET cli_sock = INVALID_SOCKET;
SOCKADDR_IN addr;
int addr_len;
lst_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
GPR_ASSERT(lst_sock != INVALID_SOCKET);
memset(&addr, 0, sizeof(addr));
GPR_ASSERT(bind(lst_sock, (struct sockaddr*)&addr, sizeof(addr)) != SOCKET_ERROR);
GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR);
GPR_ASSERT(getsockname(lst_sock, (struct sockaddr*)&addr, &addr_len) != SOCKET_ERROR);
cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
GPR_ASSERT(cli_sock != INVALID_SOCKET);
GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr*)&addr, addr_len, NULL, NULL, NULL, NULL) == 0);
svr_sock = accept(lst_sock, (struct sockaddr*)&addr, &addr_len);
GPR_ASSERT(svr_sock != INVALID_SOCKET);
closesocket(lst_sock);
sv[1] = cli_sock;
sv[0] = svr_sock;
}
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(size_t read_slice_size) {
SOCKET sv[2];
grpc_endpoint_pair p;
create_sockets(sv);
p.client = grpc_tcp_create(grpc_winsocket_create(sv[1]));
p.server = grpc_tcp_create(grpc_winsocket_create(sv[0]));
return p;
}
#endif

@ -172,6 +172,9 @@ static int multipoll_with_poll_pollset_maybe_work(
} }
r = poll(h->pfds, h->pfd_count, timeout); r = poll(h->pfds, h->pfd_count, timeout);
end_polling(pollset);
if (r < 0) { if (r < 0) {
if (errno != EINTR) { if (errno != EINTR) {
gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno));
@ -192,7 +195,6 @@ static int multipoll_with_poll_pollset_maybe_work(
} }
} }
grpc_pollset_kick_post_poll(&pollset->kick_state); grpc_pollset_kick_post_poll(&pollset->kick_state);
end_polling(pollset);
gpr_mu_lock(&pollset->mu); gpr_mu_lock(&pollset->mu);
pollset->counter = 0; pollset->counter = 0;

@ -396,6 +396,9 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT, &fd_watcher); pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT, &fd_watcher);
r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout); r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout);
grpc_fd_end_poll(&fd_watcher);
if (r < 0) { if (r < 0) {
if (errno != EINTR) { if (errno != EINTR) {
gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno));
@ -415,7 +418,6 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
} }
grpc_pollset_kick_post_poll(&pollset->kick_state); grpc_pollset_kick_post_poll(&pollset->kick_state);
grpc_fd_end_poll(&fd_watcher);
gpr_mu_lock(&pollset->mu); gpr_mu_lock(&pollset->mu);
pollset->counter = 0; pollset->counter = 0;

@ -174,7 +174,6 @@ void grpc_tcp_server_destroy(
while (s->active_ports) { while (s->active_ports) {
gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future); gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future);
} }
gpr_mu_unlock(&s->mu);
/* delete ALL the things */ /* delete ALL the things */
if (s->nports) { if (s->nports) {
@ -185,7 +184,9 @@ void grpc_tcp_server_destroy(
} }
grpc_fd_orphan(sp->emfd, destroyed_port, s); grpc_fd_orphan(sp->emfd, destroyed_port, s);
} }
gpr_mu_unlock(&s->mu);
} else { } else {
gpr_mu_unlock(&s->mu);
finish_shutdown(s); finish_shutdown(s);
} }
} }

@ -50,3 +50,19 @@ grpc_channel *grpc_secure_channel_create(grpc_credentials *creds,
return grpc_secure_channel_create_with_factories( return grpc_secure_channel_create_with_factories(
factories, GPR_ARRAY_SIZE(factories), creds, target, args); factories, GPR_ARRAY_SIZE(factories), creds, target, args);
} }
grpc_security_status grpc_server_security_context_create(
grpc_server_credentials *creds, grpc_security_context **ctx) {
grpc_security_status status = GRPC_SECURITY_ERROR;
*ctx = NULL;
if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL) == 0) {
status = grpc_ssl_server_security_context_create(
grpc_ssl_server_credentials_get_config(creds), ctx);
} else if (strcmp(creds->type,
GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) == 0) {
*ctx = grpc_fake_server_security_context_create();
status = GRPC_SECURITY_OK;
}
return status;
}

@ -206,10 +206,9 @@ grpc_channel *grpc_secure_channel_create_with_factories(
const grpc_secure_channel_factory *factories, size_t num_factories, const grpc_secure_channel_factory *factories, size_t num_factories,
grpc_credentials *creds, const char *target, const grpc_channel_args *args); grpc_credentials *creds, const char *target, const grpc_channel_args *args);
/* Secure server creation. */ /* Secure server context creation. */
grpc_server *grpc_secure_server_create_internal(grpc_completion_queue *cq, grpc_security_status grpc_server_security_context_create(
const grpc_channel_args *args, grpc_server_credentials *creds, grpc_security_context **ctx);
grpc_security_context *ctx);
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */ #endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */

@ -141,16 +141,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
/* create security context */ /* create security context */
if (creds == NULL) goto error; if (creds == NULL) goto error;
status = grpc_server_security_context_create(creds, &ctx);
if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL) == 0) {
status = grpc_ssl_server_security_context_create(
grpc_ssl_server_credentials_get_config(creds), &ctx);
} else if (strcmp(creds->type,
GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) == 0) {
ctx = grpc_fake_server_security_context_create();
status = GRPC_SECURITY_OK;
}
if (status != GRPC_SECURITY_OK) { if (status != GRPC_SECURITY_OK) {
gpr_log(GPR_ERROR, gpr_log(GPR_ERROR,
"Unable to create secure server with credentials of type %s.", "Unable to create secure server with credentials of type %s.",

@ -38,21 +38,34 @@
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
/* initial allocation size (# of slices) */ /* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */
#define INITIAL_CAPACITY 4
/* grow a buffer; requires INITIAL_CAPACITY > 1 */
#define GROW(x) (3 * (x) / 2) #define GROW(x) (3 * (x) / 2)
static void maybe_embiggen(gpr_slice_buffer *sb) {
if (sb->count == sb->capacity) {
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
if (sb->slices == sb->inlined) {
sb->slices = gpr_malloc(sb->capacity * sizeof(gpr_slice));
memcpy(sb->slices, sb->inlined, sb->count * sizeof(gpr_slice));
} else {
sb->slices = gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
}
}
void gpr_slice_buffer_init(gpr_slice_buffer *sb) { void gpr_slice_buffer_init(gpr_slice_buffer *sb) {
sb->count = 0; sb->count = 0;
sb->length = 0; sb->length = 0;
sb->capacity = INITIAL_CAPACITY; sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS;
sb->slices = gpr_malloc(sizeof(gpr_slice) * INITIAL_CAPACITY); sb->slices = sb->inlined;
} }
void gpr_slice_buffer_destroy(gpr_slice_buffer *sb) { void gpr_slice_buffer_destroy(gpr_slice_buffer *sb) {
gpr_slice_buffer_reset_and_unref(sb); gpr_slice_buffer_reset_and_unref(sb);
gpr_free(sb->slices); if (sb->slices != sb->inlined) {
gpr_free(sb->slices);
}
} }
gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) { gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) {
@ -71,11 +84,7 @@ gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) {
return out; return out;
add_new: add_new:
if (sb->count == sb->capacity) { maybe_embiggen(sb);
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
sb->slices = gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
back = &sb->slices[sb->count]; back = &sb->slices[sb->count];
sb->count++; sb->count++;
back->refcount = NULL; back->refcount = NULL;
@ -85,11 +94,7 @@ add_new:
size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice s) { size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice s) {
size_t out = sb->count; size_t out = sb->count;
if (out == sb->capacity) { maybe_embiggen(sb);
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
sb->slices = gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
sb->slices[out] = s; sb->slices[out] = s;
sb->length += GPR_SLICE_LENGTH(s); sb->length += GPR_SLICE_LENGTH(s);
sb->count = out + 1; sb->count = out + 1;
@ -116,12 +121,7 @@ void gpr_slice_buffer_add(gpr_slice_buffer *sb, gpr_slice s) {
memcpy(back->data.inlined.bytes + back->data.inlined.length, memcpy(back->data.inlined.bytes + back->data.inlined.length,
s.data.inlined.bytes, cp1); s.data.inlined.bytes, cp1);
back->data.inlined.length = GPR_SLICE_INLINED_SIZE; back->data.inlined.length = GPR_SLICE_INLINED_SIZE;
if (n == sb->capacity) { maybe_embiggen(sb);
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
sb->slices =
gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
back = &sb->slices[n]; back = &sb->slices[n];
sb->count = n + 1; sb->count = n + 1;
back->refcount = NULL; back->refcount = NULL;
@ -160,3 +160,16 @@ void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb) {
sb->count = 0; sb->count = 0;
sb->length = 0; sb->length = 0;
} }
void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b) {
gpr_slice_buffer temp = *a;
*a = *b;
*b = temp;
if (a->slices == b->inlined) {
a->slices = a->inlined;
}
if (b->slices == a->inlined) {
b->slices = b->inlined;
}
}

@ -39,6 +39,7 @@
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include <sys/timeb.h> #include <sys/timeb.h>
#include <windows.h>
gpr_timespec gpr_now(void) { gpr_timespec gpr_now(void) {
gpr_timespec now_tv; gpr_timespec now_tv;
@ -49,4 +50,23 @@ gpr_timespec gpr_now(void) {
return now_tv; return now_tv;
} }
void gpr_sleep_until(gpr_timespec until) {
gpr_timespec now;
gpr_timespec delta;
DWORD sleep_millis;
for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be
* slightly less portable. */
now = gpr_now();
if (gpr_time_cmp(until, now) <= 0) {
return;
}
delta = gpr_time_sub(until, now);
sleep_millis = delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS;
Sleep(sleep_millis);
}
}
#endif /* GPR_WIN32 */ #endif /* GPR_WIN32 */

@ -834,13 +834,10 @@ static void push_setting(transport *t, grpc_chttp2_setting_id id,
static int prepare_write(transport *t) { static int prepare_write(transport *t) {
stream *s; stream *s;
gpr_slice_buffer tempbuf;
gpr_uint32 window_delta; gpr_uint32 window_delta;
/* simple writes are queued to qbuf, and flushed here */ /* simple writes are queued to qbuf, and flushed here */
tempbuf = t->qbuf; gpr_slice_buffer_swap(&t->qbuf, &t->outbuf);
t->qbuf = t->outbuf;
t->outbuf = tempbuf;
GPR_ASSERT(t->qbuf.count == 0); GPR_ASSERT(t->qbuf.count == 0);
if (t->dirtied_local_settings && !t->sent_local_settings) { if (t->dirtied_local_settings && !t->sent_local_settings) {

@ -31,38 +31,23 @@
* *
*/ */
#include <grpc/grpc_security.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc++/channel_arguments.h> #include <grpc++/channel_arguments.h>
#include <grpc++/config.h>
#include <grpc++/credentials.h>
#include "src/cpp/client/channel.h" #include "src/cpp/client/channel.h"
#include "src/cpp/client/secure_credentials.h"
namespace grpc { namespace grpc {
class SecureCredentials GRPC_FINAL : public Credentials { std::shared_ptr<grpc::ChannelInterface> SecureCredentials::CreateChannel(
public: const string& target, const grpc::ChannelArguments& args) {
explicit SecureCredentials(grpc_credentials* c_creds) : c_creds_(c_creds) {} grpc_channel_args channel_args;
~SecureCredentials() GRPC_OVERRIDE { grpc_credentials_release(c_creds_); } args.SetChannelArgs(&channel_args);
grpc_credentials* GetRawCreds() { return c_creds_; } return std::shared_ptr<ChannelInterface>(new Channel(
args.GetSslTargetNameOverride().empty() ? target
std::shared_ptr<grpc::ChannelInterface> CreateChannel( : args.GetSslTargetNameOverride(),
const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE { grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args)));
grpc_channel_args channel_args; }
args.SetChannelArgs(&channel_args);
return std::shared_ptr<ChannelInterface>(new Channel(
args.GetSslTargetNameOverride().empty()
? target
: args.GetSslTargetNameOverride(),
grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args)));
}
SecureCredentials* AsSecureCredentials() GRPC_OVERRIDE { return this; }
private:
grpc_credentials* const c_creds_;
};
namespace { namespace {
std::unique_ptr<Credentials> WrapCredentials(grpc_credentials* creds) { std::unique_ptr<Credentials> WrapCredentials(grpc_credentials* creds) {

@ -0,0 +1,61 @@
/*
*
* 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.
*
*/
#ifndef GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H
#define GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H
#include <grpc/grpc_security.h>
#include <grpc++/config.h>
#include <grpc++/credentials.h>
namespace grpc {
class SecureCredentials GRPC_FINAL : public Credentials {
public:
explicit SecureCredentials(grpc_credentials* c_creds) : c_creds_(c_creds) {}
~SecureCredentials() GRPC_OVERRIDE { grpc_credentials_release(c_creds_); }
grpc_credentials* GetRawCreds() { return c_creds_; }
std::shared_ptr<grpc::ChannelInterface> CreateChannel(
const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE;
SecureCredentials* AsSecureCredentials() GRPC_OVERRIDE { return this; }
private:
grpc_credentials* const c_creds_;
};
} // namespace grpc
#endif // GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H

@ -31,39 +31,22 @@
* *
*/ */
#include <grpc/grpc_security.h> #include "src/cpp/server/secure_server_credentials.h"
#include <grpc++/server_credentials.h>
namespace grpc { namespace grpc {
namespace { int SecureServerCredentials::AddPortToServer(
class SecureServerCredentials GRPC_FINAL : public ServerCredentials { const grpc::string& addr, grpc_server* server) {
public: return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
explicit SecureServerCredentials(grpc_server_credentials* creds) }
: creds_(creds) {}
~SecureServerCredentials() GRPC_OVERRIDE {
grpc_server_credentials_release(creds_);
}
int AddPortToServer(const grpc::string& addr,
grpc_server* server) GRPC_OVERRIDE {
return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
}
private:
grpc_server_credentials* const creds_;
};
} // namespace
std::shared_ptr<ServerCredentials> SslServerCredentials( std::shared_ptr<ServerCredentials> SslServerCredentials(
const SslServerCredentialsOptions& options) { const SslServerCredentialsOptions& options) {
std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs; std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
for (auto key_cert_pair = options.pem_key_cert_pairs.begin(); for (auto key_cert_pair = options.pem_key_cert_pairs.begin();
key_cert_pair != options.pem_key_cert_pairs.end(); key_cert_pair != options.pem_key_cert_pairs.end(); key_cert_pair++) {
key_cert_pair++) {
grpc_ssl_pem_key_cert_pair p = {key_cert_pair->private_key.c_str(), grpc_ssl_pem_key_cert_pair p = {key_cert_pair->private_key.c_str(),
key_cert_pair->cert_chain.c_str()}; key_cert_pair->cert_chain.c_str()};
pem_key_cert_pairs.push_back(p); pem_key_cert_pairs.push_back(p);
} }
grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create( grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create(

@ -0,0 +1,60 @@
/*
*
* 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.
*
*/
#ifndef GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
#define GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
#include <grpc/grpc_security.h>
#include <grpc++/server_credentials.h>
namespace grpc {
class SecureServerCredentials GRPC_FINAL : public ServerCredentials {
public:
explicit SecureServerCredentials(grpc_server_credentials* creds)
: creds_(creds) {}
~SecureServerCredentials() GRPC_OVERRIDE {
grpc_server_credentials_release(creds_);
}
int AddPortToServer(const grpc::string& addr,
grpc_server* server) GRPC_OVERRIDE;
private:
grpc_server_credentials* const creds_;
};
} // namespace grpc
#endif // GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H

@ -33,10 +33,6 @@
'use strict'; 'use strict';
var util = require('util');
var Transform = require('stream').Transform;
var grpc = require('..'); var grpc = require('..');
var math = grpc.load(__dirname + '/math.proto').math; var math = grpc.load(__dirname + '/math.proto').math;
@ -54,11 +50,12 @@ function mathDiv(call, cb) {
// Unary + is explicit coersion to integer // Unary + is explicit coersion to integer
if (+req.divisor === 0) { if (+req.divisor === 0) {
cb(new Error('cannot divide by zero')); cb(new Error('cannot divide by zero'));
} else {
cb(null, {
quotient: req.dividend / req.divisor,
remainder: req.dividend % req.divisor
});
} }
cb(null, {
quotient: req.dividend / req.divisor,
remainder: req.dividend % req.divisor
});
} }
/** /**
@ -97,24 +94,19 @@ function mathSum(call, cb) {
} }
function mathDivMany(stream) { function mathDivMany(stream) {
// Here, call is a standard duplex Node object Stream stream.on('data', function(div_args) {
util.inherits(DivTransform, Transform);
function DivTransform() {
var options = {objectMode: true};
Transform.call(this, options);
}
DivTransform.prototype._transform = function(div_args, encoding, callback) {
if (+div_args.divisor === 0) { if (+div_args.divisor === 0) {
callback(new Error('cannot divide by zero')); stream.emit('error', new Error('cannot divide by zero'));
} else {
stream.write({
quotient: div_args.dividend / div_args.divisor,
remainder: div_args.dividend % div_args.divisor
});
} }
callback(null, { });
quotient: div_args.dividend / div_args.divisor, stream.on('end', function() {
remainder: div_args.dividend % div_args.divisor stream.end();
}); });
};
var transform = new DivTransform();
stream.pipe(transform);
transform.pipe(stream);
} }
var server = new Server({ var server = new Server({

@ -1,6 +1,6 @@
{ {
"name": "grpc", "name": "grpc",
"version": "0.6.0", "version": "0.6.1",
"author": "Google Inc.", "author": "Google Inc.",
"description": "gRPC Library for Node", "description": "gRPC Library for Node",
"homepage": "http://www.grpc.io/", "homepage": "http://www.grpc.io/",

@ -360,7 +360,9 @@ function handleUnary(call, handler, metadata) {
} }
handler.func(emitter, function sendUnaryData(err, value, trailer) { handler.func(emitter, function sendUnaryData(err, value, trailer) {
if (err) { if (err) {
err.metadata = trailer; if (trailer) {
err.metadata = trailer;
}
handleError(call, err); handleError(call, err);
} else { } else {
sendUnaryResponse(call, value, handler.serialize, trailer); sendUnaryResponse(call, value, handler.serialize, trailer);
@ -406,7 +408,9 @@ function handleClientStreaming(call, handler, metadata) {
handler.func(stream, function(err, value, trailer) { handler.func(stream, function(err, value, trailer) {
stream.terminate(); stream.terminate();
if (err) { if (err) {
err.metadata = trailer; if (trailer) {
err.metadata = trailer;
}
handleError(call, err); handleError(call, err);
} else { } else {
sendUnaryResponse(call, value, handler.serialize, trailer); sendUnaryResponse(call, value, handler.serialize, trailer);

@ -68,6 +68,13 @@ describe('Math client', function() {
done(); done();
}); });
}); });
it('should handle an error from a unary request', function(done) {
var arg = {dividend: 7, divisor: 0};
math_client.div(arg, function handleDivResult(err, value) {
assert(err);
done();
});
});
it('should handle a server streaming request', function(done) { it('should handle a server streaming request', function(done) {
var call = math_client.fib({limit: 7}); var call = math_client.fib({limit: 7});
var expected_results = [1, 1, 2, 3, 5, 8, 13]; var expected_results = [1, 1, 2, 3, 5, 8, 13];
@ -115,4 +122,17 @@ describe('Math client', function() {
done(); done();
}); });
}); });
it('should handle an error from a bidi request', function(done) {
var call = math_client.divMany();
call.on('data', function(value) {
assert.fail(value, undefined, 'Unexpected data response on failing call',
'!=');
});
call.write({dividend: 7, divisor: 0});
call.end();
call.on('status', function checkStatus(status) {
assert.notEqual(status.code, grpc.status.OK);
done();
});
});
}); });

@ -64,7 +64,7 @@ def _args():
return parser.parse_args() return parser.parse_args()
def _oauth_access_token(args): def _oauth_access_token(args):
credentials = client.GoogleCredentials.get_application_default() credentials = oauth2client_client.GoogleCredentials.get_application_default()
scoped_credentials = credentials.create_scoped([args.oauth_scope]) scoped_credentials = credentials.create_scoped([args.oauth_scope])
return scoped_credentials.get_access_token().access_token return scoped_credentials.get_access_token().access_token

@ -292,7 +292,7 @@ def _service_account_creds(stub, args):
if wanted_email != response.username: if wanted_email != response.username:
raise ValueError( raise ValueError(
'expected username %s, got %s' % (wanted_email, response.username)) 'expected username %s, got %s' % (wanted_email, response.username))
if response.oauth_scope in args.oauth_scope: if args.oauth_scope.find(response.oauth_scope) == -1:
raise ValueError( raise ValueError(
'expected to find oauth scope "%s" in received "%s"' % 'expected to find oauth scope "%s" in received "%s"' %
(response.oauth_scope, args.oauth_scope)) (response.oauth_scope, args.oauth_scope))

@ -223,7 +223,8 @@ def stub(
breakdown = _face_utilities.break_down_invocation(service_name, methods) breakdown = _face_utilities.break_down_invocation(service_name, methods)
return _Stub( return _Stub(
breakdown, host, port, secure, root_certificates, private_key, breakdown, host, port, secure, root_certificates, private_key,
certificate_chain, server_host_override=server_host_override) certificate_chain, server_host_override=server_host_override,
metadata_transformer=metadata_transformer)
def server( def server(

@ -39,203 +39,29 @@
#include <grpc/support/slice.h> #include <grpc/support/slice.h>
#include "rb_grpc.h" #include "rb_grpc.h"
/* grpc_rb_byte_buffer wraps a grpc_byte_buffer. It provides a peer ruby grpc_byte_buffer* grpc_rb_s_to_byte_buffer(char *string, size_t length) {
* object, 'mark' to minimize copying when a byte_buffer is created from gpr_slice slice = gpr_slice_from_copied_buffer(string, length);
* ruby. */ grpc_byte_buffer *buffer = grpc_byte_buffer_create(&slice, 1);
typedef struct grpc_rb_byte_buffer { gpr_slice_unref(slice);
/* Holder of ruby objects involved in constructing the status */ return buffer;
VALUE mark;
/* The actual status */
grpc_byte_buffer *wrapped;
} grpc_rb_byte_buffer;
/* Destroys ByteBuffer instances. */
static void grpc_rb_byte_buffer_free(void *p) {
grpc_rb_byte_buffer *bb = NULL;
if (p == NULL) {
return;
};
bb = (grpc_rb_byte_buffer *)p;
/* Deletes the wrapped object if the mark object is Qnil, which indicates
* that no other object is the actual owner. */
if (bb->wrapped != NULL && bb->mark == Qnil) {
grpc_byte_buffer_destroy(bb->wrapped);
}
xfree(p);
}
/* Protects the mark object from GC */
static void grpc_rb_byte_buffer_mark(void *p) {
grpc_rb_byte_buffer *bb = NULL;
if (p == NULL) {
return;
}
bb = (grpc_rb_byte_buffer *)p;
/* If it's not already cleaned up, mark the mark object */
if (bb->mark != Qnil && BUILTIN_TYPE(bb->mark) != T_NONE) {
rb_gc_mark(bb->mark);
}
} }
/* id_source is the name of the hidden ivar the preserves the original VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer) {
* byte_buffer source string */
static ID id_source;
/* Allocates ByteBuffer instances.
Provides safe default values for the byte_buffer fields. */
static VALUE grpc_rb_byte_buffer_alloc(VALUE cls) {
grpc_rb_byte_buffer *wrapper = ALLOC(grpc_rb_byte_buffer);
wrapper->wrapped = NULL;
wrapper->mark = Qnil;
return Data_Wrap_Struct(cls, grpc_rb_byte_buffer_mark,
grpc_rb_byte_buffer_free, wrapper);
}
/* Clones ByteBuffer instances.
Gives ByteBuffer a consistent implementation of Ruby's object copy/dup
protocol. */
static VALUE grpc_rb_byte_buffer_init_copy(VALUE copy, VALUE orig) {
grpc_rb_byte_buffer *orig_bb = NULL;
grpc_rb_byte_buffer *copy_bb = NULL;
if (copy == orig) {
return copy;
}
/* Raise an error if orig is not a metadata object or a subclass. */
if (TYPE(orig) != T_DATA ||
RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_byte_buffer_free) {
rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cByteBuffer));
}
Data_Get_Struct(orig, grpc_rb_byte_buffer, orig_bb);
Data_Get_Struct(copy, grpc_rb_byte_buffer, copy_bb);
/* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper
* object. */
MEMCPY(copy_bb, orig_bb, grpc_rb_byte_buffer, 1);
return copy;
}
/* id_empty is used to return the empty string from to_s when necessary. */
static ID id_empty;
static VALUE grpc_rb_byte_buffer_to_s(VALUE self) {
grpc_rb_byte_buffer *wrapper = NULL;
grpc_byte_buffer *bb = NULL;
grpc_byte_buffer_reader *reader = NULL;
char *output = NULL;
size_t length = 0; size_t length = 0;
char *string = NULL;
size_t offset = 0; size_t offset = 0;
VALUE output_obj = Qnil; grpc_byte_buffer_reader *reader = NULL;
gpr_slice next; gpr_slice next;
if (buffer == NULL) {
return Qnil;
Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper);
output_obj = rb_ivar_get(wrapper->mark, id_source);
if (output_obj != Qnil) {
/* From ruby, ByteBuffers are immutable so if a source is set, return that
* as the to_s value */
return output_obj;
}
/* Read the bytes. */
bb = wrapper->wrapped;
if (bb == NULL) {
return rb_id2str(id_empty);
}
length = grpc_byte_buffer_length(bb);
if (length == 0) {
return rb_id2str(id_empty);
} }
reader = grpc_byte_buffer_reader_create(bb); length = grpc_byte_buffer_length(buffer);
output = xmalloc(length); string = xmalloc(length + 1);
reader = grpc_byte_buffer_reader_create(buffer);
while (grpc_byte_buffer_reader_next(reader, &next) != 0) { while (grpc_byte_buffer_reader_next(reader, &next) != 0) {
memcpy(output + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); memcpy(string + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next));
offset += GPR_SLICE_LENGTH(next); offset += GPR_SLICE_LENGTH(next);
} }
output_obj = rb_str_new(output, length); return rb_str_new(string, length);
/* Save a references to the computed string in the mark object so that the
* calling to_s does not do any allocations. */
wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
rb_ivar_set(wrapper->mark, id_source, output_obj);
return output_obj;
}
/* Initializes ByteBuffer instances. */
static VALUE grpc_rb_byte_buffer_init(VALUE self, VALUE src) {
gpr_slice a_slice;
grpc_rb_byte_buffer *wrapper = NULL;
grpc_byte_buffer *byte_buffer = NULL;
if (TYPE(src) != T_STRING) {
rb_raise(rb_eTypeError, "bad byte_buffer arg: got <%s>, want <String>",
rb_obj_classname(src));
return Qnil;
}
Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper);
a_slice = gpr_slice_malloc(RSTRING_LEN(src));
memcpy(GPR_SLICE_START_PTR(a_slice), RSTRING_PTR(src), RSTRING_LEN(src));
byte_buffer = grpc_byte_buffer_create(&a_slice, 1);
gpr_slice_unref(a_slice);
if (byte_buffer == NULL) {
rb_raise(rb_eArgError, "could not create a byte_buffer, not sure why");
return Qnil;
}
wrapper->wrapped = byte_buffer;
/* Save a references to the original string in the mark object so that the
* pointers used there is valid for the lifetime of the object. */
wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
rb_ivar_set(wrapper->mark, id_source, src);
return self;
}
/* rb_cByteBuffer is the ruby class that proxies grpc_byte_buffer. */
VALUE rb_cByteBuffer = Qnil;
void Init_grpc_byte_buffer() {
rb_cByteBuffer =
rb_define_class_under(rb_mGrpcCore, "ByteBuffer", rb_cObject);
/* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cByteBuffer, grpc_rb_byte_buffer_alloc);
/* Provides a ruby constructor and support for dup/clone. */
rb_define_method(rb_cByteBuffer, "initialize", grpc_rb_byte_buffer_init, 1);
rb_define_method(rb_cByteBuffer, "initialize_copy",
grpc_rb_byte_buffer_init_copy, 1);
/* Provides a to_s method that returns the buffer value */
rb_define_method(rb_cByteBuffer, "to_s", grpc_rb_byte_buffer_to_s, 0);
id_source = rb_intern("__source");
id_empty = rb_intern("");
}
VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer *bb) {
grpc_rb_byte_buffer *byte_buffer = NULL;
if (bb == NULL) {
return Qnil;
}
byte_buffer = ALLOC(grpc_rb_byte_buffer);
byte_buffer->wrapped = bb;
byte_buffer->mark = mark;
return Data_Wrap_Struct(rb_cByteBuffer, grpc_rb_byte_buffer_mark,
grpc_rb_byte_buffer_free, byte_buffer);
}
/* Gets the wrapped byte_buffer from the ruby wrapper */
grpc_byte_buffer *grpc_rb_get_wrapped_byte_buffer(VALUE v) {
grpc_rb_byte_buffer *wrapper = NULL;
Data_Get_Struct(v, grpc_rb_byte_buffer, wrapper);
return wrapper->wrapped;
} }

@ -37,18 +37,10 @@
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <ruby.h> #include <ruby.h>
/* rb_cByteBuffer is the ByteBuffer class whose instances proxy /* Converts a char* with a length to a grpc_byte_buffer */
grpc_byte_buffer. */ grpc_byte_buffer *grpc_rb_s_to_byte_buffer(char *string, size_t length);
extern VALUE rb_cByteBuffer;
/* Initializes the ByteBuffer class. */ /* Converts a grpc_byte_buffer to a ruby string */
void Init_grpc_byte_buffer(); VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer);
/* grpc_rb_byte_buffer_create_with_mark creates a grpc_rb_byte_buffer with a
* ruby mark object that will be kept alive while the byte_buffer is alive. */
VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb);
/* Gets the wrapped byte_buffer from its ruby object. */
grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v);
#endif /* GRPC_RB_BYTE_BUFFER_H_ */ #endif /* GRPC_RB_BYTE_BUFFER_H_ */

@ -36,11 +36,20 @@
#include <ruby.h> #include <ruby.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include "rb_byte_buffer.h" #include "rb_byte_buffer.h"
#include "rb_completion_queue.h" #include "rb_completion_queue.h"
#include "rb_metadata.h"
#include "rb_grpc.h" #include "rb_grpc.h"
/* grpc_rb_sBatchResult is struct class used to hold the results of a batch
* call. */
static VALUE grpc_rb_sBatchResult;
/* grpc_rb_cMdAry is the MetadataArray class whose instances proxy
* grpc_metadata_array. */
static VALUE grpc_rb_cMdAry;
/* id_cq is the name of the hidden ivar that preserves a reference to a /* id_cq is the name of the hidden ivar that preserves a reference to a
* completion queue */ * completion queue */
static ID id_cq; static ID id_cq;
@ -62,6 +71,15 @@ static ID id_metadata;
* received by the call and subsequently saved on it. */ * received by the call and subsequently saved on it. */
static ID id_status; static ID id_status;
/* sym_* are the symbol for attributes of grpc_rb_sBatchResult. */
static VALUE sym_send_message;
static VALUE sym_send_metadata;
static VALUE sym_send_close;
static VALUE sym_send_status;
static VALUE sym_message;
static VALUE sym_status;
static VALUE sym_cancelled;
/* hash_all_calls is a hash of Call address -> reference count that is used to /* hash_all_calls is a hash of Call address -> reference count that is used to
* track the creation and destruction of rb_call instances. * track the creation and destruction of rb_call instances.
*/ */
@ -101,84 +119,6 @@ const char *grpc_call_error_detail_of(grpc_call_error err) {
return detail; return detail;
} }
/* grpc_rb_call_add_metadata_hash_cb is the hash iteration callback used by
grpc_rb_call_add_metadata.
*/
int grpc_rb_call_add_metadata_hash_cb(VALUE key, VALUE val, VALUE call_obj) {
grpc_call *call = NULL;
grpc_metadata *md = NULL;
VALUE md_obj = Qnil;
VALUE md_obj_args[2];
VALUE flags = rb_ivar_get(call_obj, id_flags);
grpc_call_error err;
int array_length;
int i;
/* Construct a metadata object from key and value and add it */
Data_Get_Struct(call_obj, grpc_call, call);
md_obj_args[0] = key;
if (TYPE(val) == T_ARRAY) {
/* If the value is an array, add each value in the array separately */
array_length = RARRAY_LEN(val);
for (i = 0; i < array_length; i++) {
md_obj_args[1] = rb_ary_entry(val, i);
md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
md = grpc_rb_get_wrapped_metadata(md_obj);
err = grpc_call_add_metadata_old(call, md, NUM2UINT(flags));
if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
grpc_call_error_detail_of(err), err);
return ST_STOP;
}
}
} else {
md_obj_args[1] = val;
md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
md = grpc_rb_get_wrapped_metadata(md_obj);
err = grpc_call_add_metadata_old(call, md, NUM2UINT(flags));
if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
grpc_call_error_detail_of(err), err);
return ST_STOP;
}
}
return ST_CONTINUE;
}
/*
call-seq:
call.add_metadata(completion_queue, hash_elements, flags=nil)
Add metadata elements to the call from a ruby hash, to be sent upon
invocation. flags is a bit-field combination of the write flags defined
above. REQUIRES: grpc_call_invoke/grpc_call_accept have not been
called on this call. Produces no events. */
static VALUE grpc_rb_call_add_metadata(int argc, VALUE *argv, VALUE self) {
VALUE metadata;
VALUE flags = Qnil;
ID id_size = rb_intern("size");
/* "11" == 1 mandatory args, 1 (flags) is optional */
rb_scan_args(argc, argv, "11", &metadata, &flags);
if (NIL_P(flags)) {
flags = UINT2NUM(0); /* Default to no flags */
}
if (TYPE(metadata) != T_HASH) {
rb_raise(rb_eTypeError, "add metadata failed: metadata should be a hash");
return Qnil;
}
if (NUM2UINT(rb_funcall(metadata, id_size, 0)) == 0) {
return Qnil;
}
rb_ivar_set(self, id_flags, flags);
rb_ivar_set(self, id_input_md, metadata);
rb_hash_foreach(metadata, grpc_rb_call_add_metadata_hash_cb, self);
return Qnil;
}
/* Called by clients to cancel an RPC on the server. /* Called by clients to cancel an RPC on the server.
Can be called multiple times, from any thread. */ Can be called multiple times, from any thread. */
static VALUE grpc_rb_call_cancel(VALUE self) { static VALUE grpc_rb_call_cancel(VALUE self) {
@ -187,7 +127,7 @@ static VALUE grpc_rb_call_cancel(VALUE self) {
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_cancel(call); err = grpc_call_cancel(call);
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "cancel failed: %s (code=%d)", rb_raise(grpc_rb_eCallError, "cancel failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
} }
@ -196,77 +136,20 @@ static VALUE grpc_rb_call_cancel(VALUE self) {
/* /*
call-seq: call-seq:
call.invoke(completion_queue, tag, flags=nil) status = call.status
Invoke the RPC. Starts sending metadata and request headers on the wire.
flags is a bit-field combination of the write flags defined above.
REQUIRES: Can be called at most once per call.
Can only be called on the client.
Produces a GRPC_INVOKE_ACCEPTED event on completion. */
static VALUE grpc_rb_call_invoke(int argc, VALUE *argv, VALUE self) {
VALUE cqueue = Qnil;
VALUE metadata_read_tag = Qnil;
VALUE finished_tag = Qnil;
VALUE flags = Qnil;
grpc_call *call = NULL;
grpc_completion_queue *cq = NULL;
grpc_call_error err;
/* "31" == 3 mandatory args, 1 (flags) is optional */ Gets the status object saved the call. */
rb_scan_args(argc, argv, "31", &cqueue, &metadata_read_tag, &finished_tag,
&flags);
if (NIL_P(flags)) {
flags = UINT2NUM(0); /* Default to no flags */
}
cq = grpc_rb_get_wrapped_completion_queue(cqueue);
Data_Get_Struct(self, grpc_call, call);
err = grpc_call_invoke_old(call, cq, ROBJECT(metadata_read_tag),
ROBJECT(finished_tag), NUM2UINT(flags));
if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "invoke failed: %s (code=%d)",
grpc_call_error_detail_of(err), err);
}
/* Add the completion queue as an instance attribute, prevents it from being
* GCed until this call object is GCed */
rb_ivar_set(self, id_cq, cqueue);
return Qnil;
}
/* Initiate a read on a call. Output event contains a byte buffer with the
result of the read.
REQUIRES: No other reads are pending on the call. It is only safe to start
the next read after the corresponding read event is received. */
static VALUE grpc_rb_call_start_read(VALUE self, VALUE tag) {
grpc_call *call = NULL;
grpc_call_error err;
Data_Get_Struct(self, grpc_call, call);
err = grpc_call_start_read_old(call, ROBJECT(tag));
if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "start read failed: %s (code=%d)",
grpc_call_error_detail_of(err), err);
}
return Qnil;
}
/*
call-seq:
status = call.status
Gets the status object saved the call. */
static VALUE grpc_rb_call_get_status(VALUE self) { static VALUE grpc_rb_call_get_status(VALUE self) {
return rb_ivar_get(self, id_status); return rb_ivar_get(self, id_status);
} }
/* /*
call-seq: call-seq:
call.status = status call.status = status
Saves a status object on the call. */ Saves a status object on the call. */
static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) { static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) {
if (!NIL_P(status) && rb_obj_class(status) != rb_sStatus) { if (!NIL_P(status) && rb_obj_class(status) != grpc_rb_sStatus) {
rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Struct::Status>", rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Struct::Status>",
rb_obj_classname(status)); rb_obj_classname(status));
return Qnil; return Qnil;
@ -277,18 +160,18 @@ static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) {
/* /*
call-seq: call-seq:
metadata = call.metadata metadata = call.metadata
Gets the metadata object saved the call. */ Gets the metadata object saved the call. */
static VALUE grpc_rb_call_get_metadata(VALUE self) { static VALUE grpc_rb_call_get_metadata(VALUE self) {
return rb_ivar_get(self, id_metadata); return rb_ivar_get(self, id_metadata);
} }
/* /*
call-seq: call-seq:
call.metadata = metadata call.metadata = metadata
Saves the metadata hash on the call. */ Saves the metadata hash on the call. */
static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) { static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) {
if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) { if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) {
rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>", rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>",
@ -299,176 +182,438 @@ static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) {
return rb_ivar_set(self, id_metadata, metadata); return rb_ivar_set(self, id_metadata, metadata);
} }
/* /* grpc_rb_md_ary_fill_hash_cb is the hash iteration callback used
call-seq: to fill grpc_metadata_array.
call.start_write(byte_buffer, tag, flags=nil)
it's capacity should have been computed via a prior call to
Queue a byte buffer for writing. grpc_rb_md_ary_fill_hash_cb
flags is a bit-field combination of the write flags defined above. */
A write with byte_buffer null is allowed, and will not send any bytes on the int grpc_rb_md_ary_fill_hash_cb(VALUE key, VALUE val, VALUE md_ary_obj) {
wire. If this is performed without GRPC_WRITE_BUFFER_HINT flag it provides grpc_metadata_array *md_ary = NULL;
a mechanism to flush any previously buffered writes to outgoing flow control. int array_length;
REQUIRES: No other writes are pending on the call. It is only safe to int i;
start the next write after the corresponding write_accepted event
is received. /* Construct a metadata object from key and value and add it */
GRPC_INVOKE_ACCEPTED must have been received by the application Data_Get_Struct(md_ary_obj, grpc_metadata_array, md_ary);
prior to calling this on the client. On the server,
grpc_call_accept must have been called successfully.
Produces a GRPC_WRITE_ACCEPTED event. */
static VALUE grpc_rb_call_start_write(int argc, VALUE *argv, VALUE self) {
VALUE byte_buffer = Qnil;
VALUE tag = Qnil;
VALUE flags = Qnil;
grpc_call *call = NULL;
grpc_byte_buffer *bfr = NULL;
grpc_call_error err;
/* "21" == 2 mandatory args, 1 (flags) is optional */ if (TYPE(val) == T_ARRAY) {
rb_scan_args(argc, argv, "21", &byte_buffer, &tag, &flags); /* If the value is an array, add capacity for each value in the array */
if (NIL_P(flags)) { array_length = RARRAY_LEN(val);
flags = UINT2NUM(0); /* Default to no flags */ for (i = 0; i < array_length; i++) {
if (TYPE(key) == T_SYMBOL) {
md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key));
} else { /* StringValueCStr does all other type exclusions for us */
md_ary->metadata[md_ary->count].key = StringValueCStr(key);
}
md_ary->metadata[md_ary->count].value = RSTRING_PTR(rb_ary_entry(val, i));
md_ary->metadata[md_ary->count].value_length =
RSTRING_LEN(rb_ary_entry(val, i));
md_ary->count += 1;
}
} else {
if (TYPE(key) == T_SYMBOL) {
md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key));
} else { /* StringValueCStr does all other type exclusions for us */
md_ary->metadata[md_ary->count].key = StringValueCStr(key);
}
md_ary->metadata[md_ary->count].value = RSTRING_PTR(val);
md_ary->metadata[md_ary->count].value_length = RSTRING_LEN(val);
md_ary->count += 1;
} }
bfr = grpc_rb_get_wrapped_byte_buffer(byte_buffer);
Data_Get_Struct(self, grpc_call, call); return ST_CONTINUE;
err = grpc_call_start_write_old(call, bfr, ROBJECT(tag), NUM2UINT(flags)); }
if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "start write failed: %s (code=%d)", /* grpc_rb_md_ary_capacity_hash_cb is the hash iteration callback used
grpc_call_error_detail_of(err), err); to pre-compute the capacity a grpc_metadata_array.
*/
int grpc_rb_md_ary_capacity_hash_cb(VALUE key, VALUE val, VALUE md_ary_obj) {
grpc_metadata_array *md_ary = NULL;
/* Construct a metadata object from key and value and add it */
Data_Get_Struct(md_ary_obj, grpc_metadata_array, md_ary);
if (TYPE(val) == T_ARRAY) {
/* If the value is an array, add capacity for each value in the array */
md_ary->capacity += RARRAY_LEN(val);
} else {
md_ary->capacity += 1;
}
return ST_CONTINUE;
}
/* grpc_rb_md_ary_convert converts a ruby metadata hash into
a grpc_metadata_array.
*/
void grpc_rb_md_ary_convert(VALUE md_ary_hash, grpc_metadata_array *md_ary) {
VALUE md_ary_obj = Qnil;
if (md_ary_hash == Qnil) {
return; /* Do nothing if the expected has value is nil */
}
if (TYPE(md_ary_hash) != T_HASH) {
rb_raise(rb_eTypeError, "md_ary_convert: got <%s>, want <Hash>",
rb_obj_classname(md_ary_hash));
return;
} }
return Qnil; /* Initialize the array, compute it's capacity, then fill it. */
grpc_metadata_array_init(md_ary);
md_ary_obj =
Data_Wrap_Struct(grpc_rb_cMdAry, GC_NOT_MARKED, GC_DONT_FREE, md_ary);
rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_capacity_hash_cb, md_ary_obj);
md_ary->metadata = gpr_malloc(md_ary->capacity * sizeof(grpc_metadata));
rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_fill_hash_cb, md_ary_obj);
} }
/* Queue a status for writing. /* Converts a metadata array to a hash. */
VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary) {
call-seq: VALUE key = Qnil;
tag = Object.new VALUE new_ary = Qnil;
call.write_status(200, "OK", tag) VALUE value = Qnil;
VALUE result = rb_hash_new();
REQUIRES: No other writes are pending on the call. It is only safe to size_t i;
start the next write after the corresponding write_accepted event
is received. for (i = 0; i < md_ary->count; i++) {
GRPC_INVOKE_ACCEPTED must have been received by the application key = rb_str_new2(md_ary->metadata[i].key);
prior to calling this. value = rb_hash_aref(result, key);
Only callable on the server. if (value == Qnil) {
Produces a GRPC_FINISHED event when the status is sent and the stream is value = rb_str_new(md_ary->metadata[i].value,
fully closed */ md_ary->metadata[i].value_length);
static VALUE grpc_rb_call_start_write_status(VALUE self, VALUE code, rb_hash_aset(result, key, value);
VALUE status, VALUE tag) { } else if (TYPE(value) == T_ARRAY) {
grpc_call *call = NULL; /* Add the string to the returned array */
grpc_call_error err; rb_ary_push(value,
Data_Get_Struct(self, grpc_call, call); rb_str_new(md_ary->metadata[i].value,
err = grpc_call_start_write_status_old(call, NUM2UINT(code), md_ary->metadata[i].value_length));
StringValueCStr(status), ROBJECT(tag)); } else {
if (err != GRPC_CALL_OK) { /* Add the current value with this key and the new one to an array */
rb_raise(rb_eCallError, "start write status: %s (code=%d)", new_ary = rb_ary_new();
grpc_call_error_detail_of(err), err); rb_ary_push(new_ary, value);
rb_ary_push(new_ary,
rb_str_new(md_ary->metadata[i].value,
md_ary->metadata[i].value_length));
rb_hash_aset(result, key, new_ary);
}
} }
return result;
}
return Qnil; /* grpc_rb_call_check_op_keys_hash_cb is a hash iteration func that checks
each key of an ops hash is valid.
*/
int grpc_rb_call_check_op_keys_hash_cb(VALUE key, VALUE val, VALUE ops_ary) {
/* Update the capacity; the value is an array, add capacity for each value in
* the array */
if (TYPE(key) != T_FIXNUM) {
rb_raise(rb_eTypeError, "invalid operation : got <%s>, want <Fixnum>",
rb_obj_classname(key));
return ST_STOP;
}
switch(NUM2INT(key)) {
case GRPC_OP_SEND_INITIAL_METADATA:
case GRPC_OP_SEND_MESSAGE:
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
case GRPC_OP_SEND_STATUS_FROM_SERVER:
case GRPC_OP_RECV_INITIAL_METADATA:
case GRPC_OP_RECV_MESSAGE:
case GRPC_OP_RECV_STATUS_ON_CLIENT:
case GRPC_OP_RECV_CLOSE_ON_SERVER:
rb_ary_push(ops_ary, key);
return ST_CONTINUE;
default:
rb_raise(rb_eTypeError, "invalid operation : bad value %d",
NUM2INT(key));
};
return ST_STOP;
} }
/* No more messages to send. /* grpc_rb_op_update_status_from_server adds the values in a ruby status
REQUIRES: No other writes are pending on the call. */ struct to the 'send_status_from_server' portion of an op.
static VALUE grpc_rb_call_writes_done(VALUE self, VALUE tag) { */
grpc_call *call = NULL; void grpc_rb_op_update_status_from_server(grpc_op *op,
grpc_call_error err; grpc_metadata_array* md_ary,
Data_Get_Struct(self, grpc_call, call); VALUE status) {
err = grpc_call_writes_done_old(call, ROBJECT(tag)); VALUE code = rb_struct_aref(status, sym_code);
if (err != GRPC_CALL_OK) { VALUE details = rb_struct_aref(status, sym_details);
rb_raise(rb_eCallError, "writes done: %s (code=%d)", VALUE metadata_hash = rb_struct_aref(status, sym_metadata);
grpc_call_error_detail_of(err), err);
/* TODO: add check to ensure status is the correct struct type */
if (TYPE(code) != T_FIXNUM) {
rb_raise(rb_eTypeError, "invalid code : got <%s>, want <Fixnum>",
rb_obj_classname(code));
return;
}
if (TYPE(details) != T_STRING) {
rb_raise(rb_eTypeError, "invalid details : got <%s>, want <String>",
rb_obj_classname(code));
return;
} }
op->data.send_status_from_server.status = NUM2INT(code);
op->data.send_status_from_server.status_details = StringValueCStr(details);
grpc_rb_md_ary_convert(metadata_hash, md_ary);
op->data.send_status_from_server.trailing_metadata_count = md_ary->count;
op->data.send_status_from_server.trailing_metadata = md_ary->metadata;
}
return Qnil; /* run_batch_stack holds various values used by the
* grpc_rb_call_run_batch function */
typedef struct run_batch_stack {
/* The batch ops */
grpc_op ops[8]; /* 8 is the maximum number of operations */
size_t op_num; /* tracks the last added operation */
/* Data being sent */
grpc_metadata_array send_metadata;
grpc_metadata_array send_trailing_metadata;
/* Data being received */
grpc_byte_buffer *recv_message;
grpc_metadata_array recv_metadata;
grpc_metadata_array recv_trailing_metadata;
int recv_cancelled;
grpc_status_code recv_status;
char *recv_status_details;
size_t recv_status_details_capacity;
} run_batch_stack;
/* grpc_run_batch_stack_init ensures the run_batch_stack is properly
* initialized */
static void grpc_run_batch_stack_init(run_batch_stack* st) {
MEMZERO(st, run_batch_stack, 1);
grpc_metadata_array_init(&st->send_metadata);
grpc_metadata_array_init(&st->send_trailing_metadata);
grpc_metadata_array_init(&st->recv_metadata);
grpc_metadata_array_init(&st->recv_trailing_metadata);
st->op_num = 0;
} }
/* call-seq: /* grpc_run_batch_stack_cleanup ensures the run_batch_stack is properly
call.server_end_initial_metadata(flag) * cleaned up */
static void grpc_run_batch_stack_cleanup(run_batch_stack* st) {
Only to be called on servers, before sending messages. grpc_metadata_array_destroy(&st->send_metadata);
flags is a bit-field combination of the write flags defined above. grpc_metadata_array_destroy(&st->send_trailing_metadata);
grpc_metadata_array_destroy(&st->recv_metadata);
REQUIRES: Can be called at most once per call. grpc_metadata_array_destroy(&st->recv_trailing_metadata);
Can only be called on the server, must be called after if (st->recv_status_details != NULL) {
grpc_call_server_accept gpr_free(st->recv_status_details);
Produces no events */ }
static VALUE grpc_rb_call_server_end_initial_metadata(int argc, VALUE *argv, }
VALUE self) {
VALUE flags = Qnil;
grpc_call *call = NULL;
grpc_call_error err;
/* "01" == 1 (flags) is optional */ /* grpc_run_batch_stack_fill_ops fills the run_batch_stack ops array from
rb_scan_args(argc, argv, "01", &flags); * ops_hash */
if (NIL_P(flags)) { static void grpc_run_batch_stack_fill_ops(run_batch_stack* st, VALUE ops_hash) {
flags = UINT2NUM(0); /* Default to no flags */ VALUE this_op = Qnil;
VALUE this_value = Qnil;
VALUE ops_ary = rb_ary_new();
size_t i = 0;
/* Create a ruby array with just the operation keys */
rb_hash_foreach(ops_hash, grpc_rb_call_check_op_keys_hash_cb, ops_ary);
/* Fill the ops array */
for (i = 0; i < (size_t)RARRAY_LEN(ops_ary); i++) {
this_op = rb_ary_entry(ops_ary, i);
this_value = rb_hash_aref(ops_hash, this_op);
switch(NUM2INT(this_op)) {
case GRPC_OP_SEND_INITIAL_METADATA:
/* N.B. later there is no need to explicitly delete the metadata keys
* and values, they are references to data in ruby objects. */
grpc_rb_md_ary_convert(this_value, &st->send_metadata);
st->ops[st->op_num].data.send_initial_metadata.count =
st->send_metadata.count;
st->ops[st->op_num].data.send_initial_metadata.metadata =
st->send_metadata.metadata;
break;
case GRPC_OP_SEND_MESSAGE:
st->ops[st->op_num].data.send_message =
grpc_rb_s_to_byte_buffer(RSTRING_PTR(this_value),
RSTRING_LEN(this_value));
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
/* N.B. later there is no need to explicitly delete the metadata keys
* and values, they are references to data in ruby objects. */
grpc_rb_op_update_status_from_server(&st->ops[st->op_num],
&st->send_trailing_metadata,
this_value);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
st->ops[st->op_num].data.recv_initial_metadata = &st->recv_metadata;
break;
case GRPC_OP_RECV_MESSAGE:
st->ops[st->op_num].data.recv_message = &st->recv_message;
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
st->ops[st->op_num].data.recv_status_on_client.trailing_metadata =
&st->recv_trailing_metadata;
st->ops[st->op_num].data.recv_status_on_client.status =
&st->recv_status;
st->ops[st->op_num].data.recv_status_on_client.status_details =
&st->recv_status_details;
st->ops[st->op_num].data.recv_status_on_client.status_details_capacity =
&st->recv_status_details_capacity;
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
st->ops[st->op_num].data.recv_close_on_server.cancelled =
&st->recv_cancelled;
break;
default:
grpc_run_batch_stack_cleanup(st);
rb_raise(rb_eTypeError, "invalid operation : bad value %d",
NUM2INT(this_op));
};
st->ops[st->op_num].op = (grpc_op_type)NUM2INT(this_op);
st->op_num++;
} }
Data_Get_Struct(self, grpc_call, call); }
err = grpc_call_server_end_initial_metadata_old(call, NUM2UINT(flags));
if (err != GRPC_CALL_OK) { /* grpc_run_batch_stack_build_result fills constructs a ruby BatchResult struct
rb_raise(rb_eCallError, "end_initial_metadata failed: %s (code=%d)", after the results have run */
grpc_call_error_detail_of(err), err); static VALUE grpc_run_batch_stack_build_result(run_batch_stack* st) {
size_t i = 0;
VALUE result = rb_struct_new(grpc_rb_sBatchResult, Qnil, Qnil, Qnil, Qnil,
Qnil, Qnil, Qnil, Qnil, NULL);
for (i = 0; i < st->op_num; i++) {
switch(st->ops[i].op) {
case GRPC_OP_SEND_INITIAL_METADATA:
rb_struct_aset(result, sym_send_metadata, Qtrue);
break;
case GRPC_OP_SEND_MESSAGE:
rb_struct_aset(result, sym_send_message, Qtrue);
grpc_byte_buffer_destroy(st->ops[i].data.send_message);
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
rb_struct_aset(result, sym_send_close, Qtrue);
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
rb_struct_aset(result, sym_send_status, Qtrue);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
rb_struct_aset(result, sym_metadata,
grpc_rb_md_ary_to_h(&st->recv_metadata));
case GRPC_OP_RECV_MESSAGE:
rb_struct_aset(result, sym_message,
grpc_rb_byte_buffer_to_s(st->recv_message));
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
rb_struct_aset(
result,
sym_status,
rb_struct_new(grpc_rb_sStatus,
UINT2NUM(st->recv_status),
(st->recv_status_details == NULL
? Qnil
: rb_str_new2(st->recv_status_details)),
grpc_rb_md_ary_to_h(&st->recv_trailing_metadata),
NULL));
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
rb_struct_aset(result, sym_send_close, Qtrue);
break;
default:
break;
}
} }
return Qnil; return result;
} }
/* call-seq: /* call-seq:
call.server_accept(completion_queue, finished_tag) cq = CompletionQueue.new
ops = {
Accept an incoming RPC, binding a completion queue to it. GRPC::Core::CallOps::SEND_INITIAL_METADATA => <op_value>,
To be called before sending or receiving messages. GRPC::Core::CallOps::SEND_MESSAGE => <op_value>,
...
REQUIRES: Can be called at most once per call. }
Can only be called on the server. tag = Object.new
Produces a GRPC_FINISHED event with finished_tag when the call has been timeout = 10
completed (there may be other events for the call pending at this call.start_batch(cqueue, tag, timeout, ops)
time) */
static VALUE grpc_rb_call_server_accept(VALUE self, VALUE cqueue, Start a batch of operations defined in the array ops; when complete, post a
VALUE finished_tag) { completion of type 'tag' to the completion queue bound to the call.
Also waits for the batch to complete, until timeout is reached.
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 */
static VALUE grpc_rb_call_run_batch(VALUE self, VALUE cqueue, VALUE tag,
VALUE timeout, VALUE ops_hash) {
run_batch_stack st;
grpc_call *call = NULL; grpc_call *call = NULL;
grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue); grpc_event *ev = NULL;
grpc_call_error err; grpc_call_error err;
VALUE result = Qnil;
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_server_accept_old(call, cq, ROBJECT(finished_tag));
/* Validate the ops args, adding them to a ruby array */
if (TYPE(ops_hash) != T_HASH) {
rb_raise(rb_eTypeError, "call#run_batch: ops hash should be a hash");
return Qnil;
}
grpc_run_batch_stack_init(&st);
grpc_run_batch_stack_fill_ops(&st, ops_hash);
/* call grpc_call_start_batch, then wait for it to complete using
* pluck_event */
err = grpc_call_start_batch(call, st.ops, st.op_num, ROBJECT(tag));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "server_accept failed: %s (code=%d)", grpc_run_batch_stack_cleanup(&st);
rb_raise(grpc_rb_eCallError,
"grpc_call_start_batch failed with %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
return;
}
ev = grpc_rb_completion_queue_pluck_event(cqueue, tag, timeout);
if (ev == NULL) {
grpc_run_batch_stack_cleanup(&st);
rb_raise(grpc_rb_eOutOfTime, "grpc_call_start_batch timed out");
return;
}
if (ev->data.op_complete != GRPC_OP_OK) {
grpc_run_batch_stack_cleanup(&st);
rb_raise(grpc_rb_eCallError, "start_batch completion failed, (code=%d)",
ev->data.op_complete);
return;
} }
/* Add the completion queue as an instance attribute, prevents it from being /* Build and return the BatchResult struct result */
* GCed until this call object is GCed */ result = grpc_run_batch_stack_build_result(&st);
rb_ivar_set(self, id_cq, cqueue); grpc_run_batch_stack_cleanup(&st);
return Qnil; return result;
} }
/* rb_cCall is the ruby class that proxies grpc_call. */ /* grpc_rb_cCall is the ruby class that proxies grpc_call. */
VALUE rb_cCall = Qnil; VALUE grpc_rb_cCall = Qnil;
/* rb_eCallError is the ruby class of the exception thrown during call /* grpc_rb_eCallError is the ruby class of the exception thrown during call
operations; */ operations; */
VALUE rb_eCallError = Qnil; VALUE grpc_rb_eCallError = Qnil;
/* grpc_rb_eOutOfTime is the ruby class of the exception thrown to indicate
a timeout. */
VALUE grpc_rb_eOutOfTime = Qnil;
void Init_grpc_error_codes() { void Init_grpc_error_codes() {
/* Constants representing the error codes of grpc_call_error in grpc.h */ /* Constants representing the error codes of grpc_call_error in grpc.h */
VALUE rb_RpcErrors = rb_define_module_under(rb_mGrpcCore, "RpcErrors"); VALUE grpc_rb_mRpcErrors =
rb_define_const(rb_RpcErrors, "OK", UINT2NUM(GRPC_CALL_OK)); rb_define_module_under(grpc_rb_mGrpcCore, "RpcErrors");
rb_define_const(rb_RpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR)); rb_define_const(grpc_rb_mRpcErrors, "OK", UINT2NUM(GRPC_CALL_OK));
rb_define_const(rb_RpcErrors, "NOT_ON_SERVER", rb_define_const(grpc_rb_mRpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR));
rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_SERVER",
UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER)); UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER));
rb_define_const(rb_RpcErrors, "NOT_ON_CLIENT", rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_CLIENT",
UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT)); UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT));
rb_define_const(rb_RpcErrors, "ALREADY_ACCEPTED", rb_define_const(grpc_rb_mRpcErrors, "ALREADY_ACCEPTED",
UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED)); UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED));
rb_define_const(rb_RpcErrors, "ALREADY_INVOKED", rb_define_const(grpc_rb_mRpcErrors, "ALREADY_INVOKED",
UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED)); UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED));
rb_define_const(rb_RpcErrors, "NOT_INVOKED", rb_define_const(grpc_rb_mRpcErrors, "NOT_INVOKED",
UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED)); UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED));
rb_define_const(rb_RpcErrors, "ALREADY_FINISHED", rb_define_const(grpc_rb_mRpcErrors, "ALREADY_FINISHED",
UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED)); UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED));
rb_define_const(rb_RpcErrors, "TOO_MANY_OPERATIONS", rb_define_const(grpc_rb_mRpcErrors, "TOO_MANY_OPERATIONS",
UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS)); UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
rb_define_const(rb_RpcErrors, "INVALID_FLAGS", rb_define_const(grpc_rb_mRpcErrors, "INVALID_FLAGS",
UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS)); UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS));
/* Add the detail strings to a Hash */ /* Add the detail strings to a Hash */
@ -496,37 +641,55 @@ void Init_grpc_error_codes() {
rb_str_new2("outstanding read or write present")); rb_str_new2("outstanding read or write present"));
rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS), rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS),
rb_str_new2("a bad flag was given")); rb_str_new2("a bad flag was given"));
rb_define_const(rb_RpcErrors, "ErrorMessages", rb_error_code_details); rb_define_const(grpc_rb_mRpcErrors, "ErrorMessages", rb_error_code_details);
rb_obj_freeze(rb_error_code_details); rb_obj_freeze(rb_error_code_details);
} }
void Init_grpc_op_codes() {
/* Constants representing operation type codes in grpc.h */
VALUE grpc_rb_mCallOps =
rb_define_module_under(grpc_rb_mGrpcCore, "CallOps");
rb_define_const(grpc_rb_mCallOps, "SEND_INITIAL_METADATA",
UINT2NUM(GRPC_OP_SEND_INITIAL_METADATA));
rb_define_const(grpc_rb_mCallOps, "SEND_MESSAGE",
UINT2NUM(GRPC_OP_SEND_MESSAGE));
rb_define_const(grpc_rb_mCallOps, "SEND_CLOSE_FROM_CLIENT",
UINT2NUM(GRPC_OP_SEND_CLOSE_FROM_CLIENT));
rb_define_const(grpc_rb_mCallOps, "SEND_STATUS_FROM_SERVER",
UINT2NUM(GRPC_OP_SEND_STATUS_FROM_SERVER));
rb_define_const(grpc_rb_mCallOps, "RECV_INITIAL_METADATA",
UINT2NUM(GRPC_OP_RECV_INITIAL_METADATA));
rb_define_const(grpc_rb_mCallOps, "RECV_MESSAGE",
UINT2NUM(GRPC_OP_RECV_MESSAGE));
rb_define_const(grpc_rb_mCallOps, "RECV_STATUS_ON_CLIENT",
UINT2NUM(GRPC_OP_RECV_STATUS_ON_CLIENT));
rb_define_const(grpc_rb_mCallOps, "RECV_CLOSE_ON_SERVER",
UINT2NUM(GRPC_OP_RECV_CLOSE_ON_SERVER));
}
void Init_grpc_call() { void Init_grpc_call() {
/* CallError inherits from Exception to signal that it is non-recoverable */ /* CallError inherits from Exception to signal that it is non-recoverable */
rb_eCallError = grpc_rb_eCallError =
rb_define_class_under(rb_mGrpcCore, "CallError", rb_eException); rb_define_class_under(grpc_rb_mGrpcCore, "CallError", rb_eException);
rb_cCall = rb_define_class_under(rb_mGrpcCore, "Call", rb_cObject); grpc_rb_eOutOfTime =
rb_define_class_under(grpc_rb_mGrpcCore, "OutOfTime", rb_eException);
grpc_rb_cCall = rb_define_class_under(grpc_rb_mGrpcCore, "Call", rb_cObject);
grpc_rb_cMdAry = rb_define_class_under(grpc_rb_mGrpcCore, "MetadataArray",
rb_cObject);
/* Prevent allocation or inialization of the Call class */ /* Prevent allocation or inialization of the Call class */
rb_define_alloc_func(rb_cCall, grpc_rb_cannot_alloc); rb_define_alloc_func(grpc_rb_cCall, grpc_rb_cannot_alloc);
rb_define_method(rb_cCall, "initialize", grpc_rb_cannot_init, 0); rb_define_method(grpc_rb_cCall, "initialize", grpc_rb_cannot_init, 0);
rb_define_method(rb_cCall, "initialize_copy", grpc_rb_cannot_init_copy, 1); rb_define_method(grpc_rb_cCall, "initialize_copy",
grpc_rb_cannot_init_copy, 1);
/* Add ruby analogues of the Call methods. */ /* Add ruby analogues of the Call methods. */
rb_define_method(rb_cCall, "server_accept", grpc_rb_call_server_accept, 2); rb_define_method(grpc_rb_cCall, "run_batch", grpc_rb_call_run_batch, 4);
rb_define_method(rb_cCall, "server_end_initial_metadata", rb_define_method(grpc_rb_cCall, "cancel", grpc_rb_call_cancel, 0);
grpc_rb_call_server_end_initial_metadata, -1); rb_define_method(grpc_rb_cCall, "status", grpc_rb_call_get_status, 0);
rb_define_method(rb_cCall, "add_metadata", grpc_rb_call_add_metadata, -1); rb_define_method(grpc_rb_cCall, "status=", grpc_rb_call_set_status, 1);
rb_define_method(rb_cCall, "cancel", grpc_rb_call_cancel, 0); rb_define_method(grpc_rb_cCall, "metadata", grpc_rb_call_get_metadata, 0);
rb_define_method(rb_cCall, "invoke", grpc_rb_call_invoke, -1); rb_define_method(grpc_rb_cCall, "metadata=", grpc_rb_call_set_metadata, 1);
rb_define_method(rb_cCall, "start_read", grpc_rb_call_start_read, 1);
rb_define_method(rb_cCall, "start_write", grpc_rb_call_start_write, -1);
rb_define_method(rb_cCall, "start_write_status",
grpc_rb_call_start_write_status, 3);
rb_define_method(rb_cCall, "writes_done", grpc_rb_call_writes_done, 1);
rb_define_method(rb_cCall, "status", grpc_rb_call_get_status, 0);
rb_define_method(rb_cCall, "status=", grpc_rb_call_set_status, 1);
rb_define_method(rb_cCall, "metadata", grpc_rb_call_get_metadata, 0);
rb_define_method(rb_cCall, "metadata=", grpc_rb_call_set_metadata, 1);
/* Ids used to support call attributes */ /* Ids used to support call attributes */
id_metadata = rb_intern("metadata"); id_metadata = rb_intern("metadata");
@ -537,12 +700,35 @@ void Init_grpc_call() {
id_flags = rb_intern("__flags"); id_flags = rb_intern("__flags");
id_input_md = rb_intern("__input_md"); id_input_md = rb_intern("__input_md");
/* Ids used in constructing the batch result. */
sym_send_message = ID2SYM(rb_intern("send_message"));
sym_send_metadata = ID2SYM(rb_intern("send_metadata"));
sym_send_close = ID2SYM(rb_intern("send_close"));
sym_send_status = ID2SYM(rb_intern("send_status"));
sym_message = ID2SYM(rb_intern("message"));
sym_status = ID2SYM(rb_intern("status"));
sym_cancelled = ID2SYM(rb_intern("cancelled"));
/* The Struct used to return the run_batch result. */
grpc_rb_sBatchResult = rb_struct_define(
"BatchResult",
"send_message",
"send_metadata",
"send_close",
"send_status",
"message",
"metadata",
"status",
"cancelled",
NULL);
/* The hash for reference counting calls, to ensure they can't be destroyed /* The hash for reference counting calls, to ensure they can't be destroyed
* more than once */ * more than once */
hash_all_calls = rb_hash_new(); hash_all_calls = rb_hash_new();
rb_define_const(rb_cCall, "INTERNAL_ALL_CALLs", hash_all_calls); rb_define_const(grpc_rb_cCall, "INTERNAL_ALL_CALLs", hash_all_calls);
Init_grpc_error_codes(); Init_grpc_error_codes();
Init_grpc_op_codes();
} }
/* Gets the call from the ruby object */ /* Gets the call from the ruby object */
@ -565,5 +751,6 @@ VALUE grpc_rb_wrap_call(grpc_call *c) {
rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c), rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c),
UINT2NUM(NUM2UINT(obj) + 1)); UINT2NUM(NUM2UINT(obj) + 1));
} }
return Data_Wrap_Struct(rb_cCall, GC_NOT_MARKED, grpc_rb_call_destroy, c); return Data_Wrap_Struct(grpc_rb_cCall, GC_NOT_MARKED,
grpc_rb_call_destroy, c);
} }

@ -46,12 +46,19 @@ VALUE grpc_rb_wrap_call(grpc_call* c);
/* Provides the details of an call error */ /* Provides the details of an call error */
const char* grpc_call_error_detail_of(grpc_call_error err); const char* grpc_call_error_detail_of(grpc_call_error err);
/* rb_cCall is the Call class whose instances proxy grpc_call. */ /* Converts a metadata array to a hash. */
extern VALUE rb_cCall; VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary);
/* rb_cCallError is the ruby class of the exception thrown during call /* grpc_rb_cCall is the Call class whose instances proxy grpc_call. */
extern VALUE grpc_rb_cCall;
/* grpc_rb_eCallError is the ruby class of the exception thrown during call
operations. */ operations. */
extern VALUE rb_eCallError; extern VALUE grpc_rb_eCallError;
/* grpc_rb_eOutOfTime is the ruby class of the exception thrown to indicate
a timeout. */
extern VALUE grpc_rb_eOutOfTime;
/* Initializes the Call class. */ /* Initializes the Call class. */
void Init_grpc_call(); void Init_grpc_call();

@ -49,12 +49,18 @@
static ID id_channel; static ID id_channel;
/* id_target is the name of the hidden ivar that preserves a reference to the /* id_target is the name of the hidden ivar that preserves a reference to the
* target string used to create the call, preserved so that is does not get * target string used to create the call, preserved so that it does not get
* GCed before the channel */ * GCed before the channel */
static ID id_target; static ID id_target;
/* id_cqueue is the name of the hidden ivar that preserves a reference to the
* completion queue used to create the call, preserved so that it does not get
* GCed before the channel */
static ID id_cqueue;
/* Used during the conversion of a hash to channel args during channel setup */ /* Used during the conversion of a hash to channel args during channel setup */
static VALUE rb_cChannelArgs; static VALUE grpc_rb_cChannelArgs;
/* grpc_rb_channel wraps a grpc_channel. It provides a peer ruby object, /* grpc_rb_channel wraps a grpc_channel. It provides a peer ruby object,
* 'mark' to minimize copying when a channel is created from ruby. */ * 'mark' to minimize copying when a channel is created from ruby. */
@ -142,6 +148,7 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
if (ch == NULL) { if (ch == NULL) {
rb_raise(rb_eRuntimeError, "could not create an rpc channel to target:%s", rb_raise(rb_eRuntimeError, "could not create an rpc channel to target:%s",
target_chars); target_chars);
return Qnil;
} }
rb_ivar_set(self, id_target, target); rb_ivar_set(self, id_target, target);
wrapper->wrapped = ch; wrapper->wrapped = ch;
@ -163,7 +170,8 @@ static VALUE grpc_rb_channel_init_copy(VALUE copy, VALUE orig) {
/* Raise an error if orig is not a channel object or a subclass. */ /* Raise an error if orig is not a channel object or a subclass. */
if (TYPE(orig) != T_DATA || if (TYPE(orig) != T_DATA ||
RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_channel_free) { RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_channel_free) {
rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cChannel)); rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(grpc_rb_cChannel));
return Qnil;
} }
Data_Get_Struct(orig, grpc_rb_channel, orig_ch); Data_Get_Struct(orig, grpc_rb_channel, orig_ch);
@ -177,34 +185,42 @@ static VALUE grpc_rb_channel_init_copy(VALUE copy, VALUE orig) {
/* Create a call given a grpc_channel, in order to call method. The request /* Create a call given a grpc_channel, in order to call method. The request
is not sent until grpc_call_invoke is called. */ is not sent until grpc_call_invoke is called. */
static VALUE grpc_rb_channel_create_call(VALUE self, VALUE method, VALUE host, static VALUE grpc_rb_channel_create_call(VALUE self, VALUE cqueue, VALUE method,
VALUE deadline) { VALUE host, VALUE deadline) {
VALUE res = Qnil; VALUE res = Qnil;
grpc_rb_channel *wrapper = NULL; grpc_rb_channel *wrapper = NULL;
grpc_channel *ch = NULL;
grpc_call *call = NULL; grpc_call *call = NULL;
grpc_channel *ch = NULL;
grpc_completion_queue *cq = NULL;
char *method_chars = StringValueCStr(method); char *method_chars = StringValueCStr(method);
char *host_chars = StringValueCStr(host); char *host_chars = StringValueCStr(host);
cq = grpc_rb_get_wrapped_completion_queue(cqueue);
Data_Get_Struct(self, grpc_rb_channel, wrapper); Data_Get_Struct(self, grpc_rb_channel, wrapper);
ch = wrapper->wrapped; ch = wrapper->wrapped;
if (ch == NULL) { if (ch == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
return Qnil;
} }
call = call =
grpc_channel_create_call_old(ch, method_chars, host_chars, grpc_channel_create_call(ch, cq, method_chars, host_chars,
grpc_rb_time_timeval(deadline, grpc_rb_time_timeval(deadline,
/* absolute time */ 0)); /* absolute time */ 0));
if (call == NULL) { if (call == NULL) {
rb_raise(rb_eRuntimeError, "cannot create call with method %s", rb_raise(rb_eRuntimeError, "cannot create call with method %s",
method_chars); method_chars);
return Qnil;
} }
res = grpc_rb_wrap_call(call); res = grpc_rb_wrap_call(call);
/* Make this channel an instance attribute of the call so that is is not GCed /* Make this channel an instance attribute of the call so that it is not GCed
* before the call. */ * before the call. */
rb_ivar_set(res, id_channel, self); rb_ivar_set(res, id_channel, self);
/* Make the completion queue an instance attribute of the call so that it is
* not GCed before the call. */
rb_ivar_set(res, id_cqueue, cqueue);
return res; return res;
} }
@ -224,35 +240,38 @@ static VALUE grpc_rb_channel_destroy(VALUE self) {
return Qnil; return Qnil;
} }
/* rb_cChannel is the ruby class that proxies grpc_channel. */ /* grpc_rb_cChannel is the ruby class that proxies grpc_channel. */
VALUE rb_cChannel = Qnil; VALUE grpc_rb_cChannel = Qnil;
void Init_grpc_channel() { void Init_grpc_channel() {
rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject); grpc_rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
rb_cChannel = rb_define_class_under(rb_mGrpcCore, "Channel", rb_cObject); grpc_rb_cChannel =
rb_define_class_under(grpc_rb_mGrpcCore, "Channel", rb_cObject);
/* Allocates an object managed by the ruby runtime */ /* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cChannel, grpc_rb_channel_alloc); rb_define_alloc_func(grpc_rb_cChannel, grpc_rb_channel_alloc);
/* Provides a ruby constructor and support for dup/clone. */ /* Provides a ruby constructor and support for dup/clone. */
rb_define_method(rb_cChannel, "initialize", grpc_rb_channel_init, -1); rb_define_method(grpc_rb_cChannel, "initialize", grpc_rb_channel_init, -1);
rb_define_method(rb_cChannel, "initialize_copy", grpc_rb_channel_init_copy, rb_define_method(grpc_rb_cChannel, "initialize_copy",
1); grpc_rb_channel_init_copy, 1);
/* Add ruby analogues of the Channel methods. */ /* Add ruby analogues of the Channel methods. */
rb_define_method(rb_cChannel, "create_call", grpc_rb_channel_create_call, 3); rb_define_method(grpc_rb_cChannel, "create_call",
rb_define_method(rb_cChannel, "destroy", grpc_rb_channel_destroy, 0); grpc_rb_channel_create_call, 4);
rb_define_alias(rb_cChannel, "close", "destroy"); rb_define_method(grpc_rb_cChannel, "destroy", grpc_rb_channel_destroy, 0);
rb_define_alias(grpc_rb_cChannel, "close", "destroy");
id_channel = rb_intern("__channel"); id_channel = rb_intern("__channel");
id_cqueue = rb_intern("__cqueue");
id_target = rb_intern("__target"); id_target = rb_intern("__target");
rb_define_const(rb_cChannel, "SSL_TARGET", rb_define_const(grpc_rb_cChannel, "SSL_TARGET",
ID2SYM(rb_intern(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG))); ID2SYM(rb_intern(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)));
rb_define_const(rb_cChannel, "ENABLE_CENSUS", rb_define_const(grpc_rb_cChannel, "ENABLE_CENSUS",
ID2SYM(rb_intern(GRPC_ARG_ENABLE_CENSUS))); ID2SYM(rb_intern(GRPC_ARG_ENABLE_CENSUS)));
rb_define_const(rb_cChannel, "MAX_CONCURRENT_STREAMS", rb_define_const(grpc_rb_cChannel, "MAX_CONCURRENT_STREAMS",
ID2SYM(rb_intern(GRPC_ARG_MAX_CONCURRENT_STREAMS))); ID2SYM(rb_intern(GRPC_ARG_MAX_CONCURRENT_STREAMS)));
rb_define_const(rb_cChannel, "MAX_MESSAGE_LENGTH", rb_define_const(grpc_rb_cChannel, "MAX_MESSAGE_LENGTH",
ID2SYM(rb_intern(GRPC_ARG_MAX_MESSAGE_LENGTH))); ID2SYM(rb_intern(GRPC_ARG_MAX_MESSAGE_LENGTH)));
} }

@ -37,8 +37,8 @@
#include <ruby.h> #include <ruby.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
/* rb_cChannel is the Channel class whose instances proxy grpc_channel. */ /* grpc_rb_cChannel is the Channel class whose instances proxy grpc_channel. */
extern VALUE rb_cChannel; extern VALUE grpc_rb_cChannel;
/* Initializes the Channel class. */ /* Initializes the Channel class. */
void Init_grpc_channel(); void Init_grpc_channel();

@ -109,7 +109,7 @@ typedef struct channel_convert_params {
static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) { static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) {
ID id_size = rb_intern("size"); ID id_size = rb_intern("size");
VALUE rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject); VALUE grpc_rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
channel_convert_params* params = (channel_convert_params*)as_value; channel_convert_params* params = (channel_convert_params*)as_value;
size_t num_args = 0; size_t num_args = 0;
@ -126,7 +126,7 @@ static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) {
MEMZERO(params->dst->args, grpc_arg, num_args); MEMZERO(params->dst->args, grpc_arg, num_args);
rb_hash_foreach(params->src_hash, rb_hash_foreach(params->src_hash,
grpc_rb_channel_create_in_process_add_args_hash_cb, grpc_rb_channel_create_in_process_add_args_hash_cb,
Data_Wrap_Struct(rb_cChannelArgs, GC_NOT_MARKED, Data_Wrap_Struct(grpc_rb_cChannelArgs, GC_NOT_MARKED,
GC_DONT_FREE, params->dst)); GC_DONT_FREE, params->dst));
/* reset num_args as grpc_rb_channel_create_in_process_add_args_hash_cb /* reset num_args as grpc_rb_channel_create_in_process_add_args_hash_cb
* decrements it during has processing */ * decrements it during has processing */

@ -38,7 +38,6 @@
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include "rb_grpc.h" #include "rb_grpc.h"
#include "rb_event.h"
/* Used to allow grpc_completion_queue_next call to release the GIL */ /* Used to allow grpc_completion_queue_next call to release the GIL */
typedef struct next_call_stack { typedef struct next_call_stack {
@ -140,8 +139,19 @@ static VALUE grpc_rb_completion_queue_next(VALUE self, VALUE timeout) {
/* Blocks until the next event for given tag is available, and returns the /* Blocks until the next event for given tag is available, and returns the
* event. */ * event. */
static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag, VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag,
VALUE timeout) { VALUE timeout) {
grpc_event *ev = grpc_rb_completion_queue_pluck_event(self, tag, timeout);
if (ev == NULL) {
return Qnil;
}
return grpc_rb_new_event(ev);
}
/* Blocks until the next event for given tag is available, and returns the
* event. */
grpc_event* grpc_rb_completion_queue_pluck_event(VALUE self, VALUE tag,
VALUE timeout) {
next_call_stack next_call; next_call_stack next_call;
MEMZERO(&next_call, next_call_stack, 1); MEMZERO(&next_call, next_call_stack, 1);
Data_Get_Struct(self, grpc_completion_queue, next_call.cq); Data_Get_Struct(self, grpc_completion_queue, next_call.cq);
@ -151,30 +161,32 @@ static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag,
rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil, rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil,
(void *)&next_call, NULL, NULL); (void *)&next_call, NULL, NULL);
if (next_call.event == NULL) { if (next_call.event == NULL) {
return Qnil; return NULL;
} }
return grpc_rb_new_event(next_call.event); return next_call.event;
} }
/* rb_cCompletionQueue is the ruby class that proxies grpc_completion_queue. */ /* grpc_rb_cCompletionQueue is the ruby class that proxies
VALUE rb_cCompletionQueue = Qnil; * grpc_completion_queue. */
VALUE grpc_rb_cCompletionQueue = Qnil;
void Init_grpc_completion_queue() { void Init_grpc_completion_queue() {
rb_cCompletionQueue = grpc_rb_cCompletionQueue =
rb_define_class_under(rb_mGrpcCore, "CompletionQueue", rb_cObject); rb_define_class_under(grpc_rb_mGrpcCore, "CompletionQueue", rb_cObject);
/* constructor: uses an alloc func without an initializer. Using a simple /* constructor: uses an alloc func without an initializer. Using a simple
alloc func works here as the grpc header does not specify any args for alloc func works here as the grpc header does not specify any args for
this func, so no separate initialization step is necessary. */ this func, so no separate initialization step is necessary. */
rb_define_alloc_func(rb_cCompletionQueue, grpc_rb_completion_queue_alloc); rb_define_alloc_func(grpc_rb_cCompletionQueue,
grpc_rb_completion_queue_alloc);
/* Add the next method that waits for the next event. */ /* Add the next method that waits for the next event. */
rb_define_method(rb_cCompletionQueue, "next", grpc_rb_completion_queue_next, rb_define_method(grpc_rb_cCompletionQueue, "next",
1); grpc_rb_completion_queue_next, 1);
/* Add the pluck method that waits for the next event of given tag */ /* Add the pluck method that waits for the next event of given tag */
rb_define_method(rb_cCompletionQueue, "pluck", grpc_rb_completion_queue_pluck, rb_define_method(grpc_rb_cCompletionQueue, "pluck",
2); grpc_rb_completion_queue_pluck, 2);
} }
/* Gets the wrapped completion queue from the ruby wrapper */ /* Gets the wrapped completion queue from the ruby wrapper */

@ -40,9 +40,17 @@
/* Gets the wrapped completion queue from the ruby wrapper */ /* Gets the wrapped completion queue from the ruby wrapper */
grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v); grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v);
/* rb_cCompletionQueue is the CompletionQueue class whose instances proxy /**
* Makes the implementation of CompletionQueue#pluck available in other files
*
* This avoids having code that holds the GIL repeated at multiple sites.
*/
grpc_event* grpc_rb_completion_queue_pluck_event(VALUE cqueue, VALUE tag,
VALUE timeout);
/* grpc_rb_cCompletionQueue is the CompletionQueue class whose instances proxy
grpc_completion_queue. */ grpc_completion_queue. */
extern VALUE rb_cCompletionQueue; extern VALUE grpc_rb_cCompletionQueue;
/* Initializes the CompletionQueue class. */ /* Initializes the CompletionQueue class. */
void Init_grpc_completion_queue(); void Init_grpc_completion_queue();

@ -107,7 +107,7 @@ static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) {
/* Raise an error if orig is not a credentials object or a subclass. */ /* Raise an error if orig is not a credentials object or a subclass. */
if (TYPE(orig) != T_DATA || if (TYPE(orig) != T_DATA ||
RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_credentials_free) { RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_credentials_free) {
rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cCredentials)); rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(grpc_rb_cCredentials));
} }
Data_Get_Struct(orig, grpc_rb_credentials, orig_cred); Data_Get_Struct(orig, grpc_rb_credentials, orig_cred);
@ -178,7 +178,7 @@ static VALUE grpc_rb_composite_credentials_create(VALUE self, VALUE other) {
} }
wrapper->mark = Qnil; wrapper->mark = Qnil;
return Data_Wrap_Struct(rb_cCredentials, grpc_rb_credentials_mark, return Data_Wrap_Struct(grpc_rb_cCredentials, grpc_rb_credentials_mark,
grpc_rb_credentials_free, wrapper); grpc_rb_credentials_free, wrapper);
} }
@ -242,30 +242,31 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
return self; return self;
} }
/* rb_cCredentials is the ruby class that proxies grpc_credentials. */ /* grpc_rb_cCredentials is the ruby class that proxies grpc_credentials. */
VALUE rb_cCredentials = Qnil; VALUE grpc_rb_cCredentials = Qnil;
void Init_grpc_credentials() { void Init_grpc_credentials() {
rb_cCredentials = grpc_rb_cCredentials =
rb_define_class_under(rb_mGrpcCore, "Credentials", rb_cObject); rb_define_class_under(grpc_rb_mGrpcCore, "Credentials", rb_cObject);
/* Allocates an object managed by the ruby runtime */ /* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cCredentials, grpc_rb_credentials_alloc); rb_define_alloc_func(grpc_rb_cCredentials, grpc_rb_credentials_alloc);
/* Provides a ruby constructor and support for dup/clone. */ /* Provides a ruby constructor and support for dup/clone. */
rb_define_method(rb_cCredentials, "initialize", grpc_rb_credentials_init, -1); rb_define_method(grpc_rb_cCredentials, "initialize",
rb_define_method(rb_cCredentials, "initialize_copy", grpc_rb_credentials_init, -1);
rb_define_method(grpc_rb_cCredentials, "initialize_copy",
grpc_rb_credentials_init_copy, 1); grpc_rb_credentials_init_copy, 1);
/* Provide static funcs that create new special instances. */ /* Provide static funcs that create new special instances. */
rb_define_singleton_method(rb_cCredentials, "default", rb_define_singleton_method(grpc_rb_cCredentials, "default",
grpc_rb_default_credentials_create, 0); grpc_rb_default_credentials_create, 0);
rb_define_singleton_method(rb_cCredentials, "compute_engine", rb_define_singleton_method(grpc_rb_cCredentials, "compute_engine",
grpc_rb_compute_engine_credentials_create, 0); grpc_rb_compute_engine_credentials_create, 0);
/* Provide other methods. */ /* Provide other methods. */
rb_define_method(rb_cCredentials, "compose", rb_define_method(grpc_rb_cCredentials, "compose",
grpc_rb_composite_credentials_create, 1); grpc_rb_composite_credentials_create, 1);
id_pem_cert_chain = rb_intern("__pem_cert_chain"); id_pem_cert_chain = rb_intern("__pem_cert_chain");

@ -37,9 +37,9 @@
#include <ruby.h> #include <ruby.h>
#include <grpc/grpc_security.h> #include <grpc/grpc_security.h>
/* rb_cCredentials is the ruby class whose instances proxy /* grpc_rb_cCredentials is the ruby class whose instances proxy
grpc_credentials. */ grpc_credentials. */
extern VALUE rb_cCredentials; extern VALUE grpc_rb_cCredentials;
/* Initializes the ruby Credentials class. */ /* Initializes the ruby Credentials class. */
void Init_grpc_credentials(); void Init_grpc_credentials();

@ -1,361 +0,0 @@
/*
*
* 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 "rb_event.h"
#include <ruby.h>
#include <grpc/grpc.h>
#include "rb_grpc.h"
#include "rb_byte_buffer.h"
#include "rb_call.h"
#include "rb_metadata.h"
/* grpc_rb_event wraps a grpc_event. It provides a peer ruby object,
* 'mark' to minimize copying when an event is created from ruby. */
typedef struct grpc_rb_event {
/* Holder of ruby objects involved in constructing the channel */
VALUE mark;
/* The actual event */
grpc_event *wrapped;
} grpc_rb_event;
/* rb_mCompletionType is a ruby module that holds the completion type values */
VALUE rb_mCompletionType = Qnil;
/* Destroys Event instances. */
static void grpc_rb_event_free(void *p) {
grpc_rb_event *ev = NULL;
if (p == NULL) {
return;
};
ev = (grpc_rb_event *)p;
/* Deletes the wrapped object if the mark object is Qnil, which indicates
* that no other object is the actual owner. */
if (ev->wrapped != NULL && ev->mark == Qnil) {
grpc_event_finish(ev->wrapped);
rb_warning("event gc: destroyed the c event");
} else {
rb_warning("event gc: did not destroy the c event");
}
xfree(p);
}
/* Protects the mark object from GC */
static void grpc_rb_event_mark(void *p) {
grpc_rb_event *event = NULL;
if (p == NULL) {
return;
}
event = (grpc_rb_event *)p;
if (event->mark != Qnil) {
rb_gc_mark(event->mark);
}
}
static VALUE grpc_rb_event_result(VALUE self);
/* Obtains the type of an event. */
static VALUE grpc_rb_event_type(VALUE self) {
grpc_event *event = NULL;
grpc_rb_event *wrapper = NULL;
Data_Get_Struct(self, grpc_rb_event, wrapper);
if (wrapper->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "finished!");
return Qnil;
}
event = wrapper->wrapped;
switch (event->type) {
case GRPC_QUEUE_SHUTDOWN:
return rb_const_get(rb_mCompletionType, rb_intern("QUEUE_SHUTDOWN"));
case GRPC_READ:
return rb_const_get(rb_mCompletionType, rb_intern("READ"));
case GRPC_WRITE_ACCEPTED:
grpc_rb_event_result(self); /* validates the result */
return rb_const_get(rb_mCompletionType, rb_intern("WRITE_ACCEPTED"));
case GRPC_FINISH_ACCEPTED:
grpc_rb_event_result(self); /* validates the result */
return rb_const_get(rb_mCompletionType, rb_intern("FINISH_ACCEPTED"));
case GRPC_CLIENT_METADATA_READ:
return rb_const_get(rb_mCompletionType,
rb_intern("CLIENT_METADATA_READ"));
case GRPC_FINISHED:
return rb_const_get(rb_mCompletionType, rb_intern("FINISHED"));
case GRPC_SERVER_RPC_NEW:
return rb_const_get(rb_mCompletionType, rb_intern("SERVER_RPC_NEW"));
default:
rb_raise(rb_eRuntimeError, "unrecognized event code for an rpc event:%d",
event->type);
}
return Qnil; /* should not be reached */
}
/* Obtains the tag associated with an event. */
static VALUE grpc_rb_event_tag(VALUE self) {
grpc_event *event = NULL;
grpc_rb_event *wrapper = NULL;
Data_Get_Struct(self, grpc_rb_event, wrapper);
if (wrapper->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "finished!");
return Qnil;
}
event = wrapper->wrapped;
if (event->tag == NULL) {
return Qnil;
}
return (VALUE)event->tag;
}
/* Obtains the call associated with an event. */
static VALUE grpc_rb_event_call(VALUE self) {
grpc_event *event = NULL;
grpc_rb_event *wrapper = NULL;
Data_Get_Struct(self, grpc_rb_event, wrapper);
if (wrapper->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "finished!");
return Qnil;
}
event = wrapper->wrapped;
if (event->call != NULL) {
return grpc_rb_wrap_call(event->call);
}
return Qnil;
}
/* Obtains the metadata associated with an event. */
static VALUE grpc_rb_event_metadata(VALUE self) {
grpc_event *event = NULL;
grpc_rb_event *wrapper = NULL;
grpc_metadata *metadata = NULL;
VALUE key = Qnil;
VALUE new_ary = Qnil;
VALUE result = Qnil;
VALUE value = Qnil;
size_t count = 0;
size_t i = 0;
Data_Get_Struct(self, grpc_rb_event, wrapper);
if (wrapper->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "finished!");
return Qnil;
}
/* Figure out which metadata to read. */
event = wrapper->wrapped;
switch (event->type) {
case GRPC_CLIENT_METADATA_READ:
count = event->data.client_metadata_read.count;
metadata = event->data.client_metadata_read.elements;
break;
case GRPC_FINISHED:
count = event->data.finished.metadata_count;
metadata = event->data.finished.metadata_elements;
break;
case GRPC_SERVER_RPC_NEW:
count = event->data.server_rpc_new.metadata_count;
metadata = event->data.server_rpc_new.metadata_elements;
break;
default:
rb_raise(rb_eRuntimeError,
"bug: bad event type metadata. got %d; want %d|%d:%d",
event->type, GRPC_CLIENT_METADATA_READ, GRPC_FINISHED,
GRPC_SERVER_RPC_NEW);
return Qnil;
}
result = rb_hash_new();
for (i = 0; i < count; i++) {
key = rb_str_new2(metadata[i].key);
value = rb_hash_aref(result, key);
if (value == Qnil) {
value = rb_str_new(metadata[i].value, metadata[i].value_length);
rb_hash_aset(result, key, value);
} else if (TYPE(value) == T_ARRAY) {
/* Add the string to the returned array */
rb_ary_push(value,
rb_str_new(metadata[i].value, metadata[i].value_length));
} else {
/* Add the current value with this key and the new one to an array */
new_ary = rb_ary_new();
rb_ary_push(new_ary, value);
rb_ary_push(new_ary,
rb_str_new(metadata[i].value, metadata[i].value_length));
rb_hash_aset(result, key, new_ary);
}
}
return result;
}
/* Obtains the data associated with an event. */
static VALUE grpc_rb_event_result(VALUE self) {
grpc_event *event = NULL;
grpc_rb_event *wrapper = NULL;
Data_Get_Struct(self, grpc_rb_event, wrapper);
if (wrapper->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "finished!");
return Qnil;
}
event = wrapper->wrapped;
switch (event->type) {
case GRPC_QUEUE_SHUTDOWN:
return Qnil;
case GRPC_READ:
return grpc_rb_byte_buffer_create_with_mark(self, event->data.read);
case GRPC_FINISH_ACCEPTED:
if (event->data.finish_accepted == GRPC_OP_OK) {
return Qnil;
}
rb_raise(rb_eEventError, "finish failed, not sure why (code=%d)",
event->data.finish_accepted);
break;
case GRPC_WRITE_ACCEPTED:
if (event->data.write_accepted == GRPC_OP_OK) {
return Qnil;
}
rb_raise(rb_eEventError, "write failed, not sure why (code=%d)",
event->data.write_accepted);
break;
case GRPC_CLIENT_METADATA_READ:
return grpc_rb_event_metadata(self);
case GRPC_FINISHED:
return rb_struct_new(rb_sStatus, UINT2NUM(event->data.finished.status),
(event->data.finished.details == NULL
? Qnil
: rb_str_new2(event->data.finished.details)),
grpc_rb_event_metadata(self), NULL);
break;
case GRPC_SERVER_RPC_NEW:
return rb_struct_new(
rb_sNewServerRpc, rb_str_new2(event->data.server_rpc_new.method),
rb_str_new2(event->data.server_rpc_new.host),
Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE,
(void *)&event->data.server_rpc_new.deadline),
grpc_rb_event_metadata(self), NULL);
default:
rb_raise(rb_eRuntimeError, "unrecognized event code for an rpc event:%d",
event->type);
}
return Qfalse;
}
static VALUE grpc_rb_event_finish(VALUE self) {
grpc_event *event = NULL;
grpc_rb_event *wrapper = NULL;
Data_Get_Struct(self, grpc_rb_event, wrapper);
if (wrapper->wrapped == NULL) { /* already closed */
return Qnil;
}
event = wrapper->wrapped;
grpc_event_finish(event);
wrapper->wrapped = NULL;
wrapper->mark = Qnil;
return Qnil;
}
/* rb_cEvent is the Event class whose instances proxy grpc_event */
VALUE rb_cEvent = Qnil;
/* rb_eEventError is the ruby class of the exception thrown on failures during
rpc event processing. */
VALUE rb_eEventError = Qnil;
void Init_grpc_event() {
rb_eEventError =
rb_define_class_under(rb_mGrpcCore, "EventError", rb_eStandardError);
rb_cEvent = rb_define_class_under(rb_mGrpcCore, "Event", rb_cObject);
/* Prevent allocation or inialization from ruby. */
rb_define_alloc_func(rb_cEvent, grpc_rb_cannot_alloc);
rb_define_method(rb_cEvent, "initialize", grpc_rb_cannot_init, 0);
rb_define_method(rb_cEvent, "initialize_copy", grpc_rb_cannot_init_copy, 1);
/* Accessors for the data available in an event. */
rb_define_method(rb_cEvent, "call", grpc_rb_event_call, 0);
rb_define_method(rb_cEvent, "result", grpc_rb_event_result, 0);
rb_define_method(rb_cEvent, "tag", grpc_rb_event_tag, 0);
rb_define_method(rb_cEvent, "type", grpc_rb_event_type, 0);
rb_define_method(rb_cEvent, "finish", grpc_rb_event_finish, 0);
rb_define_alias(rb_cEvent, "close", "finish");
/* Constants representing the completion types */
rb_mCompletionType =
rb_define_module_under(rb_mGrpcCore, "CompletionType");
rb_define_const(rb_mCompletionType, "QUEUE_SHUTDOWN",
INT2NUM(GRPC_QUEUE_SHUTDOWN));
rb_define_const(rb_mCompletionType, "OP_COMPLETE", INT2NUM(GRPC_OP_COMPLETE));
rb_define_const(rb_mCompletionType, "READ", INT2NUM(GRPC_READ));
rb_define_const(rb_mCompletionType, "WRITE_ACCEPTED",
INT2NUM(GRPC_WRITE_ACCEPTED));
rb_define_const(rb_mCompletionType, "FINISH_ACCEPTED",
INT2NUM(GRPC_FINISH_ACCEPTED));
rb_define_const(rb_mCompletionType, "CLIENT_METADATA_READ",
INT2NUM(GRPC_CLIENT_METADATA_READ));
rb_define_const(rb_mCompletionType, "FINISHED", INT2NUM(GRPC_FINISHED));
rb_define_const(rb_mCompletionType, "SERVER_RPC_NEW",
INT2NUM(GRPC_SERVER_RPC_NEW));
rb_define_const(rb_mCompletionType, "SERVER_SHUTDOWN",
INT2NUM(GRPC_SERVER_SHUTDOWN));
rb_define_const(rb_mCompletionType, "RESERVED",
INT2NUM(GRPC_COMPLETION_DO_NOT_USE));
}
VALUE grpc_rb_new_event(grpc_event *ev) {
grpc_rb_event *wrapper = ALLOC(grpc_rb_event);
wrapper->wrapped = ev;
wrapper->mark = Qnil;
return Data_Wrap_Struct(rb_cEvent, grpc_rb_event_mark, grpc_rb_event_free,
wrapper);
}

@ -39,12 +39,9 @@
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include "rb_byte_buffer.h"
#include "rb_call.h" #include "rb_call.h"
#include "rb_channel.h" #include "rb_channel.h"
#include "rb_completion_queue.h" #include "rb_completion_queue.h"
#include "rb_event.h"
#include "rb_metadata.h"
#include "rb_server.h" #include "rb_server.h"
#include "rb_credentials.h" #include "rb_credentials.h"
#include "rb_server_credentials.h" #include "rb_server_credentials.h"
@ -53,7 +50,7 @@
const RUBY_DATA_FUNC GC_NOT_MARKED = NULL; const RUBY_DATA_FUNC GC_NOT_MARKED = NULL;
const RUBY_DATA_FUNC GC_DONT_FREE = NULL; const RUBY_DATA_FUNC GC_DONT_FREE = NULL;
VALUE rb_cTimeVal = Qnil; VALUE grpc_rb_cTimeVal = Qnil;
/* Alloc func that blocks allocation of a given object by raising an /* Alloc func that blocks allocation of a given object by raising an
* exception. */ * exception. */
@ -99,7 +96,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
switch (TYPE(time)) { switch (TYPE(time)) {
case T_DATA: case T_DATA:
if (CLASS_OF(time) == rb_cTimeVal) { if (CLASS_OF(time) == grpc_rb_cTimeVal) {
Data_Get_Struct(time, gpr_timespec, time_const); Data_Get_Struct(time, gpr_timespec, time_const);
t = *time_const; t = *time_const;
} else if (CLASS_OF(time) == rb_cTime) { } else if (CLASS_OF(time) == rb_cTime) {
@ -155,35 +152,41 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
void Init_grpc_status_codes() { void Init_grpc_status_codes() {
/* Constants representing the status codes or grpc_status_code in status.h */ /* Constants representing the status codes or grpc_status_code in status.h */
VALUE rb_mStatusCodes = VALUE grpc_rb_mStatusCodes =
rb_define_module_under(rb_mGrpcCore, "StatusCodes"); rb_define_module_under(grpc_rb_mGrpcCore, "StatusCodes");
rb_define_const(rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK)); rb_define_const(grpc_rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
rb_define_const(rb_mStatusCodes, "CANCELLED", INT2NUM(GRPC_STATUS_CANCELLED)); rb_define_const(grpc_rb_mStatusCodes, "CANCELLED",
rb_define_const(rb_mStatusCodes, "UNKNOWN", INT2NUM(GRPC_STATUS_UNKNOWN)); INT2NUM(GRPC_STATUS_CANCELLED));
rb_define_const(rb_mStatusCodes, "INVALID_ARGUMENT", rb_define_const(grpc_rb_mStatusCodes, "UNKNOWN",
INT2NUM(GRPC_STATUS_UNKNOWN));
rb_define_const(grpc_rb_mStatusCodes, "INVALID_ARGUMENT",
INT2NUM(GRPC_STATUS_INVALID_ARGUMENT)); INT2NUM(GRPC_STATUS_INVALID_ARGUMENT));
rb_define_const(rb_mStatusCodes, "DEADLINE_EXCEEDED", rb_define_const(grpc_rb_mStatusCodes, "DEADLINE_EXCEEDED",
INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED)); INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED));
rb_define_const(rb_mStatusCodes, "NOT_FOUND", INT2NUM(GRPC_STATUS_NOT_FOUND)); rb_define_const(grpc_rb_mStatusCodes, "NOT_FOUND",
rb_define_const(rb_mStatusCodes, "ALREADY_EXISTS", INT2NUM(GRPC_STATUS_NOT_FOUND));
rb_define_const(grpc_rb_mStatusCodes, "ALREADY_EXISTS",
INT2NUM(GRPC_STATUS_ALREADY_EXISTS)); INT2NUM(GRPC_STATUS_ALREADY_EXISTS));
rb_define_const(rb_mStatusCodes, "PERMISSION_DENIED", rb_define_const(grpc_rb_mStatusCodes, "PERMISSION_DENIED",
INT2NUM(GRPC_STATUS_PERMISSION_DENIED)); INT2NUM(GRPC_STATUS_PERMISSION_DENIED));
rb_define_const(rb_mStatusCodes, "UNAUTHENTICATED", rb_define_const(grpc_rb_mStatusCodes, "UNAUTHENTICATED",
INT2NUM(GRPC_STATUS_UNAUTHENTICATED)); INT2NUM(GRPC_STATUS_UNAUTHENTICATED));
rb_define_const(rb_mStatusCodes, "RESOURCE_EXHAUSTED", rb_define_const(grpc_rb_mStatusCodes, "RESOURCE_EXHAUSTED",
INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED)); INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED));
rb_define_const(rb_mStatusCodes, "FAILED_PRECONDITION", rb_define_const(grpc_rb_mStatusCodes, "FAILED_PRECONDITION",
INT2NUM(GRPC_STATUS_FAILED_PRECONDITION)); INT2NUM(GRPC_STATUS_FAILED_PRECONDITION));
rb_define_const(rb_mStatusCodes, "ABORTED", INT2NUM(GRPC_STATUS_ABORTED)); rb_define_const(grpc_rb_mStatusCodes, "ABORTED",
rb_define_const(rb_mStatusCodes, "OUT_OF_RANGE", INT2NUM(GRPC_STATUS_ABORTED));
rb_define_const(grpc_rb_mStatusCodes, "OUT_OF_RANGE",
INT2NUM(GRPC_STATUS_OUT_OF_RANGE)); INT2NUM(GRPC_STATUS_OUT_OF_RANGE));
rb_define_const(rb_mStatusCodes, "UNIMPLEMENTED", rb_define_const(grpc_rb_mStatusCodes, "UNIMPLEMENTED",
INT2NUM(GRPC_STATUS_UNIMPLEMENTED)); INT2NUM(GRPC_STATUS_UNIMPLEMENTED));
rb_define_const(rb_mStatusCodes, "INTERNAL", INT2NUM(GRPC_STATUS_INTERNAL)); rb_define_const(grpc_rb_mStatusCodes, "INTERNAL",
rb_define_const(rb_mStatusCodes, "UNAVAILABLE", INT2NUM(GRPC_STATUS_INTERNAL));
rb_define_const(grpc_rb_mStatusCodes, "UNAVAILABLE",
INT2NUM(GRPC_STATUS_UNAVAILABLE)); INT2NUM(GRPC_STATUS_UNAVAILABLE));
rb_define_const(rb_mStatusCodes, "DATA_LOSS", INT2NUM(GRPC_STATUS_DATA_LOSS)); rb_define_const(grpc_rb_mStatusCodes, "DATA_LOSS",
INT2NUM(GRPC_STATUS_DATA_LOSS));
} }
/* id_at is the constructor method of the ruby standard Time class. */ /* id_at is the constructor method of the ruby standard Time class. */
@ -195,7 +198,7 @@ static ID id_inspect;
/* id_to_s is the to_s method found on various ruby objects. */ /* id_to_s is the to_s method found on various ruby objects. */
static ID id_to_s; static ID id_to_s;
/* Converts `a wrapped time constant to a standard time. */ /* Converts a wrapped time constant to a standard time. */
VALUE grpc_rb_time_val_to_time(VALUE self) { VALUE grpc_rb_time_val_to_time(VALUE self) {
gpr_timespec *time_const = NULL; gpr_timespec *time_const = NULL;
Data_Get_Struct(self, gpr_timespec, time_const); Data_Get_Struct(self, gpr_timespec, time_const);
@ -215,22 +218,25 @@ VALUE grpc_rb_time_val_to_s(VALUE self) {
/* Adds a module with constants that map to gpr's static timeval structs. */ /* Adds a module with constants that map to gpr's static timeval structs. */
void Init_grpc_time_consts() { void Init_grpc_time_consts() {
VALUE rb_mTimeConsts = VALUE grpc_rb_mTimeConsts =
rb_define_module_under(rb_mGrpcCore, "TimeConsts"); rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts");
rb_cTimeVal = grpc_rb_cTimeVal =
rb_define_class_under(rb_mGrpcCore, "TimeSpec", rb_cObject); rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject);
rb_define_const(rb_mTimeConsts, "ZERO", rb_define_const(grpc_rb_mTimeConsts, "ZERO",
Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, Data_Wrap_Struct(grpc_rb_cTimeVal,
GC_NOT_MARKED, GC_DONT_FREE,
(void *)&gpr_time_0)); (void *)&gpr_time_0));
rb_define_const(rb_mTimeConsts, "INFINITE_FUTURE", rb_define_const(grpc_rb_mTimeConsts, "INFINITE_FUTURE",
Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, Data_Wrap_Struct(grpc_rb_cTimeVal,
GC_NOT_MARKED, GC_DONT_FREE,
(void *)&gpr_inf_future)); (void *)&gpr_inf_future));
rb_define_const(rb_mTimeConsts, "INFINITE_PAST", rb_define_const(grpc_rb_mTimeConsts, "INFINITE_PAST",
Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE, Data_Wrap_Struct(grpc_rb_cTimeVal,
GC_NOT_MARKED, GC_DONT_FREE,
(void *)&gpr_inf_past)); (void *)&gpr_inf_past));
rb_define_method(rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0); rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
rb_define_method(rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0); rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
rb_define_method(rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0); rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
id_at = rb_intern("at"); id_at = rb_intern("at");
id_inspect = rb_intern("inspect"); id_inspect = rb_intern("inspect");
id_to_s = rb_intern("to_s"); id_to_s = rb_intern("to_s");
@ -242,31 +248,33 @@ void grpc_rb_shutdown(void *vm) { grpc_shutdown(); }
/* Initialize the GRPC module structs */ /* Initialize the GRPC module structs */
/* rb_sNewServerRpc is the struct that holds new server rpc details. */ /* grpc_rb_sNewServerRpc is the struct that holds new server rpc details. */
VALUE rb_sNewServerRpc = Qnil; VALUE grpc_rb_sNewServerRpc = Qnil;
/* rb_sStatus is the struct that holds status details. */ /* grpc_rb_sStatus is the struct that holds status details. */
VALUE rb_sStatus = Qnil; VALUE grpc_rb_sStatus = Qnil;
/* Initialize the GRPC module. */ /* Initialize the GRPC module. */
VALUE rb_mGRPC = Qnil; VALUE grpc_rb_mGRPC = Qnil;
VALUE rb_mGrpcCore = Qnil; VALUE grpc_rb_mGrpcCore = Qnil;
void Init_grpc() { void Init_grpc() {
grpc_init(); grpc_init();
ruby_vm_at_exit(grpc_rb_shutdown); ruby_vm_at_exit(grpc_rb_shutdown);
rb_mGRPC = rb_define_module("GRPC"); grpc_rb_mGRPC = rb_define_module("GRPC");
rb_mGrpcCore = rb_define_module_under(rb_mGRPC, "Core"); grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
rb_sNewServerRpc = rb_struct_define("NewServerRpc", "method", "host", grpc_rb_sNewServerRpc =
"deadline", "metadata", NULL); rb_struct_define("NewServerRpc", "method", "host",
rb_sStatus = rb_struct_define("Status", "code", "details", "metadata", NULL); "deadline", "metadata", "call", NULL);
grpc_rb_sStatus =
rb_struct_define("Status", "code", "details", "metadata", NULL);
sym_code = ID2SYM(rb_intern("code"));
sym_details = ID2SYM(rb_intern("details"));
sym_metadata = ID2SYM(rb_intern("metadata"));
Init_grpc_byte_buffer();
Init_grpc_event();
Init_grpc_channel(); Init_grpc_channel();
Init_grpc_completion_queue(); Init_grpc_completion_queue();
Init_grpc_call(); Init_grpc_call();
Init_grpc_credentials(); Init_grpc_credentials();
Init_grpc_metadata();
Init_grpc_server(); Init_grpc_server();
Init_grpc_server_credentials(); Init_grpc_server_credentials();
Init_grpc_status_codes(); Init_grpc_status_codes();

@ -38,17 +38,26 @@
#include <ruby.h> #include <ruby.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
/* rb_mGrpcCore is the module containing the ruby wrapper GRPC classes. */ /* grpc_rb_mGrpcCore is the module containing the ruby wrapper GRPC classes. */
extern VALUE rb_mGrpcCore; extern VALUE grpc_rb_mGrpcCore;
/* Class used to wrap timeval structs. */ /* Class used to wrap timeval structs. */
extern VALUE rb_cTimeVal; extern VALUE grpc_rb_cTimeVal;
/* rb_sNewServerRpc is the struct that holds new server rpc details. */ /* grpc_rb_sNewServerRpc is the struct that holds new server rpc details. */
extern VALUE rb_sNewServerRpc; extern VALUE grpc_rb_sNewServerRpc;
/* rb_sStruct is the struct that holds status details. */ /* grpc_rb_sStruct is the struct that holds status details. */
extern VALUE rb_sStatus; extern VALUE grpc_rb_sStatus;
/* sym_code is the symbol for the code attribute of grpc_rb_sStatus. */
VALUE sym_code;
/* sym_details is the symbol for the details attribute of grpc_rb_sStatus. */
VALUE sym_details;
/* sym_metadata is the symbol for the metadata attribute of grpc_rb_sStatus. */
VALUE sym_metadata;
/* GC_NOT_MARKED is used in calls to Data_Wrap_Struct to indicate that the /* GC_NOT_MARKED is used in calls to Data_Wrap_Struct to indicate that the
wrapped struct does not need to participate in ruby gc. */ wrapped struct does not need to participate in ruby gc. */

@ -1,215 +0,0 @@
/*
*
* 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 "rb_metadata.h"
#include <ruby.h>
#include <string.h>
#include <grpc/grpc.h>
#include "rb_grpc.h"
/* grpc_rb_metadata wraps a grpc_metadata. It provides a peer ruby object,
* 'mark' to minimize copying when a metadata is created from ruby. */
typedef struct grpc_rb_metadata {
/* Holder of ruby objects involved in constructing the metadata */
VALUE mark;
/* The actual metadata */
grpc_metadata *wrapped;
} grpc_rb_metadata;
/* Destroys Metadata instances. */
static void grpc_rb_metadata_free(void *p) {
if (p == NULL) {
return;
};
/* Because metadata is only created during a call to grpc_call_add_metadata,
* and the call takes ownership of the metadata, this does not free the
* wrapped struct, only the wrapper */
xfree(p);
}
/* Protects the mark object from GC */
static void grpc_rb_metadata_mark(void *p) {
grpc_rb_metadata *md = NULL;
if (p == NULL) {
return;
}
md = (grpc_rb_metadata *)p;
/* If it's not already cleaned up, mark the mark object */
if (md->mark != Qnil && BUILTIN_TYPE(md->mark) != T_NONE) {
rb_gc_mark(md->mark);
}
}
/* Allocates Metadata instances.
Provides safe default values for the Metadata fields. */
static VALUE grpc_rb_metadata_alloc(VALUE cls) {
grpc_rb_metadata *wrapper = ALLOC(grpc_rb_metadata);
wrapper->wrapped = NULL;
wrapper->mark = Qnil;
return Data_Wrap_Struct(cls, grpc_rb_metadata_mark, grpc_rb_metadata_free,
wrapper);
}
/* id_key and id_value are the names of the hidden ivars that preserve the
* original byte_buffer source string */
static ID id_key;
static ID id_value;
/* Initializes Metadata instances. */
static VALUE grpc_rb_metadata_init(VALUE self, VALUE key, VALUE value) {
grpc_rb_metadata *wrapper = NULL;
grpc_metadata *md = ALLOC(grpc_metadata);
/* Use direct pointers to the strings wrapped by the ruby object to avoid
* copying */
Data_Get_Struct(self, grpc_rb_metadata, wrapper);
wrapper->wrapped = md;
if (TYPE(key) == T_SYMBOL) {
md->key = (char *)rb_id2name(SYM2ID(key));
} else { /* StringValueCStr does all other type exclusions for us */
md->key = StringValueCStr(key);
}
md->value = RSTRING_PTR(value);
md->value_length = RSTRING_LEN(value);
/* Save references to the original values on the mark object so that the
* pointers used there are valid for the lifetime of the object. */
wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
rb_ivar_set(wrapper->mark, id_key, key);
rb_ivar_set(wrapper->mark, id_value, value);
return self;
}
/* Clones Metadata instances.
Gives Metadata a consistent implementation of Ruby's object copy/dup
protocol. */
static VALUE grpc_rb_metadata_init_copy(VALUE copy, VALUE orig) {
grpc_rb_metadata *orig_md = NULL;
grpc_rb_metadata *copy_md = NULL;
if (copy == orig) {
return copy;
}
/* Raise an error if orig is not a metadata object or a subclass. */
if (TYPE(orig) != T_DATA ||
RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_metadata_free) {
rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cMetadata));
}
Data_Get_Struct(orig, grpc_rb_metadata, orig_md);
Data_Get_Struct(copy, grpc_rb_metadata, copy_md);
/* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper
* object. */
MEMCPY(copy_md, orig_md, grpc_rb_metadata, 1);
return copy;
}
/* Gets the key from a metadata instance. */
static VALUE grpc_rb_metadata_key(VALUE self) {
VALUE key = Qnil;
grpc_rb_metadata *wrapper = NULL;
grpc_metadata *md = NULL;
Data_Get_Struct(self, grpc_rb_metadata, wrapper);
if (wrapper->mark != Qnil) {
key = rb_ivar_get(wrapper->mark, id_key);
if (key != Qnil) {
return key;
}
}
md = wrapper->wrapped;
if (md == NULL || md->key == NULL) {
return Qnil;
}
return rb_str_new2(md->key);
}
/* Gets the value from a metadata instance. */
static VALUE grpc_rb_metadata_value(VALUE self) {
VALUE val = Qnil;
grpc_rb_metadata *wrapper = NULL;
grpc_metadata *md = NULL;
Data_Get_Struct(self, grpc_rb_metadata, wrapper);
if (wrapper->mark != Qnil) {
val = rb_ivar_get(wrapper->mark, id_value);
if (val != Qnil) {
return val;
}
}
md = wrapper->wrapped;
if (md == NULL || md->value == NULL) {
return Qnil;
}
return rb_str_new2(md->value);
}
/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */
VALUE rb_cMetadata = Qnil;
void Init_grpc_metadata() {
rb_cMetadata =
rb_define_class_under(rb_mGrpcCore, "Metadata", rb_cObject);
/* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cMetadata, grpc_rb_metadata_alloc);
/* Provides a ruby constructor and support for dup/clone. */
rb_define_method(rb_cMetadata, "initialize", grpc_rb_metadata_init, 2);
rb_define_method(rb_cMetadata, "initialize_copy", grpc_rb_metadata_init_copy,
1);
/* Provides accessors for the code and details. */
rb_define_method(rb_cMetadata, "key", grpc_rb_metadata_key, 0);
rb_define_method(rb_cMetadata, "value", grpc_rb_metadata_value, 0);
id_key = rb_intern("__key");
id_value = rb_intern("__value");
}
/* Gets the wrapped metadata from the ruby wrapper */
grpc_metadata *grpc_rb_get_wrapped_metadata(VALUE v) {
grpc_rb_metadata *wrapper = NULL;
Data_Get_Struct(v, grpc_rb_metadata, wrapper);
return wrapper->wrapped;
}

@ -43,8 +43,11 @@
#include "rb_server_credentials.h" #include "rb_server_credentials.h"
#include "rb_grpc.h" #include "rb_grpc.h"
/* rb_cServer is the ruby class that proxies grpc_server. */ /* grpc_rb_cServer is the ruby class that proxies grpc_server. */
VALUE rb_cServer = Qnil; VALUE grpc_rb_cServer = Qnil;
/* id_at is the constructor method of the ruby standard Time class. */
static ID id_at;
/* grpc_rb_server wraps a grpc_server. It provides a peer ruby object, /* grpc_rb_server wraps a grpc_server. It provides a peer ruby object,
'mark' to minimize copying when a server is created from ruby. */ 'mark' to minimize copying when a server is created from ruby. */
@ -140,7 +143,7 @@ static VALUE grpc_rb_server_init_copy(VALUE copy, VALUE orig) {
/* Raise an error if orig is not a server object or a subclass. */ /* Raise an error if orig is not a server object or a subclass. */
if (TYPE(orig) != T_DATA || if (TYPE(orig) != T_DATA ||
RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_free) { RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_free) {
rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cServer)); rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(grpc_rb_cServer));
} }
Data_Get_Struct(orig, grpc_rb_server, orig_srv); Data_Get_Struct(orig, grpc_rb_server, orig_srv);
@ -152,18 +155,90 @@ static VALUE grpc_rb_server_init_copy(VALUE copy, VALUE orig) {
return copy; return copy;
} }
static VALUE grpc_rb_server_request_call(VALUE self, VALUE tag_new) { /* request_call_stack holds various values used by the
grpc_call_error err; * grpc_rb_server_request_call function */
typedef struct request_call_stack {
grpc_call_details details;
grpc_metadata_array md_ary;
} request_call_stack;
/* grpc_request_call_stack_init ensures the request_call_stack is properly
* initialized */
static void grpc_request_call_stack_init(request_call_stack* st) {
MEMZERO(st, request_call_stack, 1);
grpc_metadata_array_init(&st->md_ary);
grpc_call_details_init(&st->details);
st->details.method = NULL;
st->details.host = NULL;
}
/* grpc_request_call_stack_cleanup ensures the request_call_stack is properly
* cleaned up */
static void grpc_request_call_stack_cleanup(request_call_stack* st) {
grpc_metadata_array_destroy(&st->md_ary);
grpc_call_details_destroy(&st->details);
}
/* call-seq:
cq = CompletionQueue.new
tag = Object.new
timeout = 10
server.request_call(cqueue, tag, timeout)
Requests notification of a new call on a server. */
static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue,
VALUE tag_new, VALUE timeout) {
grpc_rb_server *s = NULL; grpc_rb_server *s = NULL;
grpc_call *call = NULL;
grpc_event *ev = NULL;
grpc_call_error err;
request_call_stack st;
VALUE result;
Data_Get_Struct(self, grpc_rb_server, s); Data_Get_Struct(self, grpc_rb_server, s);
if (s->wrapped == NULL) { if (s->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
return Qnil;
} else { } else {
err = grpc_server_request_call_old(s->wrapped, ROBJECT(tag_new)); grpc_request_call_stack_init(&st);
/* call grpc_server_request_call, then wait for it to complete using
* pluck_event */
err = grpc_server_request_call(
s->wrapped, &call, &st.details, &st.md_ary,
grpc_rb_get_wrapped_completion_queue(cqueue),
ROBJECT(tag_new));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "server request failed: %s (code=%d)", grpc_request_call_stack_cleanup(&st);
rb_raise(grpc_rb_eCallError,
"grpc_server_request_call failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
return Qnil;
} }
ev = grpc_rb_completion_queue_pluck_event(cqueue, tag_new, timeout);
if (ev == NULL) {
grpc_request_call_stack_cleanup(&st);
return Qnil;
}
if (ev->data.op_complete != GRPC_OP_OK) {
grpc_request_call_stack_cleanup(&st);
grpc_event_finish(ev);
rb_raise(grpc_rb_eCallError, "request_call completion failed: (code=%d)",
ev->data.op_complete);
return Qnil;
}
/* build the NewServerRpc struct result */
result = rb_struct_new(
grpc_rb_sNewServerRpc,
rb_str_new2(st.details.method),
rb_str_new2(st.details.host),
rb_funcall(rb_cTime, id_at, 2, INT2NUM(st.details.deadline.tv_sec),
INT2NUM(st.details.deadline.tv_nsec)),
grpc_rb_md_ary_to_h(&st.md_ary),
grpc_rb_wrap_call(call),
NULL);
grpc_event_finish(ev);
grpc_request_call_stack_cleanup(&st);
return result;
} }
return Qnil; return Qnil;
} }
@ -239,22 +314,27 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
} }
void Init_grpc_server() { void Init_grpc_server() {
rb_cServer = rb_define_class_under(rb_mGrpcCore, "Server", rb_cObject); grpc_rb_cServer =
rb_define_class_under(grpc_rb_mGrpcCore, "Server", rb_cObject);
/* Allocates an object managed by the ruby runtime */ /* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cServer, grpc_rb_server_alloc); rb_define_alloc_func(grpc_rb_cServer, grpc_rb_server_alloc);
/* Provides a ruby constructor and support for dup/clone. */ /* Provides a ruby constructor and support for dup/clone. */
rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, 2); rb_define_method(grpc_rb_cServer, "initialize", grpc_rb_server_init, 2);
rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1); rb_define_method(grpc_rb_cServer, "initialize_copy",
grpc_rb_server_init_copy, 1);
/* Add the server methods. */ /* Add the server methods. */
rb_define_method(rb_cServer, "request_call", grpc_rb_server_request_call, 1); rb_define_method(grpc_rb_cServer, "request_call",
rb_define_method(rb_cServer, "start", grpc_rb_server_start, 0); grpc_rb_server_request_call, 3);
rb_define_method(rb_cServer, "destroy", grpc_rb_server_destroy, 0); rb_define_method(grpc_rb_cServer, "start", grpc_rb_server_start, 0);
rb_define_alias(rb_cServer, "close", "destroy"); rb_define_method(grpc_rb_cServer, "destroy", grpc_rb_server_destroy, 0);
rb_define_method(rb_cServer, "add_http2_port", grpc_rb_server_add_http2_port, rb_define_alias(grpc_rb_cServer, "close", "destroy");
rb_define_method(grpc_rb_cServer, "add_http2_port",
grpc_rb_server_add_http2_port,
-1); -1);
id_at = rb_intern("at");
} }
/* Gets the wrapped server from the ruby wrapper */ /* Gets the wrapped server from the ruby wrapper */

@ -37,9 +37,9 @@
#include <ruby.h> #include <ruby.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
/* rb_cServer is the Server class whose instances proxy /* grpc_rb_cServer is the Server class whose instances proxy
grpc_byte_buffer. */ grpc_byte_buffer. */
extern VALUE rb_cServer; extern VALUE grpc_rb_cServer;
/* Initializes the Server class. */ /* Initializes the Server class. */
void Init_grpc_server(); void Init_grpc_server();

@ -109,7 +109,7 @@ static VALUE grpc_rb_server_credentials_init_copy(VALUE copy, VALUE orig) {
if (TYPE(orig) != T_DATA || if (TYPE(orig) != T_DATA ||
RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_credentials_free) { RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_credentials_free) {
rb_raise(rb_eTypeError, "not a %s", rb_raise(rb_eTypeError, "not a %s",
rb_obj_classname(rb_cServerCredentials)); rb_obj_classname(grpc_rb_cServerCredentials));
} }
Data_Get_Struct(orig, grpc_rb_server_credentials, orig_ch); Data_Get_Struct(orig, grpc_rb_server_credentials, orig_ch);
@ -180,21 +180,22 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
return self; return self;
} }
/* rb_cServerCredentials is the ruby class that proxies /* grpc_rb_cServerCredentials is the ruby class that proxies
grpc_server_credentials. */ grpc_server_credentials. */
VALUE rb_cServerCredentials = Qnil; VALUE grpc_rb_cServerCredentials = Qnil;
void Init_grpc_server_credentials() { void Init_grpc_server_credentials() {
rb_cServerCredentials = grpc_rb_cServerCredentials =
rb_define_class_under(rb_mGrpcCore, "ServerCredentials", rb_cObject); rb_define_class_under(grpc_rb_mGrpcCore, "ServerCredentials", rb_cObject);
/* Allocates an object managed by the ruby runtime */ /* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cServerCredentials, grpc_rb_server_credentials_alloc); rb_define_alloc_func(grpc_rb_cServerCredentials,
grpc_rb_server_credentials_alloc);
/* Provides a ruby constructor and support for dup/clone. */ /* Provides a ruby constructor and support for dup/clone. */
rb_define_method(rb_cServerCredentials, "initialize", rb_define_method(grpc_rb_cServerCredentials, "initialize",
grpc_rb_server_credentials_init, 3); grpc_rb_server_credentials_init, 3);
rb_define_method(rb_cServerCredentials, "initialize_copy", rb_define_method(grpc_rb_cServerCredentials, "initialize_copy",
grpc_rb_server_credentials_init_copy, 1); grpc_rb_server_credentials_init_copy, 1);
id_pem_cert_chain = rb_intern("__pem_cert_chain"); id_pem_cert_chain = rb_intern("__pem_cert_chain");

@ -37,9 +37,9 @@
#include <ruby.h> #include <ruby.h>
#include <grpc/grpc_security.h> #include <grpc/grpc_security.h>
/* rb_cServerCredentials is the ruby class whose instances proxy /* grpc_rb_cServerCredentials is the ruby class whose instances proxy
grpc_server_credentials. */ grpc_server_credentials. */
extern VALUE rb_cServerCredentials; extern VALUE grpc_rb_cServerCredentials;
/* Initializes the ruby ServerCredentials class. */ /* Initializes the ruby ServerCredentials class. */
void Init_grpc_server_credentials(); void Init_grpc_server_credentials();

@ -31,7 +31,6 @@ require 'grpc/errors'
require 'grpc/grpc' require 'grpc/grpc'
require 'grpc/logconfig' require 'grpc/logconfig'
require 'grpc/version' require 'grpc/version'
require 'grpc/core/event'
require 'grpc/core/time_consts' require 'grpc/core/time_consts'
require 'grpc/generic/active_call' require 'grpc/generic/active_call'
require 'grpc/generic/client_stub' require 'grpc/generic/client_stub'

@ -1,44 +0,0 @@
# 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.
require 'grpc'
# GRPC contains the General RPC module.
module GRPC
module Core
# Event is a class defined in the c extension
#
# Here, we add an inspect method.
class Event
def inspect
"<#{self.class}: type:#{type}, tag:#{tag} result:#{result}>"
end
end
end
end

@ -31,10 +31,6 @@ require 'grpc'
# GRPC contains the General RPC module. # GRPC contains the General RPC module.
module GRPC module GRPC
# OutOfTime is an exception class that indicates that an RPC exceeded its
# deadline.
OutOfTime = Class.new(StandardError)
# BadStatus is an exception class that indicates that an error occurred at # BadStatus is an exception class that indicates that an error occurred at
# either end of a GRPC connection. When raised, it indicates that a status # either end of a GRPC connection. When raised, it indicates that a status
# error should be returned to the other end of a GRPC connection; when # error should be returned to the other end of a GRPC connection; when

@ -30,20 +30,14 @@
require 'forwardable' require 'forwardable'
require 'grpc/generic/bidi_call' require 'grpc/generic/bidi_call'
def assert_event_type(ev, want)
fail OutOfTime if ev.nil?
got = ev.type
fail "Unexpected rpc event: got #{got}, want #{want}" unless got == want
end
# GRPC contains the General RPC module. # GRPC contains the General RPC module.
module GRPC module GRPC
# The ActiveCall class provides simple methods for sending marshallable # The ActiveCall class provides simple methods for sending marshallable
# data to a call # data to a call
class ActiveCall class ActiveCall
include Core::CompletionType
include Core::StatusCodes include Core::StatusCodes
include Core::TimeConsts include Core::TimeConsts
include Core::CallOps
attr_reader(:deadline) attr_reader(:deadline)
# client_invoke begins a client invocation. # client_invoke begins a client invocation.
@ -61,15 +55,14 @@ module GRPC
# @param q [CompletionQueue] the completion queue # @param q [CompletionQueue] the completion queue
# @param deadline [Fixnum,TimeSpec] the deadline # @param deadline [Fixnum,TimeSpec] the deadline
def self.client_invoke(call, q, _deadline, **kw) def self.client_invoke(call, q, _deadline, **kw)
fail(ArgumentError, 'not a call') unless call.is_a? Core::Call fail(TypeError, '!Core::Call') unless call.is_a? Core::Call
unless q.is_a? Core::CompletionQueue unless q.is_a? Core::CompletionQueue
fail(ArgumentError, 'not a CompletionQueue') fail(TypeError, '!Core::CompletionQueue')
end end
call.add_metadata(kw) if kw.length > 0 metadata_tag = Object.new
client_metadata_read = Object.new call.run_batch(q, metadata_tag, INFINITE_FUTURE,
finished_tag = Object.new SEND_INITIAL_METADATA => kw)
call.invoke(q, client_metadata_read, finished_tag) metadata_tag
[finished_tag, client_metadata_read]
end end
# Creates an ActiveCall. # Creates an ActiveCall.
@ -91,25 +84,21 @@ module GRPC
# @param marshal [Function] f(obj)->string that marshal requests # @param marshal [Function] f(obj)->string that marshal requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [Fixnum] the deadline for the call to complete # @param deadline [Fixnum] the deadline for the call to complete
# @param finished_tag [Object] the object used as the call's finish tag, # @param metadata_tag [Object] the object use obtain metadata for clients
# if the call has begun
# @param read_metadata_tag [Object] the object used as the call's finish
# tag, if the call has begun
# @param started [true|false] indicates if the call has begun # @param started [true|false] indicates if the call has begun
def initialize(call, q, marshal, unmarshal, deadline, finished_tag: nil, def initialize(call, q, marshal, unmarshal, deadline, started: true,
read_metadata_tag: nil, started: true) metadata_tag: nil)
fail(ArgumentError, 'not a call') unless call.is_a? Core::Call fail(TypeError, '!Core::Call') unless call.is_a? Core::Call
unless q.is_a? Core::CompletionQueue unless q.is_a? Core::CompletionQueue
fail(ArgumentError, 'not a CompletionQueue') fail(TypeError, '!Core::CompletionQueue')
end end
@call = call @call = call
@cq = q @cq = q
@deadline = deadline @deadline = deadline
@finished_tag = finished_tag
@read_metadata_tag = read_metadata_tag
@marshal = marshal @marshal = marshal
@started = started @started = started
@unmarshal = unmarshal @unmarshal = unmarshal
@metadata_tag = metadata_tag
end end
# Obtains the status of the call. # Obtains the status of the call.
@ -176,51 +165,38 @@ module GRPC
# writes_done indicates that all writes are completed. # writes_done indicates that all writes are completed.
# #
# It blocks until the remote endpoint acknowledges by sending a FINISHED # It blocks until the remote endpoint acknowledges with at status unless
# event, unless assert_finished is set to false. Any calls to # assert_finished is set to false. Any calls to #remote_send after this
# #remote_send after this call will fail. # call will fail.
# #
# @param assert_finished [true, false] when true(default), waits for # @param assert_finished [true, false] when true(default), waits for
# FINISHED. # FINISHED.
def writes_done(assert_finished = true) def writes_done(assert_finished = true)
@call.writes_done(self) ops = {
ev = @cq.pluck(self, INFINITE_FUTURE) SEND_CLOSE_FROM_CLIENT => nil
begin }
assert_event_type(ev, FINISH_ACCEPTED) ops[RECV_STATUS_ON_CLIENT] = nil if assert_finished
logger.debug("Writes done: waiting for finish? #{assert_finished}") @call.run_batch(@cq, self, INFINITE_FUTURE, ops)
ensure
ev.close
end
return unless assert_finished return unless assert_finished
ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
fail 'unexpected nil event' if ev.nil?
ev.close
@call.status @call.status
end end
# finished waits until the call is completed. # finished waits until a client call is completed.
# #
# It blocks until the remote endpoint acknowledges by sending a FINISHED # It blocks until the remote endpoint acknowledges by sending a status.
# event.
def finished def finished
ev = @cq.pluck(@finished_tag, INFINITE_FUTURE) batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE,
begin RECV_STATUS_ON_CLIENT => nil)
fail "unexpected event: #{ev.inspect}" unless ev.type == FINISHED if @call.metadata.nil?
if @call.metadata.nil? @call.metadata = batch_result.metadata
@call.metadata = ev.result.metadata elsif !batch_result.metadata.nil?
else @call.metadata.merge!(batch_result.metadata)
@call.metadata.merge!(ev.result.metadata)
end
if ev.result.code != Core::StatusCodes::OK
fail BadStatus.new(ev.result.code, ev.result.details)
end
res = ev.result
ensure
ev.close
end end
res if batch_result.status.code != Core::StatusCodes::OK
fail BadStatus.new(batch_result.status.code,
batch_result.status.details)
end
batch_result
end end
# remote_send sends a request to the remote endpoint. # remote_send sends a request to the remote endpoint.
@ -232,72 +208,50 @@ module GRPC
# @param marshalled [false, true] indicates if the object is already # @param marshalled [false, true] indicates if the object is already
# marshalled. # marshalled.
def remote_send(req, marshalled = false) def remote_send(req, marshalled = false)
assert_queue_is_ready
logger.debug("sending #{req.inspect}, marshalled? #{marshalled}") logger.debug("sending #{req.inspect}, marshalled? #{marshalled}")
if marshalled if marshalled
payload = req payload = req
else else
payload = @marshal.call(req) payload = @marshal.call(req)
end end
@call.start_write(Core::ByteBuffer.new(payload), self) @call.run_batch(@cq, self, INFINITE_FUTURE, SEND_MESSAGE => payload)
# call queue#pluck, and wait for WRITE_ACCEPTED, so as not to return
# until the flow control allows another send on this call.
ev = @cq.pluck(self, INFINITE_FUTURE)
begin
assert_event_type(ev, WRITE_ACCEPTED)
ensure
ev.close
end
end end
# send_status sends a status to the remote endpoint # send_status sends a status to the remote endpoint.
# #
# @param code [int] the status code to send # @param code [int] the status code to send
# @param details [String] details # @param details [String] details
# @param assert_finished [true, false] when true(default), waits for # @param assert_finished [true, false] when true(default), waits for
# FINISHED. # FINISHED.
def send_status(code = OK, details = '', assert_finished = false) def send_status(code = OK, details = '', assert_finished = false)
assert_queue_is_ready ops = {
@call.start_write_status(code, details, self) SEND_STATUS_FROM_SERVER => Struct::Status.new(code, details)
ev = @cq.pluck(self, INFINITE_FUTURE) }
begin ops[RECV_CLOSE_ON_SERVER] = nil if assert_finished
assert_event_type(ev, FINISH_ACCEPTED) @call.run_batch(@cq, self, INFINITE_FUTURE, ops)
ensure
ev.close
end
logger.debug("Status sent: #{code}:'#{details}'")
return finished if assert_finished
nil nil
end end
# remote_read reads a response from the remote endpoint. # remote_read reads a response from the remote endpoint.
# #
# It blocks until the remote endpoint sends a READ or FINISHED event. On # It blocks until the remote endpoint replies with a message or status.
# a READ, it returns the response after unmarshalling it. On # On receiving a message, it returns the response after unmarshalling it.
# FINISHED, it returns nil if the status is OK, otherwise raising # On receiving a status, it returns nil if the status is OK, otherwise
# BadStatus # raising BadStatus
def remote_read def remote_read
if @call.metadata.nil? && !@read_metadata_tag.nil? ops = { RECV_MESSAGE => nil }
ev = @cq.pluck(@read_metadata_tag, INFINITE_FUTURE) ops[RECV_INITIAL_METADATA] = nil unless @metadata_tag.nil?
assert_event_type(ev, CLIENT_METADATA_READ) batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE, ops)
@call.metadata = ev.result unless @metadata_tag.nil?
@read_metadata_tag = nil @call.metadata = batch_result.metadata
@metadata_tag = nil
end end
logger.debug("received req: #{batch_result}")
@call.start_read(self) unless batch_result.nil? || batch_result.message.nil?
ev = @cq.pluck(self, INFINITE_FUTURE) logger.debug("received req.to_s: #{batch_result.message}")
begin res = @unmarshal.call(batch_result.message)
assert_event_type(ev, READ) logger.debug("received_req (unmarshalled): #{res.inspect}")
logger.debug("received req: #{ev.result.inspect}") return res
unless ev.result.nil?
logger.debug("received req.to_s: #{ev.result}")
res = @unmarshal.call(ev.result.to_s)
logger.debug("received_req (unmarshalled): #{res.inspect}")
return res
end
ensure
ev.close
end end
logger.debug('found nil; the final response has been sent') logger.debug('found nil; the final response has been sent')
nil nil
@ -324,7 +278,6 @@ module GRPC
return enum_for(:each_remote_read) unless block_given? return enum_for(:each_remote_read) unless block_given?
loop do loop do
resp = remote_read resp = remote_read
break if resp.is_a? Struct::Status # is an OK status
break if resp.nil? # the last response was received break if resp.nil? # the last response was received
yield resp yield resp
end end
@ -461,8 +414,7 @@ module GRPC
# @return [Enumerator, nil] a response Enumerator # @return [Enumerator, nil] a response Enumerator
def bidi_streamer(requests, **kw, &blk) def bidi_streamer(requests, **kw, &blk)
start_call(**kw) unless @started start_call(**kw) unless @started
bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline, bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline)
@finished_tag)
bd.run_on_client(requests, &blk) bd.run_on_client(requests, &blk)
end end
@ -478,8 +430,7 @@ module GRPC
# #
# @param gen_each_reply [Proc] generates the BiDi stream replies # @param gen_each_reply [Proc] generates the BiDi stream replies
def run_server_bidi(gen_each_reply) def run_server_bidi(gen_each_reply)
bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline, bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline)
@finished_tag)
bd.run_on_server(gen_each_reply) bd.run_on_server(gen_each_reply)
end end
@ -516,21 +467,5 @@ module GRPC
# a Operation on the client. # a Operation on the client.
Operation = view_class(:cancel, :cancelled, :deadline, :execute, Operation = view_class(:cancel, :cancelled, :deadline, :execute,
:metadata, :status) :metadata, :status)
# confirms that no events are enqueued, and that the queue is not
# shutdown.
def assert_queue_is_ready
ev = nil
begin
ev = @cq.pluck(self, ZERO)
fail "unexpected event #{ev.inspect}" unless ev.nil?
rescue OutOfTime
logging.debug('timed out waiting for next event')
# expected, nothing should be on the queue and the deadline was ZERO,
# except things using another tag
ensure
ev.close unless ev.nil?
end
end
end end
end end

@ -30,18 +30,12 @@
require 'forwardable' require 'forwardable'
require 'grpc/grpc' require 'grpc/grpc'
def assert_event_type(ev, want)
fail OutOfTime if ev.nil?
got = ev.type
fail("Unexpected rpc event: got #{got}, want #{want}") unless got == want
end
# GRPC contains the General RPC module. # GRPC contains the General RPC module.
module GRPC module GRPC
# The BiDiCall class orchestrates exection of a BiDi stream on a client or # The BiDiCall class orchestrates exection of a BiDi stream on a client or
# server. # server.
class BidiCall class BidiCall
include Core::CompletionType include Core::CallOps
include Core::StatusCodes include Core::StatusCodes
include Core::TimeConsts include Core::TimeConsts
@ -63,8 +57,7 @@ module GRPC
# @param marshal [Function] f(obj)->string that marshal requests # @param marshal [Function] f(obj)->string that marshal requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [Fixnum] the deadline for the call to complete # @param deadline [Fixnum] the deadline for the call to complete
# @param finished_tag [Object] the object used as the call's finish tag, def initialize(call, q, marshal, unmarshal, deadline)
def initialize(call, q, marshal, unmarshal, deadline, finished_tag)
fail(ArgumentError, 'not a call') unless call.is_a? Core::Call fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
unless q.is_a? Core::CompletionQueue unless q.is_a? Core::CompletionQueue
fail(ArgumentError, 'not a CompletionQueue') fail(ArgumentError, 'not a CompletionQueue')
@ -72,7 +65,6 @@ module GRPC
@call = call @call = call
@cq = q @cq = q
@deadline = deadline @deadline = deadline
@finished_tag = finished_tag
@marshal = marshal @marshal = marshal
@readq = Queue.new @readq = Queue.new
@unmarshal = unmarshal @unmarshal = unmarshal
@ -146,30 +138,14 @@ module GRPC
requests.each do |req| requests.each do |req|
count += 1 count += 1
payload = @marshal.call(req) payload = @marshal.call(req)
@call.start_write(Core::ByteBuffer.new(payload), write_tag) @call.run_batch(@cq, write_tag, INFINITE_FUTURE,
ev = @cq.pluck(write_tag, INFINITE_FUTURE) SEND_MESSAGE => payload)
begin
assert_event_type(ev, WRITE_ACCEPTED)
ensure
ev.close
end
end end
if is_client if is_client
@call.writes_done(write_tag)
ev = @cq.pluck(write_tag, INFINITE_FUTURE)
begin
assert_event_type(ev, FINISH_ACCEPTED)
ensure
ev.close
end
logger.debug("bidi-client: sent #{count} reqs, waiting to finish") logger.debug("bidi-client: sent #{count} reqs, waiting to finish")
ev = @cq.pluck(@finished_tag, INFINITE_FUTURE) @call.run_batch(@cq, write_tag, INFINITE_FUTURE,
begin SEND_CLOSE_FROM_CLIENT => nil,
assert_event_type(ev, FINISHED) RECV_STATUS_ON_CLIENT => nil)
ensure
ev.close
end
logger.debug('bidi-client: finished received')
end end
rescue StandardError => e rescue StandardError => e
logger.warn('bidi: write_loop failed') logger.warn('bidi: write_loop failed')
@ -189,25 +165,20 @@ module GRPC
loop do loop do
logger.debug("waiting for read #{count}") logger.debug("waiting for read #{count}")
count += 1 count += 1
@call.start_read(read_tag) # TODO: ensure metadata is read if available, currently it's not
ev = @cq.pluck(read_tag, INFINITE_FUTURE) batch_result = @call.run_batch(@cq, read_tag, INFINITE_FUTURE,
begin RECV_MESSAGE => nil)
assert_event_type(ev, READ) # handle the next message
if batch_result.message.nil?
# handle the next event. @readq.push(END_OF_READS)
if ev.result.nil? logger.debug('done reading!')
@readq.push(END_OF_READS) break
logger.debug('done reading!')
break
end
# push the latest read onto the queue and continue reading
logger.debug("received req: #{ev.result}")
res = @unmarshal.call(ev.result.to_s)
@readq.push(res)
ensure
ev.close
end end
# push the latest read onto the queue and continue reading
logger.debug("received req: #{batch_result.message}")
res = @unmarshal.call(batch_result.message)
@readq.push(res)
end end
rescue StandardError => e rescue StandardError => e

@ -35,9 +35,10 @@ module GRPC
# ClientStub represents an endpoint used to send requests to GRPC servers. # ClientStub represents an endpoint used to send requests to GRPC servers.
class ClientStub class ClientStub
include Core::StatusCodes include Core::StatusCodes
include Core::TimeConsts
# Default deadline is 5 seconds. # Default timeout is 5 seconds.
DEFAULT_DEADLINE = 5 DEFAULT_TIMEOUT = 5
# setup_channel is used by #initialize to constuct a channel from its # setup_channel is used by #initialize to constuct a channel from its
# arguments. # arguments.
@ -76,8 +77,8 @@ module GRPC
# present the host and arbitrary keyword arg areignored, and the RPC # present the host and arbitrary keyword arg areignored, and the RPC
# connection uses this channel. # connection uses this channel.
# #
# - :deadline # - :timeout
# when present, this is the default deadline used for calls # when present, this is the default timeout used for calls
# #
# - :update_metadata # - :update_metadata
# when present, this a func that takes a hash and returns a hash # when present, this a func that takes a hash and returns a hash
@ -87,13 +88,13 @@ module GRPC
# @param host [String] the host the stub connects to # @param host [String] the host the stub connects to
# @param q [Core::CompletionQueue] used to wait for events # @param q [Core::CompletionQueue] used to wait for events
# @param channel_override [Core::Channel] a pre-created channel # @param channel_override [Core::Channel] a pre-created channel
# @param deadline [Number] the default deadline to use in requests # @param timeout [Number] the default timeout to use in requests
# @param creds [Core::Credentials] the channel # @param creds [Core::Credentials] the channel
# @param update_metadata a func that updates metadata as described above # @param update_metadata a func that updates metadata as described above
# @param kw [KeywordArgs]the channel arguments # @param kw [KeywordArgs]the channel arguments
def initialize(host, q, def initialize(host, q,
channel_override: nil, channel_override: nil,
deadline: DEFAULT_DEADLINE, timeout: nil,
creds: nil, creds: nil,
update_metadata: nil, update_metadata: nil,
**kw) **kw)
@ -103,7 +104,7 @@ module GRPC
@update_metadata = ClientStub.check_update_metadata(update_metadata) @update_metadata = ClientStub.check_update_metadata(update_metadata)
alt_host = kw[Core::Channel::SSL_TARGET] alt_host = kw[Core::Channel::SSL_TARGET]
@host = alt_host.nil? ? host : alt_host @host = alt_host.nil? ? host : alt_host
@deadline = deadline @timeout = timeout.nil? ? DEFAULT_TIMEOUT : timeout
end end
# request_response sends a request to a GRPC server, and returns the # request_response sends a request to a GRPC server, and returns the
@ -140,12 +141,12 @@ module GRPC
# @param req [Object] the request sent to the server # @param req [Object] the request sent to the server
# @param marshal [Function] f(obj)->string that marshals requests # @param marshal [Function] f(obj)->string that marshals requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [Numeric] (optional) the max completion time in seconds # @param timeout [Numeric] (optional) the max completion time in seconds
# @param return_op [true|false] return an Operation if true # @param return_op [true|false] return an Operation if true
# @return [Object] the response received from the server # @return [Object] the response received from the server
def request_response(method, req, marshal, unmarshal, deadline = nil, def request_response(method, req, marshal, unmarshal, timeout = nil,
return_op: false, **kw) return_op: false, **kw)
c = new_active_call(method, marshal, unmarshal, deadline || @deadline) c = new_active_call(method, marshal, unmarshal, timeout)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone)
return c.request_response(req, **md) unless return_op return c.request_response(req, **md) unless return_op
@ -197,12 +198,12 @@ module GRPC
# @param requests [Object] an Enumerable of requests to send # @param requests [Object] an Enumerable of requests to send
# @param marshal [Function] f(obj)->string that marshals requests # @param marshal [Function] f(obj)->string that marshals requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [Numeric] the max completion time in seconds # @param timeout [Numeric] the max completion time in seconds
# @param return_op [true|false] return an Operation if true # @param return_op [true|false] return an Operation if true
# @return [Object|Operation] the response received from the server # @return [Object|Operation] the response received from the server
def client_streamer(method, requests, marshal, unmarshal, deadline = nil, def client_streamer(method, requests, marshal, unmarshal, timeout = nil,
return_op: false, **kw) return_op: false, **kw)
c = new_active_call(method, marshal, unmarshal, deadline || @deadline) c = new_active_call(method, marshal, unmarshal, timeout)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone)
return c.client_streamer(requests, **md) unless return_op return c.client_streamer(requests, **md) unless return_op
@ -262,13 +263,13 @@ module GRPC
# @param req [Object] the request sent to the server # @param req [Object] the request sent to the server
# @param marshal [Function] f(obj)->string that marshals requests # @param marshal [Function] f(obj)->string that marshals requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [Numeric] the max completion time in seconds # @param timeout [Numeric] the max completion time in seconds
# @param return_op [true|false]return an Operation if true # @param return_op [true|false]return an Operation if true
# @param blk [Block] when provided, is executed for each response # @param blk [Block] when provided, is executed for each response
# @return [Enumerator|Operation|nil] as discussed above # @return [Enumerator|Operation|nil] as discussed above
def server_streamer(method, req, marshal, unmarshal, deadline = nil, def server_streamer(method, req, marshal, unmarshal, timeout = nil,
return_op: false, **kw, &blk) return_op: false, **kw, &blk)
c = new_active_call(method, marshal, unmarshal, deadline || @deadline) c = new_active_call(method, marshal, unmarshal, timeout)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone)
return c.server_streamer(req, **md, &blk) unless return_op return c.server_streamer(req, **md, &blk) unless return_op
@ -367,13 +368,13 @@ module GRPC
# @param requests [Object] an Enumerable of requests to send # @param requests [Object] an Enumerable of requests to send
# @param marshal [Function] f(obj)->string that marshals requests # @param marshal [Function] f(obj)->string that marshals requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [Numeric] (optional) the max completion time in seconds # @param timeout [Numeric] (optional) the max completion time in seconds
# @param blk [Block] when provided, is executed for each response # @param blk [Block] when provided, is executed for each response
# @param return_op [true|false] return an Operation if true # @param return_op [true|false] return an Operation if true
# @return [Enumerator|nil|Operation] as discussed above # @return [Enumerator|nil|Operation] as discussed above
def bidi_streamer(method, requests, marshal, unmarshal, deadline = nil, def bidi_streamer(method, requests, marshal, unmarshal, timeout = nil,
return_op: false, **kw, &blk) return_op: false, **kw, &blk)
c = new_active_call(method, marshal, unmarshal, deadline || @deadline) c = new_active_call(method, marshal, unmarshal, timeout)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone) md = @update_metadata.nil? ? kw : @update_metadata.call(kw.clone)
return c.bidi_streamer(requests, **md, &blk) unless return_op return c.bidi_streamer(requests, **md, &blk) unless return_op
@ -390,15 +391,14 @@ module GRPC
# Creates a new active stub # Creates a new active stub
# #
# @param ch [GRPC::Channel] the channel used to create the stub. # @param method [string] the method being called.
# @param marshal [Function] f(obj)->string that marshals requests # @param marshal [Function] f(obj)->string that marshals requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [TimeConst] # @param timeout [TimeConst]
def new_active_call(ch, marshal, unmarshal, deadline = nil) def new_active_call(method, marshal, unmarshal, timeout = nil)
absolute_deadline = Core::TimeConsts.from_relative_time(deadline) deadline = from_relative_time(timeout.nil? ? @timeout : timeout)
call = @ch.create_call(ch, @host, absolute_deadline) call = @ch.create_call(@queue, method, @host, deadline)
ActiveCall.new(call, @queue, marshal, unmarshal, absolute_deadline, ActiveCall.new(call, @queue, marshal, unmarshal, deadline, started: false)
started: false)
end end
end end
end end

@ -81,7 +81,6 @@ module GRPC
active_call.run_server_bidi(mth) active_call.run_server_bidi(mth)
end end
send_status(active_call, OK, 'OK') send_status(active_call, OK, 'OK')
active_call.finished
rescue BadStatus => e rescue BadStatus => e
# this is raised by handlers that want GRPC to send an application # this is raised by handlers that want GRPC to send an application
# error code and detail message. # error code and detail message.
@ -91,15 +90,11 @@ module GRPC
# This is raised by GRPC internals but should rarely, if ever happen. # This is raised by GRPC internals but should rarely, if ever happen.
# Log it, but don't notify the other endpoint.. # Log it, but don't notify the other endpoint..
logger.warn("failed call: #{active_call}\n#{e}") logger.warn("failed call: #{active_call}\n#{e}")
rescue OutOfTime rescue Core::OutOfTime
# This is raised when active_call#method.call exceeeds the deadline # This is raised when active_call#method.call exceeeds the deadline
# event. Send a status of deadline exceeded # event. Send a status of deadline exceeded
logger.warn("late call: #{active_call}") logger.warn("late call: #{active_call}")
send_status(active_call, DEADLINE_EXCEEDED, 'late') send_status(active_call, DEADLINE_EXCEEDED, 'late')
rescue Core::EventError => e
# This is raised by GRPC internals but should rarely, if ever happen.
# Log it, but don't notify the other endpoint..
logger.warn("failed call: #{active_call}\n#{e}")
rescue StandardError => e rescue StandardError => e
# This will usuaally be an unhandled error in the handling code. # This will usuaally be an unhandled error in the handling code.
# Send back a UNKNOWN status to the client # Send back a UNKNOWN status to the client
@ -142,7 +137,7 @@ module GRPC
def send_status(active_client, code, details) def send_status(active_client, code, details)
details = 'Not sure why' if details.nil? details = 'Not sure why' if details.nil?
active_client.send_status(code, details) active_client.send_status(code, details, code == OK)
rescue StandardError => e rescue StandardError => e
logger.warn("Could not send status #{code}:#{details}") logger.warn("Could not send status #{code}:#{details}")
logger.warn(e) logger.warn(e)

@ -38,7 +38,7 @@ module GRPC
# RpcServer hosts a number of services and makes them available on the # RpcServer hosts a number of services and makes them available on the
# network. # network.
class RpcServer class RpcServer
include Core::CompletionType include Core::CallOps
include Core::TimeConsts include Core::TimeConsts
extend ::Forwardable extend ::Forwardable
@ -202,20 +202,14 @@ module GRPC
end end
@pool.start @pool.start
@server.start @server.start
server_tag = Object.new request_call_tag = Object.new
until stopped? until stopped?
@server.request_call(server_tag) deadline = from_relative_time(@poll_period)
ev = @cq.pluck(server_tag, @poll_period) an_rpc = @server.request_call(@cq, request_call_tag, deadline)
next if ev.nil? next if an_rpc.nil?
if ev.type != SERVER_RPC_NEW c = new_active_server_call(an_rpc)
logger.warn("bad evt: got:#{ev.type}, want:#{SERVER_RPC_NEW}")
ev.close
next
end
c = new_active_server_call(ev.call, ev.result)
unless c.nil? unless c.nil?
mth = ev.result.method.to_sym mth = an_rpc.method.to_sym
ev.close
@pool.schedule(c) do |call| @pool.schedule(c) do |call|
rpc_descs[mth].run_server_method(call, rpc_handlers[mth]) rpc_descs[mth].run_server_method(call, rpc_handlers[mth])
end end
@ -224,46 +218,49 @@ module GRPC
@running = false @running = false
end end
def new_active_server_call(call, new_server_rpc) # Sends UNAVAILABLE if there are too many unprocessed jobs
# Accept the call. This is necessary even if a status is to be sent def available?(an_rpc)
# back immediately
finished_tag = Object.new
call_queue = Core::CompletionQueue.new
call.metadata = new_server_rpc.metadata # store the metadata
call.server_accept(call_queue, finished_tag)
call.server_end_initial_metadata
# Send UNAVAILABLE if there are too many unprocessed jobs
jobs_count, max = @pool.jobs_waiting, @max_waiting_requests jobs_count, max = @pool.jobs_waiting, @max_waiting_requests
logger.info("waiting: #{jobs_count}, max: #{max}") logger.info("waiting: #{jobs_count}, max: #{max}")
if @pool.jobs_waiting > @max_waiting_requests return an_rpc if @pool.jobs_waiting <= @max_waiting_requests
logger.warn("NOT AVAILABLE: too many jobs_waiting: #{new_server_rpc}") logger.warn("NOT AVAILABLE: too many jobs_waiting: #{an_rpc}")
noop = proc { |x| x } noop = proc { |x| x }
c = ActiveCall.new(call, call_queue, noop, noop, c = ActiveCall.new(an_rpc.call, @cq, noop, noop, an_rpc.deadline)
new_server_rpc.deadline, c.send_status(StatusCodes::UNAVAILABLE, '')
finished_tag: finished_tag) nil
c.send_status(StatusCodes::UNAVAILABLE, '') end
return nil
end
# Send NOT_FOUND if the method does not exist # Sends NOT_FOUND if the method can't be found
mth = new_server_rpc.method.to_sym def found?(an_rpc)
unless rpc_descs.key?(mth) mth = an_rpc.method.to_sym
logger.warn("NOT_FOUND: #{new_server_rpc}") return an_rpc if rpc_descs.key?(mth)
noop = proc { |x| x } logger.warn("NOT_FOUND: #{an_rpc}")
c = ActiveCall.new(call, call_queue, noop, noop, noop = proc { |x| x }
new_server_rpc.deadline, c = ActiveCall.new(an_rpc.call, @cq, noop, noop, an_rpc.deadline)
finished_tag: finished_tag) c.send_status(StatusCodes::NOT_FOUND, '')
c.send_status(StatusCodes::NOT_FOUND, '') nil
return nil end
end
def new_active_server_call(an_rpc)
# Accept the call. This is necessary even if a status is to be sent
# back immediately
return nil if an_rpc.nil? || an_rpc.call.nil?
# allow the metadata to be accessed from the call
handle_call_tag = Object.new
an_rpc.call.metadata = an_rpc.metadata
# TODO: add a hook to send md
an_rpc.call.run_batch(@cq, handle_call_tag, INFINITE_FUTURE,
SEND_INITIAL_METADATA => nil)
return nil unless available?(an_rpc)
return nil unless found?(an_rpc)
# Create the ActiveCall # Create the ActiveCall
rpc_desc = rpc_descs[mth] logger.info("deadline is #{an_rpc.deadline}; (now=#{Time.now})")
logger.info("deadline is #{new_server_rpc.deadline}; (now=#{Time.now})") rpc_desc = rpc_descs[an_rpc.method.to_sym]
ActiveCall.new(call, call_queue, ActiveCall.new(an_rpc.call, @cq,
rpc_desc.marshal_proc, rpc_desc.unmarshal_proc(:input), rpc_desc.marshal_proc, rpc_desc.unmarshal_proc(:input),
new_server_rpc.deadline, finished_tag: finished_tag) an_rpc.deadline)
end end
# Pool is a simple thread pool for running server requests. # Pool is a simple thread pool for running server requests.

@ -1,67 +0,0 @@
# 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.
require 'grpc'
describe GRPC::Core::ByteBuffer do
describe '#new' do
it 'is constructed from a string' do
expect { GRPC::Core::ByteBuffer.new('#new') }.not_to raise_error
end
it 'can be constructed from the empty string' do
expect { GRPC::Core::ByteBuffer.new('') }.not_to raise_error
end
it 'cannot be constructed from nil' do
expect { GRPC::Core::ByteBuffer.new(nil) }.to raise_error TypeError
end
it 'cannot be constructed from non-strings' do
[1, Object.new, :a_symbol].each do |x|
expect { GRPC::Core::ByteBuffer.new(x) }.to raise_error TypeError
end
end
end
describe '#to_s' do
it 'is the string value the ByteBuffer was constructed with' do
expect(GRPC::Core::ByteBuffer.new('#to_s').to_s).to eq('#to_s')
end
end
describe '#dup' do
it 'makes an instance whose #to_s is the original string value' do
bb = GRPC::Core::ByteBuffer.new('#dup')
a_copy = bb.dup
expect(a_copy.to_s).to eq('#dup')
expect(a_copy.dup.to_s).to eq('#dup')
end
end
end

@ -66,51 +66,34 @@ describe GRPC::Core::RpcErrors do
end end
end end
describe GRPC::Core::Call do describe GRPC::Core::CallOps do
before(:each) do before(:each) do
@tag = Object.new @known_types = {
@client_queue = GRPC::Core::CompletionQueue.new SEND_INITIAL_METADATA: 0,
fake_host = 'localhost:10101' SEND_MESSAGE: 1,
@ch = GRPC::Core::Channel.new(fake_host, nil) SEND_CLOSE_FROM_CLIENT: 2,
end SEND_STATUS_FROM_SERVER: 3,
RECV_INITIAL_METADATA: 4,
describe '#start_read' do RECV_MESSAGE: 5,
xit 'should fail if called immediately' do RECV_STATUS_ON_CLIENT: 6,
blk = proc { make_test_call.start_read(@tag) } RECV_CLOSE_ON_SERVER: 7
expect(&blk).to raise_error GRPC::Core::CallError }
end
end
describe '#start_write' do
xit 'should fail if called immediately' do
bytes = GRPC::Core::ByteBuffer.new('test string')
blk = proc { make_test_call.start_write(bytes, @tag) }
expect(&blk).to raise_error GRPC::Core::CallError
end
end end
describe '#start_write_status' do it 'should have symbols for all the known operation types' do
xit 'should fail if called immediately' do m = GRPC::Core::CallOps
blk = proc { make_test_call.start_write_status(153, 'x', @tag) } syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
expect(&blk).to raise_error GRPC::Core::CallError expect(Hash[syms_and_codes]).to eq(@known_types)
end
end end
end
describe '#writes_done' do describe GRPC::Core::Call do
xit 'should fail if called immediately' do let(:client_queue) { GRPC::Core::CompletionQueue.new }
blk = proc { make_test_call.writes_done(Object.new) } let(:test_tag) { Object.new }
expect(&blk).to raise_error GRPC::Core::CallError let(:fake_host) { 'localhost:10101' }
end
end
describe '#add_metadata' do before(:each) do
it 'adds metadata to a call without fail' do @ch = GRPC::Core::Channel.new(fake_host, nil)
call = make_test_call
n = 37
one_md = proc { |x| [sprintf('key%d', x), sprintf('value%d', x)] }
metadata = Hash[n.times.collect { |i| one_md.call i }]
expect { call.add_metadata(metadata) }.to_not raise_error
end
end end
describe '#status' do describe '#status' do
@ -154,7 +137,7 @@ describe GRPC::Core::Call do
end end
def make_test_call def make_test_call
@ch.create_call('dummy_method', 'dummy_host', deadline) @ch.create_call(client_queue, 'dummy_method', 'dummy_host', deadline)
end end
def deadline def deadline

@ -36,16 +36,13 @@ def load_test_certs
end end
describe GRPC::Core::Channel do describe GRPC::Core::Channel do
FAKE_HOST = 'localhost:0' let(:fake_host) { 'localhost:0' }
let(:cq) { GRPC::Core::CompletionQueue.new }
def create_test_cert def create_test_cert
GRPC::Core::Credentials.new(load_test_certs[0]) GRPC::Core::Credentials.new(load_test_certs[0])
end end
before(:each) do
@cq = GRPC::Core::CompletionQueue.new
end
shared_examples '#new' do shared_examples '#new' do
it 'take a host name without channel args' do it 'take a host name without channel args' do
expect { GRPC::Core::Channel.new('dummy_host', nil) }.not_to raise_error expect { GRPC::Core::Channel.new('dummy_host', nil) }.not_to raise_error
@ -115,25 +112,23 @@ describe GRPC::Core::Channel do
describe '#create_call' do describe '#create_call' do
it 'creates a call OK' do it 'creates a call OK' do
host = FAKE_HOST ch = GRPC::Core::Channel.new(fake_host, nil)
ch = GRPC::Core::Channel.new(host, nil)
deadline = Time.now + 5 deadline = Time.now + 5
blk = proc do blk = proc do
ch.create_call('dummy_method', 'dummy_host', deadline) ch.create_call(cq, 'dummy_method', 'dummy_host', deadline)
end end
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'raises an error if called on a closed channel' do it 'raises an error if called on a closed channel' do
host = FAKE_HOST ch = GRPC::Core::Channel.new(fake_host, nil)
ch = GRPC::Core::Channel.new(host, nil)
ch.close ch.close
deadline = Time.now + 5 deadline = Time.now + 5
blk = proc do blk = proc do
ch.create_call('dummy_method', 'dummy_host', deadline) ch.create_call(cq, 'dummy_method', 'dummy_host', deadline)
end end
expect(&blk).to raise_error(RuntimeError) expect(&blk).to raise_error(RuntimeError)
end end
@ -141,15 +136,13 @@ describe GRPC::Core::Channel do
describe '#destroy' do describe '#destroy' do
it 'destroys a channel ok' do it 'destroys a channel ok' do
host = FAKE_HOST ch = GRPC::Core::Channel.new(fake_host, nil)
ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.destroy } blk = proc { ch.destroy }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'can be called more than once without error' do it 'can be called more than once without error' do
host = FAKE_HOST ch = GRPC::Core::Channel.new(fake_host, nil)
ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.destroy } blk = proc { ch.destroy }
blk.call blk.call
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
@ -164,15 +157,13 @@ describe GRPC::Core::Channel do
describe '#close' do describe '#close' do
it 'closes a channel ok' do it 'closes a channel ok' do
host = FAKE_HOST ch = GRPC::Core::Channel.new(fake_host, nil)
ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.close } blk = proc { ch.close }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'can be called more than once without error' do it 'can be called more than once without error' do
host = FAKE_HOST ch = GRPC::Core::Channel.new(fake_host, nil)
ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.close } blk = proc { ch.close }
blk.call blk.call
expect(&blk).to_not raise_error expect(&blk).to_not raise_error

@ -30,7 +30,6 @@
require 'grpc' require 'grpc'
require 'spec_helper' require 'spec_helper'
include GRPC::Core::CompletionType
include GRPC::Core include GRPC::Core
def load_test_certs def load_test_certs
@ -40,6 +39,8 @@ def load_test_certs
end end
shared_context 'setup: tags' do shared_context 'setup: tags' do
let(:sent_message) { 'sent message' }
let(:reply_text) { 'the reply' }
before(:example) do before(:example) do
@server_finished_tag = Object.new @server_finished_tag = Object.new
@client_finished_tag = Object.new @client_finished_tag = Object.new
@ -52,153 +53,136 @@ shared_context 'setup: tags' do
Time.now + 2 Time.now + 2
end end
def expect_next_event_on(queue, type, tag)
ev = queue.pluck(tag, deadline)
if type.nil?
expect(ev).to be_nil
else
expect(ev).to_not be_nil
expect(ev.type).to be(type)
end
ev
end
def server_allows_client_to_proceed def server_allows_client_to_proceed
@server.request_call(@server_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
ev = @server_queue.pluck(@server_tag, deadline) expect(recvd_rpc).to_not eq nil
expect(ev).not_to be_nil server_call = recvd_rpc.call
expect(ev.type).to be(SERVER_RPC_NEW) ops = { CallOps::SEND_INITIAL_METADATA => {} }
server_call = ev.call svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, ops)
server_call.server_accept(@server_queue, @server_finished_tag) expect(svr_batch.send_metadata).to be true
server_call.server_end_initial_metadata
server_call server_call
end end
def server_responds_with(server_call, reply_text)
reply = ByteBuffer.new(reply_text)
server_call.start_read(@server_tag)
ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)
expect(ev.type).to be(READ)
server_call.start_write(reply, @server_tag)
ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)
expect(ev).not_to be_nil
expect(ev.type).to be(WRITE_ACCEPTED)
end
def client_sends(call, sent = 'a message')
req = ByteBuffer.new(sent)
call.start_write(req, @tag)
ev = @client_queue.pluck(@tag, TimeConsts::INFINITE_FUTURE)
expect(ev).not_to be_nil
expect(ev.type).to be(WRITE_ACCEPTED)
sent
end
def new_client_call def new_client_call
@ch.create_call('/method', 'foo.test.google.fr', deadline) @ch.create_call(@client_queue, '/method', 'foo.test.google.fr', deadline)
end end
end end
shared_examples 'basic GRPC message delivery is OK' do shared_examples 'basic GRPC message delivery is OK' do
include GRPC::Core
include_context 'setup: tags' include_context 'setup: tags'
it 'servers receive requests from clients and start responding' do it 'servers receive requests from clients and can respond' do
reply = ByteBuffer.new('the server payload')
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
# check the server rpc new was received CallOps::SEND_MESSAGE => sent_message
# @server.request_call(@server_tag) }
# ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) batch_result = call.run_batch(@client_queue, @client_tag, deadline,
client_ops)
# accept the call expect(batch_result.send_metadata).to be true
# server_call = ev.call expect(batch_result.send_message).to be true
# server_call.server_accept(@server_queue, @server_finished_tag)
# server_call.server_end_initial_metadata
server_call = server_allows_client_to_proceed
# client sends a message
msg = client_sends(call)
# confirm the server can read the inbound message # confirm the server can read the inbound message
server_call.start_read(@server_tag) server_call = server_allows_client_to_proceed
ev = expect_next_event_on(@server_queue, READ, @server_tag) server_ops = {
expect(ev.result.to_s).to eq(msg) CallOps::RECV_MESSAGE => nil
}
# the server response svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline,
server_call.start_write(reply, @server_tag) server_ops)
expect_next_event_on(@server_queue, WRITE_ACCEPTED, @server_tag) expect(svr_batch.message).to eq(sent_message)
end end
it 'responses written by servers are received by the client' do it 'responses written by servers are received by the client' do
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) client_ops = {
server_call = server_allows_client_to_proceed CallOps::SEND_INITIAL_METADATA => {},
client_sends(call) CallOps::SEND_MESSAGE => sent_message
server_responds_with(server_call, 'server_response') }
batch_result = call.run_batch(@client_queue, @client_tag, deadline,
client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.send_message).to be true
call.start_read(@tag) # confirm the server can read the inbound message
ev = expect_next_event_on(@client_queue, READ, @tag) server_call = server_allows_client_to_proceed
expect(ev.result.to_s).to eq('server_response') server_ops = {
CallOps::RECV_MESSAGE => nil,
CallOps::SEND_MESSAGE => reply_text
}
svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline,
server_ops)
expect(svr_batch.message).to eq(sent_message)
expect(svr_batch.send_message).to be true
end end
it 'servers can ignore a client write and send a status' do it 'servers can ignore a client write and send a status' do
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
# check the server rpc new was received CallOps::SEND_MESSAGE => sent_message
@server.request_call(@server_tag) }
ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) batch_result = call.run_batch(@client_queue, @client_tag, deadline,
expect(ev.tag).to be(@server_tag) client_ops)
expect(batch_result.send_metadata).to be true
# accept the call - need to do this to sent status. expect(batch_result.send_message).to be true
server_call = ev.call
server_call.server_accept(@server_queue, @server_finished_tag) # confirm the server can read the inbound message
server_call.server_end_initial_metadata the_status = Struct::Status.new(StatusCodes::OK, 'OK')
server_call.start_write_status(StatusCodes::NOT_FOUND, 'not found', server_call = server_allows_client_to_proceed
@server_tag) server_ops = {
CallOps::SEND_STATUS_FROM_SERVER => the_status
# Client sends some data }
client_sends(call) svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline,
server_ops)
# client gets an empty response for the read, preceeded by some metadata. expect(svr_batch.message).to eq nil
call.start_read(@tag) expect(svr_batch.send_status).to be true
expect_next_event_on(@client_queue, CLIENT_METADATA_READ,
@client_metadata_tag)
ev = expect_next_event_on(@client_queue, READ, @tag)
expect(ev.tag).to be(@tag)
expect(ev.result.to_s).to eq('')
# finally, after client sends writes_done, they get the finished.
call.writes_done(@tag)
expect_next_event_on(@client_queue, FINISH_ACCEPTED, @tag)
ev = expect_next_event_on(@client_queue, FINISHED, @client_finished_tag)
expect(ev.result.code).to eq(StatusCodes::NOT_FOUND)
end end
it 'completes calls by sending status to client and server' do it 'completes calls by sending status to client and server' do
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
CallOps::SEND_MESSAGE => sent_message
}
batch_result = call.run_batch(@client_queue, @client_tag, deadline,
client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.send_message).to be true
# confirm the server can read the inbound message and respond
the_status = Struct::Status.new(StatusCodes::OK, 'OK', {})
server_call = server_allows_client_to_proceed server_call = server_allows_client_to_proceed
client_sends(call) server_ops = {
server_responds_with(server_call, 'server_response') CallOps::RECV_MESSAGE => nil,
server_call.start_write_status(10_101, 'status code is 10101', @server_tag) CallOps::SEND_MESSAGE => reply_text,
CallOps::SEND_STATUS_FROM_SERVER => the_status
# first the client says writes are done }
call.start_read(@tag) svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline,
expect_next_event_on(@client_queue, READ, @tag) server_ops)
call.writes_done(@tag) expect(svr_batch.message).to eq sent_message
expect(svr_batch.send_status).to be true
# but nothing happens until the server sends a status expect(svr_batch.send_message).to be true
expect_next_event_on(@server_queue, FINISH_ACCEPTED, @server_tag)
ev = expect_next_event_on(@server_queue, FINISHED, @server_finished_tag) # confirm the client can receive the server response and status.
expect(ev.result).to be_a(Struct::Status) client_ops = {
CallOps::SEND_CLOSE_FROM_CLIENT => nil,
# client gets FINISHED CallOps::RECV_MESSAGE => nil,
expect_next_event_on(@client_queue, FINISH_ACCEPTED, @tag) CallOps::RECV_STATUS_ON_CLIENT => nil
ev = expect_next_event_on(@client_queue, FINISHED, @client_finished_tag) }
expect(ev.result.details).to eq('status code is 10101') batch_result = call.run_batch(@client_queue, @client_tag, deadline,
expect(ev.result.code).to eq(10_101) client_ops)
expect(batch_result.send_close).to be true
expect(batch_result.message).to eq reply_text
expect(batch_result.status).to eq the_status
# confirm the server can receive the client close.
server_ops = {
CallOps::RECV_CLOSE_ON_SERVER => nil
}
svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline,
server_ops)
expect(svr_batch.send_close).to be true
end end
end end
@ -224,25 +208,33 @@ shared_examples 'GRPC metadata delivery works OK' do
it 'raises an exception if a metadata key is invalid' do it 'raises an exception if a metadata key is invalid' do
@bad_keys.each do |md| @bad_keys.each do |md|
call = new_client_call call = new_client_call
expect { call.add_metadata(md) }.to raise_error client_ops = {
CallOps::SEND_INITIAL_METADATA => md
}
blk = proc do
call.run_batch(@client_queue, @client_tag, deadline,
client_ops)
end
expect(&blk).to raise_error
end end
end end
it 'sends all the metadata pairs when keys and values are valid' do it 'sends all the metadata pairs when keys and values are valid' do
@valid_metadata.each do |md| @valid_metadata.each do |md|
call = new_client_call call = new_client_call
call.add_metadata(md) client_ops = {
CallOps::SEND_INITIAL_METADATA => md
# Client begins a call OK }
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) batch_result = call.run_batch(@client_queue, @client_tag, deadline,
client_ops)
# ... server has all metadata available even though the client did not expect(batch_result.send_metadata).to be true
# send a write
@server.request_call(@server_tag) # confirm the server can receive the client metadata
ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
expect(recvd_rpc).to_not eq nil
recvd_md = recvd_rpc.metadata
replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }] replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
result = ev.result.metadata expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
expect(result.merge(replace_symbols)).to eq(result)
end end
end end
end end
@ -266,55 +258,81 @@ shared_examples 'GRPC metadata delivery works OK' do
it 'raises an exception if a metadata key is invalid' do it 'raises an exception if a metadata key is invalid' do
@bad_keys.each do |md| @bad_keys.each do |md|
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) # client signals that it's done sending metadata to allow server to
# respond
client_ops = {
CallOps::SEND_INITIAL_METADATA => nil
}
call.run_batch(@client_queue, @client_tag, deadline, client_ops)
# server gets the invocation # server gets the invocation
@server.request_call(@server_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) expect(recvd_rpc).to_not eq nil
expect { ev.call.add_metadata(md) }.to raise_error server_ops = {
CallOps::SEND_INITIAL_METADATA => md
}
blk = proc do
recvd_rpc.call.run_batch(@server_queue, @server_tag, deadline,
server_ops)
end
expect(&blk).to raise_error
end end
end end
it 'sends a hash that contains the status when no metadata is added' do it 'sends an empty hash if no metadata is added' do
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) # client signals that it's done sending metadata to allow server to
# respond
# server gets the invocation client_ops = {
@server.request_call(@server_tag) CallOps::SEND_INITIAL_METADATA => nil
ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) }
server_call = ev.call call.run_batch(@client_queue, @client_tag, deadline, client_ops)
# ... server accepts the call without adding metadata # server gets the invocation but sends no metadata back
server_call.server_accept(@server_queue, @server_finished_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
server_call.server_end_initial_metadata expect(recvd_rpc).to_not eq nil
server_call = recvd_rpc.call
# there is the HTTP status metadata, though there should not be any server_ops = {
# TODO: update this with the bug number to be resolved CallOps::SEND_INITIAL_METADATA => nil
ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, }
@client_metadata_tag) server_call.run_batch(@server_queue, @server_tag, deadline, server_ops)
expect(ev.result).to eq({})
# client receives nothing as expected
client_ops = {
CallOps::RECV_INITIAL_METADATA => nil
}
batch_result = call.run_batch(@client_queue, @client_tag, deadline,
client_ops)
expect(batch_result.metadata).to eq({})
end end
it 'sends all the pairs when keys and values are valid' do it 'sends all the pairs when keys and values are valid' do
@valid_metadata.each do |md| @valid_metadata.each do |md|
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag) # client signals that it's done sending metadata to allow server to
# respond
# server gets the invocation client_ops = {
@server.request_call(@server_tag) CallOps::SEND_INITIAL_METADATA => nil
ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) }
server_call = ev.call call.run_batch(@client_queue, @client_tag, deadline, client_ops)
# ... server adds metadata and accepts the call # server gets the invocation but sends no metadata back
server_call.add_metadata(md) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
server_call.server_accept(@server_queue, @server_finished_tag) expect(recvd_rpc).to_not eq nil
server_call.server_end_initial_metadata server_call = recvd_rpc.call
server_ops = {
# Now the client can read the metadata CallOps::SEND_INITIAL_METADATA => md
ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, }
@client_metadata_tag) server_call.run_batch(@server_queue, @server_tag, deadline, server_ops)
# client receives nothing as expected
client_ops = {
CallOps::RECV_INITIAL_METADATA => nil
}
batch_result = call.run_batch(@client_queue, @client_tag, deadline,
client_ops)
replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }] replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
expect(ev.result).to eq(replace_symbols) expect(batch_result.metadata).to eq(replace_symbols)
end end
end end
end end

@ -1,53 +0,0 @@
# 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.
require 'grpc'
describe GRPC::Core::CompletionType do
before(:each) do
@known_types = {
QUEUE_SHUTDOWN: 0,
OP_COMPLETE: 1,
READ: 2,
WRITE_ACCEPTED: 3,
FINISH_ACCEPTED: 4,
CLIENT_METADATA_READ: 5,
FINISHED: 6,
SERVER_RPC_NEW: 7,
SERVER_SHUTDOWN: 8,
RESERVED: 9
}
end
it 'should have all the known types' do
mod = GRPC::Core::CompletionType
blk = proc { Hash[mod.constants.collect { |c| [c, mod.const_get(c)] }] }
expect(blk.call).to eq(@known_types)
end
end

@ -34,12 +34,11 @@ include GRPC::Core::StatusCodes
describe GRPC::ActiveCall do describe GRPC::ActiveCall do
ActiveCall = GRPC::ActiveCall ActiveCall = GRPC::ActiveCall
Call = GRPC::Core::Call Call = GRPC::Core::Call
CompletionType = GRPC::Core::CompletionType CallOps = GRPC::Core::CallOps
before(:each) do before(:each) do
@pass_through = proc { |x| x } @pass_through = proc { |x| x }
@server_tag = Object.new @server_tag = Object.new
@server_done_tag = Object.new
@tag = Object.new @tag = Object.new
@client_queue = GRPC::Core::CompletionQueue.new @client_queue = GRPC::Core::CompletionQueue.new
@ -48,7 +47,7 @@ describe GRPC::ActiveCall do
@server = GRPC::Core::Server.new(@server_queue, nil) @server = GRPC::Core::Server.new(@server_queue, nil)
server_port = @server.add_http2_port(host) server_port = @server.add_http2_port(host)
@server.start @server.start
@ch = GRPC::Core::Channel.new("localhost:#{server_port}", nil) @ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil)
end end
after(:each) do after(:each) do
@ -58,12 +57,10 @@ describe GRPC::ActiveCall do
describe 'restricted view methods' do describe 'restricted view methods' do
before(:each) do before(:each) do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
@client_call = ActiveCall.new(call, @client_queue, @pass_through, @client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
end end
describe '#multi_req_view' do describe '#multi_req_view' do
@ -90,48 +87,45 @@ describe GRPC::ActiveCall do
describe '#remote_send' do describe '#remote_send' do
it 'allows a client to send a payload to the server' do it 'allows a client to send a payload to the server' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
@client_call = ActiveCall.new(call, @client_queue, @pass_through, @client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
msg = 'message is a string' msg = 'message is a string'
@client_call.remote_send(msg) @client_call.remote_send(msg)
# check that server rpc new was received # check that server rpc new was received
@server.request_call(@server_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
ev = @server_queue.next(deadline) expect(recvd_rpc).to_not eq nil
expect(ev.type).to be(CompletionType::SERVER_RPC_NEW) recvd_call = recvd_rpc.call
expect(ev.call).to be_a(Call)
expect(ev.tag).to be(@server_tag)
# Accept the call, and verify that the server reads the response ok. # Accept the call, and verify that the server reads the response ok.
ev.call.server_accept(@client_queue, @server_tag) server_ops = {
ev.call.server_end_initial_metadata CallOps::SEND_INITIAL_METADATA => {}
server_call = ActiveCall.new(ev.call, @client_queue, @pass_through, }
recvd_call.run_batch(@server_queue, @server_tag, deadline, server_ops)
server_call = ActiveCall.new(recvd_call, @server_queue, @pass_through,
@pass_through, deadline) @pass_through, deadline)
expect(server_call.remote_read).to eq(msg) expect(server_call.remote_read).to eq(msg)
end end
it 'marshals the payload using the marshal func' do it 'marshals the payload using the marshal func' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
marshal = proc { |x| 'marshalled:' + x } marshal = proc { |x| 'marshalled:' + x }
client_call = ActiveCall.new(call, @client_queue, marshal, client_call = ActiveCall.new(call, @client_queue, marshal,
@pass_through, deadline, @pass_through, deadline)
finished_tag: done_tag,
read_metadata_tag: meta_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
# confirm that the message was marshalled # confirm that the message was marshalled
@server.request_call(@server_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
ev = @server_queue.next(deadline) recvd_call = recvd_rpc.call
ev.call.server_accept(@client_queue, @server_tag) server_ops = {
ev.call.server_end_initial_metadata CallOps::SEND_INITIAL_METADATA => nil
server_call = ActiveCall.new(ev.call, @client_queue, @pass_through, }
recvd_call.run_batch(@server_queue, @server_tag, deadline, server_ops)
server_call = ActiveCall.new(recvd_call, @server_queue, @pass_through,
@pass_through, deadline) @pass_through, deadline)
expect(server_call.remote_read).to eq('marshalled:' + msg) expect(server_call.remote_read).to eq('marshalled:' + msg)
end end
@ -142,23 +136,22 @@ describe GRPC::ActiveCall do
call = make_test_call call = make_test_call
ActiveCall.client_invoke(call, @client_queue, deadline, ActiveCall.client_invoke(call, @client_queue, deadline,
k1: 'v1', k2: 'v2') k1: 'v1', k2: 'v2')
@server.request_call(@server_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
ev = @server_queue.next(deadline) recvd_call = recvd_rpc.call
expect(ev).to_not be_nil expect(recvd_call).to_not be_nil
expect(ev.result.metadata['k1']).to eq('v1') expect(recvd_rpc.metadata).to_not be_nil
expect(ev.result.metadata['k2']).to eq('v2') expect(recvd_rpc.metadata['k1']).to eq('v1')
expect(recvd_rpc.metadata['k2']).to eq('v2')
end end
end end
describe '#remote_read' do describe '#remote_read' do
it 'reads the response sent by a server' do it 'reads the response sent by a server' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
server_call = expect_server_to_receive(msg) server_call = expect_server_to_receive(msg)
@ -168,12 +161,10 @@ describe GRPC::ActiveCall do
it 'saves no metadata when the server adds no metadata' do it 'saves no metadata when the server adds no metadata' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
server_call = expect_server_to_receive(msg) server_call = expect_server_to_receive(msg)
@ -185,12 +176,10 @@ describe GRPC::ActiveCall do
it 'saves metadata add by the server' do it 'saves metadata add by the server' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
server_call = expect_server_to_receive(msg, k1: 'v1', k2: 'v2') server_call = expect_server_to_receive(msg, k1: 'v1', k2: 'v2')
@ -203,12 +192,10 @@ describe GRPC::ActiveCall do
it 'get a nil msg before a status when an OK status is sent' do it 'get a nil msg before a status when an OK status is sent' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
client_call.writes_done(false) client_call.writes_done(false)
@ -222,13 +209,11 @@ describe GRPC::ActiveCall do
it 'unmarshals the response using the unmarshal func' do it 'unmarshals the response using the unmarshal func' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
unmarshal = proc { |x| 'unmarshalled:' + x } unmarshal = proc { |x| 'unmarshalled:' + x }
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
unmarshal, deadline, unmarshal, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
# confirm the client receives the unmarshalled message # confirm the client receives the unmarshalled message
msg = 'message is a string' msg = 'message is a string'
@ -249,13 +234,11 @@ describe GRPC::ActiveCall do
it 'the returns an enumerator that can read n responses' do it 'the returns an enumerator that can read n responses' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag) msg = 'message is a string'
msg = 'message is 4a string'
reply = 'server_response' reply = 'server_response'
client_call.remote_send(msg) client_call.remote_send(msg)
server_call = expect_server_to_receive(msg) server_call = expect_server_to_receive(msg)
@ -269,12 +252,10 @@ describe GRPC::ActiveCall do
it 'the returns an enumerator that stops after an OK Status' do it 'the returns an enumerator that stops after an OK Status' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
read_metadata_tag: meta_tag, metadata_tag: md_tag)
finished_tag: done_tag)
msg = 'message is a string' msg = 'message is a string'
reply = 'server_response' reply = 'server_response'
client_call.remote_send(msg) client_call.remote_send(msg)
@ -294,12 +275,10 @@ describe GRPC::ActiveCall do
describe '#writes_done' do describe '#writes_done' do
it 'finishes ok if the server sends a status response' do it 'finishes ok if the server sends a status response' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, metadata_tag: md_tag)
read_metadata_tag: meta_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
expect { client_call.writes_done(false) }.to_not raise_error expect { client_call.writes_done(false) }.to_not raise_error
@ -312,12 +291,10 @@ describe GRPC::ActiveCall do
it 'finishes ok if the server sends an early status response' do it 'finishes ok if the server sends an early status response' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
read_metadata_tag: meta_tag, metadata_tag: md_tag)
finished_tag: done_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
server_call = expect_server_to_receive(msg) server_call = expect_server_to_receive(msg)
@ -330,12 +307,10 @@ describe GRPC::ActiveCall do
it 'finishes ok if writes_done is true' do it 'finishes ok if writes_done is true' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, md_tag = ActiveCall.client_invoke(call, @client_queue, deadline)
deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
read_metadata_tag: meta_tag, metadata_tag: md_tag)
finished_tag: done_tag)
msg = 'message is a string' msg = 'message is a string'
client_call.remote_send(msg) client_call.remote_send(msg)
server_call = expect_server_to_receive(msg) server_call = expect_server_to_receive(msg)
@ -353,21 +328,20 @@ describe GRPC::ActiveCall do
end end
def expect_server_to_be_invoked(**kw) def expect_server_to_be_invoked(**kw)
@server.request_call(@server_tag) recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
ev = @server_queue.next(deadline) expect(recvd_rpc).to_not eq nil
ev.call.add_metadata(kw) recvd_call = recvd_rpc.call
ev.call.server_accept(@client_queue, @server_done_tag) recvd_call.run_batch(@server_queue, @server_tag, deadline,
ev.call.server_end_initial_metadata CallOps::SEND_INITIAL_METADATA => kw)
ActiveCall.new(ev.call, @client_queue, @pass_through, ActiveCall.new(recvd_call, @server_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline)
finished_tag: @server_done_tag)
end end
def make_test_call def make_test_call
@ch.create_call('dummy_method', 'dummy_host', deadline) @ch.create_call(@client_queue, '/method', 'a.dummy.host', deadline)
end end
def deadline def deadline
Time.now + 1 # in 1 second; arbitrary Time.now + 2 # in 2 seconds; arbitrary
end end
end end

@ -30,15 +30,41 @@
require 'grpc' require 'grpc'
require 'xray/thread_dump_signal_handler' require 'xray/thread_dump_signal_handler'
NOOP = proc { |x| x } # Notifier is useful high-level synchronization primitive.
FAKE_HOST = 'localhost:0' class Notifier
attr_reader :payload, :notified
alias_method :notified?, :notified
def initialize
@mutex = Mutex.new
@cvar = ConditionVariable.new
@notified = false
@payload = nil
end
def wait
@mutex.synchronize do
@cvar.wait(@mutex) until notified?
end
end
def notify(payload)
@mutex.synchronize do
return Error.new('already notified') if notified?
@payload = payload
@notified = true
@cvar.signal
return nil
end
end
end
def wakey_thread(&blk) def wakey_thread(&blk)
awake_mutex, awake_cond = Mutex.new, ConditionVariable.new n = Notifier.new
t = Thread.new do t = Thread.new do
blk.call(awake_mutex, awake_cond) blk.call(n)
end end
awake_mutex.synchronize { awake_cond.wait(awake_mutex) } n.wait
t t
end end
@ -50,8 +76,11 @@ end
include GRPC::Core::StatusCodes include GRPC::Core::StatusCodes
include GRPC::Core::TimeConsts include GRPC::Core::TimeConsts
include GRPC::Core::CallOps
describe 'ClientStub' do describe 'ClientStub' do
let(:noop) { proc { |x| x } }
before(:each) do before(:each) do
Thread.abort_on_exception = true Thread.abort_on_exception = true
@server = nil @server = nil
@ -66,61 +95,56 @@ describe 'ClientStub' do
end end
describe '#new' do describe '#new' do
let(:fake_host) { 'localhost:0' }
it 'can be created from a host and args' do it 'can be created from a host and args' do
host = FAKE_HOST
opts = { a_channel_arg: 'an_arg' } opts = { a_channel_arg: 'an_arg' }
blk = proc do blk = proc do
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(fake_host, @cq, **opts)
end end
expect(&blk).not_to raise_error expect(&blk).not_to raise_error
end end
it 'can be created with a default deadline' do it 'can be created with a default deadline' do
host = FAKE_HOST
opts = { a_channel_arg: 'an_arg', deadline: 5 } opts = { a_channel_arg: 'an_arg', deadline: 5 }
blk = proc do blk = proc do
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(fake_host, @cq, **opts)
end end
expect(&blk).not_to raise_error expect(&blk).not_to raise_error
end end
it 'can be created with an channel override' do it 'can be created with an channel override' do
host = FAKE_HOST
opts = { a_channel_arg: 'an_arg', channel_override: @ch } opts = { a_channel_arg: 'an_arg', channel_override: @ch }
blk = proc do blk = proc do
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(fake_host, @cq, **opts)
end end
expect(&blk).not_to raise_error expect(&blk).not_to raise_error
end end
it 'cannot be created with a bad channel override' do it 'cannot be created with a bad channel override' do
host = FAKE_HOST
blk = proc do blk = proc do
opts = { a_channel_arg: 'an_arg', channel_override: Object.new } opts = { a_channel_arg: 'an_arg', channel_override: Object.new }
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(fake_host, @cq, **opts)
end end
expect(&blk).to raise_error expect(&blk).to raise_error
end end
it 'cannot be created with bad credentials' do it 'cannot be created with bad credentials' do
host = FAKE_HOST
blk = proc do blk = proc do
opts = { a_channel_arg: 'an_arg', creds: Object.new } opts = { a_channel_arg: 'an_arg', creds: Object.new }
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(fake_host, @cq, **opts)
end end
expect(&blk).to raise_error expect(&blk).to raise_error
end end
it 'can be created with test test credentials' do it 'can be created with test test credentials' do
certs = load_test_certs certs = load_test_certs
host = FAKE_HOST
blk = proc do blk = proc do
opts = { opts = {
GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.fr', GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.fr',
a_channel_arg: 'an_arg', a_channel_arg: 'an_arg',
creds: GRPC::Core::Credentials.new(certs[0], nil, nil) creds: GRPC::Core::Credentials.new(certs[0], nil, nil)
} }
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(fake_host, @cq, **opts)
end end
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
@ -187,7 +211,7 @@ describe 'ClientStub' do
describe 'without a call operation' do describe 'without a call operation' do
def get_response(stub) def get_response(stub)
stub.request_response(@method, @sent_msg, NOOP, NOOP, stub.request_response(@method, @sent_msg, noop, noop,
k1: 'v1', k2: 'v2') k1: 'v1', k2: 'v2')
end end
@ -196,7 +220,7 @@ describe 'ClientStub' do
describe 'via a call operation' do describe 'via a call operation' do
def get_response(stub) def get_response(stub)
op = stub.request_response(@method, @sent_msg, NOOP, NOOP, op = stub.request_response(@method, @sent_msg, noop, noop,
return_op: true, k1: 'v1', k2: 'v2') return_op: true, k1: 'v1', k2: 'v2')
expect(op).to be_a(GRPC::ActiveCall::Operation) expect(op).to be_a(GRPC::ActiveCall::Operation)
op.execute op.execute
@ -259,7 +283,7 @@ describe 'ClientStub' do
describe 'without a call operation' do describe 'without a call operation' do
def get_response(stub) def get_response(stub)
stub.client_streamer(@method, @sent_msgs, NOOP, NOOP, stub.client_streamer(@method, @sent_msgs, noop, noop,
k1: 'v1', k2: 'v2') k1: 'v1', k2: 'v2')
end end
@ -268,7 +292,7 @@ describe 'ClientStub' do
describe 'via a call operation' do describe 'via a call operation' do
def get_response(stub) def get_response(stub)
op = stub.client_streamer(@method, @sent_msgs, NOOP, NOOP, op = stub.client_streamer(@method, @sent_msgs, noop, noop,
return_op: true, k1: 'v1', k2: 'v2') return_op: true, k1: 'v1', k2: 'v2')
expect(op).to be_a(GRPC::ActiveCall::Operation) expect(op).to be_a(GRPC::ActiveCall::Operation)
op.execute op.execute
@ -333,7 +357,7 @@ describe 'ClientStub' do
describe 'without a call operation' do describe 'without a call operation' do
def get_responses(stub) def get_responses(stub)
e = stub.server_streamer(@method, @sent_msg, NOOP, NOOP, e = stub.server_streamer(@method, @sent_msg, noop, noop,
k1: 'v1', k2: 'v2') k1: 'v1', k2: 'v2')
expect(e).to be_a(Enumerator) expect(e).to be_a(Enumerator)
e e
@ -344,7 +368,7 @@ describe 'ClientStub' do
describe 'via a call operation' do describe 'via a call operation' do
def get_responses(stub) def get_responses(stub)
op = stub.server_streamer(@method, @sent_msg, NOOP, NOOP, op = stub.server_streamer(@method, @sent_msg, noop, noop,
return_op: true, k1: 'v1', k2: 'v2') return_op: true, k1: 'v1', k2: 'v2')
expect(op).to be_a(GRPC::ActiveCall::Operation) expect(op).to be_a(GRPC::ActiveCall::Operation)
e = op.execute e = op.execute
@ -361,34 +385,30 @@ describe 'ClientStub' do
before(:each) do before(:each) do
@sent_msgs = Array.new(3) { |i| 'msg_' + (i + 1).to_s } @sent_msgs = Array.new(3) { |i| 'msg_' + (i + 1).to_s }
@replys = Array.new(3) { |i| 'reply_' + (i + 1).to_s } @replys = Array.new(3) { |i| 'reply_' + (i + 1).to_s }
server_port = create_test_server
@host = "localhost:#{server_port}"
end end
it 'supports sending all the requests first', bidi: true do it 'supports sending all the requests first', bidi: true do
server_port = create_test_server
host = "localhost:#{server_port}"
th = run_bidi_streamer_handle_inputs_first(@sent_msgs, @replys, th = run_bidi_streamer_handle_inputs_first(@sent_msgs, @replys,
@pass) @pass)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(@host, @cq)
e = get_responses(stub) e = get_responses(stub)
expect(e.collect { |r| r }).to eq(@replys) expect(e.collect { |r| r }).to eq(@replys)
th.join th.join
end end
it 'supports client-initiated ping pong', bidi: true do it 'supports client-initiated ping pong', bidi: true do
server_port = create_test_server
host = "localhost:#{server_port}"
th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, true) th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, true)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(@host, @cq)
e = get_responses(stub) e = get_responses(stub)
expect(e.collect { |r| r }).to eq(@sent_msgs) expect(e.collect { |r| r }).to eq(@sent_msgs)
th.join th.join
end end
it 'supports a server-initiated ping pong', bidi: true do it 'supports a server-initiated ping pong', bidi: true do
server_port = create_test_server
host = "localhost:#{server_port}"
th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, false) th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, false)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(@host, @cq)
e = get_responses(stub) e = get_responses(stub)
expect(e.collect { |r| r }).to eq(@sent_msgs) expect(e.collect { |r| r }).to eq(@sent_msgs)
th.join th.join
@ -397,7 +417,7 @@ describe 'ClientStub' do
describe 'without a call operation' do describe 'without a call operation' do
def get_responses(stub) def get_responses(stub)
e = stub.bidi_streamer(@method, @sent_msgs, NOOP, NOOP) e = stub.bidi_streamer(@method, @sent_msgs, noop, noop)
expect(e).to be_a(Enumerator) expect(e).to be_a(Enumerator)
e e
end end
@ -407,7 +427,7 @@ describe 'ClientStub' do
describe 'via a call operation' do describe 'via a call operation' do
def get_responses(stub) def get_responses(stub)
op = stub.bidi_streamer(@method, @sent_msgs, NOOP, NOOP, op = stub.bidi_streamer(@method, @sent_msgs, noop, noop,
return_op: true) return_op: true)
expect(op).to be_a(GRPC::ActiveCall::Operation) expect(op).to be_a(GRPC::ActiveCall::Operation)
e = op.execute e = op.execute
@ -421,8 +441,8 @@ describe 'ClientStub' do
def run_server_streamer(expected_input, replys, status, **kw) def run_server_streamer(expected_input, replys, status, **kw)
wanted_metadata = kw.clone wanted_metadata = kw.clone
wakey_thread do |mtx, cnd| wakey_thread do |notifier|
c = expect_server_to_be_invoked(mtx, cnd) c = expect_server_to_be_invoked(notifier)
wanted_metadata.each do |k, v| wanted_metadata.each do |k, v|
expect(c.metadata[k.to_s]).to eq(v) expect(c.metadata[k.to_s]).to eq(v)
end end
@ -434,8 +454,8 @@ describe 'ClientStub' do
def run_bidi_streamer_handle_inputs_first(expected_inputs, replys, def run_bidi_streamer_handle_inputs_first(expected_inputs, replys,
status) status)
wakey_thread do |mtx, cnd| wakey_thread do |notifier|
c = expect_server_to_be_invoked(mtx, cnd) c = expect_server_to_be_invoked(notifier)
expected_inputs.each { |i| expect(c.remote_read).to eq(i) } expected_inputs.each { |i| expect(c.remote_read).to eq(i) }
replys.each { |r| c.remote_send(r) } replys.each { |r| c.remote_send(r) }
c.send_status(status, status == @pass ? 'OK' : 'NOK', true) c.send_status(status, status == @pass ? 'OK' : 'NOK', true)
@ -443,8 +463,8 @@ describe 'ClientStub' do
end end
def run_bidi_streamer_echo_ping_pong(expected_inputs, status, client_starts) def run_bidi_streamer_echo_ping_pong(expected_inputs, status, client_starts)
wakey_thread do |mtx, cnd| wakey_thread do |notifier|
c = expect_server_to_be_invoked(mtx, cnd) c = expect_server_to_be_invoked(notifier)
expected_inputs.each do |i| expected_inputs.each do |i|
if client_starts if client_starts
expect(c.remote_read).to eq(i) expect(c.remote_read).to eq(i)
@ -460,8 +480,8 @@ describe 'ClientStub' do
def run_client_streamer(expected_inputs, resp, status, **kw) def run_client_streamer(expected_inputs, resp, status, **kw)
wanted_metadata = kw.clone wanted_metadata = kw.clone
wakey_thread do |mtx, cnd| wakey_thread do |notifier|
c = expect_server_to_be_invoked(mtx, cnd) c = expect_server_to_be_invoked(notifier)
expected_inputs.each { |i| expect(c.remote_read).to eq(i) } expected_inputs.each { |i| expect(c.remote_read).to eq(i) }
wanted_metadata.each do |k, v| wanted_metadata.each do |k, v|
expect(c.metadata[k.to_s]).to eq(v) expect(c.metadata[k.to_s]).to eq(v)
@ -473,8 +493,8 @@ describe 'ClientStub' do
def run_request_response(expected_input, resp, status, **kw) def run_request_response(expected_input, resp, status, **kw)
wanted_metadata = kw.clone wanted_metadata = kw.clone
wakey_thread do |mtx, cnd| wakey_thread do |notifier|
c = expect_server_to_be_invoked(mtx, cnd) c = expect_server_to_be_invoked(notifier)
expect(c.remote_read).to eq(expected_input) expect(c.remote_read).to eq(expected_input)
wanted_metadata.each do |k, v| wanted_metadata.each do |k, v|
expect(c.metadata[k.to_s]).to eq(v) expect(c.metadata[k.to_s]).to eq(v)
@ -490,24 +510,16 @@ describe 'ClientStub' do
@server.add_http2_port('0.0.0.0:0') @server.add_http2_port('0.0.0.0:0')
end end
def start_test_server(awake_mutex, awake_cond) def expect_server_to_be_invoked(notifier)
@server.start @server.start
@server_tag = Object.new notifier.notify(nil)
@server.request_call(@server_tag) server_tag = Object.new
awake_mutex.synchronize { awake_cond.signal } recvd_rpc = @server.request_call(@server_queue, server_tag,
end INFINITE_FUTURE)
recvd_call = recvd_rpc.call
def expect_server_to_be_invoked(awake_mutex, awake_cond) recvd_call.metadata = recvd_rpc.metadata
start_test_server(awake_mutex, awake_cond) recvd_call.run_batch(@server_queue, server_tag, Time.now + 2,
ev = @server_queue.pluck(@server_tag, INFINITE_FUTURE) SEND_INITIAL_METADATA => nil)
fail OutOfTime if ev.nil? GRPC::ActiveCall.new(recvd_call, @server_queue, noop, noop, INFINITE_FUTURE)
server_call = ev.call
server_call.metadata = ev.result.metadata
finished_tag = Object.new
server_call.server_accept(@server_queue, finished_tag)
server_call.server_end_initial_metadata
GRPC::ActiveCall.new(server_call, @server_queue, NOOP, NOOP,
INFINITE_FUTURE,
finished_tag: finished_tag)
end end
end end

@ -37,7 +37,6 @@ describe GRPC::RpcDesc do
INTERNAL = GRPC::Core::StatusCodes::INTERNAL INTERNAL = GRPC::Core::StatusCodes::INTERNAL
UNKNOWN = GRPC::Core::StatusCodes::UNKNOWN UNKNOWN = GRPC::Core::StatusCodes::UNKNOWN
CallError = GRPC::Core::CallError CallError = GRPC::Core::CallError
EventError = GRPC::Core::EventError
before(:each) do before(:each) do
@request_response = RpcDesc.new('rr', Object.new, Object.new, 'encode', @request_response = RpcDesc.new('rr', Object.new, Object.new, 'encode',
@ -63,24 +62,17 @@ describe GRPC::RpcDesc do
it 'sends the specified status if BadStatus is raised' do it 'sends the specified status if BadStatus is raised' do
expect(@call).to receive(:remote_read).once.and_return(Object.new) expect(@call).to receive(:remote_read).once.and_return(Object.new)
expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false)
@request_response.run_server_method(@call, method(:bad_status)) @request_response.run_server_method(@call, method(:bad_status))
end end
it 'sends status UNKNOWN if other StandardErrors are raised' do it 'sends status UNKNOWN if other StandardErrors are raised' do
expect(@call).to receive(:remote_read).once.and_return(Object.new) expect(@call).to receive(:remote_read).once.and_return(Object.new)
expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason) expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason,
false)
@request_response.run_server_method(@call, method(:other_error)) @request_response.run_server_method(@call, method(:other_error))
end end
it 'absorbs EventError with no further action' do
expect(@call).to receive(:remote_read).once.and_raise(EventError)
blk = proc do
@request_response.run_server_method(@call, method(:fake_reqresp))
end
expect(&blk).to_not raise_error
end
it 'absorbs CallError with no further action' do it 'absorbs CallError with no further action' do
expect(@call).to receive(:remote_read).once.and_raise(CallError) expect(@call).to receive(:remote_read).once.and_raise(CallError)
blk = proc do blk = proc do
@ -93,8 +85,7 @@ describe GRPC::RpcDesc do
req = Object.new req = Object.new
expect(@call).to receive(:remote_read).once.and_return(req) expect(@call).to receive(:remote_read).once.and_return(req)
expect(@call).to receive(:remote_send).once.with(@ok_response) expect(@call).to receive(:remote_send).once.with(@ok_response)
expect(@call).to receive(:send_status).once.with(OK, 'OK') expect(@call).to receive(:send_status).once.with(OK, 'OK', true)
expect(@call).to receive(:finished).once
@request_response.run_server_method(@call, method(:fake_reqresp)) @request_response.run_server_method(@call, method(:fake_reqresp))
end end
end end
@ -107,23 +98,16 @@ describe GRPC::RpcDesc do
end end
it 'sends the specified status if BadStatus is raised' do it 'sends the specified status if BadStatus is raised' do
expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false)
@client_streamer.run_server_method(@call, method(:bad_status_alt)) @client_streamer.run_server_method(@call, method(:bad_status_alt))
end end
it 'sends status UNKNOWN if other StandardErrors are raised' do it 'sends status UNKNOWN if other StandardErrors are raised' do
expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason) expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason,
false)
@client_streamer.run_server_method(@call, method(:other_error_alt)) @client_streamer.run_server_method(@call, method(:other_error_alt))
end end
it 'absorbs EventError with no further action' do
expect(@call).to receive(:remote_send).once.and_raise(EventError)
blk = proc do
@client_streamer.run_server_method(@call, method(:fake_clstream))
end
expect(&blk).to_not raise_error
end
it 'absorbs CallError with no further action' do it 'absorbs CallError with no further action' do
expect(@call).to receive(:remote_send).once.and_raise(CallError) expect(@call).to receive(:remote_send).once.and_raise(CallError)
blk = proc do blk = proc do
@ -134,8 +118,7 @@ describe GRPC::RpcDesc do
it 'sends a response and closes the stream if there no errors' do it 'sends a response and closes the stream if there no errors' do
expect(@call).to receive(:remote_send).once.with(@ok_response) expect(@call).to receive(:remote_send).once.with(@ok_response)
expect(@call).to receive(:send_status).once.with(OK, 'OK') expect(@call).to receive(:send_status).once.with(OK, 'OK', true)
expect(@call).to receive(:finished).once
@client_streamer.run_server_method(@call, method(:fake_clstream)) @client_streamer.run_server_method(@call, method(:fake_clstream))
end end
end end
@ -149,24 +132,17 @@ describe GRPC::RpcDesc do
it 'sends the specified status if BadStatus is raised' do it 'sends the specified status if BadStatus is raised' do
expect(@call).to receive(:remote_read).once.and_return(Object.new) expect(@call).to receive(:remote_read).once.and_return(Object.new)
expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false)
@server_streamer.run_server_method(@call, method(:bad_status)) @server_streamer.run_server_method(@call, method(:bad_status))
end end
it 'sends status UNKNOWN if other StandardErrors are raised' do it 'sends status UNKNOWN if other StandardErrors are raised' do
expect(@call).to receive(:remote_read).once.and_return(Object.new) expect(@call).to receive(:remote_read).once.and_return(Object.new)
expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason) expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason,
false)
@server_streamer.run_server_method(@call, method(:other_error)) @server_streamer.run_server_method(@call, method(:other_error))
end end
it 'absorbs EventError with no further action' do
expect(@call).to receive(:remote_read).once.and_raise(EventError)
blk = proc do
@server_streamer.run_server_method(@call, method(:fake_svstream))
end
expect(&blk).to_not raise_error
end
it 'absorbs CallError with no further action' do it 'absorbs CallError with no further action' do
expect(@call).to receive(:remote_read).once.and_raise(CallError) expect(@call).to receive(:remote_read).once.and_raise(CallError)
blk = proc do blk = proc do
@ -179,8 +155,7 @@ describe GRPC::RpcDesc do
req = Object.new req = Object.new
expect(@call).to receive(:remote_read).once.and_return(req) expect(@call).to receive(:remote_read).once.and_return(req)
expect(@call).to receive(:remote_send).twice.with(@ok_response) expect(@call).to receive(:remote_send).twice.with(@ok_response)
expect(@call).to receive(:send_status).once.with(OK, 'OK') expect(@call).to receive(:send_status).once.with(OK, 'OK', true)
expect(@call).to receive(:finished).once
@server_streamer.run_server_method(@call, method(:fake_svstream)) @server_streamer.run_server_method(@call, method(:fake_svstream))
end end
end end
@ -197,20 +172,20 @@ describe GRPC::RpcDesc do
it 'sends the specified status if BadStatus is raised' do it 'sends the specified status if BadStatus is raised' do
e = GRPC::BadStatus.new(@bs_code, 'NOK') e = GRPC::BadStatus.new(@bs_code, 'NOK')
expect(@call).to receive(:run_server_bidi).and_raise(e) expect(@call).to receive(:run_server_bidi).and_raise(e)
expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK') expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false)
@bidi_streamer.run_server_method(@call, method(:bad_status_alt)) @bidi_streamer.run_server_method(@call, method(:bad_status_alt))
end end
it 'sends status UNKNOWN if other StandardErrors are raised' do it 'sends status UNKNOWN if other StandardErrors are raised' do
expect(@call).to receive(:run_server_bidi).and_raise(StandardError) expect(@call).to receive(:run_server_bidi).and_raise(StandardError)
expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason) expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason,
false)
@bidi_streamer.run_server_method(@call, method(:other_error_alt)) @bidi_streamer.run_server_method(@call, method(:other_error_alt))
end end
it 'closes the stream if there no errors' do it 'closes the stream if there no errors' do
expect(@call).to receive(:run_server_bidi) expect(@call).to receive(:run_server_bidi)
expect(@call).to receive(:send_status).once.with(OK, 'OK') expect(@call).to receive(:send_status).once.with(OK, 'OK', true)
expect(@call).to receive(:finished).once
@bidi_streamer.run_server_method(@call, method(:fake_bidistream)) @bidi_streamer.run_server_method(@call, method(:fake_bidistream))
end end
end end

@ -364,7 +364,7 @@ describe GRPC::RpcServer do
@srv.wait_till_running @srv.wait_till_running
req = EchoMsg.new req = EchoMsg.new
stub = SlowStub.new(@host, **@client_opts) stub = SlowStub.new(@host, **@client_opts)
deadline = service.delay + 0.5 # wait for long enough deadline = service.delay + 1.0 # wait for long enough
expect(stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2')).to be_a(EchoMsg) expect(stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }] wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
expect(service.received_md).to eq(wanted_md) expect(service.received_md).to eq(wanted_md)

@ -1,64 +0,0 @@
# 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.
require 'grpc'
describe GRPC::Core::Metadata do
describe '#new' do
it 'should create instances' do
expect { GRPC::Core::Metadata.new('a key', 'a value') }.to_not raise_error
end
end
describe '#key' do
md = GRPC::Core::Metadata.new('a key', 'a value')
it 'should be the constructor value' do
expect(md.key).to eq('a key')
end
end
describe '#value' do
md = GRPC::Core::Metadata.new('a key', 'a value')
it 'should be the constuctor value' do
expect(md.value).to eq('a value')
end
end
describe '#dup' do
it 'should create a copy that returns the correct key' do
md = GRPC::Core::Metadata.new('a key', 'a value')
expect(md.dup.key).to eq('a key')
end
it 'should create a copy that returns the correct value' do
md = GRPC::Core::Metadata.new('a key', 'a value')
expect(md.dup.value).to eq('a value')
end
end
end

@ -32,38 +32,79 @@
licenses(["notice"]) # 3-clause BSD licenses(["notice"]) # 3-clause BSD
package(default_visibility = ["//visibility:public"])
<%!
def get_deps(target_dict):
deps = []
if target_dict.get('secure', 'no') == 'yes':
deps = [
"//external:libssl",
]
if target_dict.get('build', None) == 'protoc':
deps.append("//external:protobuf_compiler")
if target_dict['name'] == 'grpc++_unsecure' or target_dict['name'] == 'grpc++':
deps.append("//external:protobuf_clib")
for d in target_dict.get('deps', []):
if d.find('//') == 0 or d[0] == ':':
deps.append(d)
else:
deps.append(':%s' % (d))
return deps
%>
% for lib in libs: % for lib in libs:
% if lib.build == "all" and lib.language == 'c': % if lib.build != "private":
${makelib(lib)} ${cc_library(lib)}
% endif % endif
% endfor % endfor
<%def name="makelib(lib)"> % for tgt in targets:
% if tgt.build == 'protoc':
${cc_binary(tgt)}
% endif
% endfor
<%def name="cc_library(lib)">
cc_library( cc_library(
name = "${lib.name}", name = "${lib.name}",
srcs = [ srcs = [
% for hdr in lib.get("headers", []): % for hdr in lib.get("headers", []):
"${hdr}", "${hdr}",
% endfor % endfor
% for src in lib.src: % for src in lib.src:
"${src}", "${src}",
% endfor % endfor
], ],
hdrs = [ hdrs = [
% for hdr in lib.get("public_headers", []): % for hdr in lib.get("public_headers", []):
"${hdr}", "${hdr}",
% endfor % endfor
], ],
includes = [ includes = [
"include", "include",
".", ".",
], ],
deps = [ deps = [
% for dep in lib.get("deps", []): % for dep in get_deps(lib):
":${dep}", "${dep}",
% endfor % endfor
], ],
) )
</%def>
<%def name="cc_binary(tgt)">
cc_binary(
name = "${tgt.name}",
srcs = [
% for src in tgt.src:
"${src}",
% endfor
],
deps = [
% for dep in get_deps(tgt):
"${dep}",
% endfor
],
)
</%def> </%def>

@ -930,6 +930,7 @@ else
$(Q) $(INSTALL) -d $(prefix)/lib $(Q) $(INSTALL) -d $(prefix)/lib
$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/lib${lib.name}.$(SHARED_EXT) $(prefix)/lib/lib${lib.name}.$(SHARED_EXT) $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/lib${lib.name}.$(SHARED_EXT) $(prefix)/lib/lib${lib.name}.$(SHARED_EXT)
ifneq ($(SYSTEM),Darwin) ifneq ($(SYSTEM),Darwin)
$(Q) ln -sf lib${lib.name}.$(SHARED_EXT) $(prefix)/lib/lib${lib.name}.so.${settings.version.major}
$(Q) ln -sf lib${lib.name}.$(SHARED_EXT) $(prefix)/lib/lib${lib.name}.so $(Q) ln -sf lib${lib.name}.$(SHARED_EXT) $(prefix)/lib/lib${lib.name}.so
endif endif
endif endif
@ -1268,9 +1269,6 @@ $(BINDIR)/$(CONFIG)/${tgt.name}: $(${tgt.name.upper()}_OBJS)\
$(Q) mkdir -p `dirname $@` $(Q) mkdir -p `dirname $@`
$(Q) $(LDXX) $(LDFLAGS) $(${tgt.name.upper()}_OBJS)\ $(Q) $(LDXX) $(LDFLAGS) $(${tgt.name.upper()}_OBJS)\
% endif % endif
% if tgt.build == 'test':
$(GTEST_LIB)\
% endif
% else: % else:
## C-only targets specificities. ## C-only targets specificities.
$(E) "[LD] Linking $@" $(E) "[LD] Linking $@"
@ -1296,6 +1294,9 @@ $(BINDIR)/$(CONFIG)/${tgt.name}: $(${tgt.name.upper()}_OBJS)\
$(HOST_LDLIBS_PROTOC)\ $(HOST_LDLIBS_PROTOC)\
% elif tgt.get('secure', 'check') == 'yes' or tgt.get('secure', 'check') == 'check': % elif tgt.get('secure', 'check') == 'yes' or tgt.get('secure', 'check') == 'check':
$(LDLIBS_SECURE)\ $(LDLIBS_SECURE)\
% endif
% if tgt.language == 'c++' and tgt.build == 'test':
$(GTEST_LIB)\
% endif % endif
-o $(BINDIR)/$(CONFIG)/${tgt.name} -o $(BINDIR)/$(CONFIG)/${tgt.name}
% if tgt.build == 'protoc' or tgt.language == 'c++': % if tgt.build == 'protoc' or tgt.language == 'c++':

@ -4,7 +4,8 @@ import json
${json.dumps([{"name": tgt.name, ${json.dumps([{"name": tgt.name,
"language": tgt.language, "language": tgt.language,
"flaky": tgt.get("flaky", False)} "platforms": tgt.platforms,
"flaky": tgt.flaky}
for tgt in targets for tgt in targets
if tgt.get('run', True) and tgt.build == 'test'], if tgt.get('run', True) and tgt.build == 'test'],
sort_keys=True, indent=2)} sort_keys=True, indent=2)}

@ -32,8 +32,12 @@
<%def name="to_windows_path(path)">${path.replace('/','\\')}</%def>\ <%def name="to_windows_path(path)">${path.replace('/','\\')}</%def>\
<% <%
allowed_dependencies = set(['gpr', 'grpc', 'gpr_test_util', 'grpc_test_util']) allowed_dependencies = set(['gpr', 'grpc', 'gpr_test_util', 'grpc_test_util'])
buildable_targets = [ target for target in targets if set(target.deps).issubset(allowed_dependencies) and all([src.endswith('.c') for src in target.src])] buildable_targets = [ target for target in targets
test_targets = [ target for target in buildable_targets if target.name.endswith('_test') ] if set(target.deps).issubset(allowed_dependencies) and
all([src.endswith('.c') for src in target.src]) and
'windows' in target.platforms ]
c_test_targets = [ target for target in buildable_targets if target.build == 'test' and not target.language == 'c++' ]
cxx_test_targets = [ target for target in buildable_targets if target.build == 'test' and target.language == 'c++' ]
%>\ %>\
# NMake file to build secondary gRPC targets on Windows. # NMake file to build secondary gRPC targets on Windows.
# Use grpc.sln to solution to build the gRPC libraries. # Use grpc.sln to solution to build the gRPC libraries.
@ -62,29 +66,24 @@ grpc_test_util:
$(OUT_DIR): $(OUT_DIR):
mkdir $(OUT_DIR) mkdir $(OUT_DIR)
buildtests: \ buildtests: buildtests_c buildtests_cxx
% for target in test_targets:
buildtests_c: \
% for target in c_test_targets:
${target.name}.exe \ ${target.name}.exe \
% endfor % endfor
echo All tests built. echo All tests built.
test: \ buildtests_cxx: \
% for target in test_targets: % for target in cxx_test_targets:
${target.name} \ ${target.name}.exe \
% endfor
echo All tests ran.
test_gpr: \
% for target in [ tgt for tgt in test_targets if tgt.name.startswith('gpr_')]:
${target.name} \
% endfor % endfor
echo All tests ran. echo All tests built.
% for target in buildable_targets: % for target in buildable_targets:
${target.name}.exe: grpc_test_util ${target.name}.exe: grpc_test_util $(OUT_DIR)
echo Building ${target.name} echo Building ${target.name}
$(CC) $(CFLAGS) /Fo:$(OUT_DIR)\ \ $(CC) $(CFLAGS) /Fo:$(OUT_DIR)\ \
%for source in target.src: %for source in target.src:

@ -192,7 +192,7 @@ int main(int argc, char **argv) {
do_ipv6 = 0; do_ipv6 = 0;
} }
/* For coverage, test with and without dualstack sockets. */ /* For coverage, test with and without dualstack sockets. */
for (grpc_forbid_dualstack_sockets_for_testing = 0; for (grpc_forbid_dualstack_sockets_for_testing = 0;
grpc_forbid_dualstack_sockets_for_testing <= 1; grpc_forbid_dualstack_sockets_for_testing <= 1;
grpc_forbid_dualstack_sockets_for_testing++) { grpc_forbid_dualstack_sockets_for_testing++) {

@ -37,13 +37,9 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>

@ -38,6 +38,7 @@
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>

@ -0,0 +1,82 @@
/*
*
* 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.
*
*/
/* Test of gpr thread local storage support. */
#include <stdio.h>
#include <stdlib.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/thd.h>
#include <grpc/support/tls.h>
#include "test/core/util/test_config.h"
#define NUM_THREADS 100
GPR_TLS_DECL(test_var);
static void thd_body(void *arg) {
gpr_intptr i;
GPR_ASSERT(gpr_tls_get(&test_var) == 0);
for (i = 0; i < 10000000; i++) {
gpr_tls_set(&test_var, i);
GPR_ASSERT(gpr_tls_get(&test_var) == i);
}
}
/* ------------------------------------------------- */
int main(int argc, char *argv[]) {
gpr_thd_options opt = gpr_thd_options_default();
int i;
gpr_thd_id threads[NUM_THREADS];
grpc_test_init(argc, argv);
gpr_tls_init(&test_var);
gpr_thd_options_set_joinable(&opt);
for (i = 0; i < NUM_THREADS; i++) {
gpr_thd_new(&threads[i], thd_body, NULL, &opt);
}
for (i = 0; i < NUM_THREADS; i++) {
gpr_thd_join(threads[i]);
}
gpr_tls_destroy(&test_var);
return 0;
}

@ -38,8 +38,6 @@
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
#include "src/core/iomgr/iomgr.h" #include "src/core/iomgr/iomgr.h"

@ -0,0 +1,158 @@
/*
*
* 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>
#include "test/core/util/test_config.h"
#if defined(GPR_WINSOCK_SOCKET) && defined(GRPC_TEST_PICK_PORT)
#include "src/core/iomgr/sockaddr_utils.h"
#include "test/core/util/port.h"
#include <process.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <grpc/support/log.h>
#define NUM_RANDOM_PORTS_TO_PICK 100
static int is_port_available(int *port, int is_tcp) {
const int proto = is_tcp ? IPPROTO_TCP : 0;
const SOCKET fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto);
int one = 1;
struct sockaddr_in addr;
socklen_t alen = sizeof(addr);
int actual_port;
GPR_ASSERT(*port >= 0);
GPR_ASSERT(*port <= 65535);
if (fd < 0) {
gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno));
return 0;
}
/* Reuseaddr lets us start up a server immediately after it exits */
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one)) < 0) {
gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno));
closesocket(fd);
return 0;
}
/* Try binding to port */
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(*port);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno));
closesocket(fd);
return 0;
}
/* Get the bound port number */
if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) {
gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno));
closesocket(fd);
return 0;
}
GPR_ASSERT(alen <= sizeof(addr));
actual_port = ntohs(addr.sin_port);
GPR_ASSERT(actual_port > 0);
if (*port == 0) {
*port = actual_port;
} else {
GPR_ASSERT(*port == actual_port);
}
closesocket(fd);
return 1;
}
int grpc_pick_unused_port(void) {
/* We repeatedly pick a port and then see whether or not it is
available for use both as a TCP socket and a UDP socket. First, we
pick a random large port number. For subsequent
iterations, we bind to an anonymous port and let the OS pick the
port number. The random port picking reduces the probability of
races with other processes on kernels that want to reuse the same
port numbers over and over. */
/* In alternating iterations we try UDP ports before TCP ports UDP
ports -- it could be the case that this machine has been using up
UDP ports and they are scarcer. */
/* Type of port to first pick in next iteration */
int is_tcp = 1;
int try = 0;
for (;;) {
int port;
try++;
if (try == 1) {
port = _getpid() % (65536 - 30000) + 30000;
} else if (try <= NUM_RANDOM_PORTS_TO_PICK) {
port = rand() % (65536 - 30000) + 30000;
} else {
port = 0;
}
if (!is_port_available(&port, is_tcp)) {
continue;
}
GPR_ASSERT(port > 0);
/* Check that the port # is free for the other type of socket also */
if (!is_port_available(&port, !is_tcp)) {
/* In the next iteration try to bind to the other type first
because perhaps it is more rare. */
is_tcp = !is_tcp;
continue;
}
/* TODO(ctiller): consider caching this port in some structure, to avoid
handing it out again */
return port;
}
/* The port iterator reached the end without finding a suitable port. */
return 0;
}
int grpc_pick_unused_port_or_die(void) {
int port = grpc_pick_unused_port();
GPR_ASSERT(port > 0);
return port;
}
#endif /* GPR_WINSOCK_SOCKET && GRPC_TEST_PICK_PORT */

@ -31,29 +31,19 @@
* *
*/ */
#include <chrono>
#include <fstream>
#include <memory> #include <memory>
#include <sstream>
#include <string>
#include <thread>
#include <unistd.h> #include <unistd.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include <grpc++/channel_arguments.h>
#include <grpc++/channel_interface.h> #include <grpc++/channel_interface.h>
#include <grpc++/client_context.h> #include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include <grpc++/credentials.h>
#include <grpc++/status.h> #include <grpc++/status.h>
#include <grpc++/stream.h> #include <grpc++/stream.h>
#include "test/cpp/util/create_test_channel.h" #include "test/cpp/interop/client_helper.h"
#include "test/cpp/interop/test.grpc.pb.h" #include "test/cpp/interop/interop_client.h"
#include "test/cpp/interop/empty.grpc.pb.h"
#include "test/cpp/interop/messages.grpc.pb.h"
DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
DEFINE_bool(use_prod_roots, false, "True to use SSL roots for google"); DEFINE_bool(use_prod_roots, false, "True to use SSL roots for google");
@ -81,21 +71,8 @@ DEFINE_string(service_account_key_file, "",
"Path to service account json key file."); "Path to service account json key file.");
DEFINE_string(oauth_scope, "", "Scope for OAuth tokens."); DEFINE_string(oauth_scope, "", "Scope for OAuth tokens.");
using grpc::ChannelInterface; using grpc::testing::CreateChannelForTestCase;
using grpc::ClientContext; using grpc::testing::GetServiceAccountJsonKey;
using grpc::ComputeEngineCredentials;
using grpc::CreateTestChannel;
using grpc::Credentials;
using grpc::JWTCredentials;
using grpc::ServiceAccountCredentials;
using grpc::testing::ResponseParameters;
using grpc::testing::SimpleRequest;
using grpc::testing::SimpleResponse;
using grpc::testing::StreamingInputCallRequest;
using grpc::testing::StreamingInputCallResponse;
using grpc::testing::StreamingOutputCallRequest;
using grpc::testing::StreamingOutputCallResponse;
using grpc::testing::TestService;
// In some distros, gflags is in the namespace google, and in some others, // In some distros, gflags is in the namespace google, and in some others,
// in gflags. This hack is enabling us to find both. // in gflags. This hack is enabling us to find both.
@ -104,362 +81,48 @@ namespace gflags {}
using namespace google; using namespace google;
using namespace gflags; using namespace gflags;
namespace {
// The same value is defined by the Java client.
const std::vector<int> request_stream_sizes = {27182, 8, 1828, 45904};
const std::vector<int> response_stream_sizes = {31415, 9, 2653, 58979};
const int kNumResponseMessages = 2000;
const int kResponseMessageSize = 1030;
const int kReceiveDelayMilliSeconds = 20;
const int kLargeRequestSize = 314159;
const int kLargeResponseSize = 271812;
} // namespace
grpc::string GetServiceAccountJsonKey() {
static grpc::string json_key;
if (json_key.empty()) {
std::ifstream json_key_file(FLAGS_service_account_key_file);
std::stringstream key_stream;
key_stream << json_key_file.rdbuf();
json_key = key_stream.str();
}
return json_key;
}
std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
const grpc::string& test_case) {
GPR_ASSERT(FLAGS_server_port);
const int host_port_buf_size = 1024;
char host_port[host_port_buf_size];
snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
FLAGS_server_port);
if (test_case == "service_account_creds") {
std::unique_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_enable_ssl);
grpc::string json_key = GetServiceAccountJsonKey();
creds = ServiceAccountCredentials(json_key, FLAGS_oauth_scope,
std::chrono::hours(1));
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else if (test_case == "compute_engine_creds") {
std::unique_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_enable_ssl);
creds = ComputeEngineCredentials();
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else if (test_case == "jwt_token_creds") {
std::unique_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_enable_ssl);
grpc::string json_key = GetServiceAccountJsonKey();
creds = JWTCredentials(json_key, std::chrono::hours(1));
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else {
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots);
}
}
void AssertOkOrPrintErrorStatus(const grpc::Status& s) {
if (s.IsOk()) {
return;
}
gpr_log(GPR_INFO, "Error status code: %d, message: %s", s.code(),
s.details().c_str());
GPR_ASSERT(0);
}
void DoEmpty() {
gpr_log(GPR_INFO, "Sending an empty rpc...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("empty_unary");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
grpc::testing::Empty request = grpc::testing::Empty::default_instance();
grpc::testing::Empty response = grpc::testing::Empty::default_instance();
ClientContext context;
grpc::Status s = stub->EmptyCall(&context, request, &response);
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Empty rpc done.");
}
// Shared code to set large payload, make rpc and check response payload.
void PerformLargeUnary(std::shared_ptr<ChannelInterface> channel,
SimpleRequest* request, SimpleResponse* response) {
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
ClientContext context;
request->set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
request->set_response_size(kLargeResponseSize);
grpc::string payload(kLargeRequestSize, '\0');
request->mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
grpc::Status s = stub->UnaryCall(&context, *request, response);
AssertOkOrPrintErrorStatus(s);
GPR_ASSERT(response->payload().type() ==
grpc::testing::PayloadType::COMPRESSABLE);
GPR_ASSERT(response->payload().body() ==
grpc::string(kLargeResponseSize, '\0'));
}
void DoComputeEngineCreds() {
gpr_log(GPR_INFO,
"Sending a large unary rpc with compute engine credentials ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("compute_engine_creds");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
request.set_fill_oauth_scope(true);
PerformLargeUnary(channel, &request, &response);
gpr_log(GPR_INFO, "Got username %s", response.username().c_str());
gpr_log(GPR_INFO, "Got oauth_scope %s", response.oauth_scope().c_str());
GPR_ASSERT(!response.username().empty());
GPR_ASSERT(response.username().c_str() == FLAGS_default_service_account);
GPR_ASSERT(!response.oauth_scope().empty());
const char* oauth_scope_str = response.oauth_scope().c_str();
GPR_ASSERT(FLAGS_oauth_scope.find(oauth_scope_str) != grpc::string::npos);
gpr_log(GPR_INFO, "Large unary with compute engine creds done.");
}
void DoServiceAccountCreds() {
gpr_log(GPR_INFO,
"Sending a large unary rpc with service account credentials ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("service_account_creds");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
request.set_fill_oauth_scope(true);
PerformLargeUnary(channel, &request, &response);
GPR_ASSERT(!response.username().empty());
GPR_ASSERT(!response.oauth_scope().empty());
grpc::string json_key = GetServiceAccountJsonKey();
GPR_ASSERT(json_key.find(response.username()) != grpc::string::npos);
const char* oauth_scope_str = response.oauth_scope().c_str();
GPR_ASSERT(FLAGS_oauth_scope.find(oauth_scope_str) != grpc::string::npos);
gpr_log(GPR_INFO, "Large unary with service account creds done.");
}
void DoJwtTokenCreds() {
gpr_log(GPR_INFO, "Sending a large unary rpc with JWT token credentials ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("jwt_token_creds");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
PerformLargeUnary(channel, &request, &response);
GPR_ASSERT(!response.username().empty());
grpc::string json_key = GetServiceAccountJsonKey();
GPR_ASSERT(json_key.find(response.username()) != grpc::string::npos);
gpr_log(GPR_INFO, "Large unary with JWT token creds done.");
}
void DoLargeUnary() {
gpr_log(GPR_INFO, "Sending a large unary rpc...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("large_unary");
SimpleRequest request;
SimpleResponse response;
PerformLargeUnary(channel, &request, &response);
gpr_log(GPR_INFO, "Large unary done.");
}
void DoRequestStreaming() {
gpr_log(GPR_INFO, "Sending request steaming rpc ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("client_streaming");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
grpc::ClientContext context;
StreamingInputCallRequest request;
StreamingInputCallResponse response;
std::unique_ptr<grpc::ClientWriter<StreamingInputCallRequest>> stream(
stub->StreamingInputCall(&context, &response));
int aggregated_payload_size = 0;
for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
grpc::testing::Payload* payload = request.mutable_payload();
payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
GPR_ASSERT(stream->Write(request));
aggregated_payload_size += request_stream_sizes[i];
}
stream->WritesDone();
grpc::Status s = stream->Finish();
GPR_ASSERT(response.aggregated_payload_size() == aggregated_payload_size);
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Request streaming done.");
}
void DoResponseStreaming() {
gpr_log(GPR_INFO, "Receiving response steaming rpc ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("server_streaming");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
grpc::ClientContext context;
StreamingOutputCallRequest request;
for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
ResponseParameters* response_parameter = request.add_response_parameters();
response_parameter->set_size(response_stream_sizes[i]);
}
StreamingOutputCallResponse response;
std::unique_ptr<grpc::ClientReader<StreamingOutputCallResponse>> stream(
stub->StreamingOutputCall(&context, request));
unsigned int i = 0;
while (stream->Read(&response)) {
GPR_ASSERT(response.payload().body() ==
grpc::string(response_stream_sizes[i], '\0'));
++i;
}
GPR_ASSERT(response_stream_sizes.size() == i);
grpc::Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Response streaming done.");
}
void DoResponseStreamingWithSlowConsumer() {
gpr_log(GPR_INFO, "Receiving response steaming rpc with slow consumer ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("slow_consumer");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
grpc::ClientContext context;
StreamingOutputCallRequest request;
for (int i = 0; i < kNumResponseMessages; ++i) {
ResponseParameters* response_parameter = request.add_response_parameters();
response_parameter->set_size(kResponseMessageSize);
}
StreamingOutputCallResponse response;
std::unique_ptr<grpc::ClientReader<StreamingOutputCallResponse>> stream(
stub->StreamingOutputCall(&context, request));
int i = 0;
while (stream->Read(&response)) {
GPR_ASSERT(response.payload().body() ==
grpc::string(kResponseMessageSize, '\0'));
gpr_log(GPR_INFO, "received message %d", i);
usleep(kReceiveDelayMilliSeconds * 1000);
++i;
}
GPR_ASSERT(kNumResponseMessages == i);
grpc::Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Response streaming done.");
}
void DoHalfDuplex() {
gpr_log(GPR_INFO, "Sending half-duplex streaming rpc ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("half_duplex");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
grpc::ClientContext context;
std::unique_ptr<grpc::ClientReaderWriter<StreamingOutputCallRequest,
StreamingOutputCallResponse>>
stream(stub->HalfDuplexCall(&context));
StreamingOutputCallRequest request;
ResponseParameters* response_parameter = request.add_response_parameters();
for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
response_parameter->set_size(response_stream_sizes[i]);
GPR_ASSERT(stream->Write(request));
}
stream->WritesDone();
unsigned int i = 0;
StreamingOutputCallResponse response;
while (stream->Read(&response)) {
GPR_ASSERT(response.payload().has_body());
GPR_ASSERT(response.payload().body() ==
grpc::string(response_stream_sizes[i], '\0'));
++i;
}
GPR_ASSERT(response_stream_sizes.size() == i);
grpc::Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Half-duplex streaming rpc done.");
}
void DoPingPong() {
gpr_log(GPR_INFO, "Sending Ping Pong streaming rpc ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("ping_pong");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
grpc::ClientContext context;
std::unique_ptr<grpc::ClientReaderWriter<StreamingOutputCallRequest,
StreamingOutputCallResponse>>
stream(stub->FullDuplexCall(&context));
StreamingOutputCallRequest request;
request.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
ResponseParameters* response_parameter = request.add_response_parameters();
grpc::testing::Payload* payload = request.mutable_payload();
StreamingOutputCallResponse response;
for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
response_parameter->set_size(response_stream_sizes[i]);
payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
GPR_ASSERT(stream->Write(request));
GPR_ASSERT(stream->Read(&response));
GPR_ASSERT(response.payload().has_body());
GPR_ASSERT(response.payload().body() ==
grpc::string(response_stream_sizes[i], '\0'));
}
stream->WritesDone();
GPR_ASSERT(!stream->Read(&response));
grpc::Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Ping pong streaming done.");
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
grpc_init(); grpc_init();
ParseCommandLineFlags(&argc, &argv, true); ParseCommandLineFlags(&argc, &argv, true);
grpc::testing::InteropClient client(
CreateChannelForTestCase(FLAGS_test_case));
if (FLAGS_test_case == "empty_unary") { if (FLAGS_test_case == "empty_unary") {
DoEmpty(); client.DoEmpty();
} else if (FLAGS_test_case == "large_unary") { } else if (FLAGS_test_case == "large_unary") {
DoLargeUnary(); client.DoLargeUnary();
} else if (FLAGS_test_case == "client_streaming") { } else if (FLAGS_test_case == "client_streaming") {
DoRequestStreaming(); client.DoRequestStreaming();
} else if (FLAGS_test_case == "server_streaming") { } else if (FLAGS_test_case == "server_streaming") {
DoResponseStreaming(); client.DoResponseStreaming();
} else if (FLAGS_test_case == "slow_consumer") { } else if (FLAGS_test_case == "slow_consumer") {
DoResponseStreamingWithSlowConsumer(); client.DoResponseStreamingWithSlowConsumer();
} else if (FLAGS_test_case == "half_duplex") { } else if (FLAGS_test_case == "half_duplex") {
DoHalfDuplex(); client.DoHalfDuplex();
} else if (FLAGS_test_case == "ping_pong") { } else if (FLAGS_test_case == "ping_pong") {
DoPingPong(); client.DoPingPong();
} else if (FLAGS_test_case == "service_account_creds") { } else if (FLAGS_test_case == "service_account_creds") {
DoServiceAccountCreds(); grpc::string json_key = GetServiceAccountJsonKey();
client.DoServiceAccountCreds(json_key, FLAGS_oauth_scope);
} else if (FLAGS_test_case == "compute_engine_creds") { } else if (FLAGS_test_case == "compute_engine_creds") {
DoComputeEngineCreds(); client.DoComputeEngineCreds(FLAGS_default_service_account,
FLAGS_oauth_scope);
} else if (FLAGS_test_case == "jwt_token_creds") { } else if (FLAGS_test_case == "jwt_token_creds") {
DoJwtTokenCreds(); grpc::string json_key = GetServiceAccountJsonKey();
client.DoJwtTokenCreds(json_key);
} else if (FLAGS_test_case == "all") { } else if (FLAGS_test_case == "all") {
DoEmpty(); client.DoEmpty();
DoLargeUnary(); client.DoLargeUnary();
DoRequestStreaming(); client.DoRequestStreaming();
DoResponseStreaming(); client.DoResponseStreaming();
DoHalfDuplex(); client.DoHalfDuplex();
DoPingPong(); client.DoPingPong();
// service_account_creds and jwt_token_creds can only run with ssl. // service_account_creds and jwt_token_creds can only run with ssl.
if (FLAGS_enable_ssl) { if (FLAGS_enable_ssl) {
DoServiceAccountCreds(); grpc::string json_key = GetServiceAccountJsonKey();
DoJwtTokenCreds(); client.DoServiceAccountCreds(json_key, FLAGS_oauth_scope);
client.DoJwtTokenCreds(json_key);
} }
// compute_engine_creds only runs in GCE. // compute_engine_creds only runs in GCE.
} else { } else {
@ -470,6 +133,7 @@ int main(int argc, char** argv) {
"service_account_creds|compute_engine_creds|jwt_token_creds", "service_account_creds|compute_engine_creds|jwt_token_creds",
FLAGS_test_case.c_str()); FLAGS_test_case.c_str());
} }
client.Reset(nullptr);
grpc_shutdown(); grpc_shutdown();
return 0; return 0;

@ -0,0 +1,119 @@
/*
*
* 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 "test/cpp/interop/client_helper.h"
#include <fstream>
#include <memory>
#include <sstream>
#include <unistd.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <gflags/gflags.h>
#include <grpc++/channel_arguments.h>
#include <grpc++/channel_interface.h>
#include <grpc++/create_channel.h>
#include <grpc++/credentials.h>
#include <grpc++/stream.h>
#include "test/cpp/util/create_test_channel.h"
DECLARE_bool(enable_ssl);
DECLARE_bool(use_prod_roots);
DECLARE_int32(server_port);
DECLARE_string(server_host);
DECLARE_string(server_host_override);
DECLARE_string(test_case);
DECLARE_string(default_service_account);
DECLARE_string(service_account_key_file);
DECLARE_string(oauth_scope);
// In some distros, gflags is in the namespace google, and in some others,
// in gflags. This hack is enabling us to find both.
namespace google {}
namespace gflags {}
using namespace google;
using namespace gflags;
namespace grpc {
namespace testing {
grpc::string GetServiceAccountJsonKey() {
static grpc::string json_key;
if (json_key.empty()) {
std::ifstream json_key_file(FLAGS_service_account_key_file);
std::stringstream key_stream;
key_stream << json_key_file.rdbuf();
json_key = key_stream.str();
}
return json_key;
}
std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
const grpc::string& test_case) {
GPR_ASSERT(FLAGS_server_port);
const int host_port_buf_size = 1024;
char host_port[host_port_buf_size];
snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
FLAGS_server_port);
if (test_case == "service_account_creds") {
std::unique_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_enable_ssl);
grpc::string json_key = GetServiceAccountJsonKey();
creds = ServiceAccountCredentials(json_key, FLAGS_oauth_scope,
std::chrono::hours(1));
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else if (test_case == "compute_engine_creds") {
std::unique_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_enable_ssl);
creds = ComputeEngineCredentials();
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else if (test_case == "jwt_token_creds") {
std::unique_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_enable_ssl);
grpc::string json_key = GetServiceAccountJsonKey();
creds = JWTCredentials(json_key, std::chrono::hours(1));
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else {
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots);
}
}
} // namespace testing
} // namespace grpc

@ -31,23 +31,23 @@
* *
*/ */
#ifndef GRPC_RB_EVENT_H_ #ifndef GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H
#define GRPC_RB_EVENT_H_ #define GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H
#include <ruby.h> #include <memory>
#include <grpc/grpc.h>
/* rb_cEvent is the Event class whose instances proxy grpc_event. */ #include <grpc++/config.h>
extern VALUE rb_cEvent; #include <grpc++/channel_interface.h>
/* rb_cEventError is the ruby class that acts the exception thrown during rpc namespace grpc {
event processing. */ namespace testing {
extern VALUE rb_eEventError;
/* Used to create new ruby event objects */ grpc::string GetServiceAccountJsonKey();
VALUE grpc_rb_new_event(grpc_event *ev);
/* Initializes the Event and EventError classes. */ std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
void Init_grpc_event(); const grpc::string& test_case);
#endif /* GRPC_RB_EVENT_H_ */ } // namespace testing
} // namespace grpc
#endif // GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H

@ -0,0 +1,311 @@
/*
*
* 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 "test/cpp/interop/interop_client.h"
#include <memory>
#include <unistd.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpc++/channel_interface.h>
#include <grpc++/client_context.h>
#include <grpc++/status.h>
#include <grpc++/stream.h>
#include "test/cpp/interop/test.grpc.pb.h"
#include "test/cpp/interop/empty.grpc.pb.h"
#include "test/cpp/interop/messages.grpc.pb.h"
namespace grpc {
namespace testing {
namespace {
// The same value is defined by the Java client.
const std::vector<int> request_stream_sizes = {27182, 8, 1828, 45904};
const std::vector<int> response_stream_sizes = {31415, 9, 2653, 58979};
const int kNumResponseMessages = 2000;
const int kResponseMessageSize = 1030;
const int kReceiveDelayMilliSeconds = 20;
const int kLargeRequestSize = 314159;
const int kLargeResponseSize = 271812;
} // namespace
InteropClient::InteropClient(std::shared_ptr<ChannelInterface> channel)
: channel_(channel) {}
void InteropClient::AssertOkOrPrintErrorStatus(const Status& s) {
if (s.IsOk()) {
return;
}
gpr_log(GPR_INFO, "Error status code: %d, message: %s", s.code(),
s.details().c_str());
GPR_ASSERT(0);
}
void InteropClient::DoEmpty() {
gpr_log(GPR_INFO, "Sending an empty rpc...");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
Empty request = Empty::default_instance();
Empty response = Empty::default_instance();
ClientContext context;
Status s = stub->EmptyCall(&context, request, &response);
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Empty rpc done.");
}
// Shared code to set large payload, make rpc and check response payload.
void InteropClient::PerformLargeUnary(SimpleRequest* request,
SimpleResponse* response) {
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
ClientContext context;
request->set_response_type(PayloadType::COMPRESSABLE);
request->set_response_size(kLargeResponseSize);
grpc::string payload(kLargeRequestSize, '\0');
request->mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
Status s = stub->UnaryCall(&context, *request, response);
AssertOkOrPrintErrorStatus(s);
GPR_ASSERT(response->payload().type() == PayloadType::COMPRESSABLE);
GPR_ASSERT(response->payload().body() ==
grpc::string(kLargeResponseSize, '\0'));
}
void InteropClient::DoComputeEngineCreds(
const grpc::string& default_service_account,
const grpc::string& oauth_scope) {
gpr_log(GPR_INFO,
"Sending a large unary rpc with compute engine credentials ...");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
request.set_fill_oauth_scope(true);
PerformLargeUnary(&request, &response);
gpr_log(GPR_INFO, "Got username %s", response.username().c_str());
gpr_log(GPR_INFO, "Got oauth_scope %s", response.oauth_scope().c_str());
GPR_ASSERT(!response.username().empty());
GPR_ASSERT(response.username().c_str() == default_service_account);
GPR_ASSERT(!response.oauth_scope().empty());
const char* oauth_scope_str = response.oauth_scope().c_str();
GPR_ASSERT(oauth_scope.find(oauth_scope_str) != grpc::string::npos);
gpr_log(GPR_INFO, "Large unary with compute engine creds done.");
}
void InteropClient::DoServiceAccountCreds(const grpc::string& username,
const grpc::string& oauth_scope) {
gpr_log(GPR_INFO,
"Sending a large unary rpc with service account credentials ...");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
request.set_fill_oauth_scope(true);
PerformLargeUnary(&request, &response);
GPR_ASSERT(!response.username().empty());
GPR_ASSERT(!response.oauth_scope().empty());
GPR_ASSERT(username.find(response.username()) != grpc::string::npos);
const char* oauth_scope_str = response.oauth_scope().c_str();
GPR_ASSERT(oauth_scope.find(oauth_scope_str) != grpc::string::npos);
gpr_log(GPR_INFO, "Large unary with service account creds done.");
}
void InteropClient::DoJwtTokenCreds(const grpc::string& username) {
gpr_log(GPR_INFO, "Sending a large unary rpc with JWT token credentials ...");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
PerformLargeUnary(&request, &response);
GPR_ASSERT(!response.username().empty());
GPR_ASSERT(username.find(response.username()) != grpc::string::npos);
gpr_log(GPR_INFO, "Large unary with JWT token creds done.");
}
void InteropClient::DoLargeUnary() {
gpr_log(GPR_INFO, "Sending a large unary rpc...");
SimpleRequest request;
SimpleResponse response;
PerformLargeUnary(&request, &response);
gpr_log(GPR_INFO, "Large unary done.");
}
void InteropClient::DoRequestStreaming() {
gpr_log(GPR_INFO, "Sending request steaming rpc ...");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
ClientContext context;
StreamingInputCallRequest request;
StreamingInputCallResponse response;
std::unique_ptr<ClientWriter<StreamingInputCallRequest>> stream(
stub->StreamingInputCall(&context, &response));
int aggregated_payload_size = 0;
for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
Payload* payload = request.mutable_payload();
payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
GPR_ASSERT(stream->Write(request));
aggregated_payload_size += request_stream_sizes[i];
}
stream->WritesDone();
Status s = stream->Finish();
GPR_ASSERT(response.aggregated_payload_size() == aggregated_payload_size);
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Request streaming done.");
}
void InteropClient::DoResponseStreaming() {
gpr_log(GPR_INFO, "Receiving response steaming rpc ...");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
ClientContext context;
StreamingOutputCallRequest request;
for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
ResponseParameters* response_parameter = request.add_response_parameters();
response_parameter->set_size(response_stream_sizes[i]);
}
StreamingOutputCallResponse response;
std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream(
stub->StreamingOutputCall(&context, request));
unsigned int i = 0;
while (stream->Read(&response)) {
GPR_ASSERT(response.payload().body() ==
grpc::string(response_stream_sizes[i], '\0'));
++i;
}
GPR_ASSERT(response_stream_sizes.size() == i);
Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Response streaming done.");
}
void InteropClient::DoResponseStreamingWithSlowConsumer() {
gpr_log(GPR_INFO, "Receiving response steaming rpc with slow consumer ...");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
ClientContext context;
StreamingOutputCallRequest request;
for (int i = 0; i < kNumResponseMessages; ++i) {
ResponseParameters* response_parameter = request.add_response_parameters();
response_parameter->set_size(kResponseMessageSize);
}
StreamingOutputCallResponse response;
std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream(
stub->StreamingOutputCall(&context, request));
int i = 0;
while (stream->Read(&response)) {
GPR_ASSERT(response.payload().body() ==
grpc::string(kResponseMessageSize, '\0'));
gpr_log(GPR_INFO, "received message %d", i);
usleep(kReceiveDelayMilliSeconds * 1000);
++i;
}
GPR_ASSERT(kNumResponseMessages == i);
Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Response streaming done.");
}
void InteropClient::DoHalfDuplex() {
gpr_log(GPR_INFO, "Sending half-duplex streaming rpc ...");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
ClientContext context;
std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest,
StreamingOutputCallResponse>>
stream(stub->HalfDuplexCall(&context));
StreamingOutputCallRequest request;
ResponseParameters* response_parameter = request.add_response_parameters();
for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
response_parameter->set_size(response_stream_sizes[i]);
GPR_ASSERT(stream->Write(request));
}
stream->WritesDone();
unsigned int i = 0;
StreamingOutputCallResponse response;
while (stream->Read(&response)) {
GPR_ASSERT(response.payload().has_body());
GPR_ASSERT(response.payload().body() ==
grpc::string(response_stream_sizes[i], '\0'));
++i;
}
GPR_ASSERT(response_stream_sizes.size() == i);
Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Half-duplex streaming rpc done.");
}
void InteropClient::DoPingPong() {
gpr_log(GPR_INFO, "Sending Ping Pong streaming rpc ...");
std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
ClientContext context;
std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest,
StreamingOutputCallResponse>>
stream(stub->FullDuplexCall(&context));
StreamingOutputCallRequest request;
request.set_response_type(PayloadType::COMPRESSABLE);
ResponseParameters* response_parameter = request.add_response_parameters();
Payload* payload = request.mutable_payload();
StreamingOutputCallResponse response;
for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
response_parameter->set_size(response_stream_sizes[i]);
payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
GPR_ASSERT(stream->Write(request));
GPR_ASSERT(stream->Read(&response));
GPR_ASSERT(response.payload().has_body());
GPR_ASSERT(response.payload().body() ==
grpc::string(response_stream_sizes[i], '\0'));
}
stream->WritesDone();
GPR_ASSERT(!stream->Read(&response));
Status s = stream->Finish();
AssertOkOrPrintErrorStatus(s);
gpr_log(GPR_INFO, "Ping pong streaming done.");
}
} // namespace testing
} // namespace grpc

@ -0,0 +1,79 @@
/*
*
* 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.
*
*/
#ifndef GRPC_TEST_CPP_INTEROP_INTEROP_CLIENT_H
#define GRPC_TEST_CPP_INTEROP_INTEROP_CLIENT_H
#include <memory>
#include <grpc/grpc.h>
#include <grpc++/channel_interface.h>
#include <grpc++/status.h>
#include "test/cpp/interop/messages.grpc.pb.h"
namespace grpc {
namespace testing {
class InteropClient {
public:
explicit InteropClient(std::shared_ptr<ChannelInterface> channel);
~InteropClient() {}
void Reset(std::shared_ptr<ChannelInterface> channel) { channel_ = channel; }
void DoEmpty();
void DoLargeUnary();
void DoPingPong();
void DoHalfDuplex();
void DoRequestStreaming();
void DoResponseStreaming();
void DoResponseStreamingWithSlowConsumer();
// Auth tests.
// username is a string containing the user email
void DoJwtTokenCreds(const grpc::string& username);
void DoComputeEngineCreds(const grpc::string& default_service_account,
const grpc::string& oauth_scope);
// username is a string containing the user email
void DoServiceAccountCreds(const grpc::string& username,
const grpc::string& oauth_scope);
private:
void PerformLargeUnary(SimpleRequest* request, SimpleResponse* response);
void AssertOkOrPrintErrorStatus(const Status& s);
std::shared_ptr<ChannelInterface> channel_;
};
} // namespace testing
} // namespace grpc
#endif // GRPC_TEST_CPP_INTEROP_INTEROP_CLIENT_H

@ -41,7 +41,6 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include "test/core/end2end/data/ssl_test_data.h"
#include <grpc++/config.h> #include <grpc++/config.h>
#include <grpc++/server.h> #include <grpc++/server.h>
#include <grpc++/server_builder.h> #include <grpc++/server_builder.h>
@ -52,6 +51,7 @@
#include "test/cpp/interop/test.grpc.pb.h" #include "test/cpp/interop/test.grpc.pb.h"
#include "test/cpp/interop/empty.grpc.pb.h" #include "test/cpp/interop/empty.grpc.pb.h"
#include "test/cpp/interop/messages.grpc.pb.h" #include "test/cpp/interop/messages.grpc.pb.h"
#include "test/cpp/interop/server_helper.h"
DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
DEFINE_int32(port, 0, "Server port."); DEFINE_int32(port, 0, "Server port.");
@ -211,15 +211,8 @@ void RunServer() {
ServerBuilder builder; ServerBuilder builder;
builder.RegisterService(&service); builder.RegisterService(&service);
std::shared_ptr<ServerCredentials> creds = grpc::InsecureServerCredentials(); std::shared_ptr<ServerCredentials> creds =
if (FLAGS_enable_ssl) { grpc::testing::CreateInteropServerCredentials();
SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
test_server1_cert};
SslServerCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs = "";
ssl_opts.pem_key_cert_pairs.push_back(pkcp);
creds = grpc::SslServerCredentials(ssl_opts);
}
builder.AddListeningPort(server_address.str(), creds); builder.AddListeningPort(server_address.str(), creds);
std::unique_ptr<Server> server(builder.BuildAndStart()); std::unique_ptr<Server> server(builder.BuildAndStart());
gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str()); gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());

@ -0,0 +1,69 @@
/*
*
* 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 "test/cpp/interop/server_helper.h"
#include <memory>
#include <gflags/gflags.h>
#include "test/core/end2end/data/ssl_test_data.h"
#include <grpc++/config.h>
#include <grpc++/server_credentials.h>
DECLARE_bool(enable_ssl);
// In some distros, gflags is in the namespace google, and in some others,
// in gflags. This hack is enabling us to find both.
namespace google {}
namespace gflags {}
using namespace google;
using namespace gflags;
namespace grpc {
namespace testing {
std::shared_ptr<ServerCredentials> CreateInteropServerCredentials() {
if (FLAGS_enable_ssl) {
SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
test_server1_cert};
SslServerCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs = "";
ssl_opts.pem_key_cert_pairs.push_back(pkcp);
return SslServerCredentials(ssl_opts);
} else {
return InsecureServerCredentials();
}
}
} // namespace testing
} // namespace grpc

@ -31,23 +31,19 @@
* *
*/ */
#ifndef GRPC_RB_METADATA_H_ #ifndef GRPC_TEST_CPP_INTEROP_SERVER_HELPER_H
#define GRPC_RB_METADATA_H_ #define GRPC_TEST_CPP_INTEROP_SERVER_HELPER_H
#include <grpc/grpc.h> #include <memory>
#include <ruby.h>
/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */ #include <grpc++/server_credentials.h>
extern VALUE rb_cMetadata;
/* grpc_rb_metadata_create_with_mark creates a grpc_rb_metadata with a ruby mark namespace grpc {
* object that will be kept alive while the metadata is alive. */ namespace testing {
extern VALUE grpc_rb_metadata_create_with_mark(VALUE mark, grpc_metadata* md);
/* Gets the wrapped metadata from the ruby wrapper */ std::shared_ptr<ServerCredentials> CreateInteropServerCredentials();
grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v);
/* Initializes the Metadata class. */ } // namespace testing
void Init_grpc_metadata(); } // namespace grpc
#endif /* GRPC_RB_METADATA_H_ */ #endif // GRPC_TEST_CPP_INTEROP_SERVER_HELPER_H

@ -104,7 +104,7 @@ class Client {
void EndThreads() { threads_.clear(); } void EndThreads() { threads_.clear(); }
virtual void ThreadFunc(Histogram* histogram, size_t thread_idx) = 0; virtual bool ThreadFunc(Histogram* histogram, size_t thread_idx) = 0;
private: private:
class Thread { class Thread {
@ -113,20 +113,24 @@ class Client {
: done_(false), : done_(false),
new_(nullptr), new_(nullptr),
impl_([this, idx, client]() { impl_([this, idx, client]() {
for (;;) { for (;;) {
// run the loop body // run the loop body
client->ThreadFunc(&histogram_, idx); bool thread_still_ok = client->ThreadFunc(&histogram_, idx);
// lock, see if we're done // lock, see if we're done
std::lock_guard<std::mutex> g(mu_); std::lock_guard<std::mutex> g(mu_);
if (done_) {return;} if (!thread_still_ok) {
// check if we're marking, swap out the histogram if so gpr_log(GPR_ERROR, "Finishing client thread due to RPC error");
if (new_) { done_ = true;
new_->Swap(&histogram_); }
new_ = nullptr; if (done_) {return;}
cv_.notify_one(); // check if we're marking, swap out the histogram if so
if (new_) {
new_->Swap(&histogram_);
new_ = nullptr;
cv_.notify_one();
}
} }
} }) {}
}) {}
~Thread() { ~Thread() {
{ {

@ -137,13 +137,7 @@ class AsyncUnaryClient GRPC_FINAL : public Client {
cli_cqs_.emplace_back(new CompletionQueue); cli_cqs_.emplace_back(new CompletionQueue);
} }
auto payload_size = config.payload_size(); auto check_done = [](grpc::Status s, SimpleResponse* response) {};
auto check_done = [payload_size](grpc::Status s, SimpleResponse* response) {
GPR_ASSERT(s.IsOk() && (response->payload().type() ==
grpc::testing::PayloadType::COMPRESSABLE) &&
(response->payload().body().length() ==
static_cast<size_t>(payload_size)));
};
int t = 0; int t = 0;
for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) { for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) {
@ -179,10 +173,14 @@ class AsyncUnaryClient GRPC_FINAL : public Client {
} }
} }
void ThreadFunc(Histogram* histogram, size_t thread_idx) GRPC_OVERRIDE { bool ThreadFunc(Histogram* histogram, size_t thread_idx) GRPC_OVERRIDE {
void* got_tag; void* got_tag;
bool ok; bool ok;
cli_cqs_[thread_idx]->Next(&got_tag, &ok); switch (cli_cqs_[thread_idx]->AsyncNext(&got_tag, &ok, std::chrono::system_clock::now() + std::chrono::seconds(1))) {
case CompletionQueue::SHUTDOWN: return false;
case CompletionQueue::TIMEOUT: return true;
case CompletionQueue::GOT_EVENT: break;
}
ClientRpcContext* ctx = ClientRpcContext::detag(got_tag); ClientRpcContext* ctx = ClientRpcContext::detag(got_tag);
if (ctx->RunNextState(ok, histogram) == false) { if (ctx->RunNextState(ok, histogram) == false) {
@ -191,6 +189,8 @@ class AsyncUnaryClient GRPC_FINAL : public Client {
ctx->StartNewClone(); ctx->StartNewClone();
delete ctx; delete ctx;
} }
return true;
} }
std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_; std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_;
@ -270,13 +270,7 @@ class AsyncStreamingClient GRPC_FINAL : public Client {
cli_cqs_.emplace_back(new CompletionQueue); cli_cqs_.emplace_back(new CompletionQueue);
} }
auto payload_size = config.payload_size(); auto check_done = [](grpc::Status s, SimpleResponse* response) {};
auto check_done = [payload_size](grpc::Status s, SimpleResponse *response) {
GPR_ASSERT(s.IsOk() && (response->payload().type() ==
grpc::testing::PayloadType::COMPRESSABLE) &&
(response->payload().body().length() ==
static_cast<size_t>(payload_size)));
};
int t = 0; int t = 0;
for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) { for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) {
@ -313,10 +307,14 @@ class AsyncStreamingClient GRPC_FINAL : public Client {
} }
} }
void ThreadFunc(Histogram *histogram, size_t thread_idx) GRPC_OVERRIDE { bool ThreadFunc(Histogram *histogram, size_t thread_idx) GRPC_OVERRIDE {
void *got_tag; void *got_tag;
bool ok; bool ok;
cli_cqs_[thread_idx]->Next(&got_tag, &ok); switch (cli_cqs_[thread_idx]->AsyncNext(&got_tag, &ok, std::chrono::system_clock::now() + std::chrono::seconds(1))) {
case CompletionQueue::SHUTDOWN: return false;
case CompletionQueue::TIMEOUT: return true;
case CompletionQueue::GOT_EVENT: break;
}
ClientRpcContext *ctx = ClientRpcContext::detag(got_tag); ClientRpcContext *ctx = ClientRpcContext::detag(got_tag);
if (ctx->RunNextState(ok, histogram) == false) { if (ctx->RunNextState(ok, histogram) == false) {
@ -325,6 +323,8 @@ class AsyncStreamingClient GRPC_FINAL : public Client {
ctx->StartNewClone(); ctx->StartNewClone();
delete ctx; delete ctx;
} }
return true;
} }
std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_; std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_;

@ -83,13 +83,14 @@ class SynchronousUnaryClient GRPC_FINAL : public SynchronousClient {
SynchronousClient(config) {StartThreads(num_threads_);} SynchronousClient(config) {StartThreads(num_threads_);}
~SynchronousUnaryClient() {} ~SynchronousUnaryClient() {}
void ThreadFunc(Histogram* histogram, size_t thread_idx) GRPC_OVERRIDE { bool ThreadFunc(Histogram* histogram, size_t thread_idx) GRPC_OVERRIDE {
auto* stub = channels_[thread_idx % channels_.size()].get_stub(); auto* stub = channels_[thread_idx % channels_.size()].get_stub();
double start = Timer::Now(); double start = Timer::Now();
grpc::ClientContext context; grpc::ClientContext context;
grpc::Status s = grpc::Status s =
stub->UnaryCall(&context, request_, &responses_[thread_idx]); stub->UnaryCall(&context, request_, &responses_[thread_idx]);
histogram->Add((Timer::Now() - start) * 1e9); histogram->Add((Timer::Now() - start) * 1e9);
return s.IsOk();
} }
}; };
@ -111,11 +112,13 @@ class SynchronousStreamingClient GRPC_FINAL : public SynchronousClient {
} }
} }
void ThreadFunc(Histogram* histogram, size_t thread_idx) GRPC_OVERRIDE { bool ThreadFunc(Histogram* histogram, size_t thread_idx) GRPC_OVERRIDE {
double start = Timer::Now(); double start = Timer::Now();
EXPECT_TRUE(stream_->Write(request_)); if (stream_->Write(request_) && stream_->Read(&responses_[thread_idx])) {
EXPECT_TRUE(stream_->Read(&responses_[thread_idx])); histogram->Add((Timer::Now() - start) * 1e9);
histogram->Add((Timer::Now() - start) * 1e9); return true;
}
return false;
} }
private: private:
grpc::ClientContext context_; grpc::ClientContext context_;

@ -50,10 +50,10 @@ class Histogram {
void Merge(Histogram* h) { gpr_histogram_merge(impl_, h->impl_); } void Merge(Histogram* h) { gpr_histogram_merge(impl_, h->impl_); }
void Add(double value) { gpr_histogram_add(impl_, value); } void Add(double value) { gpr_histogram_add(impl_, value); }
double Percentile(double pctile) { double Percentile(double pctile) const {
return gpr_histogram_percentile(impl_, pctile); return gpr_histogram_percentile(impl_, pctile);
} }
double Count() { return gpr_histogram_count(impl_); } double Count() const { return gpr_histogram_count(impl_); }
void Swap(Histogram* other) { std::swap(impl_, other->impl_); } void Swap(Histogram* other) { std::swap(impl_, other->impl_); }
void FillProto(HistogramData* p) { void FillProto(HistogramData* p) {
size_t n; size_t n;

@ -35,7 +35,7 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include "test/cpp/qps/driver.h" #include "test/cpp/qps/driver.h"
#include "test/cpp/qps/stats.h" #include "test/cpp/qps/report.h"
DEFINE_int32(num_clients, 1, "Number of client binaries"); DEFINE_int32(num_clients, 1, "Number of client binaries");
DEFINE_int32(num_servers, 1, "Number of server binaries"); DEFINE_int32(num_servers, 1, "Number of server binaries");
@ -65,7 +65,6 @@ using grpc::testing::ClientType;
using grpc::testing::ServerType; using grpc::testing::ServerType;
using grpc::testing::RpcType; using grpc::testing::RpcType;
using grpc::testing::ResourceUsage; using grpc::testing::ResourceUsage;
using grpc::testing::sum;
// In some distros, gflags is in the namespace google, and in some others, // In some distros, gflags is in the namespace google, and in some others,
// in gflags. This hack is enabling us to find both. // in gflags. This hack is enabling us to find both.
@ -105,37 +104,9 @@ int main(int argc, char** argv) {
server_config, FLAGS_num_servers, server_config, FLAGS_num_servers,
FLAGS_warmup_seconds, FLAGS_benchmark_seconds); FLAGS_warmup_seconds, FLAGS_benchmark_seconds);
gpr_log(GPR_INFO, "QPS: %.1f", ReportQPSPerCore(result, server_config);
result.latencies.Count() / ReportLatency(result);
average(result.client_resources, ReportTimes(result);
[](ResourceUsage u) { return u.wall_time; }));
gpr_log(GPR_INFO, "Latencies (50/95/99/99.9%%-ile): %.1f/%.1f/%.1f/%.1f us",
result.latencies.Percentile(50) / 1000,
result.latencies.Percentile(95) / 1000,
result.latencies.Percentile(99) / 1000,
result.latencies.Percentile(99.9) / 1000);
gpr_log(GPR_INFO, "Server system time: %.2f%%",
100.0 * sum(result.server_resources,
[](ResourceUsage u) { return u.system_time; }) /
sum(result.server_resources,
[](ResourceUsage u) { return u.wall_time; }));
gpr_log(GPR_INFO, "Server user time: %.2f%%",
100.0 * sum(result.server_resources,
[](ResourceUsage u) { return u.user_time; }) /
sum(result.server_resources,
[](ResourceUsage u) { return u.wall_time; }));
gpr_log(GPR_INFO, "Client system time: %.2f%%",
100.0 * sum(result.client_resources,
[](ResourceUsage u) { return u.system_time; }) /
sum(result.client_resources,
[](ResourceUsage u) { return u.wall_time; }));
gpr_log(GPR_INFO, "Client user time: %.2f%%",
100.0 * sum(result.client_resources,
[](ResourceUsage u) { return u.user_time; }) /
sum(result.client_resources,
[](ResourceUsage u) { return u.wall_time; }));
grpc_shutdown(); grpc_shutdown();
return 0; return 0;

@ -0,0 +1,94 @@
/*
*
* 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 "test/cpp/qps/report.h"
#include <grpc/support/log.h>
#include "test/cpp/qps/stats.h"
namespace grpc {
namespace testing {
// QPS: XXX
void ReportQPS(const ScenarioResult& result) {
gpr_log(GPR_INFO, "QPS: %.1f",
result.latencies.Count() /
average(result.client_resources,
[](ResourceUsage u) { return u.wall_time; }));
}
// QPS: XXX (YYY/server core)
void ReportQPSPerCore(const ScenarioResult& result, const ServerConfig& server_config) {
auto qps =
result.latencies.Count() /
average(result.client_resources,
[](ResourceUsage u) { return u.wall_time; });
gpr_log(GPR_INFO, "QPS: %.1f (%.1f/server core)", qps, qps/server_config.threads());
}
// Latency (50/90/95/99/99.9%-ile): AA/BB/CC/DD/EE us
void ReportLatency(const ScenarioResult& result) {
gpr_log(GPR_INFO, "Latencies (50/90/95/99/99.9%%-ile): %.1f/%.1f/%.1f/%.1f/%.1f us",
result.latencies.Percentile(50) / 1000,
result.latencies.Percentile(90) / 1000,
result.latencies.Percentile(95) / 1000,
result.latencies.Percentile(99) / 1000,
result.latencies.Percentile(99.9) / 1000);
}
void ReportTimes(const ScenarioResult& result) {
gpr_log(GPR_INFO, "Server system time: %.2f%%",
100.0 * sum(result.server_resources,
[](ResourceUsage u) { return u.system_time; }) /
sum(result.server_resources,
[](ResourceUsage u) { return u.wall_time; }));
gpr_log(GPR_INFO, "Server user time: %.2f%%",
100.0 * sum(result.server_resources,
[](ResourceUsage u) { return u.user_time; }) /
sum(result.server_resources,
[](ResourceUsage u) { return u.wall_time; }));
gpr_log(GPR_INFO, "Client system time: %.2f%%",
100.0 * sum(result.client_resources,
[](ResourceUsage u) { return u.system_time; }) /
sum(result.client_resources,
[](ResourceUsage u) { return u.wall_time; }));
gpr_log(GPR_INFO, "Client user time: %.2f%%",
100.0 * sum(result.client_resources,
[](ResourceUsage u) { return u.user_time; }) /
sum(result.client_resources,
[](ResourceUsage u) { return u.wall_time; }));
}
} // namespace testing
} // namespace grpc

@ -0,0 +1,57 @@
/*
*
* 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.
*
*/
#ifndef TEST_QPS_REPORT_H
#define TEST_QPS_REPORT_H
#include "test/cpp/qps/driver.h"
namespace grpc {
namespace testing {
// QPS: XXX
void ReportQPS(const ScenarioResult& result);
// QPS: XXX (YYY/server core)
void ReportQPSPerCore(const ScenarioResult& result, const ServerConfig& config);
// Latency (50/90/95/99/99.9%-ile): AA/BB/CC/DD/EE us
void ReportLatency(const ScenarioResult& result);
// Server system time: XX%
// Server user time: XX%
// Client system time: XX%
// Client user time: XX%
void ReportTimes(const ScenarioResult& result);
} // namespace testing
} // namespace grpc
#endif

@ -97,15 +97,15 @@ class AsyncQpsServerTest : public Server {
bool ok; bool ok;
void* got_tag; void* got_tag;
while (srv_cq_.Next(&got_tag, &ok)) { while (srv_cq_.Next(&got_tag, &ok)) {
ServerRpcContext* ctx = detag(got_tag); ServerRpcContext* ctx = detag(got_tag);
// The tag is a pointer to an RPC context to invoke // The tag is a pointer to an RPC context to invoke
if (ctx->RunNextState(ok) == false) { if (ctx->RunNextState(ok) == false) {
// this RPC context is done, so refresh it // this RPC context is done, so refresh it
std::lock_guard<std::mutex> g(shutdown_mutex_); std::lock_guard<std::mutex> g(shutdown_mutex_);
if (!shutdown_) { if (!shutdown_) {
ctx->Reset(); ctx->Reset();
} }
} }
} }
return; return;
})); }));
@ -175,8 +175,9 @@ class AsyncQpsServerTest : public Server {
private: private:
bool finisher(bool) { return false; } bool finisher(bool) { return false; }
bool invoker(bool ok) { bool invoker(bool ok) {
if (!ok) if (!ok) {
return false; return false;
}
ResponseType response; ResponseType response;
@ -230,8 +231,9 @@ class AsyncQpsServerTest : public Server {
private: private:
bool request_done(bool ok) { bool request_done(bool ok) {
if (!ok) if (!ok) {
return false; return false;
}
stream_.Read(&req_, AsyncQpsServerTest::tag(this)); stream_.Read(&req_, AsyncQpsServerTest::tag(this));
next_state_ = &ServerRpcContextStreamingImpl::read_done; next_state_ = &ServerRpcContextStreamingImpl::read_done;
return true; return true;

@ -0,0 +1,149 @@
/*
*
* 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/log.h>
#include "test/cpp/qps/driver.h"
#include "test/cpp/qps/report.h"
namespace grpc {
namespace testing {
static const int WARMUP = 5;
static const int BENCHMARK = 10;
static void RunSynchronousUnaryPingPong() {
gpr_log(GPR_INFO, "Running Synchronous Unary Ping Pong");
ClientConfig client_config;
client_config.set_client_type(SYNCHRONOUS_CLIENT);
client_config.set_enable_ssl(false);
client_config.set_outstanding_rpcs_per_channel(1);
client_config.set_client_channels(1);
client_config.set_payload_size(1);
client_config.set_rpc_type(UNARY);
ServerConfig server_config;
server_config.set_server_type(SYNCHRONOUS_SERVER);
server_config.set_enable_ssl(false);
server_config.set_threads(1);
auto result = RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK);
ReportQPS(result);
ReportLatency(result);
}
static void RunSynchronousStreamingPingPong() {
gpr_log(GPR_INFO, "Running Synchronous Streaming Ping Pong");
ClientConfig client_config;
client_config.set_client_type(SYNCHRONOUS_CLIENT);
client_config.set_enable_ssl(false);
client_config.set_outstanding_rpcs_per_channel(1);
client_config.set_client_channels(1);
client_config.set_payload_size(1);
client_config.set_rpc_type(STREAMING);
ServerConfig server_config;
server_config.set_server_type(SYNCHRONOUS_SERVER);
server_config.set_enable_ssl(false);
server_config.set_threads(1);
auto result = RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK);
ReportQPS(result);
ReportLatency(result);
}
static void RunAsyncUnaryPingPong() {
gpr_log(GPR_INFO, "Running Async Unary Ping Pong");
ClientConfig client_config;
client_config.set_client_type(ASYNC_CLIENT);
client_config.set_enable_ssl(false);
client_config.set_outstanding_rpcs_per_channel(1);
client_config.set_client_channels(1);
client_config.set_payload_size(1);
client_config.set_async_client_threads(1);
client_config.set_rpc_type(UNARY);
ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER);
server_config.set_enable_ssl(false);
server_config.set_threads(1);
auto result = RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK);
ReportQPS(result);
ReportLatency(result);
}
static void RunQPS() {
gpr_log(GPR_INFO, "Running QPS test");
ClientConfig client_config;
client_config.set_client_type(ASYNC_CLIENT);
client_config.set_enable_ssl(false);
client_config.set_outstanding_rpcs_per_channel(1000);
client_config.set_client_channels(8);
client_config.set_payload_size(1);
client_config.set_async_client_threads(8);
client_config.set_rpc_type(UNARY);
ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER);
server_config.set_enable_ssl(false);
server_config.set_threads(4);
auto result = RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK);
ReportQPSPerCore(result, server_config);
ReportLatency(result);
}
} // namespace testing
} // namespace grpc
int main(int argc, char** argv) {
grpc_init();
using namespace grpc::testing;
RunSynchronousStreamingPingPong();
RunSynchronousUnaryPingPong();
RunAsyncUnaryPingPong();
RunQPS();
grpc_shutdown();
return 0;
}

@ -0,0 +1,28 @@
#!/bin/sh
# performs a single qps run with one client and one server
set -ex
cd $(dirname $0)/../../..
killall qps_worker || true
config=opt
NUMCPUS=`python2.7 -c 'import multiprocessing; print multiprocessing.cpu_count()'`
make CONFIG=$config qps_worker qps_smoke_test -j$NUMCPUS
bins/$config/qps_worker -driver_port 10000 -server_port 10001 &
PID1=$!
bins/$config/qps_worker -driver_port 10010 -server_port 10011 &
PID2=$!
export QPS_WORKERS="localhost:10000,localhost:10010"
bins/$config/qps_smoke_test $*
kill -2 $PID1 $PID2
wait

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/sh
# Copyright 2015, Google Inc. # Copyright 2015, Google Inc.
# All rights reserved. # All rights reserved.
# #
@ -31,7 +31,7 @@
set -e set -e
if [ "x$TEST" == "x" ] ; then if [ "x$TEST" = "x" ] ; then
TEST=false TEST=false
fi fi
@ -61,12 +61,12 @@ for dir in . ; do
out=${out%.*} # strip template extension out=${out%.*} # strip template extension
json_files="build.json $end2end_test_build" json_files="build.json $end2end_test_build"
data=`for i in $json_files; do echo -n "-d $i "; done` data=`for i in $json_files; do echo -n "-d $i "; done`
if [ $TEST == true ] ; then if [ "x$TEST" = "xtrue" ] ; then
actual_out=$out actual_out=$out
out=`mktemp /tmp/gentXXXXXX` out=`mktemp /tmp/gentXXXXXX`
fi fi
$mako_renderer $plugins $data -o $out $file $mako_renderer $plugins $data -o $out $file
if [ $TEST == true ] ; then if [ "x$TEST" = "xtrue" ] ; then
diff -q $out $actual_out diff -q $out $actual_out
rm $out rm $out
fi fi

@ -27,18 +27,25 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc' """Buildgen expand binary attributes plugin.
describe 'Wrapped classes where .new cannot create an instance' do This fills in any optional attributes.
describe GRPC::Core::Event do
it 'should fail .new fail with a runtime error' do """
expect { GRPC::Core::Event.new }.to raise_error(TypeError)
end
end def mako_plugin(dictionary):
"""The exported plugin code for expand_filegroups.
describe GRPC::Core::Call do
it 'should fail .new fail with a runtime error' do The list of libs in the build.json file can contain "filegroups" tags.
expect { GRPC::Core::Event.new }.to raise_error(TypeError) These refer to the filegroups in the root object. We will expand and
end merge filegroups on the src, headers and public_headers properties.
end
end """
targets = dictionary.get('targets')
for tgt in targets:
tgt['flaky'] = tgt.get('flaky', False)
tgt['platforms'] = tgt.get('platforms', ['windows', 'posix'])

@ -66,5 +66,8 @@ RUN cd /var/local/git/grpc \
# Add a cacerts directory containing the Google root pem file, allowing the interop client to access the production test instance # Add a cacerts directory containing the Google root pem file, allowing the interop client to access the production test instance
ADD cacerts cacerts ADD cacerts cacerts
# Add a service_account directory containing the auth creds file
ADD service_account service_account
# Specify the default command such that the interop server runs on its known testing port # Specify the default command such that the interop server runs on its known testing port
CMD ["/bin/bash", "-l", "-c", "python2.7 -m interop.server --use_tls --port 8050"] CMD ["/bin/bash", "-l", "-c", "python2.7 -m interop.server --use_tls --port 8050"]

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

Loading…
Cancel
Save