Merge github.com:grpc/grpc into varint_test

pull/4310/head
Craig Tiller 9 years ago
commit 11596c95c8
  1. 142
      Makefile
  2. 30
      build.yaml
  3. 1
      examples/objective-c/auth_sample/Podfile
  4. 1
      examples/objective-c/helloworld/Podfile
  5. 1
      examples/objective-c/route_guide/Podfile
  6. 2
      gRPC.podspec
  7. 9
      include/grpc/support/port_platform.h
  8. 3
      package.json
  9. 6
      src/core/census/context.h
  10. 3
      src/core/iomgr/endpoint_pair_posix.c
  11. 3
      src/core/support/log.c
  12. 7
      src/core/support/slice.c
  13. 1
      src/core/surface/call.c
  14. 13
      src/core/surface/call.h
  15. 23
      src/core/surface/call_log_batch.c
  16. 2
      src/core/transport/chttp2/frame_data.c
  17. 3
      src/core/transport/chttp2/frame_data.h
  18. 6
      src/core/transport/chttp2/hpack_encoder.c
  19. 61
      src/core/transport/chttp2/hpack_parser.c
  20. 2
      src/core/transport/chttp2/hpack_parser.h
  21. 18
      src/core/transport/chttp2/incoming_metadata.c
  22. 2
      src/core/transport/chttp2/incoming_metadata.h
  23. 3
      src/core/transport/chttp2/internal.h
  24. 26
      src/core/transport/chttp2/stream_lists.c
  25. 8
      src/core/tsi/transport_security.c
  26. 6
      src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs
  27. 84
      src/csharp/Grpc.Core/AsyncAuthInterceptor.cs
  28. 8
      src/csharp/Grpc.Core/CallCredentials.cs
  29. 7
      src/csharp/Grpc.Core/Grpc.Core.csproj
  30. 13
      src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
  31. 3
      src/csharp/Grpc.Examples/Grpc.Examples.csproj
  32. 3
      src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
  33. 52
      src/csharp/Grpc.HealthCheck/proto/health.proto
  34. 2
      src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
  35. 7
      src/csharp/ext/grpc_csharp_ext.c
  36. 22
      src/csharp/generate_proto_csharp.sh
  37. 5
      src/node/ext/call.cc
  38. 3
      src/node/health_check/health.js
  39. 49
      src/node/health_check/health.proto
  40. 336
      src/node/performance/benchmark_client.js
  41. 162
      src/node/performance/benchmark_server.js
  42. 180
      src/node/performance/histogram.js
  43. 119
      src/node/performance/perf_test.js
  44. 137
      src/node/performance/qps_test.js
  45. 60
      src/node/performance/worker_server.js
  46. 132
      src/node/performance/worker_service_impl.js
  47. 5
      src/node/src/credentials.js
  48. 139
      src/node/src/server.js
  49. 2
      src/node/test/async_test.js
  50. 80
      src/node/test/math/math.proto
  51. 3
      src/node/test/math/math_server.js
  52. 2
      src/node/test/math_client_test.js
  53. 51
      src/node/test/surface_test.js
  54. 1428
      src/objective-c/BoringSSL.podspec
  55. 43
      src/objective-c/GRPCClient/GRPCCall.h
  56. 2
      src/objective-c/GRPCClient/GRPCCall.m
  57. 13
      src/objective-c/GRPCClient/private/GRPCRequestHeaders.h
  58. 43
      src/objective-c/GRPCClient/private/GRPCRequestHeaders.m
  59. 2
      src/objective-c/GRPCClient/private/GRPCWrappedCall.h
  60. 2
      src/objective-c/GRPCClient/private/GRPCWrappedCall.m
  61. 1
      src/objective-c/examples/Sample/Podfile
  62. 1
      src/objective-c/examples/SwiftSample/Podfile
  63. 1
      src/objective-c/tests/Podfile
  64. 3
      src/php/tests/generated_code/math.proto
  65. 1
      src/proto/grpc/health/v1alpha/health.proto
  66. 0
      src/proto/math/math.proto
  67. 7
      src/python/grpcio/.gitignore
  68. 1
      src/python/grpcio/MANIFEST.in
  69. 127
      src/python/grpcio/commands.py
  70. 286
      src/python/grpcio/grpc/_adapter/_c/types.h
  71. 186
      src/python/grpcio/grpc/_adapter/_c/types/call.c
  72. 203
      src/python/grpcio/grpc/_adapter/_c/types/call_credentials.c
  73. 187
      src/python/grpcio/grpc/_adapter/_c/types/channel.c
  74. 165
      src/python/grpcio/grpc/_adapter/_c/types/channel_credentials.c
  75. 124
      src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c
  76. 196
      src/python/grpcio/grpc/_adapter/_c/types/server.c
  77. 137
      src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c
  78. 524
      src/python/grpcio/grpc/_adapter/_c/utility.c
  79. 33
      src/python/grpcio/grpc/_adapter/_implementations.py
  80. 45
      src/python/grpcio/grpc/_adapter/_intermediary_low.py
  81. 214
      src/python/grpcio/grpc/_adapter/_low.py
  82. 94
      src/python/grpcio/grpc/_adapter/_types.py
  83. 28
      src/python/grpcio/grpc/_cython/_cygrpc/call.pyx
  84. 31
      src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx
  85. 11
      src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx
  86. 23
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd
  87. 82
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx
  88. 59
      src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd
  89. 1
      src/python/grpcio/grpc/_cython/_cygrpc/records.pxd
  90. 82
      src/python/grpcio/grpc/_cython/_cygrpc/records.pyx
  91. 2
      src/python/grpcio/grpc/_cython/_cygrpc/server.pyx
  92. 6
      src/python/grpcio/grpc/_cython/cygrpc.pyx
  93. 12
      src/python/grpcio/grpc/_links/invocation.py
  94. 6
      src/python/grpcio/grpc/_links/service.py
  95. 175
      src/python/grpcio/grpc/beta/_server.py
  96. 124
      src/python/grpcio/grpc/beta/_stub.py
  97. 96
      src/python/grpcio/grpc/beta/implementations.py
  98. 49
      src/python/grpcio/grpc/beta/interfaces.py
  99. 60
      src/python/grpcio/grpc/framework/core/_end.py
  100. 1
      src/python/grpcio/requirements.txt
  101. Some files were not shown because too many files have changed in this diff Show More

File diff suppressed because one or more lines are too long

@ -1335,6 +1335,26 @@ targets:
- mac
- linux
- posix
- name: init_test
build: test
language: c
src:
- test/core/surface/init_test.c
deps:
- grpc_test_util
- grpc
- gpr_test_util
- gpr
- name: invalid_call_argument_test
build: test
language: c
src:
- test/core/end2end/invalid_call_argument_test.c
deps:
- grpc_test_util
- grpc
- gpr_test_util
- gpr
- name: json_rewrite
build: test
run: false
@ -1408,16 +1428,6 @@ targets:
- grpc
- gpr_test_util
- gpr
- name: multi_init_test
build: test
language: c
src:
- test/core/surface/multi_init_test.c
deps:
- grpc_test_util
- grpc
- gpr_test_util
- gpr
- name: multiple_server_queues_test
build: test
language: c

@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'Protobuf', :path => "../../../third_party/protobuf"
pod 'BoringSSL', :podspec => "../../../src/objective-c"
pod 'gRPC', :path => "../../.."
target 'AuthSample' do

@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'Protobuf', :path => "../../../third_party/protobuf"
pod 'BoringSSL', :podspec => "../../../src/objective-c"
pod 'gRPC', :path => "../../.."
target 'HelloWorld' do

@ -3,6 +3,7 @@ platform :ios, '8.0'
target 'RouteGuideClient' do
pod 'Protobuf', :path => "../../../third_party/protobuf"
pod 'BoringSSL', :podspec => "../../../src/objective-c"
pod 'gRPC', :path => "../../.."
# Depend on the generated RouteGuide library.
pod 'RouteGuide', :path => '.'

@ -589,7 +589,7 @@ Pod::Spec.new do |s|
ss.requires_arc = false
ss.libraries = 'z'
ss.dependency 'OpenSSL', '~> 1.0.204.1'
ss.dependency 'BoringSSL', '~> 1.0'
# ss.compiler_flags = '-GCC_WARN_INHIBIT_ALL_WARNINGS', '-w'
end

@ -183,7 +183,7 @@
#endif
#define GPR_MSG_IOVLEN_TYPE int
#if TARGET_OS_IPHONE
#define GPR_FORBID_UNREACHABLE_CODE
#define GPR_FORBID_UNREACHABLE_CODE 1
#define GPR_PLATFORM_STRING "ios"
#define GPR_CPU_IPHONE 1
#define GPR_PTHREAD_TLS 1
@ -252,6 +252,11 @@
#define GPR_PLATFORM_STRING "unknown"
#endif
#ifdef GPR_GCOV
#undef GPR_FORBID_UNREACHABLE_CODE
#define GPR_FORBID_UNREACHABLE_CODE 1
#endif
/* For a common case, assume that the platform has a C99-like stdint.h */
#include <stdint.h>
@ -337,7 +342,7 @@ typedef uintptr_t gpr_uintptr;
#endif
#endif
#ifdef GPR_FORBID_UNREACHABLE_CODE
#if GPR_FORBID_UNREACHABLE_CODE
#define GPR_UNREACHABLE_CODE(STATEMENT)
#else
#define GPR_UNREACHABLE_CODE(STATEMENT) \

@ -39,7 +39,8 @@
"minimist": "^1.1.0",
"mocha": "~1.21.0",
"mocha-jenkins-reporter": "^0.1.9",
"mustache": "^2.0.0"
"mustache": "^2.0.0",
"poisson-process": "^0.2.1"
},
"engines": {
"node": ">=0.10.13"

@ -39,11 +39,7 @@
/* census_context is the in-memory representation of information needed to
* maintain tracing, RPC statistics and resource usage information. */
struct census_context {
gpr_uint64 op_id; /* Operation identifier - unique per-context */
gpr_uint64 trace_id; /* Globally unique trace identifier */
/* TODO(aveitch) Add census tags:
const census_tag_set *tags;
*/
census_tag_set *tags; /* Opaque data structure for census tags. */
};
#endif /* GRPC_INTERNAL_CORE_CENSUS_CONTEXT_H */

@ -36,6 +36,7 @@
#ifdef GPR_POSIX_SOCKET
#include "src/core/iomgr/endpoint_pair.h"
#include "src/core/iomgr/socket_utils_posix.h"
#include <errno.h>
#include <fcntl.h>
@ -56,6 +57,8 @@ static void create_sockets(int sv[2]) {
GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
flags = fcntl(sv[1], F_GETFL, 0);
GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]));
GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]));
}
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name,

@ -32,6 +32,7 @@
*/
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#include <stdio.h>
#include <string.h>
@ -48,7 +49,7 @@ const char *gpr_log_severity_string(gpr_log_severity severity) {
case GPR_LOG_SEVERITY_ERROR:
return "E";
}
return "UNKNOWN";
GPR_UNREACHABLE_CODE(return "UNKNOWN");
}
void gpr_log_message(const char *file, int line, gpr_log_severity severity,

@ -341,10 +341,3 @@ int gpr_slice_str_cmp(gpr_slice a, const char *b) {
if (d != 0) return d;
return memcmp(GPR_SLICE_START_PTR(a), b, b_length);
}
char *gpr_slice_to_cstring(gpr_slice slice) {
char *result = gpr_malloc(GPR_SLICE_LENGTH(slice) + 1);
memcpy(result, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice));
result[GPR_SLICE_LENGTH(slice)] = '\0';
return result;
}

@ -1270,6 +1270,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
}
if (call->receiving_message) {
error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
goto done_with_error;
}
call->receiving_message = 1;
bctl->recv_message = 1;

@ -91,19 +91,6 @@ void grpc_call_log_batch(char *file, int line, gpr_log_severity severity,
grpc_call *call, const grpc_op *ops, size_t nops,
void *tag);
void grpc_server_log_request_call(char *file, int line,
gpr_log_severity severity,
grpc_server *server, grpc_call **call,
grpc_call_details *details,
grpc_metadata_array *initial_metadata,
grpc_completion_queue *cq_bound_to_call,
grpc_completion_queue *cq_for_notification,
void *tag);
void grpc_server_log_shutdown(char *file, int line, gpr_log_severity severity,
grpc_server *server, grpc_completion_queue *cq,
void *tag);
/* Set a context pointer.
No thread safety guarantees are made wrt this value. */
void grpc_call_context_set(grpc_call *call, grpc_context_index elem,

@ -117,26 +117,3 @@ void grpc_call_log_batch(char *file, int line, gpr_log_severity severity,
}
}
void grpc_server_log_request_call(char *file, int line,
gpr_log_severity severity,
grpc_server *server, grpc_call **call,
grpc_call_details *details,
grpc_metadata_array *initial_metadata,
grpc_completion_queue *cq_bound_to_call,
grpc_completion_queue *cq_for_notification,
void *tag) {
gpr_log(file, line, severity,
"grpc_server_request_call(server=%p, call=%p, details=%p, "
"initial_metadata=%p, cq_bound_to_call=%p, cq_for_notification=%p, "
"tag=%p)",
server, call, details, initial_metadata, cq_bound_to_call,
cq_for_notification, tag);
}
void grpc_server_log_shutdown(char *file, int line, gpr_log_severity severity,
grpc_server *server, grpc_completion_queue *cq,
void *tag) {
gpr_log(file, line, severity,
"grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", server,
cq, tag);
}

@ -118,7 +118,7 @@ void grpc_chttp2_encode_data(gpr_uint32 id, gpr_slice_buffer *inbuf,
hdr = gpr_slice_malloc(9);
p = GPR_SLICE_START_PTR(hdr);
GPR_ASSERT(write_bytes < 16777316);
GPR_ASSERT(write_bytes < (1<<24));
*p++ = (gpr_uint8)(write_bytes >> 16);
*p++ = (gpr_uint8)(write_bytes >> 8);
*p++ = (gpr_uint8)(write_bytes);

@ -94,9 +94,6 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
grpc_chttp2_transport_parsing *transport_parsing,
grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
/* create a slice with an empty data frame and is_last set */
gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id);
void grpc_chttp2_encode_data(gpr_uint32 id, gpr_slice_buffer *inbuf,
gpr_uint32 write_bytes, int is_eof,
gpr_slice_buffer *outbuf);

@ -458,12 +458,6 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
GRPC_MDELEM_UNREF(mdelem);
}
gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) {
gpr_slice slice = gpr_slice_malloc(9);
fill_header(GPR_SLICE_START_PTR(slice), GRPC_CHTTP2_FRAME_DATA, id, 0, 1);
return slice;
}
static gpr_uint32 elems_for_bytes(gpr_uint32 bytes) {
return (bytes + 31) / 32;
}

@ -728,6 +728,7 @@ static int finish_indexed_field(grpc_chttp2_hpack_parser *p,
/* parse an indexed field with index < 127 */
static int parse_indexed_field(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
p->dynamic_table_update_allowed = 0;
p->index = (*cur) & 0x7f;
return finish_indexed_field(p, cur + 1, end);
}
@ -737,6 +738,7 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
static const grpc_chttp2_hpack_parser_state and_then[] = {
finish_indexed_field};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
p->index = 0x7f;
p->parsing.value = &p->index;
@ -748,6 +750,7 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
GPR_ASSERT(md != NULL); /* handled in string parsing */
return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
1) &&
@ -768,6 +771,7 @@ static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_value_string_with_indexed_key, finish_lithdr_incidx};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
p->index = (*cur) & 0x3f;
return parse_string_prefix(p, cur + 1, end);
@ -779,6 +783,7 @@ static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_string_prefix, parse_value_string_with_indexed_key,
finish_lithdr_incidx};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
p->index = 0x3f;
p->parsing.value = &p->index;
@ -791,6 +796,7 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_key_string, parse_string_prefix,
parse_value_string_with_literal_key, finish_lithdr_incidx_v};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
return parse_string_prefix(p, cur + 1, end);
}
@ -799,6 +805,7 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
GPR_ASSERT(md != NULL); /* handled in string parsing */
return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0) &&
@ -819,6 +826,7 @@ static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_value_string_with_indexed_key, finish_lithdr_notidx};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
p->index = (*cur) & 0xf;
return parse_string_prefix(p, cur + 1, end);
@ -830,6 +838,7 @@ static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_string_prefix, parse_value_string_with_indexed_key,
finish_lithdr_notidx};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
p->index = 0xf;
p->parsing.value = &p->index;
@ -842,6 +851,7 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_key_string, parse_string_prefix,
parse_value_string_with_literal_key, finish_lithdr_notidx_v};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
return parse_string_prefix(p, cur + 1, end);
}
@ -850,6 +860,7 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
GPR_ASSERT(md != NULL); /* handled in string parsing */
return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0) &&
@ -870,6 +881,7 @@ static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_value_string_with_indexed_key, finish_lithdr_nvridx};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
p->index = (*cur) & 0xf;
return parse_string_prefix(p, cur + 1, end);
@ -881,6 +893,7 @@ static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_string_prefix, parse_value_string_with_indexed_key,
finish_lithdr_nvridx};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
p->index = 0xf;
p->parsing.value = &p->index;
@ -893,6 +906,7 @@ static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = {
parse_key_string, parse_string_prefix,
parse_value_string_with_literal_key, finish_lithdr_nvridx_v};
p->dynamic_table_update_allowed = 0;
p->next_state = and_then;
return parse_string_prefix(p, cur + 1, end);
}
@ -908,6 +922,10 @@ static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p,
/* parse a max table size change, max size < 15 */
static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end) {
if (p->dynamic_table_update_allowed == 0) {
return 0;
}
p->dynamic_table_update_allowed--;
p->index = (*cur) & 0x1f;
return finish_max_tbl_size(p, cur + 1, end);
}
@ -917,6 +935,10 @@ static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
static const grpc_chttp2_hpack_parser_state and_then[] = {
finish_max_tbl_size};
if (p->dynamic_table_update_allowed == 0) {
return 0;
}
p->dynamic_table_update_allowed--;
p->next_state = and_then;
p->index = 0x1f;
p->parsing.value = &p->index;
@ -1300,7 +1322,10 @@ static is_binary_header is_binary_literal_header(grpc_chttp2_hpack_parser *p) {
static is_binary_header is_binary_indexed_header(grpc_chttp2_hpack_parser *p) {
grpc_mdelem *elem = grpc_chttp2_hptbl_lookup(&p->table, p->index);
if (!elem) return ERROR_HEADER;
if (!elem) {
gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index);
return ERROR_HEADER;
}
return grpc_is_binary_header(
(const char *)GPR_SLICE_START_PTR(elem->key->slice),
GPR_SLICE_LENGTH(elem->key->slice))
@ -1338,15 +1363,7 @@ static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p,
/* PUBLIC INTERFACE */
static void on_header_not_set(void *user_data, grpc_mdelem *md) {
char *keyhex = gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
char *valuehex =
gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex,
valuehex);
gpr_free(keyhex);
gpr_free(valuehex);
GRPC_MDELEM_UNREF(md);
abort();
GPR_UNREACHABLE_CODE(return );
}
void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) {
@ -1359,6 +1376,7 @@ void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) {
p->value.str = NULL;
p->value.capacity = 0;
p->value.length = 0;
p->dynamic_table_update_allowed = 2;
grpc_chttp2_hptbl_init(&p->table);
}
@ -1400,20 +1418,25 @@ grpc_chttp2_parse_error grpc_chttp2_header_parser_parse(
GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
return GRPC_CHTTP2_CONNECTION_ERROR;
}
if (parser->is_boundary) {
stream_parsing
->got_metadata_on_parse[stream_parsing->header_frames_received] = 1;
stream_parsing->header_frames_received++;
grpc_chttp2_list_add_parsing_seen_stream(transport_parsing,
stream_parsing);
}
if (parser->is_eof) {
stream_parsing->received_close = 1;
/* need to check for null stream: this can occur if we receive an invalid
stream id on a header */
if (stream_parsing != NULL) {
if (parser->is_boundary) {
stream_parsing
->got_metadata_on_parse[stream_parsing->header_frames_received] = 1;
stream_parsing->header_frames_received++;
grpc_chttp2_list_add_parsing_seen_stream(transport_parsing,
stream_parsing);
}
if (parser->is_eof) {
stream_parsing->received_close = 1;
}
}
parser->on_header = on_header_not_set;
parser->on_header_user_data = NULL;
parser->is_boundary = 0xde;
parser->is_eof = 0xde;
parser->dynamic_table_update_allowed = 2;
}
GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
return GRPC_CHTTP2_PARSE_OK;

@ -85,6 +85,8 @@ struct grpc_chttp2_hpack_parser {
gpr_uint8 binary;
/* is the current string huffman encoded? */
gpr_uint8 huff;
/* is a dynamic table update allowed? */
gpr_uint8 dynamic_table_update_allowed;
/* set by higher layers, used by grpc_chttp2_header_parser_parse to signal
it should append a metadata boundary at the end of frame */
gpr_uint8 is_boundary;

@ -56,16 +56,6 @@ void grpc_chttp2_incoming_metadata_buffer_destroy(
gpr_free(buffer->elems);
}
void grpc_chttp2_incoming_metadata_buffer_reset(
grpc_chttp2_incoming_metadata_buffer *buffer) {
size_t i;
GPR_ASSERT(!buffer->published);
for (i = 0; i < buffer->count; i++) {
GRPC_MDELEM_UNREF(buffer->elems[i].md);
}
buffer->count = 0;
}
void grpc_chttp2_incoming_metadata_buffer_add(
grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem *elem) {
GPR_ASSERT(!buffer->published);
@ -83,14 +73,6 @@ void grpc_chttp2_incoming_metadata_buffer_set_deadline(
buffer->deadline = deadline;
}
void grpc_chttp2_incoming_metadata_buffer_swap(
grpc_chttp2_incoming_metadata_buffer *a,
grpc_chttp2_incoming_metadata_buffer *b) {
GPR_ASSERT(!a->published);
GPR_ASSERT(!b->published);
GPR_SWAP(grpc_chttp2_incoming_metadata_buffer, *a, *b);
}
void grpc_chttp2_incoming_metadata_buffer_publish(
grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch) {
GPR_ASSERT(!buffer->published);

@ -49,8 +49,6 @@ void grpc_chttp2_incoming_metadata_buffer_init(
grpc_chttp2_incoming_metadata_buffer *buffer);
void grpc_chttp2_incoming_metadata_buffer_destroy(
grpc_chttp2_incoming_metadata_buffer *buffer);
void grpc_chttp2_incoming_metadata_buffer_reset(
grpc_chttp2_incoming_metadata_buffer *buffer);
void grpc_chttp2_incoming_metadata_buffer_publish(
grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch);

@ -512,9 +512,6 @@ void grpc_chttp2_publish_reads(grpc_exec_ctx *exec_ctx,
void grpc_chttp2_list_add_writable_stream(
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global);
void grpc_chttp2_list_add_first_writable_stream(
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global);
int grpc_chttp2_list_pop_writable_stream(
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_transport_writing *transport_writing,

@ -108,23 +108,6 @@ static void stream_list_maybe_remove(grpc_chttp2_transport *t,
}
}
static void stream_list_add_head(grpc_chttp2_transport *t,
grpc_chttp2_stream *s,
grpc_chttp2_stream_list_id id) {
grpc_chttp2_stream *old_head;
GPR_ASSERT(!s->included[id]);
old_head = t->lists[id].head;
s->links[id].next = old_head;
s->links[id].prev = NULL;
if (old_head) {
old_head->links[id].prev = s;
} else {
t->lists[id].tail = s;
}
t->lists[id].head = s;
s->included[id] = 1;
}
static void stream_list_add_tail(grpc_chttp2_transport *t,
grpc_chttp2_stream *s,
grpc_chttp2_stream_list_id id) {
@ -161,15 +144,6 @@ void grpc_chttp2_list_add_writable_stream(
STREAM_FROM_GLOBAL(stream_global), GRPC_CHTTP2_LIST_WRITABLE);
}
void grpc_chttp2_list_add_first_writable_stream(
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global) {
GPR_ASSERT(stream_global->id != 0);
stream_list_add_head(TRANSPORT_FROM_GLOBAL(transport_global),
STREAM_FROM_GLOBAL(stream_global),
GRPC_CHTTP2_LIST_WRITABLE);
}
int grpc_chttp2_list_pop_writable_stream(
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_transport_writing *transport_writing,

@ -145,7 +145,9 @@ void tsi_frame_protector_destroy(tsi_frame_protector *self) {
tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self,
unsigned char *bytes,
size_t *bytes_size) {
if (self == NULL) return TSI_INVALID_ARGUMENT;
if (self == NULL || bytes == NULL || bytes_size == NULL) {
return TSI_INVALID_ARGUMENT;
}
if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size);
}
@ -153,7 +155,9 @@ tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self,
tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self,
const unsigned char *bytes,
size_t *bytes_size) {
if (self == NULL) return TSI_INVALID_ARGUMENT;
if (self == NULL || bytes == NULL || bytes_size == NULL) {
return TSI_INVALID_ARGUMENT;
}
if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
return self->vtable->process_bytes_from_peer(self, bytes, bytes_size);
}

@ -57,9 +57,9 @@ namespace Grpc.Auth
/// <returns>The interceptor.</returns>
public static AsyncAuthInterceptor FromCredential(ITokenAccess credential)
{
return new AsyncAuthInterceptor(async (authUri, metadata) =>
return new AsyncAuthInterceptor(async (context, metadata) =>
{
var accessToken = await credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None).ConfigureAwait(false);
var accessToken = await credential.GetAccessTokenForRequestAsync(context.ServiceUrl, CancellationToken.None).ConfigureAwait(false);
metadata.Add(CreateBearerTokenHeader(accessToken));
});
}
@ -72,7 +72,7 @@ namespace Grpc.Auth
public static AsyncAuthInterceptor FromAccessToken(string accessToken)
{
Preconditions.CheckNotNull(accessToken);
return new AsyncAuthInterceptor(async (authUri, metadata) =>
return new AsyncAuthInterceptor(async (context, metadata) =>
{
metadata.Add(CreateBearerTokenHeader(accessToken));
});

@ -0,0 +1,84 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Asynchronous authentication interceptor for <see cref="CallCredentials"/>.
/// </summary>
/// <param name="context">The interceptor context.</param>
/// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param>
/// <returns></returns>
public delegate Task AsyncAuthInterceptor(AuthInterceptorContext context, Metadata metadata);
/// <summary>
/// Context for an RPC being intercepted by <see cref="AsyncAuthInterceptor"/>.
/// </summary>
public class AuthInterceptorContext
{
readonly string serviceUrl;
readonly string methodName;
/// <summary>
/// Initializes a new instance of <c>AuthInterceptorContext</c>.
/// </summary>
public AuthInterceptorContext(string serviceUrl, string methodName)
{
this.serviceUrl = Preconditions.CheckNotNull(serviceUrl);
this.methodName = Preconditions.CheckNotNull(methodName);
}
/// <summary>
/// The fully qualified service URL for the RPC being called.
/// </summary>
public string ServiceUrl
{
get { return serviceUrl; }
}
/// <summary>
/// The method name of the RPC being called.
/// </summary>
public string MethodName
{
get { return methodName; }
}
}
}

@ -40,14 +40,6 @@ using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Asynchronous authentication interceptor for <see cref="CallCredentials"/>.
/// </summary>
/// <param name="authUri">URL of a service to which current remote call needs to authenticate</param>
/// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param>
/// <returns></returns>
public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata);
/// <summary>
/// Client-side call credentials. Provide authorization with per-call granularity.
/// </summary>

@ -46,6 +46,7 @@
<ItemGroup>
<Compile Include="AsyncDuplexStreamingCall.cs" />
<Compile Include="AsyncServerStreamingCall.cs" />
<Compile Include="AsyncAuthInterceptor.cs" />
<Compile Include="CallCredentials.cs" />
<Compile Include="IClientStreamWriter.cs" />
<Compile Include="Internal\NativeMetadataCredentialsPlugin.cs" />
@ -148,8 +149,8 @@
<Error Condition="!Exists('..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets'))" />
<Error Condition="!Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets'))" />
</Target>
<ItemGroup />
<ItemGroup />
<Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets')" />
<Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets')" />
</Project>
<ItemGroup />
<ItemGroup />
</Project>

@ -38,7 +38,7 @@ using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy);
internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy);
internal class NativeMetadataCredentialsPlugin
{
@ -71,7 +71,7 @@ namespace Grpc.Core.Internal
get { return credentials; }
}
private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy)
private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy)
{
if (isDestroy)
{
@ -81,8 +81,9 @@ namespace Grpc.Core.Internal
try
{
string serviceUrl = Marshal.PtrToStringAnsi(serviceUrlPtr);
StartGetMetadata(serviceUrl, callbackPtr, userDataPtr);
var context = new AuthInterceptorContext(Marshal.PtrToStringAnsi(serviceUrlPtr),
Marshal.PtrToStringAnsi(methodNamePtr));
StartGetMetadata(context, callbackPtr, userDataPtr);
}
catch (Exception e)
{
@ -91,12 +92,12 @@ namespace Grpc.Core.Internal
}
}
private async void StartGetMetadata(string serviceUrl, IntPtr callbackPtr, IntPtr userDataPtr)
private async void StartGetMetadata(AuthInterceptorContext context, IntPtr callbackPtr, IntPtr userDataPtr)
{
try
{
var metadata = new Metadata();
await interceptor(serviceUrl, metadata).ConfigureAwait(false);
await interceptor(context, metadata).ConfigureAwait(false);
using (var metadataArray = MetadataArraySafeHandle.Create(metadata))
{

@ -66,6 +66,5 @@
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="proto\math.proto" />
</ItemGroup>
</Project>
</Project>

@ -65,7 +65,6 @@
<ItemGroup>
<None Include="Grpc.HealthCheck.nuspec" />
<None Include="packages.config" />
<None Include="proto\health.proto" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj">
@ -81,4 +80,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

@ -1,52 +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.
// TODO(jtattermusch): switch to proto3 once C# supports that.
syntax = "proto3";
package grpc.health.v1alpha;
option csharp_namespace = "Grpc.Health.V1Alpha";
message HealthCheckRequest {
string host = 1;
string service = 2;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}

@ -67,7 +67,7 @@ namespace Grpc.IntegrationTesting
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
var asyncAuthInterceptor = new AsyncAuthInterceptor(async (authUri, metadata) =>
var asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) =>
{
await Task.Delay(100); // make sure the operation is asynchronous.
metadata.Add("authorization", "SECRET_TOKEN");

@ -927,7 +927,8 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin(
}
typedef void(GPR_CALLTYPE *grpcsharp_metadata_interceptor_func)(
void *state, const char *service_url, grpc_credentials_plugin_metadata_cb cb,
void *state, const char *service_url, const char *method_name,
grpc_credentials_plugin_metadata_cb cb,
void *user_data, gpr_int32 is_destroy);
static void grpcsharp_get_metadata_handler(
@ -935,13 +936,13 @@ static void grpcsharp_get_metadata_handler(
grpc_credentials_plugin_metadata_cb cb, void *user_data) {
grpcsharp_metadata_interceptor_func interceptor =
(grpcsharp_metadata_interceptor_func)(gpr_intptr)state;
interceptor(state, context.service_url, cb, user_data, 0);
interceptor(state, context.service_url, context.method_name, cb, user_data, 0);
}
static void grpcsharp_metadata_credentials_destroy_handler(void *state) {
grpcsharp_metadata_interceptor_func interceptor =
(grpcsharp_metadata_interceptor_func)(gpr_intptr)state;
interceptor(state, NULL, NULL, NULL, 1);
interceptor(state, NULL, NULL, NULL, NULL, 1);
}
GPR_EXPORT grpc_call_credentials *GPR_CALLTYPE grpcsharp_metadata_credentials_create_from_plugin(

@ -30,19 +30,19 @@
# Regenerates gRPC service stubs from proto files.
set +e
cd $(dirname $0)
cd $(dirname $0)/../..
PROTOC=../../bins/opt/protobuf/protoc
PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin
EXAMPLES_DIR=Grpc.Examples
TESTING_DIR=Grpc.IntegrationTesting
HEALTHCHECK_DIR=Grpc.HealthCheck
PROTOC=bins/opt/protobuf/protoc
PLUGIN=protoc-gen-grpc=bins/opt/grpc_csharp_plugin
EXAMPLES_DIR=src/csharp/Grpc.Examples
HEALTHCHECK_DIR=src/csharp/Grpc.HealthCheck
TESTING_DIR=src/csharp/Grpc.IntegrationTesting
$PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \
-I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \
-I ../.. ../../test/proto/*.proto ../../test/proto/benchmarks/*.proto
-I src/proto/math src/proto/math/math.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \
-I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto
-I src/proto/grpc/health/v1alpha src/proto/grpc/health/v1alpha/health.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \
-I . test/proto/{empty,messages,test}.proto test/proto/benchmarks/*.proto

@ -234,7 +234,9 @@ class SendMetadataOp : public Op {
class SendMessageOp : public Op {
public:
SendMessageOp() { send_message = NULL; }
SendMessageOp() {
send_message = NULL;
}
~SendMessageOp() {
if (send_message != NULL) {
grpc_byte_buffer_destroy(send_message);
@ -269,7 +271,6 @@ class SendMessageOp : public Op {
std::string GetTypeString() const {
return "send_message";
}
private:
grpc_byte_buffer *send_message;
};

@ -37,7 +37,8 @@ var grpc = require('../');
var _ = require('lodash');
var health_proto = grpc.load(__dirname + '/health.proto');
var health_proto = grpc.load(__dirname +
'/../../proto/grpc/health/v1alpha/health.proto');
var HealthClient = health_proto.grpc.health.v1alpha.Health;

@ -1,49 +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.
syntax = "proto3";
package grpc.health.v1alpha;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}

@ -0,0 +1,336 @@
/*
*
* 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.
*
*/
/**
* Benchmark client module
* @module
*/
'use strict';
var fs = require('fs');
var path = require('path');
var util = require('util');
var EventEmitter = require('events');
var _ = require('lodash');
var PoissonProcess = require('poisson-process');
var Histogram = require('./histogram');
var grpc = require('../../../');
var serviceProto = grpc.load({
root: __dirname + '/../../..',
file: 'test/proto/benchmarks/services.proto'}).grpc.testing;
/**
* Create a buffer filled with size zeroes
* @param {number} size The length of the buffer
* @return {Buffer} The new buffer
*/
function zeroBuffer(size) {
var zeros = new Buffer(size);
zeros.fill(0);
return zeros;
}
/**
* Convert a time difference, as returned by process.hrtime, to a number of
* nanoseconds.
* @param {Array.<number>} time_diff The time diff, represented as
* [seconds, nanoseconds]
* @return {number} The total number of nanoseconds
*/
function timeDiffToNanos(time_diff) {
return time_diff[0] * 1e9 + time_diff[1];
}
/**
* The BenchmarkClient class. Opens channels to servers and makes RPCs based on
* parameters from the driver, and records statistics about those RPCs.
* @param {Array.<string>} server_targets List of servers to connect to
* @param {number} channels The total number of channels to open
* @param {Object} histogram_params Options for setting up the histogram
* @param {Object=} security_params Options for TLS setup. If absent, don't use
* TLS
*/
function BenchmarkClient(server_targets, channels, histogram_params,
security_params) {
var options = {};
var creds;
if (security_params) {
var ca_path;
if (security_params.use_test_ca) {
ca_path = path.join(__dirname, '../test/data/ca.pem');
var ca_data = fs.readFileSync(ca_path);
creds = grpc.credentials.createSsl(ca_data);
} else {
creds = grpc.credentials.createSsl();
}
if (security_params.server_host_override) {
var host_override = security_params.server_host_override;
options['grpc.ssl_target_name_override'] = host_override;
options['grpc.default_authority'] = host_override;
}
} else {
creds = grpc.credentials.createInsecure();
}
this.clients = [];
for (var i = 0; i < channels; i++) {
this.clients[i] = new serviceProto.BenchmarkService(
server_targets[i % server_targets.length], creds, options);
}
this.histogram = new Histogram(histogram_params.resolution,
histogram_params.max_possible);
this.running = false;
this.pending_calls = 0;
};
util.inherits(BenchmarkClient, EventEmitter);
/**
* Start a closed-loop test. For each channel, start
* outstanding_rpcs_per_channel RPCs. Then, whenever an RPC finishes, start
* another one.
* @param {number} outstanding_rpcs_per_channel Number of RPCs to start per
* channel
* @param {string} rpc_type Which method to call. Should be 'UNARY' or
* 'STREAMING'
* @param {number} req_size The size of the payload to send with each request
* @param {number} resp_size The size of payload to request be sent in responses
*/
BenchmarkClient.prototype.startClosedLoop = function(
outstanding_rpcs_per_channel, rpc_type, req_size, resp_size) {
var self = this;
self.running = true;
self.last_wall_time = process.hrtime();
var makeCall;
var argument = {
response_size: resp_size,
payload: {
body: zeroBuffer(req_size)
}
};
if (rpc_type == 'UNARY') {
makeCall = function(client) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
client.unaryCall(argument, function(error, response) {
if (error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
return;
}
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
makeCall(client);
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
}
};
} else {
makeCall = function(client) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
var call = client.streamingCall();
call.write(argument);
call.on('data', function() {
});
call.on('end', function() {
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
makeCall(client);
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
call.on('error', function(error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
});
}
};
}
_.each(self.clients, function(client) {
_.times(outstanding_rpcs_per_channel, function() {
makeCall(client);
});
});
};
/**
* Start a poisson test. For each channel, this initiates a number of Poisson
* processes equal to outstanding_rpcs_per_channel, where each Poisson process
* has the load parameter offered_load.
* @param {number} outstanding_rpcs_per_channel Number of RPCs to start per
* channel
* @param {string} rpc_type Which method to call. Should be 'UNARY' or
* 'STREAMING'
* @param {number} req_size The size of the payload to send with each request
* @param {number} resp_size The size of payload to request be sent in responses
* @param {number} offered_load The load parameter for the Poisson process
*/
BenchmarkClient.prototype.startPoisson = function(
outstanding_rpcs_per_channel, rpc_type, req_size, resp_size, offered_load) {
var self = this;
self.running = true;
self.last_wall_time = process.hrtime();
var makeCall;
var argument = {
response_size: resp_size,
payload: {
body: zeroBuffer(req_size)
}
};
if (rpc_type == 'UNARY') {
makeCall = function(client, poisson) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
client.unaryCall(argument, function(error, response) {
if (error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
return;
}
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
} else {
poisson.stop();
}
};
} else {
makeCall = function(client, poisson) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
var call = client.streamingCall();
call.write(argument);
call.on('data', function() {
});
call.on('end', function() {
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
call.on('error', function(error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
});
} else {
poisson.stop();
}
};
}
var averageIntervalMs = (1 / offered_load) * 1000;
_.each(self.clients, function(client) {
_.times(outstanding_rpcs_per_channel, function() {
var p = PoissonProcess.create(averageIntervalMs, function() {
makeCall(client, p);
});
p.start();
});
});
};
/**
* Return curent statistics for the client. If reset is set, restart
* statistic collection.
* @param {boolean} reset Indicates that statistics should be reset
* @return {object} Client statistics
*/
BenchmarkClient.prototype.mark = function(reset) {
var wall_time_diff = process.hrtime(this.last_wall_time);
var histogram = this.histogram;
if (reset) {
this.last_wall_time = process.hrtime();
this.histogram = new Histogram(histogram.resolution,
histogram.max_possible);
}
return {
latencies: {
bucket: histogram.getContents(),
min_seen: histogram.minimum(),
max_seen: histogram.maximum(),
sum: histogram.getSum(),
sum_of_squares: histogram.sumOfSquares(),
count: histogram.getCount()
},
time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
// Not sure how to measure these values
time_user: 0,
time_system: 0
};
};
/**
* Stop the clients.
* @param {function} callback Called when the clients have finished shutting
* down
*/
BenchmarkClient.prototype.stop = function(callback) {
this.running = false;
this.on('finished', callback);
};
module.exports = BenchmarkClient;

@ -0,0 +1,162 @@
/*
*
* 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.
*
*/
/**
* Benchmark server module
* @module
*/
'use strict';
var fs = require('fs');
var path = require('path');
var grpc = require('../../../');
var serviceProto = grpc.load({
root: __dirname + '/../../..',
file: 'test/proto/benchmarks/services.proto'}).grpc.testing;
/**
* Create a buffer filled with size zeroes
* @param {number} size The length of the buffer
* @return {Buffer} The new buffer
*/
function zeroBuffer(size) {
var zeros = new Buffer(size);
zeros.fill(0);
return zeros;
}
/**
* Handler for the unary benchmark method. Simply responds with a payload
* containing the requested number of zero bytes.
* @param {Call} call The call object to be handled
* @param {function} callback The callback to call with the response
*/
function unaryCall(call, callback) {
var req = call.request;
var payload = {body: zeroBuffer(req.response_size)};
callback(null, {payload: payload});
}
/**
* Handler for the streaming benchmark method. Simply responds to each request
* with a payload containing the requested number of zero bytes.
* @param {Call} call The call object to be handled
*/
function streamingCall(call) {
call.on('data', function(value) {
var payload = {body: zeroBuffer(value.repsonse_size)};
call.write({payload: payload});
});
call.on('end', function() {
call.end();
});
}
/**
* BenchmarkServer class. Constructed based on parameters from the driver and
* stores statistics.
* @param {string} host The host to serve on
* @param {number} port The port to listen to
* @param {tls} Indicates whether TLS should be used
*/
function BenchmarkServer(host, port, tls) {
var server_creds;
var host_override;
if (tls) {
var key_path = path.join(__dirname, '../test/data/server1.key');
var pem_path = path.join(__dirname, '../test/data/server1.pem');
var key_data = fs.readFileSync(key_path);
var pem_data = fs.readFileSync(pem_path);
server_creds = grpc.ServerCredentials.createSsl(null,
[{private_key: key_data,
cert_chain: pem_data}]);
} else {
server_creds = grpc.ServerCredentials.createInsecure();
}
var server = new grpc.Server();
this.port = server.bind(host + ':' + port, server_creds);
server.addProtoService(serviceProto.BenchmarkService.service, {
unaryCall: unaryCall,
streamingCall: streamingCall
});
this.server = server;
}
/**
* Start the benchmark server.
*/
BenchmarkServer.prototype.start = function() {
this.server.start();
this.last_wall_time = process.hrtime();
};
/**
* Return the port number that the server is bound to.
* @return {Number} The port number
*/
BenchmarkServer.prototype.getPort = function() {
return this.port;
};
/**
* Return current statistics for the server. If reset is set, restart
* statistic collection.
* @param {boolean} reset Indicates that statistics should be reset
* @return {object} Server statistics
*/
BenchmarkServer.prototype.mark = function(reset) {
var wall_time_diff = process.hrtime(this.last_wall_time);
if (reset) {
this.last_wall_time = process.hrtime();
}
return {
time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
// Not sure how to measure these values
time_user: 0,
time_system: 0
};
};
/**
* Stop the server.
* @param {function} callback Called when the server has finished shutting down
*/
BenchmarkServer.prototype.stop = function(callback) {
this.server.tryShutdown(callback);
};
module.exports = BenchmarkServer;

@ -0,0 +1,180 @@
/*
*
* 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.
*
*/
/**
* Histogram module. Exports the Histogram class
* @module
*/
'use strict';
/**
* Histogram class. Collects data and exposes a histogram and other statistics.
* This data structure is taken directly from src/core/support/histogram.c, but
* pared down to the statistics needed for client stats in
* test/proto/benchmarks/stats.proto.
* @constructor
* @param {number} resolution The histogram's bucket resolution. Must be positive
* @param {number} max_possible The maximum allowed value. Must be greater than 1
*/
function Histogram(resolution, max_possible) {
this.resolution = resolution;
this.max_possible = max_possible;
this.sum = 0;
this.sum_of_squares = 0;
this.multiplier = 1 + resolution;
this.count = 0;
this.min_seen = max_possible;
this.max_seen = 0;
this.buckets = [];
for (var i = 0; i < this.bucketFor(max_possible) + 1; i++) {
this.buckets[i] = 0;
}
}
/**
* Get the bucket index for a given value.
* @param {number} value The value to check
* @return {number} The bucket index
*/
Histogram.prototype.bucketFor = function(value) {
return Math.floor(Math.log(value) / Math.log(this.multiplier));
};
/**
* Get the minimum value for a given bucket index
* @param {number} The bucket index to check
* @return {number} The minimum value for that bucket
*/
Histogram.prototype.bucketStart = function(index) {
return Math.pow(this.multiplier, index);
};
/**
* Add a value to the histogram. This updates all statistics with the new
* value. Those statistics should not be modified except with this function
* @param {number} value The value to add
*/
Histogram.prototype.add = function(value) {
// Ensure value is a number
value = +value;
this.sum += value;
this.sum_of_squares += value * value;
this.count++;
if (value < this.min_seen) {
this.min_seen = value;
}
if (value > this.max_seen) {
this.max_seen = value;
}
this.buckets[this.bucketFor(value)]++;
};
/**
* Get the mean of all added values
* @return {number} The mean
*/
Histogram.prototype.mean = function() {
return this.sum / this.count;
};
/**
* Get the variance of all added values. Used to calulate the standard deviation
* @return {number} The variance
*/
Histogram.prototype.variance = function() {
if (this.count == 0) {
return 0;
}
return (this.sum_of_squares * this.count - this.sum * this.sum) /
(this.count * this.count);
};
/**
* Get the standard deviation of all added values
* @return {number} The standard deviation
*/
Histogram.prototype.stddev = function() {
return Math.sqrt(this.variance);
};
/**
* Get the maximum among all added values
* @return {number} The maximum
*/
Histogram.prototype.maximum = function() {
return this.max_seen;
};
/**
* Get the minimum among all added values
* @return {number} The minimum
*/
Histogram.prototype.minimum = function() {
return this.min_seen;
};
/**
* Get the number of all added values
* @return {number} The count
*/
Histogram.prototype.getCount = function() {
return this.count;
};
/**
* Get the sum of all added values
* @return {number} The sum
*/
Histogram.prototype.getSum = function() {
return this.sum;
};
/**
* Get the sum of squares of all added values
* @return {number} The sum of squares
*/
Histogram.prototype.sumOfSquares = function() {
return this.sum_of_squares;
};
/**
* Get the raw histogram as a list of bucket sizes
* @return {Array.<number>} The buckets
*/
Histogram.prototype.getContents = function() {
return this.buckets;
};
module.exports = Histogram;

@ -1,119 +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.
*
*/
'use strict';
var grpc = require('..');
var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing;
var _ = require('lodash');
var interop_server = require('../interop/interop_server.js');
function runTest(iterations, callback) {
var testServer = interop_server.getServer(0, false);
testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port,
grpc.credentials.createInsecure());
function runIterations(finish) {
var start = process.hrtime();
var intervals = [];
function next(i) {
if (i >= iterations) {
testServer.server.shutdown();
var totalDiff = process.hrtime(start);
finish({
total: totalDiff[0] * 1000000 + totalDiff[1] / 1000,
intervals: intervals
});
} else{
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var startTime = process.hrtime();
client.emptyCall({}, function(err, resp) {
var timeDiff = process.hrtime(startTime);
intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000;
next(i+1);
}, {}, {deadline: deadline});
}
}
next(0);
}
function warmUp(num) {
var pending = num;
function startCall() {
client.emptyCall({}, function(err, resp) {
pending--;
if (pending === 0) {
runIterations(callback);
}
});
}
for (var i = 0; i < num; i++) {
startCall();
}
}
warmUp(100);
}
function percentile(arr, pct) {
if (pct > 99) {
pct = 99;
}
if (pct < 0) {
pct = 0;
}
var index = Math.floor(arr.length * pct / 100);
return arr[index];
}
if (require.main === module) {
var count;
if (process.argv.length >= 3) {
count = process.argv[2];
} else {
count = 100;
}
runTest(count, function(results) {
var sorted_intervals = _.sortBy(results.intervals, _.identity);
console.log('count:', count);
console.log('total time:', results.total, 'us');
console.log('median:', percentile(sorted_intervals, 50), 'us');
console.log('90th percentile:', percentile(sorted_intervals, 90), 'us');
console.log('95th percentile:', percentile(sorted_intervals, 95), 'us');
console.log('99th percentile:', percentile(sorted_intervals, 99), 'us');
console.log('QPS:', (count / results.total) * 1000000);
});
}
module.exports = runTest;

@ -1,137 +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.
*
*/
/**
* This script runs a QPS test. It sends requests for a specified length of time
* with a specified number pending at any one time. It then outputs the measured
* QPS. Usage:
* node qps_test.js [--concurrent=count] [--time=seconds]
* concurrent defaults to 100 and time defaults to 10
*/
'use strict';
var async = require('async');
var parseArgs = require('minimist');
var grpc = require('..');
var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing;
var interop_server = require('../interop/interop_server.js');
/**
* Runs the QPS test. Sends requests constantly for the given number of seconds,
* and keeps concurrent_calls requests pending at all times. When the test ends,
* the callback is called with the number of calls that completed within the
* time limit.
* @param {number} concurrent_calls The number of calls to have pending
* simultaneously
* @param {number} seconds The number of seconds to run the test for
* @param {function(Error, number)} callback Callback for test completion
*/
function runTest(concurrent_calls, seconds, callback) {
var testServer = interop_server.getServer(0, false);
testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port,
grpc.credentials.createInsecure());
var warmup_num = 100;
/**
* Warms up the client to avoid counting startup time in the test result
* @param {function(Error)} callback Called when warmup is complete
*/
function warmUp(callback) {
var pending = warmup_num;
function startCall() {
client.emptyCall({}, function(err, resp) {
if (err) {
callback(err);
return;
}
pending--;
if (pending === 0) {
callback(null);
}
});
}
for (var i = 0; i < warmup_num; i++) {
startCall();
}
}
/**
* Run the QPS test. Starts concurrent_calls requests, then starts a new
* request whenever one completes until time runs out.
* @param {function(Error, number)} callback Called when the test is complete.
* The second argument is the number of calls that finished within the
* time limit
*/
function run(callback) {
var running = 0;
var count = 0;
var start = process.hrtime();
function responseCallback(err, resp) {
if (process.hrtime(start)[0] < seconds) {
count += 1;
client.emptyCall({}, responseCallback);
} else {
running -= 1;
if (running <= 0) {
callback(null, count);
}
}
}
for (var i = 0; i < concurrent_calls; i++) {
running += 1;
client.emptyCall({}, responseCallback);
}
}
async.waterfall([warmUp, run], function(err, count) {
testServer.server.shutdown();
callback(err, count);
});
}
if (require.main === module) {
var argv = parseArgs(process.argv.slice(2), {
default: {'concurrent': 100,
'time': 10}
});
runTest(argv.concurrent, argv.time, function(err, count) {
if (err) {
throw err;
}
console.log('Concurrent calls:', argv.concurrent);
console.log('Time:', argv.time, 'seconds');
console.log('QPS:', (count/argv.time));
});
}

@ -31,37 +31,33 @@
*
*/
#include <stdlib.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include "grpc/_adapter/_c/types.h"
static PyMethodDef c_methods[] = {
{NULL}
};
PyMODINIT_FUNC init_c(void) {
PyObject *module;
module = Py_InitModule3("_c", c_methods,
"Wrappings of C structures and functions.");
if (pygrpc_module_add_types(module) < 0) {
return;
}
if (PyModule_AddStringConstant(
module, "PRIMARY_USER_AGENT_KEY",
GRPC_ARG_PRIMARY_USER_AGENT_STRING) < 0) {
return;
}
'use strict';
var worker_service_impl = require('./worker_service_impl');
var grpc = require('../../../');
var serviceProto = grpc.load({
root: __dirname + '/../../..',
file: 'test/proto/benchmarks/services.proto'}).grpc.testing;
function runServer(port) {
var server_creds = grpc.ServerCredentials.createInsecure();
var server = new grpc.Server();
server.addProtoService(serviceProto.WorkerService.service,
worker_service_impl);
var address = '0.0.0.0:' + port;
server.bind(address, server_creds);
server.start();
return server;
}
/* GRPC maintains an internal counter of how many times it has been
initialized and handles multiple pairs of grpc_init()/grpc_shutdown()
invocations accordingly. */
grpc_init();
atexit(&grpc_shutdown);
if (require.main === module) {
Error.stackTraceLimit = Infinity;
var parseArgs = require('minimist');
var argv = parseArgs(process.argv, {
string: ['driver_port']
});
runServer(argv.driver_port);
}
exports.runServer = runServer;

@ -0,0 +1,132 @@
/*
*
* 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.
*
*/
'use strict';
var BenchmarkClient = require('./benchmark_client');
var BenchmarkServer = require('./benchmark_server');
exports.runClient = function runClient(call) {
var client;
call.on('data', function(request) {
var stats;
switch (request.argtype) {
case 'setup':
var setup = request.setup;
client = new BenchmarkClient(setup.server_targets,
setup.client_channels,
setup.histogram_params,
setup.security_params);
client.on('error', function(error) {
call.emit('error', error);
});
switch (setup.load_params.load) {
case 'closed_loop':
client.startClosedLoop(setup.outstanding_rpcs_per_channel,
setup.rpc_type,
setup.payload_config.simple_params.req_size,
setup.payload_config.simple_params.resp_size);
break;
case 'poisson':
client.startPoisson(setup.outstanding_rpcs_per_channel,
setup.rpc_type, setup.payload_config.req_size,
setup.payload_config.resp_size,
setup.load_params.poisson.offered_load);
break;
default:
call.emit('error', new Error('Unsupported LoadParams type' +
setup.load_params.load));
}
stats = client.mark();
call.write({
stats: stats
});
break;
case 'mark':
if (client) {
stats = client.mark(request.mark.reset);
call.write({
stats: stats
});
} else {
call.emit('error', new Error('Got Mark before ClientConfig'));
}
break;
default:
throw new Error('Nonexistent client argtype option: ' + request.argtype);
}
});
call.on('end', function() {
client.stop(function() {
call.end();
});
});
};
exports.runServer = function runServer(call) {
var server;
call.on('data', function(request) {
var stats;
switch (request.argtype) {
case 'setup':
server = new BenchmarkServer(request.setup.host, request.setup.port,
request.setup.security_params);
server.start();
stats = server.mark();
call.write({
stats: stats,
port: server.getPort()
});
break;
case 'mark':
if (server) {
stats = server.mark(request.mark.reset);
call.write({
stats: stats,
port: server.getPort(),
cores: 1
});
} else {
call.emit('error', new Error('Got Mark before ServerConfig'));
}
break;
default:
throw new Error('Nonexistent server argtype option');
}
});
call.on('end', function() {
server.stop(function() {
call.end();
});
});
};

@ -91,7 +91,7 @@ exports.createSsl = ChannelCredentials.createSsl;
*/
exports.createFromMetadataGenerator = function(metadata_generator) {
return CallCredentials.createFromPlugin(function(service_url, callback) {
metadata_generator(service_url, function(error, metadata) {
metadata_generator({service_url: service_url}, function(error, metadata) {
var code = grpc.status.OK;
var message = '';
if (error) {
@ -114,7 +114,8 @@ exports.createFromMetadataGenerator = function(metadata_generator) {
* @return {CallCredentials} The resulting credentials object
*/
exports.createFromGoogleCredential = function(google_credential) {
return exports.createFromMetadataGenerator(function(service_url, callback) {
return exports.createFromMetadataGenerator(function(auth_context, callback) {
var service_url = auth_context.service_url;
google_credential.getRequestMetadata(service_url, function(err, header) {
if (err) {
callback(err);

@ -100,28 +100,6 @@ function handleError(call, error) {
call.startBatch(error_batch, function(){});
}
/**
* Wait for the client to close, then emit a cancelled event if the client
* cancelled.
* @access private
* @param {grpc.Call} call The call object to wait on
* @param {EventEmitter} emitter The event emitter to emit the cancelled event
* on
*/
function waitForCancel(call, emitter) {
var cancel_batch = {};
cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true;
call.startBatch(cancel_batch, function(err, result) {
if (err) {
emitter.emit('error', err);
}
if (result.cancelled) {
emitter.cancelled = true;
emitter.emit('cancelled');
}
});
}
/**
* Send a response to a unary or client streaming call.
* @access private
@ -258,6 +236,13 @@ function setUpReadable(stream, deserialize) {
});
}
util.inherits(ServerUnaryCall, EventEmitter);
function ServerUnaryCall(call) {
EventEmitter.call(this);
this.call = call;
}
util.inherits(ServerWritableStream, Writable);
/**
@ -311,33 +296,6 @@ function _write(chunk, encoding, callback) {
ServerWritableStream.prototype._write = _write;
/**
* Send the initial metadata for a writable stream.
* @param {Metadata} responseMetadata Metadata to send
*/
function sendMetadata(responseMetadata) {
/* jshint validthis: true */
var self = this;
if (!this.call.metadataSent) {
this.call.metadataSent = true;
var batch = [];
batch[grpc.opType.SEND_INITIAL_METADATA] =
responseMetadata._getCoreRepresentation();
this.call.startBatch(batch, function(err) {
if (err) {
self.emit('error', err);
return;
}
});
}
}
/**
* @inheritdoc
* @alias module:src/server~ServerWritableStream#sendMetadata
*/
ServerWritableStream.prototype.sendMetadata = sendMetadata;
util.inherits(ServerReadableStream, Readable);
/**
@ -427,6 +385,31 @@ function ServerDuplexStream(call, serialize, deserialize) {
ServerDuplexStream.prototype._read = _read;
ServerDuplexStream.prototype._write = _write;
/**
* Send the initial metadata for a writable stream.
* @param {Metadata} responseMetadata Metadata to send
*/
function sendMetadata(responseMetadata) {
/* jshint validthis: true */
var self = this;
if (!this.call.metadataSent) {
this.call.metadataSent = true;
var batch = {};
batch[grpc.opType.SEND_INITIAL_METADATA] =
responseMetadata._getCoreRepresentation();
this.call.startBatch(batch, function(err) {
if (err) {
self.emit('error', err);
return;
}
});
}
}
ServerUnaryCall.prototype.sendMetadata = sendMetadata;
ServerWritableStream.prototype.sendMetadata = sendMetadata;
ServerReadableStream.prototype.sendMetadata = sendMetadata;
ServerDuplexStream.prototype.sendMetadata = sendMetadata;
/**
@ -438,10 +421,36 @@ function getPeer() {
return this.call.getPeer();
}
ServerUnaryCall.prototype.getPeer = getPeer;
ServerReadableStream.prototype.getPeer = getPeer;
ServerWritableStream.prototype.getPeer = getPeer;
ServerDuplexStream.prototype.getPeer = getPeer;
/**
* Wait for the client to close, then emit a cancelled event if the client
* cancelled.
*/
function waitForCancel() {
/* jshint validthis: true */
var self = this;
var cancel_batch = {};
cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true;
self.call.startBatch(cancel_batch, function(err, result) {
if (err) {
self.emit('error', err);
}
if (result.cancelled) {
self.cancelled = true;
self.emit('cancelled');
}
});
}
ServerUnaryCall.prototype.waitForCancel = waitForCancel;
ServerReadableStream.prototype.waitForCancel = waitForCancel;
ServerWritableStream.prototype.waitForCancel = waitForCancel;
ServerDuplexStream.prototype.waitForCancel = waitForCancel;
/**
* Fully handle a unary call
* @access private
@ -450,25 +459,12 @@ ServerDuplexStream.prototype.getPeer = getPeer;
* @param {Metadata} metadata Metadata from the client
*/
function handleUnary(call, handler, metadata) {
var emitter = new EventEmitter();
emitter.sendMetadata = function(responseMetadata) {
if (!call.metadataSent) {
call.metadataSent = true;
var batch = {};
batch[grpc.opType.SEND_INITIAL_METADATA] =
responseMetadata._getCoreRepresentation();
call.startBatch(batch, function() {});
}
};
emitter.getPeer = function() {
return call.getPeer();
};
var emitter = new ServerUnaryCall(call);
emitter.on('error', function(error) {
handleError(call, error);
});
emitter.metadata = metadata;
waitForCancel(call, emitter);
emitter.call = call;
emitter.waitForCancel();
var batch = {};
batch[grpc.opType.RECV_MESSAGE] = true;
call.startBatch(batch, function(err, result) {
@ -508,7 +504,7 @@ function handleUnary(call, handler, metadata) {
*/
function handleServerStreaming(call, handler, metadata) {
var stream = new ServerWritableStream(call, handler.serialize);
waitForCancel(call, stream);
stream.waitForCancel();
stream.metadata = metadata;
var batch = {};
batch[grpc.opType.RECV_MESSAGE] = true;
@ -537,19 +533,10 @@ function handleServerStreaming(call, handler, metadata) {
*/
function handleClientStreaming(call, handler, metadata) {
var stream = new ServerReadableStream(call, handler.deserialize);
stream.sendMetadata = function(responseMetadata) {
if (!call.metadataSent) {
call.metadataSent = true;
var batch = {};
batch[grpc.opType.SEND_INITIAL_METADATA] =
responseMetadata._getCoreRepresentation();
call.startBatch(batch, function() {});
}
};
stream.on('error', function(error) {
handleError(call, error);
});
waitForCancel(call, stream);
stream.waitForCancel();
stream.metadata = metadata;
handler.func(stream, function(err, value, trailer, flags) {
stream.terminate();
@ -574,7 +561,7 @@ function handleClientStreaming(call, handler, metadata) {
function handleBidiStreaming(call, handler, metadata) {
var stream = new ServerDuplexStream(call, handler.serialize,
handler.deserialize);
waitForCancel(call, stream);
stream.waitForCancel();
stream.metadata = metadata;
handler.func(stream);
}

@ -36,7 +36,7 @@
var assert = require('assert');
var grpc = require('..');
var math = grpc.load(__dirname + '/math/math.proto').math;
var math = grpc.load(__dirname + '/../../proto/math/math.proto').math;
/**

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

@ -34,7 +34,8 @@
'use strict';
var grpc = require('../..');
var math = grpc.load(__dirname + '/math.proto').math;
var math = grpc.load(__dirname + '/../../../proto/math/math.proto').math;
/**
* Server function for division. Provides the /Math/DivMany and /Math/Div

@ -36,7 +36,7 @@
var assert = require('assert');
var grpc = require('..');
var math = grpc.load(__dirname + '/math/math.proto').math;
var math = grpc.load(__dirname + '/../../proto/math/math.proto').math;
/**
* Client to use to make requests to a running server.

@ -41,7 +41,8 @@ var ProtoBuf = require('protobufjs');
var grpc = require('..');
var math_proto = ProtoBuf.loadProtoFile(__dirname + '/math/math.proto');
var math_proto = ProtoBuf.loadProtoFile(__dirname +
'/../../proto/math/math.proto');
var mathService = math_proto.lookup('math.Math');
@ -312,6 +313,54 @@ describe('Generic client and server', function() {
});
});
});
describe('Server-side getPeer', function() {
function toString(val) {
return val.toString();
}
function toBuffer(str) {
return new Buffer(str);
}
var string_service_attrs = {
'getPeer' : {
path: '/string/getPeer',
requestStream: false,
responseStream: false,
requestSerialize: toBuffer,
requestDeserialize: toString,
responseSerialize: toBuffer,
responseDeserialize: toString
}
};
var client;
var server;
before(function() {
server = new grpc.Server();
server.addService(string_service_attrs, {
getPeer: function(call, callback) {
try {
callback(null, call.getPeer());
} catch (e) {
call.emit('error', e);
}
}
});
var port = server.bind('localhost:0', server_insecure_creds);
server.start();
var Client = grpc.makeGenericClientConstructor(string_service_attrs);
client = new Client('localhost:' + port,
grpc.credentials.createInsecure());
});
after(function() {
server.forceShutdown();
});
it('should respond with a string representing the client', function(done) {
client.getPeer('', function(err, response) {
assert.ifError(err);
// We don't expect a specific value, just that it worked without error
done();
});
});
});
describe('Echo metadata', function() {
var client;
var server;

File diff suppressed because it is too large Load Diff

@ -50,6 +50,8 @@
#import <Foundation/Foundation.h>
#import <RxLibrary/GRXWriter.h>
#include <AvailabilityMacros.h>
#pragma mark gRPC errors
/** Domain of NSError objects produced by gRPC. */
@ -161,6 +163,9 @@ extern id const kGRPCTrailersKey;
#pragma mark GRPCCall
/** Represents a single gRPC remote call. */
@interface GRPCCall : GRXWriter
/**
* The container of the request headers of an RPC conforms to this protocol, which is a subset of
* NSMutableDictionary's interface. It will become a NSMutableDictionary later on.
@ -170,21 +175,6 @@ extern id const kGRPCTrailersKey;
* A header value is a NSString object (with only ASCII characters), unless the header name has the
* suffix "-bin", in which case the value has to be a NSData object.
*/
@protocol GRPCRequestHeaders <NSObject>
@property(nonatomic, readonly) NSUInteger count;
- (id)objectForKeyedSubscript:(NSString *)key;
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
- (void)removeAllObjects;
- (void)removeObjectForKey:(NSString *)key;
@end
/** Represents a single gRPC remote call. */
@interface GRPCCall : GRXWriter
/**
* These HTTP headers will be passed to the server as part of this call. Each HTTP header is a
* name-value pair with string names and either string or binary values.
@ -200,7 +190,7 @@ extern id const kGRPCTrailersKey;
*
* The property is initialized to an empty NSMutableDictionary.
*/
@property(atomic, readonly) id<GRPCRequestHeaders> requestHeaders;
@property(atomic, readonly) NSMutableDictionary *requestHeaders;
/**
* This dictionary is populated with the HTTP headers received from the server. This happens before
@ -243,3 +233,24 @@ extern id const kGRPCTrailersKey;
// TODO(jcanizales): Let specify a deadline. As a category of GRXWriter?
@end
#pragma mark Backwards compatibiity
/** This protocol is kept for backwards compatibility with existing code. */
DEPRECATED_MSG_ATTRIBUTE("Use NSDictionary or NSMutableDictionary instead.")
@protocol GRPCRequestHeaders <NSObject>
@property(nonatomic, readonly) NSUInteger count;
- (id)objectForKeyedSubscript:(NSString *)key;
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
- (void)removeAllObjects;
- (void)removeObjectForKey:(NSString *)key;
@end
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
/** This is only needed for backwards-compatibility. */
@interface NSMutableDictionary (GRPCRequestHeaders) <GRPCRequestHeaders>
@end
#pragma clang diagnostic pop

@ -221,7 +221,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
#pragma mark Send headers
- (void)sendHeaders:(id<GRPCRequestHeaders>)headers {
- (void)sendHeaders:(NSDictionary *)headers {
// TODO(jcanizales): Add error handlers for async failures
[_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers
handler:nil]]];

@ -32,21 +32,14 @@
*/
#import <Foundation/Foundation.h>
#include <grpc/grpc.h>
#import "../GRPCCall.h"
@interface GRPCRequestHeaders : NSObject<GRPCRequestHeaders>
@property(nonatomic, readonly) NSUInteger count;
@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray;
@interface GRPCRequestHeaders : NSMutableDictionary
- (instancetype)initWithCall:(GRPCCall *)call;
- (id)objectForKeyedSubscript:(NSString *)key;
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
- (void)removeAllObjects;
- (void)removeObjectForKey:(NSString *)key;
- (instancetype)initWithCall:(GRPCCall *)call
storage:(NSMutableDictionary *)storage NS_DESIGNATED_INITIALIZER;
@end

@ -68,17 +68,44 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) {
@implementation GRPCRequestHeaders {
__weak GRPCCall *_call;
// The NSMutableDictionary superclass doesn't hold any storage (so that people can implement their
// own in subclasses). As that's not the reason we're subclassing, we just delegate storage to the
// default NSMutableDictionary subclass returned by the cluster (e.g. __NSDictionaryM on iOS 9).
NSMutableDictionary *_delegate;
}
- (instancetype)init {
return [self initWithCall:nil];
}
- (instancetype)initWithCapacity:(NSUInteger)numItems {
return [self init];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
return [self init];
}
- (instancetype)initWithCall:(GRPCCall *)call {
return [self initWithCall:call storage:[NSMutableDictionary dictionary]];
}
// Designated initializer
- (instancetype)initWithCall:(GRPCCall *)call storage:(NSMutableDictionary *)storage {
// TODO(jcanizales): Throw if call or storage are nil.
if ((self = [super init])) {
_call = call;
_delegate = [NSMutableDictionary dictionary];
_delegate = storage;
}
return self;
}
- (instancetype)initWithObjects:(const id _Nonnull __unsafe_unretained *)objects
forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys
count:(NSUInteger)cnt {
return [self init];
}
- (void)checkCallIsNotStarted {
if (_call.state != GRXWriterStateNotStarted) {
[NSException raise:@"Invalid modification"
@ -86,11 +113,11 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) {
}
}
- (id)objectForKeyedSubscript:(NSString *)key {
- (id)objectForKey:(NSString *)key {
return _delegate[key.lowercaseString];
}
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
- (void)setObject:(id)obj forKey:(NSString *)key {
[self checkCallIsNotStarted];
CheckIsNonNilASCII(@"Header name", key);
key = key.lowercaseString;
@ -103,16 +130,12 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) {
[_delegate removeObjectForKey:key.lowercaseString];
}
- (void)removeAllObjects {
[self checkCallIsNotStarted];
[_delegate removeAllObjects];
}
- (NSUInteger)count {
return _delegate.count;
}
- (grpc_metadata *)grpc_metadataArray {
return _delegate.grpc_metadataArray;
- (NSEnumerator * _Nonnull)keyEnumerator {
return [_delegate keyEnumerator];
}
@end

@ -45,7 +45,7 @@
@interface GRPCOpSendMetadata : GRPCOperation
- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata
- (instancetype)initWithMetadata:(NSDictionary *)metadata
handler:(void(^)())handler NS_DESIGNATED_INITIALIZER;
@end

@ -65,7 +65,7 @@
return [self initWithMetadata:nil handler:nil];
}
- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler {
- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)())handler {
if (self = [super init]) {
_op.op = GRPC_OP_SEND_INITIAL_METADATA;
_op.data.send_initial_metadata.count = metadata.count;

@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'Protobuf', :path => "../../../../third_party/protobuf"
pod 'BoringSSL', :podspec => "../.."
pod 'gRPC', :path => "../../../.."
pod 'RemoteTest', :path => "../RemoteTestClient"

@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'Protobuf', :path => "../../../../third_party/protobuf"
pod 'BoringSSL', :podspec => "../.."
pod 'gRPC', :path => "../../../.."
pod 'RemoteTest', :path => "../RemoteTestClient"

@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'Protobuf', :path => "../../../third_party/protobuf"
pod 'BoringSSL', :podspec => ".."
pod 'gRPC', :path => "../../.."
pod 'RemoteTest', :path => "RemoteTestClient"

@ -28,6 +28,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// TODO: start using src/proto/math/math.proto and remove this file once
// PHP supports proto3.
syntax = "proto2";
package math;

@ -30,6 +30,7 @@
syntax = "proto3";
package grpc.health.v1alpha;
option csharp_namespace = "Grpc.Health.V1Alpha";
message HealthCheckRequest {
string host = 1;

@ -5,5 +5,12 @@ dist/
*.egg
*.egg/
*.eggs/
*_pb2.py
.coverage
.coverage.*
.cache/
.tox/
nosetests.xml
doc/
_grpcio_metadata.py
htmlcov/

@ -1,3 +1,4 @@
graft grpc
graft tests
include commands.py
include requirements.txt

@ -29,14 +29,18 @@
"""Provides distutils command classes for the GRPC Python setup process."""
import distutils
import os
import os.path
import re
import subprocess
import sys
import setuptools
from setuptools.command import build_py
from setuptools.command import test
_CONF_PY_ADDENDUM = """
CONF_PY_ADDENDUM = """
extensions.append('sphinx.ext.napoleon')
napoleon_google_docstring = True
napoleon_numpy_docstring = True
@ -48,7 +52,7 @@ html_theme = 'sphinx_rtd_theme'
class SphinxDocumentation(setuptools.Command):
"""Command to generate documentation via sphinx."""
description = ''
description = 'generate sphinx documentation'
user_options = []
def initialize_options(self):
@ -72,14 +76,61 @@ class SphinxDocumentation(setuptools.Command):
'-o', os.path.join('doc', 'src'), src_dir])
conf_filepath = os.path.join('doc', 'src', 'conf.py')
with open(conf_filepath, 'a') as conf_file:
conf_file.write(_CONF_PY_ADDENDUM)
conf_file.write(CONF_PY_ADDENDUM)
sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')])
class BuildProtoModules(setuptools.Command):
"""Command to generate project *_pb2.py modules from proto files."""
description = 'build protobuf modules'
user_options = [
('include=', None, 'path patterns to include in protobuf generation'),
('exclude=', None, 'path patterns to exclude from protobuf generation')
]
def initialize_options(self):
self.exclude = None
self.include = r'.*\.proto$'
self.protoc_command = None
self.grpc_python_plugin_command = None
def finalize_options(self):
self.protoc_command = distutils.spawn.find_executable('protoc')
self.grpc_python_plugin_command = distutils.spawn.find_executable(
'grpc_python_plugin')
def run(self):
include_regex = re.compile(self.include)
exclude_regex = re.compile(self.exclude) if self.exclude else None
paths = []
root_directory = os.getcwd()
for walk_root, directories, filenames in os.walk(root_directory):
for filename in filenames:
path = os.path.join(walk_root, filename)
if include_regex.match(path) and not (
exclude_regex and exclude_regex.match(path)):
paths.append(path)
command = [
self.protoc_command,
'--plugin=protoc-gen-python-grpc={}'.format(
self.grpc_python_plugin_command),
'-I {}'.format(root_directory),
'--python_out={}'.format(root_directory),
'--python-grpc_out={}'.format(root_directory),
] + paths
try:
subprocess.check_output(' '.join(command), cwd=root_directory, shell=True,
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
raise Exception('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format(
command, e.message, e.output))
class BuildProjectMetadata(setuptools.Command):
"""Command to generate project metadata in a module."""
description = ''
description = 'build grpcio project metadata files'
user_options = []
def initialize_options(self):
@ -98,5 +149,73 @@ class BuildPy(build_py.build_py):
"""Custom project build command."""
def run(self):
self.run_command('build_proto_modules')
self.run_command('build_project_metadata')
build_py.build_py.run(self)
class Gather(setuptools.Command):
"""Command to gather project dependencies."""
description = 'gather dependencies for grpcio'
user_options = [
('test', 't', 'flag indicating to gather test dependencies'),
('install', 'i', 'flag indicating to gather install dependencies')
]
def initialize_options(self):
self.test = False
self.install = False
def finalize_options(self):
# distutils requires this override.
pass
def run(self):
if self.install and self.distribution.install_requires:
self.distribution.fetch_build_eggs(self.distribution.install_requires)
if self.test and self.distribution.tests_require:
self.distribution.fetch_build_eggs(self.distribution.tests_require)
class RunInterop(test.test):
description = 'run interop test client/server'
user_options = [
('args=', 'a', 'pass-thru arguments for the client/server'),
('client', 'c', 'flag indicating to run the client'),
('server', 's', 'flag indicating to run the server')
]
def initialize_options(self):
self.args = ''
self.client = False
self.server = False
def finalize_options(self):
if self.client and self.server:
raise DistutilsOptionError('you may only specify one of client or server')
def run(self):
if self.distribution.install_requires:
self.distribution.fetch_build_eggs(self.distribution.install_requires)
if self.distribution.tests_require:
self.distribution.fetch_build_eggs(self.distribution.tests_require)
if self.client:
self.run_client()
elif self.server:
self.run_server()
def run_server(self):
# We import here to ensure that our setuptools parent has had a chance to
# edit the Python system path.
from tests.interop import server
sys.argv[1:] = self.args.split()
server.serve()
def run_client(self):
# We import here to ensure that our setuptools parent has had a chance to
# edit the Python system path.
from tests.interop import client
sys.argv[1:] = self.args.split()
client.test_interoperability()

@ -1,286 +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.
*
*/
#ifndef GRPC__ADAPTER__C_TYPES_H_
#define GRPC__ADAPTER__C_TYPES_H_
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
/*=========================*/
/* Client-side credentials */
/*=========================*/
typedef struct ChannelCredentials {
PyObject_HEAD
grpc_channel_credentials *c_creds;
} ChannelCredentials;
void pygrpc_ChannelCredentials_dealloc(ChannelCredentials *self);
ChannelCredentials *pygrpc_ChannelCredentials_google_default(
PyTypeObject *type, PyObject *ignored);
ChannelCredentials *pygrpc_ChannelCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ChannelCredentials *pygrpc_ChannelCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_ChannelCredentials_type;
typedef struct CallCredentials {
PyObject_HEAD
grpc_call_credentials *c_creds;
} CallCredentials;
void pygrpc_CallCredentials_dealloc(CallCredentials *self);
CallCredentials *pygrpc_CallCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
CallCredentials *pygrpc_CallCredentials_compute_engine(
PyTypeObject *type, PyObject *ignored);
CallCredentials *pygrpc_CallCredentials_jwt(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
CallCredentials *pygrpc_CallCredentials_refresh_token(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
CallCredentials *pygrpc_CallCredentials_iam(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_CallCredentials_type;
/*=========================*/
/* Server-side credentials */
/*=========================*/
typedef struct ServerCredentials {
PyObject_HEAD
grpc_server_credentials *c_creds;
} ServerCredentials;
void pygrpc_ServerCredentials_dealloc(ServerCredentials *self);
ServerCredentials *pygrpc_ServerCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_ServerCredentials_type;
/*==================*/
/* Completion queue */
/*==================*/
typedef struct CompletionQueue {
PyObject_HEAD
grpc_completion_queue *c_cq;
} CompletionQueue;
CompletionQueue *pygrpc_CompletionQueue_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_CompletionQueue_dealloc(CompletionQueue *self);
PyObject *pygrpc_CompletionQueue_next(
CompletionQueue *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_CompletionQueue_shutdown(
CompletionQueue *self, PyObject *ignored);
extern PyTypeObject pygrpc_CompletionQueue_type;
/*======*/
/* Call */
/*======*/
typedef struct Call {
PyObject_HEAD
grpc_call *c_call;
CompletionQueue *cq;
} Call;
Call *pygrpc_Call_new_empty(CompletionQueue *cq);
void pygrpc_Call_dealloc(Call *self);
PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Call_peer(Call *self);
PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args,
PyObject *kwargs);
extern PyTypeObject pygrpc_Call_type;
/*=========*/
/* Channel */
/*=========*/
typedef struct Channel {
PyObject_HEAD
grpc_channel *c_chan;
} Channel;
Channel *pygrpc_Channel_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_Channel_dealloc(Channel *self);
Call *pygrpc_Channel_create_call(
Channel *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Channel_check_connectivity_state(Channel *self, PyObject *args,
PyObject *kwargs);
PyObject *pygrpc_Channel_watch_connectivity_state(Channel *self, PyObject *args,
PyObject *kwargs);
PyObject *pygrpc_Channel_target(Channel *self);
extern PyTypeObject pygrpc_Channel_type;
/*========*/
/* Server */
/*========*/
typedef struct Server {
PyObject_HEAD
grpc_server *c_serv;
CompletionQueue *cq;
int shutdown_called;
} Server;
Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_Server_dealloc(Server *self);
PyObject *pygrpc_Server_request_call(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_add_http2_port(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_start(Server *self, PyObject *ignored);
PyObject *pygrpc_Server_shutdown(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused);
extern PyTypeObject pygrpc_Server_type;
/*=========*/
/* Utility */
/*=========*/
/* Every tag that passes from Python GRPC to GRPC core is of this type. */
typedef struct pygrpc_tag {
PyObject *user_tag;
Call *call;
grpc_call_details request_call_details;
grpc_metadata_array request_metadata;
grpc_op *ops;
size_t nops;
int is_new_call;
} pygrpc_tag;
/* Construct a tag associated with a batch call. Does not take ownership of the
resources in the elements of ops. */
pygrpc_tag *pygrpc_produce_batch_tag(PyObject *user_tag, Call *call,
grpc_op *ops, size_t nops);
/* Construct a tag associated with a server request. The calling code should
use the appropriate fields of the produced tag in the invocation of
grpc_server_request_call. */
pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call);
/* Construct a tag associated with a server shutdown. */
pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag);
/* Construct a tag associated with a channel state change. */
pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag);
/* Frees all resources owned by the tag and the tag itself. */
void pygrpc_discard_tag(pygrpc_tag *tag);
/* Consumes an event and its associated tag, providing a Python tuple of the
form `(type, tag, call, call_details, results)` (where type is an integer
corresponding to a grpc_completion_type, tag is an arbitrary PyObject, call
is the call object associated with the event [if any], call_details is a
tuple of form `(method, host, deadline)` [if such details are available],
and resultd is a list of tuples of form `(type, metadata, message, status,
cancelled)` [where type corresponds to a grpc_op_type, metadata is a
sequence of 2-sequences of strings, message is a byte string, and status is
a 2-tuple of an integer corresponding to grpc_status_code and a string of
status details]).
Frees all resources associated with the event tag. */
PyObject *pygrpc_consume_event(grpc_event event);
/* Transliterate the Python tuple of form `(type, metadata, message,
status)` (where type is an integer corresponding to a grpc_op_type, metadata
is a sequence of 2-sequences of strings, message is a byte string, and
status is 2-tuple of an integer corresponding to grpc_status_code and a
string of status details) to a grpc_op suitable for use in a
grpc_call_start_batch invocation. The grpc_op is a 'directory' of resources
that must be freed after GRPC core is done with them.
Calls gpr_malloc (or the appropriate type-specific grpc_*_create function)
to populate the appropriate union-discriminated members of the op.
Returns true on success, false on failure. */
int pygrpc_produce_op(PyObject *op, grpc_op *result);
/* Discards all resources associated with the passed in op that was produced by
pygrpc_produce_op. */
void pygrpc_discard_op(grpc_op op);
/* Transliterate the grpc_ops (which have been sent through a
grpc_call_start_batch invocation and whose corresponding event has appeared
on a completion queue) to a Python tuple of form `(type, metadata, message,
status, cancelled)` (where type is an integer corresponding to a
grpc_op_type, metadata is a sequence of 2-sequences of strings, message is a
byte string, and status is 2-tuple of an integer corresponding to
grpc_status_code and a string of status details).
Calls gpr_free (or the appropriate type-specific grpc_*_destroy function) on
the appropriate union-discriminated populated members of the ops. */
PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops);
/* Transliterate from a gpr_timespec to a double (in units of seconds, either
from the epoch if interpreted absolutely or as a delta otherwise). */
double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec);
/* Transliterate from a double (in units of seconds from the epoch if
interpreted absolutely or as a delta otherwise) to a gpr_timespec. */
gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds);
/* Returns true on success, false on failure. */
int pygrpc_cast_pyseq_to_send_metadata(
PyObject *pyseq, grpc_metadata **metadata, size_t *count);
/* Returns a metadata array as a Python object on success, else NULL. */
PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata);
/* Transliterate from a list of python channel arguments (2-tuples of string
and string|integer|None) to a grpc_channel_args object. The strings placed
in the grpc_channel_args object's grpc_arg elements are views of the Python
object. The Python object must live long enough for the grpc_channel_args
to be used. Arguments set to None are silently ignored. Returns true on
success, false on failure. */
int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args);
void pygrpc_discard_channel_args(grpc_channel_args args);
/* Read the bytes from grpc_byte_buffer to a gpr_malloc'd array of bytes;
output to result and result_size. */
void pygrpc_byte_buffer_to_bytes(
grpc_byte_buffer *buffer, char **result, size_t *result_size);
/*========*/
/* Module */
/*========*/
/* Returns 0 on success, -1 on failure. */
int pygrpc_module_add_types(PyObject *module);
#endif /* GRPC__ADAPTER__C_TYPES_H_ */

@ -1,186 +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 "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_Call_methods[] = {
{"start_batch", (PyCFunction)pygrpc_Call_start_batch, METH_KEYWORDS, ""},
{"cancel", (PyCFunction)pygrpc_Call_cancel, METH_KEYWORDS, ""},
{"peer", (PyCFunction)pygrpc_Call_peer, METH_NOARGS, ""},
{"set_credentials", (PyCFunction)pygrpc_Call_set_credentials, METH_KEYWORDS,
""},
{NULL}
};
const char pygrpc_Call_doc[] = "See grpc._adapter._types.Call.";
PyTypeObject pygrpc_Call_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Call", /* tp_name */
sizeof(Call), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Call_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Call_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Call_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
Call *pygrpc_Call_new_empty(CompletionQueue *cq) {
Call *call = (Call *)pygrpc_Call_type.tp_alloc(&pygrpc_Call_type, 0);
call->c_call = NULL;
call->cq = cq;
Py_XINCREF(call->cq);
return call;
}
void pygrpc_Call_dealloc(Call *self) {
if (self->c_call) {
grpc_call_destroy(self->c_call);
}
Py_XDECREF(self->cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs) {
PyObject *op_list;
PyObject *user_tag;
grpc_op *ops;
size_t nops;
size_t i;
size_t j;
pygrpc_tag *tag;
grpc_call_error errcode;
static char *keywords[] = {"ops", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:start_batch", keywords,
&op_list, &user_tag)) {
return NULL;
}
if (!PyList_Check(op_list)) {
PyErr_SetString(PyExc_TypeError, "expected a list of OpArgs");
return NULL;
}
nops = PyList_Size(op_list);
ops = gpr_malloc(sizeof(grpc_op) * nops);
for (i = 0; i < nops; ++i) {
PyObject *item = PyList_GET_ITEM(op_list, i);
if (!pygrpc_produce_op(item, &ops[i])) {
for (j = 0; j < i; ++j) {
pygrpc_discard_op(ops[j]);
}
return NULL;
}
}
tag = pygrpc_produce_batch_tag(user_tag, self, ops, nops);
errcode = grpc_call_start_batch(self->c_call, tag->ops, tag->nops, tag, NULL);
gpr_free(ops);
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs) {
PyObject *py_code = NULL;
grpc_call_error errcode;
int code;
char *details = NULL;
static char *keywords[] = {"code", "details", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Os:start_batch", keywords,
&py_code, &details)) {
return NULL;
}
if (py_code != NULL && details != NULL) {
if (!PyInt_Check(py_code)) {
PyErr_SetString(PyExc_TypeError, "expected integer code");
return NULL;
}
code = PyInt_AsLong(py_code);
errcode = grpc_call_cancel_with_status(self->c_call, code, details, NULL);
} else if (py_code != NULL || details != NULL) {
PyErr_SetString(PyExc_ValueError,
"if `code` is specified, so must `details`");
return NULL;
} else {
errcode = grpc_call_cancel(self->c_call, NULL);
}
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Call_peer(Call *self) {
char *peer = grpc_call_get_peer(self->c_call);
PyObject *py_peer = PyString_FromString(peer);
gpr_free(peer);
return py_peer;
}
PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args,
PyObject *kwargs) {
CallCredentials *creds;
grpc_call_error errcode;
static char *keywords[] = {"creds", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "O!:set_credentials", keywords,
&pygrpc_CallCredentials_type, &creds)) {
return NULL;
}
errcode = grpc_call_set_credentials(self->c_call, creds->c_creds);
return PyInt_FromLong(errcode);
}

@ -1,203 +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 "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
PyMethodDef pygrpc_CallCredentials_methods[] = {
{"composite", (PyCFunction)pygrpc_CallCredentials_composite,
METH_CLASS|METH_KEYWORDS, ""},
{"compute_engine", (PyCFunction)pygrpc_CallCredentials_compute_engine,
METH_CLASS|METH_NOARGS, ""},
{"jwt", (PyCFunction)pygrpc_CallCredentials_jwt,
METH_CLASS|METH_KEYWORDS, ""},
{"refresh_token", (PyCFunction)pygrpc_CallCredentials_refresh_token,
METH_CLASS|METH_KEYWORDS, ""},
{"iam", (PyCFunction)pygrpc_CallCredentials_iam,
METH_CLASS|METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_CallCredentials_doc[] = "";
PyTypeObject pygrpc_CallCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"CallCredentials", /* tp_name */
sizeof(CallCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_CallCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_CallCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_CallCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_CallCredentials_dealloc(CallCredentials *self) {
grpc_call_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
CallCredentials *pygrpc_CallCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
CallCredentials *creds1;
CallCredentials *creds2;
static char *keywords[] = {"creds1", "creds2", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords,
&pygrpc_CallCredentials_type, &creds1,
&pygrpc_CallCredentials_type, &creds2)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds =
grpc_composite_call_credentials_create(
creds1->c_creds, creds2->c_creds, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create composite credentials");
return NULL;
}
return self;
}
CallCredentials *pygrpc_CallCredentials_compute_engine(
PyTypeObject *type, PyObject *ignored) {
CallCredentials *self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_google_compute_engine_credentials_create(NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create compute engine credentials");
return NULL;
}
return self;
}
/* TODO: Rename this credentials to something like service_account_jwt_access */
CallCredentials *pygrpc_CallCredentials_jwt(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
const char *json_key;
double lifetime;
static char *keywords[] = {"json_key", "token_lifetime", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sd:jwt", keywords,
&json_key, &lifetime)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_service_account_jwt_access_credentials_create(
json_key, pygrpc_cast_double_to_gpr_timespec(lifetime), NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create JWT credentials");
return NULL;
}
return self;
}
CallCredentials *pygrpc_CallCredentials_refresh_token(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
const char *json_refresh_token;
static char *keywords[] = {"json_refresh_token", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:refresh_token", keywords,
&json_refresh_token)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds =
grpc_google_refresh_token_credentials_create(json_refresh_token, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create credentials from refresh token");
return NULL;
}
return self;
}
CallCredentials *pygrpc_CallCredentials_iam(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
const char *authorization_token;
const char *authority_selector;
static char *keywords[] = {"authorization_token", "authority_selector", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss:iam", keywords,
&authorization_token, &authority_selector)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_google_iam_credentials_create(authorization_token,
authority_selector, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create IAM credentials");
return NULL;
}
return self;
}

@ -1,187 +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 "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_Channel_methods[] = {
{"create_call", (PyCFunction)pygrpc_Channel_create_call, METH_KEYWORDS, ""},
{"check_connectivity_state", (PyCFunction)pygrpc_Channel_check_connectivity_state, METH_KEYWORDS, ""},
{"watch_connectivity_state", (PyCFunction)pygrpc_Channel_watch_connectivity_state, METH_KEYWORDS, ""},
{"target", (PyCFunction)pygrpc_Channel_target, METH_NOARGS, ""},
{NULL}
};
const char pygrpc_Channel_doc[] = "See grpc._adapter._types.Channel.";
PyTypeObject pygrpc_Channel_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Channel", /* tp_name */
sizeof(Channel), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Channel_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Channel_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Channel_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_Channel_new /* tp_new */
};
Channel *pygrpc_Channel_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
Channel *self;
const char *target;
PyObject *py_args;
ChannelCredentials *creds = NULL;
grpc_channel_args c_args;
char *keywords[] = {"target", "args", "creds", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!:Channel", keywords,
&target, &py_args, &pygrpc_ChannelCredentials_type, &creds)) {
return NULL;
}
if (!pygrpc_produce_channel_args(py_args, &c_args)) {
return NULL;
}
self = (Channel *)type->tp_alloc(type, 0);
if (creds) {
self->c_chan =
grpc_secure_channel_create(creds->c_creds, target, &c_args, NULL);
} else {
self->c_chan = grpc_insecure_channel_create(target, &c_args, NULL);
}
pygrpc_discard_channel_args(c_args);
return self;
}
void pygrpc_Channel_dealloc(Channel *self) {
grpc_channel_destroy(self->c_chan);
self->ob_type->tp_free((PyObject *)self);
}
Call *pygrpc_Channel_create_call(
Channel *self, PyObject *args, PyObject *kwargs) {
Call *call;
CompletionQueue *cq;
const char *method;
const char *host;
double deadline;
char *keywords[] = {"cq", "method", "host", "deadline", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!szd:create_call", keywords,
&pygrpc_CompletionQueue_type, &cq, &method, &host, &deadline)) {
return NULL;
}
call = pygrpc_Call_new_empty(cq);
call->c_call = grpc_channel_create_call(
self->c_chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq->c_cq, method, host,
pygrpc_cast_double_to_gpr_timespec(deadline), NULL);
return call;
}
PyObject *pygrpc_Channel_check_connectivity_state(
Channel *self, PyObject *args, PyObject *kwargs) {
PyObject *py_try_to_connect;
int try_to_connect;
char *keywords[] = {"try_to_connect", NULL};
grpc_connectivity_state state;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:connectivity_state", keywords,
&py_try_to_connect)) {
return NULL;
}
if (!PyBool_Check(py_try_to_connect)) {
Py_XDECREF(py_try_to_connect);
return NULL;
}
try_to_connect = Py_True == py_try_to_connect;
Py_DECREF(py_try_to_connect);
state = grpc_channel_check_connectivity_state(self->c_chan, try_to_connect);
return PyInt_FromLong(state);
}
PyObject *pygrpc_Channel_watch_connectivity_state(
Channel *self, PyObject *args, PyObject *kwargs) {
PyObject *tag;
double deadline;
int last_observed_state;
CompletionQueue *completion_queue;
char *keywords[] = {"last_observed_state", "deadline",
"completion_queue", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "idO!O:watch_connectivity_state", keywords,
&last_observed_state, &deadline, &pygrpc_CompletionQueue_type,
&completion_queue, &tag)) {
return NULL;
}
grpc_channel_watch_connectivity_state(
self->c_chan, (grpc_connectivity_state)last_observed_state,
pygrpc_cast_double_to_gpr_timespec(deadline), completion_queue->c_cq,
pygrpc_produce_channel_state_change_tag(tag));
Py_RETURN_NONE;
}
PyObject *pygrpc_Channel_target(Channel *self) {
char *target = grpc_channel_get_target(self->c_chan);
PyObject *py_target = PyString_FromString(target);
gpr_free(target);
return py_target;
}

@ -1,165 +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 "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
PyMethodDef pygrpc_ChannelCredentials_methods[] = {
{"google_default", (PyCFunction)pygrpc_ChannelCredentials_google_default,
METH_CLASS|METH_NOARGS, ""},
{"ssl", (PyCFunction)pygrpc_ChannelCredentials_ssl,
METH_CLASS|METH_KEYWORDS, ""},
{"composite", (PyCFunction)pygrpc_ChannelCredentials_composite,
METH_CLASS|METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_ChannelCredentials_doc[] = "";
PyTypeObject pygrpc_ChannelCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"ChannelCredentials", /* tp_name */
sizeof(ChannelCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_ChannelCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_ChannelCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_ChannelCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_ChannelCredentials_dealloc(ChannelCredentials *self) {
grpc_channel_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
ChannelCredentials *pygrpc_ChannelCredentials_google_default(
PyTypeObject *type, PyObject *ignored) {
ChannelCredentials *self = (ChannelCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_google_default_credentials_create();
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create Google default credentials");
return NULL;
}
return self;
}
ChannelCredentials *pygrpc_ChannelCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ChannelCredentials *self;
const char *root_certs;
const char *private_key = NULL;
const char *cert_chain = NULL;
grpc_ssl_pem_key_cert_pair key_cert_pair;
static char *keywords[] = {"root_certs", "private_key", "cert_chain", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|zz:ssl", keywords,
&root_certs, &private_key, &cert_chain)) {
return NULL;
}
self = (ChannelCredentials *)type->tp_alloc(type, 0);
if (private_key && cert_chain) {
key_cert_pair.private_key = private_key;
key_cert_pair.cert_chain = cert_chain;
self->c_creds =
grpc_ssl_credentials_create(root_certs, &key_cert_pair, NULL);
} else {
self->c_creds = grpc_ssl_credentials_create(root_certs, NULL, NULL);
}
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create ssl credentials");
return NULL;
}
return self;
}
ChannelCredentials *pygrpc_ChannelCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ChannelCredentials *self;
ChannelCredentials *creds1;
CallCredentials *creds2;
static char *keywords[] = {"creds1", "creds2", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords,
&pygrpc_ChannelCredentials_type, &creds1,
&pygrpc_CallCredentials_type, &creds2)) {
return NULL;
}
self = (ChannelCredentials *)type->tp_alloc(type, 0);
self->c_creds =
grpc_composite_channel_credentials_create(
creds1->c_creds, creds2->c_creds, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(
PyExc_RuntimeError, "couldn't create composite credentials");
return NULL;
}
return self;
}

@ -1,124 +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 "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
PyMethodDef pygrpc_CompletionQueue_methods[] = {
{"next", (PyCFunction)pygrpc_CompletionQueue_next, METH_KEYWORDS, ""},
{"shutdown", (PyCFunction)pygrpc_CompletionQueue_shutdown, METH_NOARGS, ""},
{NULL}
};
const char pygrpc_CompletionQueue_doc[] =
"See grpc._adapter._types.CompletionQueue.";
PyTypeObject pygrpc_CompletionQueue_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"CompletionQueue", /* tp_name */
sizeof(CompletionQueue), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_CompletionQueue_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_CompletionQueue_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_CompletionQueue_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_CompletionQueue_new /* tp_new */
};
CompletionQueue *pygrpc_CompletionQueue_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CompletionQueue *self = (CompletionQueue *)type->tp_alloc(type, 0);
self->c_cq = grpc_completion_queue_create(NULL);
return self;
}
void pygrpc_CompletionQueue_dealloc(CompletionQueue *self) {
grpc_completion_queue_destroy(self->c_cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_CompletionQueue_next(
CompletionQueue *self, PyObject *args, PyObject *kwargs) {
double deadline;
grpc_event event;
PyObject *transliterated_event;
static char *keywords[] = {"deadline", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:next", keywords,
&deadline)) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
event = grpc_completion_queue_next(
self->c_cq, pygrpc_cast_double_to_gpr_timespec(deadline), NULL);
Py_END_ALLOW_THREADS;
transliterated_event = pygrpc_consume_event(event);
return transliterated_event;
}
PyObject *pygrpc_CompletionQueue_shutdown(
CompletionQueue *self, PyObject *ignored) {
grpc_completion_queue_shutdown(self->c_cq);
Py_RETURN_NONE;
}

@ -1,196 +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 "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
PyMethodDef pygrpc_Server_methods[] = {
{"request_call", (PyCFunction)pygrpc_Server_request_call,
METH_KEYWORDS, ""},
{"add_http2_port", (PyCFunction)pygrpc_Server_add_http2_port,
METH_KEYWORDS, ""},
{"start", (PyCFunction)pygrpc_Server_start, METH_NOARGS, ""},
{"shutdown", (PyCFunction)pygrpc_Server_shutdown, METH_KEYWORDS, ""},
{"cancel_all_calls", (PyCFunction)pygrpc_Server_cancel_all_calls,
METH_NOARGS, ""},
{NULL}
};
const char pygrpc_Server_doc[] = "See grpc._adapter._types.Server.";
PyTypeObject pygrpc_Server_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Server", /* tp_name */
sizeof(Server), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Server_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Server_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Server_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_Server_new /* tp_new */
};
Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
Server *self;
CompletionQueue *cq;
PyObject *py_args;
grpc_channel_args c_args;
char *keywords[] = {"cq", "args", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O:Server", keywords,
&pygrpc_CompletionQueue_type, &cq, &py_args)) {
return NULL;
}
if (!pygrpc_produce_channel_args(py_args, &c_args)) {
return NULL;
}
self = (Server *)type->tp_alloc(type, 0);
self->c_serv = grpc_server_create(&c_args, NULL);
grpc_server_register_completion_queue(self->c_serv, cq->c_cq, NULL);
pygrpc_discard_channel_args(c_args);
self->cq = cq;
Py_INCREF(self->cq);
self->shutdown_called = 0;
return self;
}
void pygrpc_Server_dealloc(Server *self) {
grpc_server_destroy(self->c_serv);
Py_XDECREF(self->cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_Server_request_call(
Server *self, PyObject *args, PyObject *kwargs) {
CompletionQueue *cq;
PyObject *user_tag;
pygrpc_tag *tag;
Call *empty_call;
grpc_call_error errcode;
static char *keywords[] = {"cq", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "O!O", keywords,
&pygrpc_CompletionQueue_type, &cq, &user_tag)) {
return NULL;
}
empty_call = pygrpc_Call_new_empty(cq);
tag = pygrpc_produce_request_tag(user_tag, empty_call);
errcode = grpc_server_request_call(
self->c_serv, &tag->call->c_call, &tag->request_call_details,
&tag->request_metadata, tag->call->cq->c_cq, self->cq->c_cq, tag);
Py_DECREF(empty_call);
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Server_add_http2_port(
Server *self, PyObject *args, PyObject *kwargs) {
const char *addr;
ServerCredentials *creds = NULL;
int port;
static char *keywords[] = {"addr", "creds", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "s|O!:add_http2_port", keywords,
&addr, &pygrpc_ServerCredentials_type, &creds)) {
return NULL;
}
if (creds) {
port = grpc_server_add_secure_http2_port(
self->c_serv, addr, creds->c_creds);
} else {
port = grpc_server_add_insecure_http2_port(self->c_serv, addr);
}
return PyInt_FromLong(port);
}
PyObject *pygrpc_Server_start(Server *self, PyObject *ignored) {
grpc_server_start(self->c_serv);
self->shutdown_called = 0;
Py_RETURN_NONE;
}
PyObject *pygrpc_Server_shutdown(
Server *self, PyObject *args, PyObject *kwargs) {
PyObject *user_tag;
pygrpc_tag *tag;
static char *keywords[] = {"tag", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) {
return NULL;
}
tag = pygrpc_produce_server_shutdown_tag(user_tag);
grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag);
self->shutdown_called = 1;
Py_RETURN_NONE;
}
PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused) {
if (!self->shutdown_called) {
PyErr_SetString(
PyExc_RuntimeError,
"shutdown must have been called prior to calling cancel_all_calls!");
return NULL;
}
grpc_server_cancel_all_calls(self->c_serv);
Py_RETURN_NONE;
}

@ -1,137 +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 "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_ServerCredentials_methods[] = {
{"ssl", (PyCFunction)pygrpc_ServerCredentials_ssl,
METH_CLASS|METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_ServerCredentials_doc[] = "";
PyTypeObject pygrpc_ServerCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"ServerCredentials", /* tp_name */
sizeof(ServerCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_ServerCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_ServerCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_ServerCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_ServerCredentials_dealloc(ServerCredentials *self) {
grpc_server_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
ServerCredentials *pygrpc_ServerCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ServerCredentials *self;
const char *root_certs;
PyObject *py_key_cert_pairs;
grpc_ssl_pem_key_cert_pair *key_cert_pairs;
int force_client_auth;
size_t num_key_cert_pairs;
size_t i;
static char *keywords[] = {
"root_certs", "key_cert_pairs", "force_client_auth", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zOi:ssl", keywords,
&root_certs, &py_key_cert_pairs, &force_client_auth)) {
return NULL;
}
if (!PyList_Check(py_key_cert_pairs)) {
PyErr_SetString(PyExc_TypeError, "expected a list of 2-tuples of strings");
return NULL;
}
num_key_cert_pairs = PyList_Size(py_key_cert_pairs);
key_cert_pairs =
gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs);
for (i = 0; i < num_key_cert_pairs; ++i) {
PyObject *item = PyList_GET_ITEM(py_key_cert_pairs, i);
const char *key;
const char *cert;
if (!PyArg_ParseTuple(item, "zz", &key, &cert)) {
gpr_free(key_cert_pairs);
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuples of strings");
return NULL;
}
key_cert_pairs[i].private_key = key;
key_cert_pairs[i].cert_chain = cert;
}
self = (ServerCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_ssl_server_credentials_create(
root_certs, key_cert_pairs, num_key_cert_pairs, force_client_auth, NULL);
gpr_free(key_cert_pairs);
return self;
}

@ -1,524 +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 <math.h>
#include <string.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/byte_buffer_reader.h>
#include <grpc/support/alloc.h>
#include <grpc/support/slice.h>
#include <grpc/support/time.h>
#include <grpc/support/string_util.h>
#include "grpc/_adapter/_c/types.h"
pygrpc_tag *pygrpc_produce_batch_tag(
PyObject *user_tag, Call *call, grpc_op *ops, size_t nops) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = call;
Py_XINCREF(tag->call);
tag->ops = gpr_malloc(sizeof(grpc_op)*nops);
memcpy(tag->ops, ops, sizeof(grpc_op)*nops);
tag->nops = nops;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = empty_call;
Py_XINCREF(tag->call);
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 1;
return tag;
}
pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = NULL;
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = NULL;
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
void pygrpc_discard_tag(pygrpc_tag *tag) {
if (!tag) {
return;
}
Py_XDECREF(tag->user_tag);
Py_XDECREF(tag->call);
gpr_free(tag->ops);
grpc_call_details_destroy(&tag->request_call_details);
grpc_metadata_array_destroy(&tag->request_metadata);
gpr_free(tag);
}
PyObject *pygrpc_consume_event(grpc_event event) {
pygrpc_tag *tag;
PyObject *result;
if (event.type == GRPC_QUEUE_TIMEOUT) {
Py_RETURN_NONE;
}
tag = event.tag;
switch (event.type) {
case GRPC_QUEUE_SHUTDOWN:
result = Py_BuildValue("iOOOOO", GRPC_QUEUE_SHUTDOWN,
Py_None, Py_None, Py_None, Py_None, Py_True);
break;
case GRPC_OP_COMPLETE:
if (tag->is_new_call) {
result = Py_BuildValue(
"iOO(ssd)[(iNOOOO)]O", GRPC_OP_COMPLETE, tag->user_tag, tag->call,
tag->request_call_details.method, tag->request_call_details.host,
pygrpc_cast_gpr_timespec_to_double(tag->request_call_details.deadline),
GRPC_OP_RECV_INITIAL_METADATA,
pygrpc_cast_metadata_array_to_pyseq(tag->request_metadata), Py_None,
Py_None, Py_None, Py_None,
event.success ? Py_True : Py_False);
} else {
result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag,
tag->call ? (PyObject*)tag->call : Py_None, Py_None,
pygrpc_consume_ops(tag->ops, tag->nops),
event.success ? Py_True : Py_False);
}
break;
default:
PyErr_SetString(PyExc_ValueError,
"unknown completion type; could not translate event");
return NULL;
}
pygrpc_discard_tag(tag);
return result;
}
int pygrpc_produce_op(PyObject *op, grpc_op *result) {
static const int OP_TUPLE_SIZE = 6;
static const int STATUS_TUPLE_SIZE = 2;
static const int TYPE_INDEX = 0;
static const int INITIAL_METADATA_INDEX = 1;
static const int TRAILING_METADATA_INDEX = 2;
static const int MESSAGE_INDEX = 3;
static const int STATUS_INDEX = 4;
static const int STATUS_CODE_INDEX = 0;
static const int STATUS_DETAILS_INDEX = 1;
static const int WRITE_FLAGS_INDEX = 5;
int type;
Py_ssize_t message_size;
char *message;
char *status_details;
gpr_slice message_slice;
grpc_op c_op;
if (!PyTuple_Check(op)) {
PyErr_SetString(PyExc_TypeError, "expected tuple op");
return 0;
}
if (PyTuple_Size(op) != OP_TUPLE_SIZE) {
char *buf;
gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE);
PyErr_SetString(PyExc_ValueError, buf);
gpr_free(buf);
return 0;
}
type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX));
if (PyErr_Occurred()) {
return 0;
}
c_op.op = type;
c_op.reserved = NULL;
c_op.flags = PyInt_AsLong(PyTuple_GET_ITEM(op, WRITE_FLAGS_INDEX));
if (PyErr_Occurred()) {
return 0;
}
switch (type) {
case GRPC_OP_SEND_INITIAL_METADATA:
if (!pygrpc_cast_pyseq_to_send_metadata(
PyTuple_GetItem(op, INITIAL_METADATA_INDEX),
&c_op.data.send_initial_metadata.metadata,
&c_op.data.send_initial_metadata.count)) {
return 0;
}
break;
case GRPC_OP_SEND_MESSAGE:
PyString_AsStringAndSize(
PyTuple_GET_ITEM(op, MESSAGE_INDEX), &message, &message_size);
message_slice = gpr_slice_from_copied_buffer(message, message_size);
c_op.data.send_message = grpc_raw_byte_buffer_create(&message_slice, 1);
gpr_slice_unref(message_slice);
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Don't need to fill in any other fields. */
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
if (!pygrpc_cast_pyseq_to_send_metadata(
PyTuple_GetItem(op, TRAILING_METADATA_INDEX),
&c_op.data.send_status_from_server.trailing_metadata,
&c_op.data.send_status_from_server.trailing_metadata_count)) {
return 0;
}
if (!PyTuple_Check(PyTuple_GET_ITEM(op, STATUS_INDEX))) {
char *buf;
gpr_asprintf(&buf, "expected tuple status in op of length %d",
STATUS_TUPLE_SIZE);
PyErr_SetString(PyExc_ValueError, buf);
gpr_free(buf);
return 0;
}
c_op.data.send_status_from_server.status = PyInt_AsLong(
PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_CODE_INDEX));
status_details = PyString_AsString(
PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_DETAILS_INDEX));
if (PyErr_Occurred()) {
return 0;
}
c_op.data.send_status_from_server.status_details =
gpr_malloc(strlen(status_details) + 1);
strcpy((char *)c_op.data.send_status_from_server.status_details,
status_details);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
c_op.data.recv_initial_metadata = gpr_malloc(sizeof(grpc_metadata_array));
grpc_metadata_array_init(c_op.data.recv_initial_metadata);
break;
case GRPC_OP_RECV_MESSAGE:
c_op.data.recv_message = gpr_malloc(sizeof(grpc_byte_buffer *));
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
c_op.data.recv_status_on_client.trailing_metadata =
gpr_malloc(sizeof(grpc_metadata_array));
grpc_metadata_array_init(c_op.data.recv_status_on_client.trailing_metadata);
c_op.data.recv_status_on_client.status =
gpr_malloc(sizeof(grpc_status_code *));
c_op.data.recv_status_on_client.status_details =
gpr_malloc(sizeof(char *));
*c_op.data.recv_status_on_client.status_details = NULL;
c_op.data.recv_status_on_client.status_details_capacity =
gpr_malloc(sizeof(size_t));
*c_op.data.recv_status_on_client.status_details_capacity = 0;
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
c_op.data.recv_close_on_server.cancelled = gpr_malloc(sizeof(int));
break;
default:
return 0;
}
*result = c_op;
return 1;
}
void pygrpc_discard_op(grpc_op op) {
size_t i;
switch(op.op) {
case GRPC_OP_SEND_INITIAL_METADATA:
/* Whenever we produce send-metadata, we allocate new strings (to handle
arbitrary sequence input as opposed to just lists or just tuples). We
thus must free those elements. */
for (i = 0; i < op.data.send_initial_metadata.count; ++i) {
gpr_free((void *)op.data.send_initial_metadata.metadata[i].key);
gpr_free((void *)op.data.send_initial_metadata.metadata[i].value);
}
gpr_free(op.data.send_initial_metadata.metadata);
break;
case GRPC_OP_SEND_MESSAGE:
grpc_byte_buffer_destroy(op.data.send_message);
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Don't need to free any fields. */
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
/* Whenever we produce send-metadata, we allocate new strings (to handle
arbitrary sequence input as opposed to just lists or just tuples). We
thus must free those elements. */
for (i = 0; i < op.data.send_status_from_server.trailing_metadata_count;
++i) {
gpr_free(
(void *)op.data.send_status_from_server.trailing_metadata[i].key);
gpr_free(
(void *)op.data.send_status_from_server.trailing_metadata[i].value);
}
gpr_free(op.data.send_status_from_server.trailing_metadata);
gpr_free((char *)op.data.send_status_from_server.status_details);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
grpc_metadata_array_destroy(op.data.recv_initial_metadata);
gpr_free(op.data.recv_initial_metadata);
break;
case GRPC_OP_RECV_MESSAGE:
grpc_byte_buffer_destroy(*op.data.recv_message);
gpr_free(op.data.recv_message);
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
grpc_metadata_array_destroy(op.data.recv_status_on_client.trailing_metadata);
gpr_free(op.data.recv_status_on_client.trailing_metadata);
gpr_free(op.data.recv_status_on_client.status);
gpr_free(*op.data.recv_status_on_client.status_details);
gpr_free(op.data.recv_status_on_client.status_details);
gpr_free(op.data.recv_status_on_client.status_details_capacity);
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
gpr_free(op.data.recv_close_on_server.cancelled);
break;
}
}
PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops) {
static const int TYPE_INDEX = 0;
static const int INITIAL_METADATA_INDEX = 1;
static const int TRAILING_METADATA_INDEX = 2;
static const int MESSAGE_INDEX = 3;
static const int STATUS_INDEX = 4;
static const int CANCELLED_INDEX = 5;
static const int OPRESULT_LENGTH = 6;
PyObject *list;
size_t i;
size_t j;
char *bytes;
size_t bytes_size;
PyObject *results = PyList_New(nops);
if (!results) {
return NULL;
}
for (i = 0; i < nops; ++i) {
PyObject *result = PyTuple_Pack(OPRESULT_LENGTH, Py_None, Py_None, Py_None,
Py_None, Py_None, Py_None);
PyTuple_SetItem(result, TYPE_INDEX, PyInt_FromLong(op[i].op));
switch(op[i].op) {
case GRPC_OP_RECV_INITIAL_METADATA:
PyTuple_SetItem(result, INITIAL_METADATA_INDEX,
list=PyList_New(op[i].data.recv_initial_metadata->count));
for (j = 0; j < op[i].data.recv_initial_metadata->count; ++j) {
grpc_metadata md = op[i].data.recv_initial_metadata->metadata[j];
PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value,
(Py_ssize_t)md.value_length));
}
break;
case GRPC_OP_RECV_MESSAGE:
if (*op[i].data.recv_message) {
pygrpc_byte_buffer_to_bytes(
*op[i].data.recv_message, &bytes, &bytes_size);
PyTuple_SetItem(result, MESSAGE_INDEX,
PyString_FromStringAndSize(bytes, bytes_size));
gpr_free(bytes);
} else {
PyTuple_SetItem(result, MESSAGE_INDEX, Py_BuildValue(""));
}
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
PyTuple_SetItem(
result, TRAILING_METADATA_INDEX,
list = PyList_New(op[i].data.recv_status_on_client.trailing_metadata->count));
for (j = 0; j < op[i].data.recv_status_on_client.trailing_metadata->count; ++j) {
grpc_metadata md =
op[i].data.recv_status_on_client.trailing_metadata->metadata[j];
PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value,
(Py_ssize_t)md.value_length));
}
PyTuple_SetItem(
result, STATUS_INDEX, Py_BuildValue(
"is", *op[i].data.recv_status_on_client.status,
*op[i].data.recv_status_on_client.status_details));
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
PyTuple_SetItem(
result, CANCELLED_INDEX,
PyBool_FromLong(*op[i].data.recv_close_on_server.cancelled));
break;
default:
break;
}
pygrpc_discard_op(op[i]);
PyList_SetItem(results, i, result);
}
return results;
}
double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) {
timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME);
return timespec.tv_sec + 1e-9*timespec.tv_nsec;
}
/* Because C89 doesn't have a way to check for infinity... */
static int pygrpc_isinf(double x) {
return x * 0 != 0;
}
gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) {
gpr_timespec result;
if (pygrpc_isinf(seconds)) {
result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME)
: gpr_inf_past(GPR_CLOCK_REALTIME);
} else {
result.tv_sec = (time_t)seconds;
result.tv_nsec = ((seconds - result.tv_sec) * 1e9);
result.clock_type = GPR_CLOCK_REALTIME;
}
return result;
}
int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args) {
size_t num_args = PyList_Size(py_args);
size_t i;
grpc_channel_args args;
args.num_args = num_args;
args.args = gpr_malloc(sizeof(grpc_arg) * num_args);
for (i = 0; i < args.num_args; ++i) {
char *key;
PyObject *value;
if (!PyArg_ParseTuple(PyList_GetItem(py_args, i), "zO", &key, &value)) {
gpr_free(args.args);
args.num_args = 0;
args.args = NULL;
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuple of str and str|int|None");
return 0;
}
args.args[i].key = key;
if (PyInt_Check(value)) {
args.args[i].type = GRPC_ARG_INTEGER;
args.args[i].value.integer = PyInt_AsLong(value);
} else if (PyString_Check(value)) {
args.args[i].type = GRPC_ARG_STRING;
args.args[i].value.string = PyString_AsString(value);
} else if (value == Py_None) {
--args.num_args;
--i;
continue;
} else {
gpr_free(args.args);
args.num_args = 0;
args.args = NULL;
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuple of str and str|int|None");
return 0;
}
}
*c_args = args;
return 1;
}
void pygrpc_discard_channel_args(grpc_channel_args args) {
gpr_free(args.args);
}
int pygrpc_cast_pyseq_to_send_metadata(
PyObject *pyseq, grpc_metadata **metadata, size_t *count) {
size_t i;
Py_ssize_t value_length;
char *key;
char *value;
if (!PySequence_Check(pyseq)) {
return 0;
}
*count = PySequence_Size(pyseq);
*metadata = gpr_malloc(sizeof(grpc_metadata) * *count);
for (i = 0; i < *count; ++i) {
PyObject *item = PySequence_GetItem(pyseq, i);
if (!PyArg_ParseTuple(item, "ss#", &key, &value, &value_length)) {
Py_DECREF(item);
gpr_free(*metadata);
*count = 0;
*metadata = NULL;
return 0;
} else {
(*metadata)[i].key = gpr_strdup(key);
(*metadata)[i].value = gpr_malloc(value_length);
memcpy((void *)(*metadata)[i].value, value, value_length);
Py_DECREF(item);
}
(*metadata)[i].value_length = value_length;
}
return 1;
}
PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) {
PyObject *result = PyTuple_New(metadata.count);
size_t i;
for (i = 0; i < metadata.count; ++i) {
PyTuple_SetItem(
result, i, Py_BuildValue(
"ss#", metadata.metadata[i].key, metadata.metadata[i].value,
(Py_ssize_t)metadata.metadata[i].value_length));
if (PyErr_Occurred()) {
Py_DECREF(result);
return NULL;
}
}
return result;
}
void pygrpc_byte_buffer_to_bytes(
grpc_byte_buffer *buffer, char **result, size_t *result_size) {
grpc_byte_buffer_reader reader;
gpr_slice slice;
char *read_result = NULL;
size_t size = 0;
grpc_byte_buffer_reader_init(&reader, buffer);
while (grpc_byte_buffer_reader_next(&reader, &slice)) {
read_result = gpr_realloc(read_result, size + GPR_SLICE_LENGTH(slice));
memcpy(read_result + size, GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice));
size = size + GPR_SLICE_LENGTH(slice);
gpr_slice_unref(slice);
}
*result_size = size;
*result = read_result;
}

@ -27,29 +27,22 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import time
import unittest
import collections
from grpc._adapter import _c
from grpc._adapter import _types
from grpc.beta import interfaces
class AuthMetadataContext(collections.namedtuple(
'AuthMetadataContext', [
'service_url',
'method_name'
]), interfaces.GRPCAuthMetadataContext):
pass
class CTypeSmokeTest(unittest.TestCase):
def testCompletionQueueUpDown(self):
completion_queue = _c.CompletionQueue()
del completion_queue
class AuthMetadataPluginCallback(interfaces.GRPCAuthMetadataContext):
def testServerUpDown(self):
completion_queue = _c.CompletionQueue()
serv = _c.Server(completion_queue, [])
del serv
del completion_queue
def __init__(self, callback):
self._callback = callback
def testChannelUpDown(self):
channel = _c.Channel('[::]:0', [])
del channel
if __name__ == '__main__':
unittest.main(verbosity=2)
def __call__(self, metadata, error):
self._callback(metadata, error)

@ -115,16 +115,20 @@ class Call(object):
return call
def invoke(self, completion_queue, metadata_tag, finish_tag):
err0 = self._internal.start_batch([
err = self._internal.start_batch([
_types.OpArgs.send_initial_metadata(self._metadata)
], _IGNORE_ME_TAG)
err1 = self._internal.start_batch([
if err != _types.CallError.OK:
return err
err = self._internal.start_batch([
_types.OpArgs.recv_initial_metadata()
], _TagAdapter(metadata_tag, Event.Kind.METADATA_ACCEPTED))
err2 = self._internal.start_batch([
if err != _types.CallError.OK:
return err
err = self._internal.start_batch([
_types.OpArgs.recv_status_on_client()
], _TagAdapter(finish_tag, Event.Kind.FINISH))
return err0 if err0 != _types.CallError.OK else err1 if err1 != _types.CallError.OK else err2 if err2 != _types.CallError.OK else _types.CallError.OK
return err
def write(self, message, tag, flags):
return self._internal.start_batch([
@ -158,7 +162,8 @@ class Call(object):
def status(self, status, tag):
return self._internal.start_batch([
_types.OpArgs.send_status_from_server(self._metadata, status.code, status.details)
_types.OpArgs.send_status_from_server(
self._metadata, status.code, status.details)
], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED))
def cancel(self):
@ -168,20 +173,17 @@ class Call(object):
return self._internal.peer()
def set_credentials(self, creds):
return self._internal.set_credentials(creds._internal)
return self._internal.set_credentials(creds)
class Channel(object):
"""Adapter from old _low.Channel interface to new _low.Channel."""
def __init__(self, hostport, client_credentials, server_host_override=None):
def __init__(self, hostport, channel_credentials, server_host_override=None):
args = []
if server_host_override:
args.append((_types.GrpcChannelArgumentKeys.SSL_TARGET_NAME_OVERRIDE.value, server_host_override))
creds = None
if client_credentials:
creds = client_credentials._internal
self._internal = _low.Channel(hostport, args, creds)
self._internal = _low.Channel(hostport, args, channel_credentials)
class CompletionQueue(object):
@ -192,7 +194,7 @@ class CompletionQueue(object):
def get(self, deadline=None):
if deadline is None:
ev = self._internal.next()
ev = self._internal.next(float('+inf'))
else:
ev = self._internal.next(deadline)
if ev is None:
@ -240,7 +242,7 @@ class Server(object):
if server_credentials is None:
return self._internal.add_http2_port(addr, None)
else:
return self._internal.add_http2_port(addr, server_credentials._internal)
return self._internal.add_http2_port(addr, server_credentials)
def start(self):
return self._internal.start()
@ -248,20 +250,9 @@ class Server(object):
def service(self, tag):
return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED))
def cancel_all_calls(self):
self._internal.cancel_all_calls()
def stop(self):
return self._internal.shutdown(_TagAdapter(None, Event.Kind.STOP))
class ClientCredentials(object):
"""Adapter from old _low.ClientCredentials interface to new _low.ChannelCredentials."""
def __init__(self, root_certificates, private_key, certificate_chain):
self._internal = _low.ChannelCredentials.ssl(root_certificates, private_key, certificate_chain)
class ServerCredentials(object):
"""Adapter from old _low.ServerCredentials interface to new _low.ServerCredentials."""
def __init__(self, root_credentials, pair_sequence, force_client_auth):
self._internal = _low.ServerCredentials.ssl(
root_credentials, list(pair_sequence), force_client_auth)

@ -27,36 +27,157 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import threading
from grpc import _grpcio_metadata
from grpc._adapter import _c
from grpc._cython import cygrpc
from grpc._adapter import _implementations
from grpc._adapter import _types
_USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__)
ChannelCredentials = _c.ChannelCredentials
CallCredentials = _c.CallCredentials
ServerCredentials = _c.ServerCredentials
ChannelCredentials = cygrpc.ChannelCredentials
CallCredentials = cygrpc.CallCredentials
ServerCredentials = cygrpc.ServerCredentials
channel_credentials_composite = cygrpc.channel_credentials_composite
call_credentials_composite = cygrpc.call_credentials_composite
def server_credentials_ssl(root_credentials, pair_sequence, force_client_auth):
return cygrpc.server_credentials_ssl(
root_credentials,
[cygrpc.SslPemKeyCertPair(key, pem) for key, pem in pair_sequence],
force_client_auth)
def channel_credentials_ssl(
root_certificates, private_key, certificate_chain):
pair = None
if private_key is not None or certificate_chain is not None:
pair = cygrpc.SslPemKeyCertPair(private_key, certificate_chain)
return cygrpc.channel_credentials_ssl(root_certificates, pair)
class _WrappedCygrpcCallback(object):
def __init__(self, cygrpc_callback):
self.is_called = False
self.error = None
self.is_called_lock = threading.Lock()
self.cygrpc_callback = cygrpc_callback
def _invoke_failure(self, error):
# TODO(atash) translate different Exception superclasses into different
# status codes.
self.cygrpc_callback(
cygrpc.Metadata([]), cygrpc.StatusCode.internal, error.message)
def _invoke_success(self, metadata):
try:
cygrpc_metadata = cygrpc.Metadata(
cygrpc.Metadatum(key, value)
for key, value in metadata)
except Exception as error:
self._invoke_failure(error)
return
self.cygrpc_callback(cygrpc_metadata, cygrpc.StatusCode.ok, '')
def __call__(self, metadata, error):
with self.is_called_lock:
if self.is_called:
raise RuntimeError('callback should only ever be invoked once')
if self.error:
self._invoke_failure(self.error)
return
self.is_called = True
if error is None:
self._invoke_success(metadata)
else:
self._invoke_failure(error)
def notify_failure(self, error):
with self.is_called_lock:
if not self.is_called:
self.error = error
class _WrappedPlugin(object):
def __init__(self, plugin):
self.plugin = plugin
def __call__(self, context, cygrpc_callback):
wrapped_cygrpc_callback = _WrappedCygrpcCallback(cygrpc_callback)
wrapped_context = _implementations.AuthMetadataContext(context.service_url,
context.method_name)
try:
self.plugin(
wrapped_context,
_implementations.AuthMetadataPluginCallback(wrapped_cygrpc_callback))
except Exception as error:
wrapped_cygrpc_callback.notify_failure(error)
raise
def call_credentials_metadata_plugin(plugin, name):
"""
Args:
plugin: A callable accepting a _types.AuthMetadataContext
object and a callback (itself accepting a list of metadata key/value
2-tuples and a None-able exception value). The callback must be eventually
called, but need not be called in plugin's invocation.
plugin's invocation must be non-blocking.
"""
return cygrpc.call_credentials_metadata_plugin(
cygrpc.CredentialsMetadataPlugin(_WrappedPlugin(plugin), name))
class CompletionQueue(_types.CompletionQueue):
def __init__(self):
self.completion_queue = _c.CompletionQueue()
self.completion_queue = cygrpc.CompletionQueue()
def next(self, deadline=float('+inf')):
raw_event = self.completion_queue.next(deadline)
if raw_event is None:
raw_event = self.completion_queue.poll(cygrpc.Timespec(deadline))
if raw_event.type == cygrpc.CompletionType.queue_timeout:
return None
event = _types.Event(*raw_event)
if event.call is not None:
event = event._replace(call=Call(event.call))
if event.call_details is not None:
event = event._replace(call_details=_types.CallDetails(*event.call_details))
if event.results is not None:
new_results = [_types.OpResult(*r) for r in event.results]
new_results = [r if r.status is None else r._replace(status=_types.Status(_types.StatusCode(r.status[0]), r.status[1])) for r in new_results]
event = event._replace(results=new_results)
return event
event_type = raw_event.type
event_tag = raw_event.tag
event_call = Call(raw_event.operation_call)
if raw_event.request_call_details:
event_call_details = _types.CallDetails(
raw_event.request_call_details.method,
raw_event.request_call_details.host,
float(raw_event.request_call_details.deadline))
else:
event_call_details = None
event_success = raw_event.success
event_results = []
if raw_event.is_new_request:
event_results.append(_types.OpResult(
_types.OpType.RECV_INITIAL_METADATA, raw_event.request_metadata,
None, None, None, None))
else:
if raw_event.batch_operations:
for operation in raw_event.batch_operations:
result_type = operation.type
result_initial_metadata = operation.received_metadata_or_none
result_trailing_metadata = operation.received_metadata_or_none
result_message = operation.received_message_or_none
if result_message is not None:
result_message = result_message.bytes()
result_cancelled = operation.received_cancelled_or_none
if operation.has_status:
result_status = _types.Status(
operation.received_status_code_or_none,
operation.received_status_details_or_none)
else:
result_status = None
event_results.append(
_types.OpResult(result_type, result_initial_metadata,
result_trailing_metadata, result_message,
result_status, result_cancelled))
return _types.Event(event_type, event_tag, event_call, event_call_details,
event_results, event_success)
def shutdown(self):
self.completion_queue.shutdown()
@ -68,7 +189,36 @@ class Call(_types.Call):
self.call = call
def start_batch(self, ops, tag):
return self.call.start_batch(ops, tag)
translated_ops = []
for op in ops:
if op.type == _types.OpType.SEND_INITIAL_METADATA:
translated_op = cygrpc.operation_send_initial_metadata(
cygrpc.Metadata(
cygrpc.Metadatum(key, value)
for key, value in op.initial_metadata))
elif op.type == _types.OpType.SEND_MESSAGE:
translated_op = cygrpc.operation_send_message(op.message)
elif op.type == _types.OpType.SEND_CLOSE_FROM_CLIENT:
translated_op = cygrpc.operation_send_close_from_client()
elif op.type == _types.OpType.SEND_STATUS_FROM_SERVER:
translated_op = cygrpc.operation_send_status_from_server(
cygrpc.Metadata(
cygrpc.Metadatum(key, value)
for key, value in op.trailing_metadata),
op.status.code,
op.status.details)
elif op.type == _types.OpType.RECV_INITIAL_METADATA:
translated_op = cygrpc.operation_receive_initial_metadata()
elif op.type == _types.OpType.RECV_MESSAGE:
translated_op = cygrpc.operation_receive_message()
elif op.type == _types.OpType.RECV_STATUS_ON_CLIENT:
translated_op = cygrpc.operation_receive_status_on_client()
elif op.type == _types.OpType.RECV_CLOSE_ON_SERVER:
translated_op = cygrpc.operation_receive_close_on_server()
else:
raise ValueError('unexpected operation type {}'.format(op.type))
translated_ops.append(translated_op)
return self.call.start_batch(cygrpc.Operations(translated_ops), tag)
def cancel(self, code=None, details=None):
if code is None and details is None:
@ -86,14 +236,20 @@ class Call(_types.Call):
class Channel(_types.Channel):
def __init__(self, target, args, creds=None):
args = list(args) + [(_c.PRIMARY_USER_AGENT_KEY, _USER_AGENT)]
args = list(args) + [
(cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)]
args = cygrpc.ChannelArgs(
cygrpc.ChannelArg(key, value) for key, value in args)
if creds is None:
self.channel = _c.Channel(target, args)
self.channel = cygrpc.Channel(target, args)
else:
self.channel = _c.Channel(target, args, creds)
self.channel = cygrpc.Channel(target, args, creds)
def create_call(self, completion_queue, method, host, deadline=None):
return Call(self.channel.create_call(completion_queue.completion_queue, method, host, deadline))
internal_call = self.channel.create_call(
None, 0, completion_queue.completion_queue, method, host,
cygrpc.Timespec(deadline))
return Call(internal_call)
def check_connectivity_state(self, try_to_connect):
return self.channel.check_connectivity_state(try_to_connect)
@ -101,7 +257,8 @@ class Channel(_types.Channel):
def watch_connectivity_state(self, last_observed_state, deadline,
completion_queue, tag):
self.channel.watch_connectivity_state(
last_observed_state, deadline, completion_queue.completion_queue, tag)
last_observed_state, cygrpc.Timespec(deadline),
completion_queue.completion_queue, tag)
def target(self):
return self.channel.target()
@ -112,7 +269,11 @@ _NO_TAG = object()
class Server(_types.Server):
def __init__(self, completion_queue, args):
self.server = _c.Server(completion_queue.completion_queue, args)
args = cygrpc.ChannelArgs(
cygrpc.ChannelArg(key, value) for key, value in args)
self.server = cygrpc.Server(args)
self.server.register_completion_queue(completion_queue.completion_queue)
self.server_queue = completion_queue
def add_http2_port(self, addr, creds=None):
if creds is None:
@ -124,10 +285,11 @@ class Server(_types.Server):
return self.server.start()
def shutdown(self, tag=None):
return self.server.shutdown(tag)
return self.server.shutdown(self.server_queue.completion_queue, tag)
def request_call(self, completion_queue, tag):
return self.server.request_call(completion_queue.completion_queue, tag)
return self.server.request_call(completion_queue.completion_queue,
self.server_queue.completion_queue, tag)
def cancel_all_calls(self):
return self.server.cancel_all_calls()

@ -31,6 +31,8 @@ import abc
import collections
import enum
from grpc._cython import cygrpc
class GrpcChannelArgumentKeys(enum.Enum):
"""Mirrors keys used in grpc_channel_args for GRPC-specific arguments."""
@ -40,77 +42,77 @@ class GrpcChannelArgumentKeys(enum.Enum):
@enum.unique
class CallError(enum.IntEnum):
"""Mirrors grpc_call_error in the C core."""
OK = 0
ERROR = 1
ERROR_NOT_ON_SERVER = 2
ERROR_NOT_ON_CLIENT = 3
ERROR_ALREADY_ACCEPTED = 4
ERROR_ALREADY_INVOKED = 5
ERROR_NOT_INVOKED = 6
ERROR_ALREADY_FINISHED = 7
ERROR_TOO_MANY_OPERATIONS = 8
ERROR_INVALID_FLAGS = 9
ERROR_INVALID_METADATA = 10
OK = cygrpc.CallError.ok
ERROR = cygrpc.CallError.error
ERROR_NOT_ON_SERVER = cygrpc.CallError.not_on_server
ERROR_NOT_ON_CLIENT = cygrpc.CallError.not_on_client
ERROR_ALREADY_ACCEPTED = cygrpc.CallError.already_accepted
ERROR_ALREADY_INVOKED = cygrpc.CallError.already_invoked
ERROR_NOT_INVOKED = cygrpc.CallError.not_invoked
ERROR_ALREADY_FINISHED = cygrpc.CallError.already_finished
ERROR_TOO_MANY_OPERATIONS = cygrpc.CallError.too_many_operations
ERROR_INVALID_FLAGS = cygrpc.CallError.invalid_flags
ERROR_INVALID_METADATA = cygrpc.CallError.invalid_metadata
@enum.unique
class StatusCode(enum.IntEnum):
"""Mirrors grpc_status_code in the C core."""
OK = 0
CANCELLED = 1
UNKNOWN = 2
INVALID_ARGUMENT = 3
DEADLINE_EXCEEDED = 4
NOT_FOUND = 5
ALREADY_EXISTS = 6
PERMISSION_DENIED = 7
RESOURCE_EXHAUSTED = 8
FAILED_PRECONDITION = 9
ABORTED = 10
OUT_OF_RANGE = 11
UNIMPLEMENTED = 12
INTERNAL = 13
UNAVAILABLE = 14
DATA_LOSS = 15
UNAUTHENTICATED = 16
OK = cygrpc.StatusCode.ok
CANCELLED = cygrpc.StatusCode.cancelled
UNKNOWN = cygrpc.StatusCode.unknown
INVALID_ARGUMENT = cygrpc.StatusCode.invalid_argument
DEADLINE_EXCEEDED = cygrpc.StatusCode.deadline_exceeded
NOT_FOUND = cygrpc.StatusCode.not_found
ALREADY_EXISTS = cygrpc.StatusCode.already_exists
PERMISSION_DENIED = cygrpc.StatusCode.permission_denied
RESOURCE_EXHAUSTED = cygrpc.StatusCode.resource_exhausted
FAILED_PRECONDITION = cygrpc.StatusCode.failed_precondition
ABORTED = cygrpc.StatusCode.aborted
OUT_OF_RANGE = cygrpc.StatusCode.out_of_range
UNIMPLEMENTED = cygrpc.StatusCode.unimplemented
INTERNAL = cygrpc.StatusCode.internal
UNAVAILABLE = cygrpc.StatusCode.unavailable
DATA_LOSS = cygrpc.StatusCode.data_loss
UNAUTHENTICATED = cygrpc.StatusCode.unauthenticated
@enum.unique
class OpWriteFlags(enum.IntEnum):
"""Mirrors defined write-flag constants in the C core."""
WRITE_BUFFER_HINT = 1
WRITE_NO_COMPRESS = 2
WRITE_BUFFER_HINT = cygrpc.WriteFlag.buffer_hint
WRITE_NO_COMPRESS = cygrpc.WriteFlag.no_compress
@enum.unique
class OpType(enum.IntEnum):
"""Mirrors grpc_op_type in the C core."""
SEND_INITIAL_METADATA = 0
SEND_MESSAGE = 1
SEND_CLOSE_FROM_CLIENT = 2
SEND_STATUS_FROM_SERVER = 3
RECV_INITIAL_METADATA = 4
RECV_MESSAGE = 5
RECV_STATUS_ON_CLIENT = 6
RECV_CLOSE_ON_SERVER = 7
SEND_INITIAL_METADATA = cygrpc.OperationType.send_initial_metadata
SEND_MESSAGE = cygrpc.OperationType.send_message
SEND_CLOSE_FROM_CLIENT = cygrpc.OperationType.send_close_from_client
SEND_STATUS_FROM_SERVER = cygrpc.OperationType.send_status_from_server
RECV_INITIAL_METADATA = cygrpc.OperationType.receive_initial_metadata
RECV_MESSAGE = cygrpc.OperationType.receive_message
RECV_STATUS_ON_CLIENT = cygrpc.OperationType.receive_status_on_client
RECV_CLOSE_ON_SERVER = cygrpc.OperationType.receive_close_on_server
@enum.unique
class EventType(enum.IntEnum):
"""Mirrors grpc_completion_type in the C core."""
QUEUE_SHUTDOWN = 0
QUEUE_TIMEOUT = 1 # if seen on the Python side, something went horridly wrong
OP_COMPLETE = 2
QUEUE_SHUTDOWN = cygrpc.CompletionType.queue_shutdown
QUEUE_TIMEOUT = cygrpc.CompletionType.queue_timeout
OP_COMPLETE = cygrpc.CompletionType.operation_complete
@enum.unique
class ConnectivityState(enum.IntEnum):
"""Mirrors grpc_connectivity_state in the C core."""
IDLE = 0
CONNECTING = 1
READY = 2
TRANSIENT_FAILURE = 3
FATAL_FAILURE = 4
IDLE = cygrpc.ConnectivityState.idle
CONNECTING = cygrpc.ConnectivityState.connecting
READY = cygrpc.ConnectivityState.ready
TRANSIENT_FAILURE = cygrpc.ConnectivityState.transient_failure
FATAL_FAILURE = cygrpc.ConnectivityState.fatal_failure
class Status(collections.namedtuple(

@ -53,24 +53,24 @@ cdef class Call:
self.c_call, cy_operations.c_ops, cy_operations.c_nops,
<cpython.PyObject *>operation_tag, NULL)
def cancel(self,
grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE,
details=None):
def cancel(
self, grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE,
details=None):
if not self.is_valid:
raise ValueError("invalid call object cannot be used from Python")
if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE):
raise ValueError("if error_code is specified, so must details "
"(and vice-versa)")
if isinstance(details, bytes):
pass
elif isinstance(details, basestring):
details = details.encode()
else:
raise TypeError("expected details to be str or bytes")
if error_code != grpc.GRPC_STATUS__DO_NOT_USE:
if isinstance(details, bytes):
pass
elif isinstance(details, basestring):
details = details.encode()
else:
raise TypeError("expected details to be str or bytes")
self.references.append(details)
return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details,
NULL)
return grpc.grpc_call_cancel_with_status(
self.c_call, error_code, details, NULL)
else:
return grpc.grpc_call_cancel(self.c_call, NULL)
@ -79,6 +79,12 @@ cdef class Call:
return grpc.grpc_call_set_credentials(
self.c_call, call_credentials.c_credentials)
def peer(self):
cdef char *peer = grpc.grpc_call_get_peer(self.c_call)
result = <bytes>peer
grpc.gpr_free(peer)
return result
def __dealloc__(self):
if self.c_call != NULL:
grpc.grpc_call_destroy(self.c_call)

@ -27,6 +27,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport completion_queue
from grpc._cython._cygrpc cimport credentials
@ -70,12 +72,16 @@ cdef class Channel:
method = method.encode()
else:
raise TypeError("expected method to be str or bytes")
if isinstance(host, bytes):
cdef char *host_c_string = NULL
if host is None:
pass
elif isinstance(host, bytes):
host_c_string = host
elif isinstance(host, basestring):
host = host.encode()
host_c_string = host
else:
raise TypeError("expected host to be str or bytes")
raise TypeError("expected host to be str, bytes, or None")
cdef call.Call operation_call = call.Call()
operation_call.references = [self, method, host, queue]
cdef grpc.grpc_call *parent_call = NULL
@ -83,10 +89,29 @@ cdef class Channel:
parent_call = parent.c_call
operation_call.c_call = grpc.grpc_channel_create_call(
self.c_channel, parent_call, flags,
queue.c_completion_queue, method, host, deadline.c_time,
queue.c_completion_queue, method, host_c_string, deadline.c_time,
NULL)
return operation_call
def check_connectivity_state(self, bint try_to_connect):
return grpc.grpc_channel_check_connectivity_state(self.c_channel,
try_to_connect)
def watch_connectivity_state(
self, last_observed_state, records.Timespec deadline not None,
completion_queue.CompletionQueue queue not None, tag):
cdef records.OperationTag operation_tag = records.OperationTag(tag)
cpython.Py_INCREF(operation_tag)
grpc.grpc_channel_watch_connectivity_state(
self.c_channel, last_observed_state, deadline.c_time,
queue.c_completion_queue, <cpython.PyObject *>operation_tag)
def target(self):
cdef char * target = grpc.grpc_channel_get_target(self.c_channel)
result = <bytes>target
grpc.gpr_free(target)
return result
def __dealloc__(self):
if self.c_channel != NULL:
grpc.grpc_channel_destroy(self.c_channel)

@ -62,6 +62,8 @@ cdef class CompletionQueue:
cdef grpc.grpc_event event
# Poll within a critical section
# TODO consider making queue polling contention a hard error to enable
# easier bug discovery
with self.poll_condition:
while self.is_polling:
self.poll_condition.wait(float(deadline) - time.time())
@ -74,10 +76,12 @@ cdef class CompletionQueue:
self.poll_condition.notify()
if event.type == grpc.GRPC_QUEUE_TIMEOUT:
return records.Event(event.type, False, None, None, None, None, None)
return records.Event(
event.type, False, None, None, None, None, False, None)
elif event.type == grpc.GRPC_QUEUE_SHUTDOWN:
self.is_shutdown = True
return records.Event(event.type, True, None, None, None, None, None)
return records.Event(
event.type, True, None, None, None, None, False, None)
else:
if event.tag != NULL:
tag = <records.OperationTag>event.tag
@ -97,7 +101,8 @@ cdef class CompletionQueue:
operation_call.references.extend(tag.references)
return records.Event(
event.type, event.success, user_tag, operation_call,
request_call_details, request_metadata, batch_operations)
request_call_details, request_metadata, tag.is_new_request,
batch_operations)
def shutdown(self):
grpc.grpc_completion_queue_shutdown(self.c_completion_queue)

@ -27,7 +27,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport records
cdef class ChannelCredentials:
@ -49,3 +52,23 @@ cdef class ServerCredentials:
cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs
cdef size_t c_ssl_pem_key_cert_pairs_count
cdef list references
cdef class CredentialsMetadataPlugin:
cdef object plugin_callback
cdef str plugin_name
cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self)
cdef class AuthMetadataContext:
cdef grpc.grpc_auth_metadata_context context
cdef void plugin_get_metadata(
void *state, grpc.grpc_auth_metadata_context context,
grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil
cdef void plugin_destroy_c_plugin_state(void *state)

@ -27,6 +27,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport records
@ -71,19 +73,80 @@ cdef class ServerCredentials:
def __cinit__(self):
self.c_credentials = NULL
self.references = []
def __dealloc__(self):
if self.c_credentials != NULL:
grpc.grpc_server_credentials_release(self.c_credentials)
cdef class CredentialsMetadataPlugin:
def __cinit__(self, object plugin_callback, str name):
"""
Args:
plugin_callback (callable): Callback accepting a service URL (str/bytes)
and callback object (accepting a records.Metadata,
grpc.grpc_status_code, and a str/bytes error message). This argument
when called should be non-blocking and eventually call the callback
object with the appropriate status code/details and metadata (if
successful).
name (str): Plugin name.
"""
if not callable(plugin_callback):
raise ValueError('expected callable plugin_callback')
self.plugin_callback = plugin_callback
self.plugin_name = name
@staticmethod
cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self):
cdef grpc.grpc_metadata_credentials_plugin result
result.get_metadata = plugin_get_metadata
result.destroy = plugin_destroy_c_plugin_state
result.state = <void *>self
result.type = self.plugin_name
cpython.Py_INCREF(self)
return result
cdef class AuthMetadataContext:
def __cinit__(self):
self.context.service_url = NULL
self.context.method_name = NULL
@property
def service_url(self):
return self.context.service_url
@property
def method_name(self):
return self.context.method_name
cdef void plugin_get_metadata(
void *state, grpc.grpc_auth_metadata_context context,
grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil:
def python_callback(
records.Metadata metadata, grpc.grpc_status_code status,
const char *error_details):
cb(user_data, metadata.c_metadata_array.metadata,
metadata.c_metadata_array.count, status, error_details)
cdef CredentialsMetadataPlugin self = <CredentialsMetadataPlugin>state
cdef AuthMetadataContext cy_context = AuthMetadataContext()
cy_context.context = context
self.plugin_callback(cy_context, python_callback)
cdef void plugin_destroy_c_plugin_state(void *state):
cpython.Py_DECREF(<CredentialsMetadataPlugin>state)
def channel_credentials_google_default():
cdef ChannelCredentials credentials = ChannelCredentials();
credentials.c_credentials = grpc.grpc_google_default_credentials_create()
return credentials
def channel_credentials_ssl(pem_root_certificates,
records.SslPemKeyCertPair ssl_pem_key_cert_pair):
records.SslPemKeyCertPair ssl_pem_key_cert_pair):
if pem_root_certificates is None:
pass
elif isinstance(pem_root_certificates, bytes):
@ -104,6 +167,7 @@ def channel_credentials_ssl(pem_root_certificates,
else:
credentials.c_credentials = grpc.grpc_ssl_credentials_create(
c_pem_root_certificates, NULL, NULL)
return credentials
def channel_credentials_composite(
ChannelCredentials credentials_1 not None,
@ -135,7 +199,6 @@ def call_credentials_google_compute_engine():
grpc.grpc_google_compute_engine_credentials_create(NULL))
return credentials
#TODO rename to something like client_credentials_service_account_jwt_access.
def call_credentials_service_account_jwt_access(
json_key, records.Timespec token_lifetime not None):
if isinstance(json_key, bytes):
@ -184,14 +247,25 @@ def call_credentials_google_iam(authorization_token, authority_selector):
credentials.references.append(authority_selector)
return credentials
def call_credentials_metadata_plugin(CredentialsMetadataPlugin plugin):
cdef CallCredentials credentials = CallCredentials()
credentials.c_credentials = (
grpc.grpc_metadata_credentials_create_from_plugin(plugin.make_c_plugin(),
NULL))
# TODO(atash): the following held reference is *probably* never necessary
credentials.references.append(plugin)
return credentials
def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
bint force_client_auth):
cdef char *c_pem_root_certs = NULL
if pem_root_certs is None:
pass
elif isinstance(pem_root_certs, bytes):
pass
c_pem_root_certs = pem_root_certs
elif isinstance(pem_root_certs, basestring):
pem_root_certs = pem_root_certs.encode()
c_pem_root_certs = pem_root_certs
else:
raise TypeError("expected pem_root_certs to be str or bytes")
pem_key_cert_pairs = list(pem_key_cert_pairs)
@ -212,7 +286,7 @@ def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
credentials.c_ssl_pem_key_cert_pairs[i] = (
(<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
credentials.c_credentials = grpc.grpc_ssl_server_credentials_create(
pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
credentials.c_ssl_pem_key_cert_pairs_count, force_client_auth, NULL)
return credentials

@ -132,6 +132,20 @@ cdef extern from "grpc/byte_buffer.h":
cdef extern from "grpc/grpc.h":
const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
const char *GRPC_ARG_ENABLE_CENSUS
const char *GRPC_ARG_MAX_CONCURRENT_STREAMS
const char *GRPC_ARG_MAX_MESSAGE_LENGTH
const char *GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
const char *GRPC_ARG_DEFAULT_AUTHORITY
const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
const char *GRPC_ARG_SECONDARY_USER_AGENT_STRING
const char *GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
const int GRPC_WRITE_BUFFER_HINT
const int GRPC_WRITE_NO_COMPRESS
const int GRPC_WRITE_USED_MASK
ctypedef struct grpc_completion_queue:
# We don't care about the internals (and in fact don't know them)
pass
@ -149,9 +163,9 @@ cdef extern from "grpc/grpc.h":
pass
ctypedef enum grpc_arg_type:
grpc_arg_string "GRPC_ARG_STRING"
grpc_arg_integer "GRPC_ARG_INTEGER"
grpc_arg_pointer "GRPC_ARG_POINTER"
GRPC_ARG_STRING
GRPC_ARG_INTEGER
GRPC_ARG_POINTER
ctypedef struct grpc_arg_value_pointer:
void *address "p"
@ -185,6 +199,13 @@ cdef extern from "grpc/grpc.h":
GRPC_CALL_ERROR_INVALID_FLAGS
GRPC_CALL_ERROR_INVALID_METADATA
ctypedef enum grpc_connectivity_state:
GRPC_CHANNEL_IDLE
GRPC_CHANNEL_CONNECTING
GRPC_CHANNEL_READY
GRPC_CHANNEL_TRANSIENT_FAILURE
GRPC_CHANNEL_FATAL_FAILURE
ctypedef struct grpc_metadata:
const char *key
const char *value
@ -279,9 +300,9 @@ cdef extern from "grpc/grpc.h":
grpc_status_code status,
const char *description,
void *reserved)
char *grpc_call_get_peer(grpc_call *call)
void grpc_call_destroy(grpc_call *call)
grpc_channel *grpc_insecure_channel_create(const char *target,
const grpc_channel_args *args,
void *reserved)
@ -291,6 +312,12 @@ cdef extern from "grpc/grpc.h":
grpc_completion_queue *completion_queue,
const char *method, const char *host,
gpr_timespec deadline, void *reserved)
grpc_connectivity_state grpc_channel_check_connectivity_state(
grpc_channel *channel, int try_to_connect)
void grpc_channel_watch_connectivity_state(
grpc_channel *channel, grpc_connectivity_state last_observed_state,
gpr_timespec deadline, grpc_completion_queue *cq, void *tag)
char *grpc_channel_get_target(grpc_channel *channel)
void grpc_channel_destroy(grpc_channel *channel)
grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved)
@ -367,3 +394,27 @@ cdef extern from "grpc/grpc_security.h":
grpc_call_error grpc_call_set_credentials(grpc_call *call,
grpc_call_credentials *creds)
ctypedef struct grpc_auth_context:
# We don't care about the internals (and in fact don't know them)
pass
ctypedef struct grpc_auth_metadata_context:
const char *service_url
const char *method_name
const grpc_auth_context *channel_auth_context
ctypedef void (*grpc_credentials_plugin_metadata_cb)(
void *user_data, const grpc_metadata *creds_md, size_t num_creds_md,
grpc_status_code status, const char *error_details)
ctypedef struct grpc_metadata_credentials_plugin:
void (*get_metadata)(
void *state, grpc_auth_metadata_context context,
grpc_credentials_plugin_metadata_cb cb, void *user_data)
void (*destroy)(void *state)
void *state
const char *type
grpc_call_credentials *grpc_metadata_credentials_create_from_plugin(
grpc_metadata_credentials_plugin plugin, void *reserved)

@ -66,6 +66,7 @@ cdef class Event:
cdef readonly call.Call operation_call
# For Server.request_call
cdef readonly bint is_new_request
cdef readonly CallDetails request_call_details
cdef readonly Metadata request_metadata

@ -32,6 +32,30 @@ from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport server
class ConnectivityState:
idle = grpc.GRPC_CHANNEL_IDLE
connecting = grpc.GRPC_CHANNEL_CONNECTING
ready = grpc.GRPC_CHANNEL_READY
transient_failure = grpc.GRPC_CHANNEL_TRANSIENT_FAILURE
fatal_failure = grpc.GRPC_CHANNEL_FATAL_FAILURE
class ChannelArgKey:
enable_census = grpc.GRPC_ARG_ENABLE_CENSUS
max_concurrent_streams = grpc.GRPC_ARG_MAX_CONCURRENT_STREAMS
max_message_length = grpc.GRPC_ARG_MAX_MESSAGE_LENGTH
http2_initial_sequence_number = grpc.GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
default_authority = grpc.GRPC_ARG_DEFAULT_AUTHORITY
primary_user_agent_string = grpc.GRPC_ARG_PRIMARY_USER_AGENT_STRING
secondary_user_agent_string = grpc.GRPC_ARG_SECONDARY_USER_AGENT_STRING
ssl_target_name_override = grpc.GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
class WriteFlag:
buffer_hint = grpc.GRPC_WRITE_BUFFER_HINT
no_compress = grpc.GRPC_WRITE_NO_COMPRESS
class StatusCode:
ok = grpc.GRPC_STATUS_OK
cancelled = grpc.GRPC_STATUS_CANCELLED
@ -88,7 +112,10 @@ cdef class Timespec:
def __cinit__(self, time):
if time is None:
self.c_time = grpc.gpr_now(grpc.GPR_CLOCK_REALTIME)
elif isinstance(time, float):
return
if isinstance(time, int):
time = float(time)
if isinstance(time, float):
if time == float("+inf"):
self.c_time = grpc.gpr_inf_future(grpc.GPR_CLOCK_REALTIME)
elif time == float("-inf"):
@ -97,8 +124,11 @@ cdef class Timespec:
self.c_time.seconds = time
self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9
self.c_time.clock_type = grpc.GPR_CLOCK_REALTIME
elif isinstance(time, Timespec):
self.c_time = (<Timespec>time).c_time
else:
raise TypeError("expected time to be float")
raise TypeError("expected time to be float, int, or Timespec, not {}"
.format(type(time)))
@property
def seconds(self):
@ -166,6 +196,7 @@ cdef class Event:
object tag, call.Call operation_call,
CallDetails request_call_details,
Metadata request_metadata,
bint is_new_request,
Operations batch_operations):
self.type = type
self.success = success
@ -174,6 +205,7 @@ cdef class Event:
self.request_call_details = request_call_details
self.request_metadata = request_metadata
self.batch_operations = batch_operations
self.is_new_request = is_new_request
cdef class ByteBuffer:
@ -186,8 +218,14 @@ cdef class ByteBuffer:
pass
elif isinstance(data, basestring):
data = data.encode()
elif isinstance(data, ByteBuffer):
data = (<ByteBuffer>data).bytes()
if data is None:
self.c_byte_buffer = NULL
return
else:
raise TypeError("expected value to be of type str or bytes")
raise TypeError("expected value to be of type str, bytes, or "
"ByteBuffer, not {}".format(type(data)))
cdef char *c_data = data
data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data))
@ -409,12 +447,22 @@ cdef class Operation:
def type(self):
return self.c_op.type
@property
def has_status(self):
return self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
@property
def received_message(self):
if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
raise TypeError("self must be an operation receiving a message")
return self._received_message
@property
def received_message_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
return None
return self._received_message
@property
def received_metadata(self):
if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
@ -422,12 +470,25 @@ cdef class Operation:
raise TypeError("self must be an operation receiving metadata")
return self._received_metadata
@property
def received_metadata_or_none(self):
if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT):
return None
return self._received_metadata
@property
def received_status_code(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
raise TypeError("self must be an operation receiving a status code")
return self._received_status_code
@property
def received_status_code_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
return None
return self._received_status_code
@property
def received_status_details(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
@ -437,6 +498,15 @@ cdef class Operation:
else:
return None
@property
def received_status_details_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
return None
if self._received_status_details:
return self._received_status_details
else:
return None
@property
def received_cancelled(self):
if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
@ -444,6 +514,12 @@ cdef class Operation:
"information")
return False if self._received_cancelled == 0 else True
@property
def received_cancelled_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
return None
return False if self._received_cancelled == 0 else True
def __dealloc__(self):
# We *almost* don't need to do anything; most of the objects are handled by
# Python. The remaining one(s) are primitive fields filled in by GRPC core.

@ -132,7 +132,7 @@ cdef class Server:
def cancel_all_calls(self):
if not self.is_shutting_down:
raise ValueError("the server must be shutting down to cancel all calls")
raise RuntimeError("the server must be shutting down to cancel all calls")
elif self.is_shutdown:
return
else:

@ -44,6 +44,9 @@ from grpc._cython._cygrpc import completion_queue
from grpc._cython._cygrpc import records
from grpc._cython._cygrpc import server
ConnectivityState = records.ConnectivityState
ChannelArgKey = records.ChannelArgKey
WriteFlag = records.WriteFlag
StatusCode = records.StatusCode
CallError = records.CallError
CompletionType = records.CompletionType
@ -73,6 +76,8 @@ Operations = records.Operations
CallCredentials = credentials.CallCredentials
ChannelCredentials = credentials.ChannelCredentials
ServerCredentials = credentials.ServerCredentials
CredentialsMetadataPlugin = credentials.CredentialsMetadataPlugin
AuthMetadataContext = credentials.AuthMetadataContext
channel_credentials_google_default = (
credentials.channel_credentials_google_default)
@ -88,6 +93,7 @@ call_credentials_jwt_access = (
call_credentials_refresh_token = (
credentials.call_credentials_google_refresh_token)
call_credentials_google_iam = credentials.call_credentials_google_iam
call_credentials_metadata_plugin = credentials.call_credentials_metadata_plugin
server_credentials_ssl = credentials.server_credentials_ssl
CompletionQueue = completion_queue.CompletionQueue

@ -182,15 +182,15 @@ class _Kernel(object):
def _on_finish_event(self, operation_id, event, rpc_state):
_no_longer_due(_FINISH, rpc_state, operation_id, self._rpc_states)
if event.status.code is _intermediary_low.Code.OK:
if event.status.code == _intermediary_low.Code.OK:
termination = links.Ticket.Termination.COMPLETION
elif event.status.code is _intermediary_low.Code.CANCELLED:
elif event.status.code == _intermediary_low.Code.CANCELLED:
termination = links.Ticket.Termination.CANCELLATION
elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED:
elif event.status.code == _intermediary_low.Code.DEADLINE_EXCEEDED:
termination = links.Ticket.Termination.EXPIRATION
elif event.status.code is _intermediary_low.Code.UNIMPLEMENTED:
elif event.status.code == _intermediary_low.Code.UNIMPLEMENTED:
termination = links.Ticket.Termination.REMOTE_FAILURE
elif event.status.code is _intermediary_low.Code.UNKNOWN:
elif event.status.code == _intermediary_low.Code.UNKNOWN:
termination = links.Ticket.Termination.LOCAL_FAILURE
else:
termination = links.Ticket.Termination.TRANSMISSION_FAILURE
@ -262,7 +262,7 @@ class _Kernel(object):
self._channel, self._completion_queue, '/%s/%s' % (group, method),
self._host, time.time() + timeout)
if options is not None and options.credentials is not None:
call.set_credentials(options.credentials._intermediary_low_credentials)
call.set_credentials(options.credentials._low_credentials)
if transformed_initial_metadata is not None:
for metadata_key, metadata_value in transformed_initial_metadata:
call.add_metadata(metadata_key, metadata_value)

@ -254,12 +254,12 @@ class _Kernel(object):
rpc_state = self._rpc_states[call]
_no_longer_due(_FINISH, rpc_state, call, self._rpc_states)
code = event.status.code
if code is _intermediary_low.Code.OK:
if code == _intermediary_low.Code.OK:
return
if code is _intermediary_low.Code.CANCELLED:
if code == _intermediary_low.Code.CANCELLED:
termination = links.Ticket.Termination.CANCELLATION
elif code is _intermediary_low.Code.DEADLINE_EXCEEDED:
elif code == _intermediary_low.Code.DEADLINE_EXCEEDED:
termination = links.Ticket.Termination.EXPIRATION
else:
termination = links.Ticket.Termination.TRANSMISSION_FAILURE

@ -44,6 +44,12 @@ _DEFAULT_TIMEOUT = 300
_MAXIMUM_TIMEOUT = 24 * 60 * 60
def _set_event():
event = threading.Event()
event.set()
return event
class _GRPCServicer(base.Servicer):
def __init__(self, delegate):
@ -61,86 +67,143 @@ class _GRPCServicer(base.Servicer):
raise
def _disassemble(grpc_link, end_link, pool, event, grace):
grpc_link.begin_stop()
end_link.stop(grace).wait()
grpc_link.end_stop()
grpc_link.join_link(utilities.NULL_LINK)
end_link.join_link(utilities.NULL_LINK)
if pool is not None:
pool.shutdown(wait=True)
event.set()
class _Server(interfaces.Server):
def __init__(
self, implementations, multi_implementation, pool, pool_size,
default_timeout, maximum_timeout, grpc_link):
self._lock = threading.Lock()
self._implementations = implementations
self._multi_implementation = multi_implementation
self._customer_pool = pool
self._pool_size = pool_size
self._default_timeout = default_timeout
self._maximum_timeout = maximum_timeout
self._grpc_link = grpc_link
class Server(interfaces.Server):
self._end_link = None
self._stop_events = None
self._pool = None
def __init__(self, grpc_link, end_link, pool):
self._grpc_link = grpc_link
self._end_link = end_link
self._pool = pool
def _start(self):
with self._lock:
if self._end_link is not None:
raise ValueError('Cannot start already-started server!')
if self._customer_pool is None:
self._pool = logging_pool.pool(self._pool_size)
assembly_pool = self._pool
else:
assembly_pool = self._customer_pool
servicer = _GRPCServicer(
_crust_implementations.servicer(
self._implementations, self._multi_implementation, assembly_pool))
self._end_link = _core_implementations.service_end_link(
servicer, self._default_timeout, self._maximum_timeout)
self._grpc_link.join_link(self._end_link)
self._end_link.join_link(self._grpc_link)
self._grpc_link.start()
self._end_link.start()
def _dissociate_links_and_shut_down_pool(self):
self._grpc_link.end_stop()
self._grpc_link.join_link(utilities.NULL_LINK)
self._end_link.join_link(utilities.NULL_LINK)
self._end_link = None
if self._pool is not None:
self._pool.shutdown(wait=True)
self._pool = None
def _stop_stopping(self):
self._dissociate_links_and_shut_down_pool()
for stop_event in self._stop_events:
stop_event.set()
self._stop_events = None
def _stop_started(self):
self._grpc_link.begin_stop()
self._end_link.stop(0).wait()
self._dissociate_links_and_shut_down_pool()
def _foreign_thread_stop(self, end_stop_event, stop_events):
end_stop_event.wait()
with self._lock:
if self._stop_events is stop_events:
self._stop_stopping()
def _schedule_stop(self, grace):
with self._lock:
if self._end_link is None:
return _set_event()
server_stop_event = threading.Event()
if self._stop_events is None:
self._stop_events = [server_stop_event]
self._grpc_link.begin_stop()
else:
self._stop_events.append(server_stop_event)
end_stop_event = self._end_link.stop(grace)
end_stop_thread = threading.Thread(
target=self._foreign_thread_stop,
args=(end_stop_event, self._stop_events))
end_stop_thread.start()
return server_stop_event
def _stop_now(self):
with self._lock:
if self._end_link is not None:
if self._stop_events is None:
self._stop_started()
else:
self._stop_stopping()
def add_insecure_port(self, address):
return self._grpc_link.add_port(address, None)
with self._lock:
if self._end_link is None:
return self._grpc_link.add_port(address, None)
else:
raise ValueError('Can\'t add port to serving server!')
def add_secure_port(self, address, server_credentials):
return self._grpc_link.add_port(
address, server_credentials._intermediary_low_credentials) # pylint: disable=protected-access
def _start(self):
self._grpc_link.join_link(self._end_link)
self._end_link.join_link(self._grpc_link)
self._grpc_link.start()
self._end_link.start()
def _stop(self, grace):
stop_event = threading.Event()
if 0 < grace:
disassembly_thread = threading.Thread(
target=_disassemble,
args=(
self._grpc_link, self._end_link, self._pool, stop_event, grace,))
disassembly_thread.start()
return stop_event
else:
_disassemble(self._grpc_link, self._end_link, self._pool, stop_event, 0)
return stop_event
with self._lock:
if self._end_link is None:
return self._grpc_link.add_port(
address, server_credentials._low_credentials) # pylint: disable=protected-access
else:
raise ValueError('Can\'t add port to serving server!')
def start(self):
self._start()
def stop(self, grace):
return self._stop(grace)
if 0 < grace:
return self._schedule_stop(grace)
else:
self._stop_now()
return _set_event()
def __enter__(self):
self._start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self._stop(0).wait()
self._stop_now()
return False
def __del__(self):
self._stop_now()
def server(
implementations, multi_implementation, request_deserializers,
response_serializers, thread_pool, thread_pool_size, default_timeout,
maximum_timeout):
if thread_pool is None:
service_thread_pool = logging_pool.pool(
_DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size)
assembly_thread_pool = service_thread_pool
else:
service_thread_pool = thread_pool
assembly_thread_pool = None
servicer = _GRPCServicer(
_crust_implementations.servicer(
implementations, multi_implementation, service_thread_pool))
grpc_link = service.service_link(request_deserializers, response_serializers)
end_link = _core_implementations.service_end_link(
servicer,
return _Server(
implementations, multi_implementation, thread_pool,
_DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size,
_DEFAULT_TIMEOUT if default_timeout is None else default_timeout,
_MAXIMUM_TIMEOUT if maximum_timeout is None else maximum_timeout)
return Server(grpc_link, end_link, assembly_thread_pool)
_MAXIMUM_TIMEOUT if maximum_timeout is None else maximum_timeout,
grpc_link)

@ -42,76 +42,114 @@ _DEFAULT_POOL_SIZE = 6
class _AutoIntermediary(object):
def __init__(self, delegate, on_deletion):
def __init__(self, up, down, delegate):
self._lock = threading.Lock()
self._up = up
self._down = down
self._in_context = False
self._delegate = delegate
self._on_deletion = on_deletion
def __getattr__(self, attr):
return getattr(self._delegate, attr)
with self._lock:
if self._delegate is None:
raise AttributeError('No useful attributes out of context!')
else:
return getattr(self._delegate, attr)
def __enter__(self):
return self
with self._lock:
if self._in_context:
raise ValueError('Already in context!')
elif self._delegate is None:
self._delegate = self._up()
self._in_context = True
return self
def __exit__(self, exc_type, exc_val, exc_tb):
return False
with self._lock:
if not self._in_context:
raise ValueError('Not in context!')
self._down()
self._in_context = False
self._delegate = None
return False
def __del__(self):
self._on_deletion()
with self._lock:
if self._delegate is not None:
self._down()
self._delegate = None
class _StubAssemblyManager(object):
def __init__(
self, thread_pool, thread_pool_size, end_link, grpc_link, stub_creator):
self._thread_pool = thread_pool
self._pool_size = thread_pool_size
self._end_link = end_link
self._grpc_link = grpc_link
self._stub_creator = stub_creator
self._own_pool = None
def up(self):
if self._thread_pool is None:
self._own_pool = logging_pool.pool(
_DEFAULT_POOL_SIZE if self._pool_size is None else self._pool_size)
assembly_pool = self._own_pool
else:
assembly_pool = self._thread_pool
self._end_link.join_link(self._grpc_link)
self._grpc_link.join_link(self._end_link)
self._end_link.start()
self._grpc_link.start()
return self._stub_creator(self._end_link, assembly_pool)
def down(self):
self._end_link.stop(0).wait()
self._grpc_link.stop()
self._end_link.join_link(utilities.NULL_LINK)
self._grpc_link.join_link(utilities.NULL_LINK)
if self._own_pool is not None:
self._own_pool.shutdown(wait=True)
self._own_pool = None
def _assemble(
channel, host, metadata_transformer, request_serializers,
response_deserializers, thread_pool, thread_pool_size):
response_deserializers, thread_pool, thread_pool_size, stub_creator):
end_link = _core_implementations.invocation_end_link()
grpc_link = invocation.invocation_link(
channel, host, metadata_transformer, request_serializers,
response_deserializers)
if thread_pool is None:
invocation_pool = logging_pool.pool(
_DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size)
assembly_pool = invocation_pool
else:
invocation_pool = thread_pool
assembly_pool = None
end_link.join_link(grpc_link)
grpc_link.join_link(end_link)
end_link.start()
grpc_link.start()
return end_link, grpc_link, invocation_pool, assembly_pool
def _disassemble(end_link, grpc_link, pool):
end_link.stop(24 * 60 * 60).wait()
grpc_link.stop()
end_link.join_link(utilities.NULL_LINK)
grpc_link.join_link(utilities.NULL_LINK)
if pool is not None:
pool.shutdown(wait=True)
def _wrap_assembly(stub, end_link, grpc_link, assembly_pool):
disassembly_thread = threading.Thread(
target=_disassemble, args=(end_link, grpc_link, assembly_pool))
return _AutoIntermediary(stub, disassembly_thread.start)
stub_assembly_manager = _StubAssemblyManager(
thread_pool, thread_pool_size, end_link, grpc_link, stub_creator)
stub = stub_assembly_manager.up()
return _AutoIntermediary(
stub_assembly_manager.up, stub_assembly_manager.down, stub)
def _dynamic_stub_creator(service, cardinalities):
def create_dynamic_stub(end_link, invocation_pool):
return _crust_implementations.dynamic_stub(
end_link, service, cardinalities, invocation_pool)
return create_dynamic_stub
def generic_stub(
channel, host, metadata_transformer, request_serializers,
response_deserializers, thread_pool, thread_pool_size):
end_link, grpc_link, invocation_pool, assembly_pool = _assemble(
return _assemble(
channel, host, metadata_transformer, request_serializers,
response_deserializers, thread_pool, thread_pool_size)
stub = _crust_implementations.generic_stub(end_link, invocation_pool)
return _wrap_assembly(stub, end_link, grpc_link, assembly_pool)
response_deserializers, thread_pool, thread_pool_size,
_crust_implementations.generic_stub)
def dynamic_stub(
channel, host, service, cardinalities, metadata_transformer,
request_serializers, response_deserializers, thread_pool,
thread_pool_size):
end_link, grpc_link, invocation_pool, assembly_pool = _assemble(
return _assemble(
channel, host, metadata_transformer, request_serializers,
response_deserializers, thread_pool, thread_pool_size)
stub = _crust_implementations.dynamic_stub(
end_link, service, cardinalities, invocation_pool)
return _wrap_assembly(stub, end_link, grpc_link, assembly_pool)
response_deserializers, thread_pool, thread_pool_size,
_dynamic_stub_creator(service, cardinalities))

@ -36,6 +36,7 @@ import threading # pylint: disable=unused-import
# cardinality and face are referenced from specification in this module.
from grpc._adapter import _intermediary_low
from grpc._adapter import _low
from grpc._adapter import _types
from grpc.beta import _connectivity_channel
from grpc.beta import _server
@ -48,7 +49,7 @@ _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = (
'Exception calling channel subscription callback!')
class ClientCredentials(object):
class ChannelCredentials(object):
"""A value encapsulating the data required to create a secure Channel.
This class and its instances have no supported interface - it exists to define
@ -56,13 +57,12 @@ class ClientCredentials(object):
functions.
"""
def __init__(self, low_credentials, intermediary_low_credentials):
def __init__(self, low_credentials):
self._low_credentials = low_credentials
self._intermediary_low_credentials = intermediary_low_credentials
def ssl_client_credentials(root_certificates, private_key, certificate_chain):
"""Creates a ClientCredentials for use with an SSL-enabled Channel.
def ssl_channel_credentials(root_certificates, private_key, certificate_chain):
"""Creates a ChannelCredentials for use with an SSL-enabled Channel.
Args:
root_certificates: The PEM-encoded root certificates or None to ask for
@ -73,12 +73,73 @@ def ssl_client_credentials(root_certificates, private_key, certificate_chain):
certificate chain should be used.
Returns:
A ClientCredentials for use with an SSL-enabled Channel.
A ChannelCredentials for use with an SSL-enabled Channel.
"""
intermediary_low_credentials = _intermediary_low.ClientCredentials(
root_certificates, private_key, certificate_chain)
return ClientCredentials(
intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access
return ChannelCredentials(_low.channel_credentials_ssl(
root_certificates, private_key, certificate_chain))
class CallCredentials(object):
"""A value encapsulating data asserting an identity over an *established*
channel. May be composed with ChannelCredentials to always assert identity for
every call over that channel.
This class and its instances have no supported interface - it exists to define
the type of its instances and its instances exist to be passed to other
functions.
"""
def __init__(self, low_credentials):
self._low_credentials = low_credentials
def metadata_call_credentials(metadata_plugin, name=None):
"""Construct CallCredentials from an interfaces.GRPCAuthMetadataPlugin.
Args:
metadata_plugin: An interfaces.GRPCAuthMetadataPlugin to use in constructing
the CallCredentials object.
Returns:
A CallCredentials object for use in a GRPCCallOptions object.
"""
if name is None:
name = metadata_plugin.__name__
return CallCredentials(
_low.call_credentials_metadata_plugin(metadata_plugin, name))
def composite_call_credentials(call_credentials, additional_call_credentials):
"""Compose two CallCredentials to make a new one.
Args:
call_credentials: A CallCredentials object.
additional_call_credentials: Another CallCredentials object to compose on
top of call_credentials.
Returns:
A CallCredentials object for use in a GRPCCallOptions object.
"""
return CallCredentials(
_low.call_credentials_composite(
call_credentials._low_credentials,
additional_call_credentials._low_credentials))
def composite_channel_credentials(channel_credentials,
additional_call_credentials):
"""Compose ChannelCredentials on top of client credentials to make a new one.
Args:
channel_credentials: A ChannelCredentials object.
additional_call_credentials: A CallCredentials object to compose on
top of channel_credentials.
Returns:
A ChannelCredentials object for use in a GRPCCallOptions object.
"""
return ChannelCredentials(
_low.channel_credentials_composite(
channel_credentials._low_credentials,
additional_call_credentials._low_credentials))
class Channel(object):
@ -135,19 +196,19 @@ def insecure_channel(host, port):
return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access
def secure_channel(host, port, client_credentials):
def secure_channel(host, port, channel_credentials):
"""Creates a secure Channel to a remote host.
Args:
host: The name of the remote host to which to connect.
port: The port of the remote host to which to connect.
client_credentials: A ClientCredentials.
channel_credentials: A ChannelCredentials.
Returns:
A secure Channel to the remote host through which RPCs may be conducted.
"""
intermediary_low_channel = _intermediary_low.Channel(
'%s:%d' % (host, port), client_credentials._intermediary_low_credentials)
'%s:%d' % (host, port), channel_credentials._low_credentials)
return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access
@ -251,9 +312,8 @@ class ServerCredentials(object):
functions.
"""
def __init__(self, low_credentials, intermediary_low_credentials):
def __init__(self, low_credentials):
self._low_credentials = low_credentials
self._intermediary_low_credentials = intermediary_low_credentials
def ssl_server_credentials(
@ -282,11 +342,9 @@ def ssl_server_credentials(
raise ValueError(
'Illegal to require client auth without providing root certificates!')
else:
intermediary_low_credentials = _intermediary_low.ServerCredentials(
return ServerCredentials(_low.server_credentials_ssl(
root_certificates, private_key_certificate_chain_pairs,
require_client_auth)
return ServerCredentials(
intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access
require_client_auth))
class ServerOptions(object):

@ -100,14 +100,55 @@ def grpc_call_options(disable_compression=False, credentials=None):
disable_compression: A boolean indicating whether or not compression should
be disabled for the request object of the RPC. Only valid for
request-unary RPCs.
credentials: Reserved for gRPC per-call credentials. The type for this does
not exist yet at the Python level.
credentials: A CallCredentials object to use for the invoked RPC.
"""
if credentials is not None:
raise ValueError('`credentials` is a reserved argument')
return GRPCCallOptions(disable_compression, None, credentials)
class GRPCAuthMetadataContext(object):
"""Provides information to call credentials metadata plugins.
Attributes:
service_url: A string URL of the service being called into.
method_name: A string of the fully qualified method name being called.
"""
__metaclass__ = abc.ABCMeta
class GRPCAuthMetadataPluginCallback(object):
"""Callback object received by a metadata plugin."""
__metaclass__ = abc.ABCMeta
def __call__(self, metadata, error):
"""Inform the gRPC runtime of the metadata to construct a CallCredentials.
Args:
metadata: An iterable of 2-sequences (e.g. tuples) of metadata key/value
pairs.
error: An Exception to indicate error or None to indicate success.
"""
raise NotImplementedError()
class GRPCAuthMetadataPlugin(object):
"""
"""
__metaclass__ = abc.ABCMeta
def __call__(self, context, callback):
"""Invoke the plugin.
Must not block. Need only be called by the gRPC runtime.
Args:
context: A GRPCAuthMetadataContext providing information on what the
plugin is being used for.
callback: A GRPCAuthMetadataPluginCallback to be invoked either
synchronously or asynchronously.
"""
raise NotImplementedError()
class GRPCServicerContext(object):
"""Exposes gRPC-specific options and behaviors to code servicing RPCs."""
__metaclass__ = abc.ABCMeta

@ -85,35 +85,6 @@ def _future_shutdown(lock, cycle, event):
return in_future
def _termination_action(lock, stats, operation_id, cycle):
"""Constructs the termination action for a single operation.
Args:
lock: A lock to hold during the termination action.
stats: A mapping from base.Outcome.Kind values to integers to increment
with the outcome kind given to the termination action.
operation_id: The operation ID for the termination action.
cycle: A _Cycle value to be updated during the termination action.
Returns:
A callable that takes an operation outcome kind as its sole parameter and
that should be used as the termination action for the operation
associated with the given operation ID.
"""
def termination_action(outcome_kind):
with lock:
stats[outcome_kind] += 1
cycle.operations.pop(operation_id, None)
if not cycle.operations:
for action in cycle.idle_actions:
cycle.pool.submit(action)
cycle.idle_actions = []
if cycle.grace:
_cancel_futures(cycle.futures)
cycle.pool.shutdown(wait=False)
return termination_action
class _End(End):
"""An End implementation."""
@ -133,6 +104,31 @@ class _End(End):
self._cycle = None
def _termination_action(self, operation_id):
"""Constructs the termination action for a single operation.
Args:
operation_id: The operation ID for the termination action.
Returns:
A callable that takes an operation outcome kind as its sole parameter and
that should be used as the termination action for the operation
associated with the given operation ID.
"""
def termination_action(outcome_kind):
with self._lock:
self._stats[outcome_kind] += 1
self._cycle.operations.pop(operation_id, None)
if not self._cycle.operations:
for action in self._cycle.idle_actions:
self._cycle.pool.submit(action)
self._cycle.idle_actions = []
if self._cycle.grace:
_cancel_futures(self._cycle.futures)
self._cycle.pool.shutdown(wait=False)
self._cycle = None
return termination_action
def start(self):
"""See base.End.start for specification."""
with self._lock:
@ -174,8 +170,7 @@ class _End(End):
with self._lock:
if self._cycle is None or self._cycle.grace:
raise ValueError('Can\'t operate on stopped or stopping End!')
termination_action = _termination_action(
self._lock, self._stats, operation_id, self._cycle)
termination_action = self._termination_action(operation_id)
operation = _operation.invocation_operate(
operation_id, group, method, subscription, timeout, protocol_options,
initial_metadata, payload, completion, self._mate.accept_ticket,
@ -208,8 +203,7 @@ class _End(End):
if operation is not None:
operation.handle_ticket(ticket)
elif self._servicer_package is not None and not self._cycle.grace:
termination_action = _termination_action(
self._lock, self._stats, ticket.operation_id, self._cycle)
termination_action = self._termination_action(ticket.operation_id)
operation = _operation.service_operate(
self._servicer_package, ticket, self._mate.accept_ticket,
termination_action, self._cycle.pool)

@ -1,3 +1,4 @@
enum34>=1.0.4
futures>=2.2.0
cython>=0.23
coverage>=4.0

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

Loading…
Cancel
Save