Merge remote-tracking branch 'upstream/master'

pull/388/head
Vijay Pai 10 years ago
commit 08b5ba1cdd
  1. 7
      .editorconfig
  2. 6
      .gitignore
  3. 273
      Makefile
  4. 6
      README.md
  5. 125
      build.json
  6. 32
      examples/tips/client.cc
  7. 3
      examples/tips/client.h
  8. 45
      examples/tips/client_main.cc
  9. 2
      include/grpc/byte_buffer_reader.h
  10. 2
      include/grpc/support/port_platform.h
  11. 2
      include/grpc/support/slice_buffer.h
  12. 2
      src/core/channel/channel_args.c
  13. 3
      src/core/channel/channel_stack.c
  14. 2
      src/core/channel/http_client_filter.c
  15. 20
      src/core/channel/http_server_filter.c
  16. 84
      src/core/iomgr/fd_posix.c
  17. 4
      src/core/iomgr/fd_posix.h
  18. 2
      src/core/iomgr/iomgr.c
  19. 11
      src/core/iomgr/iomgr_posix.c
  20. 2
      src/core/iomgr/tcp_server.h
  21. 6
      src/core/iomgr/tcp_server_posix.c
  22. 64
      src/core/json/json.c
  23. 88
      src/core/json/json.h
  24. 49
      src/core/json/json_common.h
  25. 653
      src/core/json/json_reader.c
  26. 160
      src/core/json/json_reader.h
  27. 391
      src/core/json/json_string.c
  28. 252
      src/core/json/json_writer.c
  29. 93
      src/core/json/json_writer.h
  30. 3
      src/core/security/base64.c
  31. 49
      src/core/security/credentials.c
  32. 99
      src/core/security/json_token.c
  33. 4
      src/core/security/secure_endpoint.c
  34. 3
      src/core/security/server_secure_chttp2.c
  35. 4
      src/core/statistics/census_log.c
  36. 2
      src/core/statistics/census_rpc_stats.c
  37. 2
      src/core/statistics/hash_table.c
  38. 2
      src/core/statistics/window_stats.c
  39. 4
      src/core/support/cpu.h
  40. 5
      src/core/support/cpu_linux.c
  41. 4
      src/core/support/cpu_posix.c
  42. 1
      src/core/support/histogram.c
  43. 4
      src/core/support/slice_buffer.c
  44. 4
      src/core/support/string_posix.c
  45. 1
      src/core/support/thd_posix.c
  46. 2
      src/core/surface/server_chttp2.c
  47. 42
      src/core/transport/chttp2_transport.c
  48. 3
      src/core/tsi/fake_transport_security.c
  49. 7
      src/core/tsi/ssl_transport_security.c
  50. 62
      src/node/examples/stock.proto
  51. 43
      src/node/examples/stock_client.js
  52. 83
      src/node/examples/stock_server.js
  53. 22
      src/node/ext/byte_buffer.cc
  54. 4
      src/node/ext/byte_buffer.h
  55. 59
      src/node/ext/call.cc
  56. 62
      src/node/ext/event.cc
  57. 43
      src/node/interop/interop_client.js
  58. 2
      src/node/interop/interop_server.js
  59. 27
      src/node/src/server.js
  60. 43
      src/node/src/surface_server.js
  61. 24
      src/node/test/call_test.js
  62. 36
      src/node/test/client_server_test.js
  63. 59
      src/node/test/end_to_end_test.js
  64. 8
      src/node/test/interop_sanity_test.js
  65. 4
      src/node/test/server_test.js
  66. 2
      src/node/test/surface_test.js
  67. 7
      src/php/ext/grpc/byte_buffer.c
  68. 6
      src/php/lib/Grpc/ActiveCall.php
  69. 34
      src/php/lib/Grpc/BaseStub.php
  70. 9
      src/php/tests/interop/empty.php
  71. 13
      src/php/tests/interop/interop_client.php
  72. 213
      src/php/tests/interop/messages.php
  73. 34
      src/php/tests/interop/test.php
  74. 86
      src/python/setup.py
  75. 0
      src/python/src/__init__.py
  76. 0
      src/python/src/_adapter/__init__.py
  77. 17
      src/python/src/_adapter/_blocking_invocation_inline_service_test.py
  78. 77
      src/python/src/_adapter/_c.c
  79. 141
      src/python/src/_adapter/_c_test.py
  80. 292
      src/python/src/_adapter/_call.c
  81. 46
      src/python/src/_adapter/_call.h
  82. 109
      src/python/src/_adapter/_channel.c
  83. 46
      src/python/src/_adapter/_channel.h
  84. 76
      src/python/src/_adapter/_common.py
  85. 541
      src/python/src/_adapter/_completion_queue.c
  86. 48
      src/python/src/_adapter/_completion_queue.h
  87. 86
      src/python/src/_adapter/_datatypes.py
  88. 79
      src/python/src/_adapter/_error.c
  89. 42
      src/python/src/_adapter/_error.h
  90. 46
      src/python/src/_adapter/_event_invocation_synchronous_event_service_test.py
  91. 124
      src/python/src/_adapter/_face_test_case.py
  92. 46
      src/python/src/_adapter/_future_invocation_asynchronous_event_service_test.py
  93. 245
      src/python/src/_adapter/_links_test.py
  94. 97
      src/python/src/_adapter/_lonely_rear_link_test.py
  95. 55
      src/python/src/_adapter/_low.py
  96. 371
      src/python/src/_adapter/_low_test.py
  97. 261
      src/python/src/_adapter/_proto_scenarios.py
  98. 167
      src/python/src/_adapter/_server.c
  99. 44
      src/python/src/_adapter/_server.h
  100. 80
      src/python/src/_adapter/_test_links.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,7 @@
root = true
[**]
end_of_line = LF
indent_style = space
indent_size = 2
insert_final_newline = true
tab_width = 8

6
.gitignore vendored

@ -21,4 +21,8 @@ coverage
.run_tests_cache .run_tests_cache
# emacs temp files # emacs temp files
*~ *~
# vim temp files
.*.swp

File diff suppressed because one or more lines are too long

@ -24,7 +24,7 @@ Developers using gRPC typically start with the description of an RPC service
(a collection of methods), and generate client and server side interfaces (a collection of methods), and generate client and server side interfaces
which they use on the client-side and implement on the server side. which they use on the client-side and implement on the server side.
By default, gRPC uses [Protocol Buffers](github.com/google/protobuf) as the By default, gRPC uses [Protocol Buffers](https://github.com/google/protobuf) as the
Interface Definition Language (IDL) for describing both the service interface Interface Definition Language (IDL) for describing both the service interface
and the structure of the payload messages. It is possible to use other and the structure of the payload messages. It is possible to use other
alternatives if desired. alternatives if desired.
@ -67,7 +67,7 @@ fleshing out the details of each of the required operations.
A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory `Call Header`, followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata`). A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory `Call Header`, followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata`).
## Implementation over HTTP/2 ## Implementation over HTTP/2
The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPAC compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers). The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPACK compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers).
## Flow Control ## Flow Control
gRPC inherits the flow control mchanims in HTTP/2 and uses them to enable fine-grained control of the amount of memory used for buffering in-flight messages. gRPC inherits the flow control mechanisms in HTTP/2 and uses them to enable fine-grained control of the amount of memory used for buffering in-flight messages.

@ -61,8 +61,12 @@
"src/core/iomgr/tcp_posix.h", "src/core/iomgr/tcp_posix.h",
"src/core/iomgr/tcp_server.h", "src/core/iomgr/tcp_server.h",
"src/core/iomgr/time_averaged_stats.h", "src/core/iomgr/time_averaged_stats.h",
"src/core/iomgr/wakeup_fd_posix.h",
"src/core/iomgr/wakeup_fd_pipe.h", "src/core/iomgr/wakeup_fd_pipe.h",
"src/core/iomgr/wakeup_fd_posix.h",
"src/core/json/json.h",
"src/core/json/json_common.h",
"src/core/json/json_reader.h",
"src/core/json/json_writer.h",
"src/core/statistics/census_interface.h", "src/core/statistics/census_interface.h",
"src/core/statistics/census_log.h", "src/core/statistics/census_log.h",
"src/core/statistics/census_rpc_stats.h", "src/core/statistics/census_rpc_stats.h",
@ -144,6 +148,10 @@
"src/core/iomgr/wakeup_fd_nospecial.c", "src/core/iomgr/wakeup_fd_nospecial.c",
"src/core/iomgr/wakeup_fd_pipe.c", "src/core/iomgr/wakeup_fd_pipe.c",
"src/core/iomgr/wakeup_fd_posix.c", "src/core/iomgr/wakeup_fd_posix.c",
"src/core/json/json.c",
"src/core/json/json_reader.c",
"src/core/json/json_string.c",
"src/core/json/json_writer.c",
"src/core/statistics/census_init.c", "src/core/statistics/census_init.c",
"src/core/statistics/census_log.c", "src/core/statistics/census_log.c",
"src/core/statistics/census_rpc_stats.c", "src/core/statistics/census_rpc_stats.c",
@ -184,8 +192,7 @@
"src/core/transport/chttp2_transport.c", "src/core/transport/chttp2_transport.c",
"src/core/transport/metadata.c", "src/core/transport/metadata.c",
"src/core/transport/stream_op.c", "src/core/transport/stream_op.c",
"src/core/transport/transport.c", "src/core/transport/transport.c"
"third_party/cJSON/cJSON.c"
] ]
} }
], ],
@ -1185,6 +1192,48 @@
"gpr" "gpr"
] ]
}, },
{
"name": "json_rewrite",
"build": "test",
"language": "c",
"src": [
"test/core/json/json_rewrite.c"
],
"deps": [
"grpc",
"gpr"
],
"run": false
},
{
"name": "json_rewrite_test",
"build": "test",
"language": "c",
"src": [
"test/core/json/json_rewrite_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
},
{
"name": "json_test",
"build": "test",
"language": "c",
"src": [
"test/core/json/json_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "lame_client_test", "name": "lame_client_test",
"build": "test", "build": "test",
@ -1518,41 +1567,6 @@
], ],
"run": false "run": false
}, },
{
"name": "tips_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"examples/tips/client_main.cc"
],
"deps": [
"tips_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "tips_client_test",
"build": "test",
"language": "c++",
"src": [
"examples/tips/client_test.cc"
],
"deps": [
"tips_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "qps_client", "name": "qps_client",
"build": "test", "build": "test",
@ -1649,6 +1663,41 @@
"gpr_test_util", "gpr_test_util",
"gpr" "gpr"
] ]
},
{
"name": "tips_client",
"build": "test",
"language": "c++",
"src": [
"examples/tips/client_main.cc"
],
"deps": [
"tips_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
},
{
"name": "tips_client_test",
"build": "test",
"language": "c++",
"src": [
"examples/tips/client_test.cc"
],
"deps": [
"tips_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
} }
] ]
} }

@ -36,7 +36,11 @@
#include "examples/tips/client.h" #include "examples/tips/client.h"
using tech::pubsub::Topic; using tech::pubsub::Topic;
using tech::pubsub::DeleteTopicRequest;
using tech::pubsub::GetTopicRequest;
using tech::pubsub::PublisherService; using tech::pubsub::PublisherService;
using tech::pubsub::ListTopicsRequest;
using tech::pubsub::ListTopicsResponse;
namespace grpc { namespace grpc {
namespace examples { namespace examples {
@ -55,6 +59,34 @@ Status Client::CreateTopic(grpc::string topic) {
return stub_->CreateTopic(&context, request, &response); return stub_->CreateTopic(&context, request, &response);
} }
Status Client::ListTopics() {
ListTopicsRequest request;
ListTopicsResponse response;
ClientContext context;
return stub_->ListTopics(&context, request, &response);
}
Status Client::GetTopic(grpc::string topic) {
GetTopicRequest request;
Topic response;
ClientContext context;
request.set_topic(topic);
return stub_->GetTopic(&context, request, &response);
}
Status Client::DeleteTopic(grpc::string topic) {
DeleteTopicRequest request;
proto2::Empty response;
ClientContext context;
request.set_topic(topic);
return stub_->DeleteTopic(&context, request, &response);
}
} // namespace tips } // namespace tips
} // namespace examples } // namespace examples
} // namespace grpc } // namespace grpc

@ -47,6 +47,9 @@ class Client {
public: public:
Client(std::shared_ptr<grpc::ChannelInterface> channel); Client(std::shared_ptr<grpc::ChannelInterface> channel);
Status CreateTopic(grpc::string topic); Status CreateTopic(grpc::string topic);
Status GetTopic(grpc::string topic);
Status DeleteTopic(grpc::string topic);
Status ListTopics();
private: private:
std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_; std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;

@ -31,6 +31,13 @@
* *
*/ */
#include <chrono>
#include <fstream>
#include <memory>
#include <sstream>
#include <string>
#include <thread>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <google/gflags.h> #include <google/gflags.h>
@ -45,6 +52,20 @@
DEFINE_int32(server_port, 443, "Server port."); DEFINE_int32(server_port, 443, "Server port.");
DEFINE_string(server_host, DEFINE_string(server_host,
"pubsub-staging.googleapis.com", "Server host to connect to"); "pubsub-staging.googleapis.com", "Server host to connect to");
DEFINE_string(service_account_key_file, "",
"Path to service account json key file.");
DEFINE_string(oauth_scope, "", "Scope for OAuth tokens.");
grpc::string GetServiceAccountJsonKey() {
static grpc::string json_key;
if (json_key.empty()) {
std::ifstream json_key_file(FLAGS_service_account_key_file);
std::stringstream key_stream;
key_stream << json_key_file.rdbuf();
json_key = key_stream.str();
}
return json_key;
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
grpc_init(); grpc_init();
@ -56,8 +77,15 @@ int main(int argc, char** argv) {
snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(), snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
FLAGS_server_port); FLAGS_server_port);
std::unique_ptr<grpc::Credentials> creds = std::unique_ptr<grpc::Credentials> creds;
grpc::CredentialsFactory::ComputeEngineCredentials(); if (FLAGS_service_account_key_file != "") {
grpc::string json_key = GetServiceAccountJsonKey();
creds = grpc::CredentialsFactory::ServiceAccountCredentials(
json_key, FLAGS_oauth_scope, std::chrono::hours(1));
} else {
creds = grpc::CredentialsFactory::ComputeEngineCredentials();
}
std::shared_ptr<grpc::ChannelInterface> channel( std::shared_ptr<grpc::ChannelInterface> channel(
grpc::CreateTestChannel( grpc::CreateTestChannel(
host_port, host_port,
@ -67,8 +95,17 @@ int main(int argc, char** argv) {
creds)); creds));
grpc::examples::tips::Client client(channel); grpc::examples::tips::Client client(channel);
grpc::Status s = client.CreateTopic("test");
gpr_log(GPR_INFO, "return code %d", s.code()); grpc::Status s = client.CreateTopic("/topics/stoked-keyword-656/testtopics");
gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
s = client.GetTopic("/topics/stoked-keyword-656/testtopics");
gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
s = client.DeleteTopic("/topics/stoked-keyword-656/testtopics");
gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk()); GPR_ASSERT(s.IsOk());
channel.reset(); channel.reset();

@ -42,7 +42,7 @@ struct grpc_byte_buffer_reader {
/* Different current objects correspond to different types of byte buffers */ /* Different current objects correspond to different types of byte buffers */
union { union {
/* Index into a slice buffer's array of slices */ /* Index into a slice buffer's array of slices */
int index; unsigned index;
} current; } current;
}; };

@ -155,7 +155,7 @@ typedef uintmax_t gpr_uintmax;
typedef uintptr_t gpr_uintptr; typedef uintptr_t gpr_uintptr;
/* INT64_MAX is unavailable on some platforms. */ /* INT64_MAX is unavailable on some platforms. */
#define GPR_INT64_MAX (~(gpr_uint64)0 >> 1) #define GPR_INT64_MAX (gpr_int64)(~(gpr_uint64)0 >> 1)
/* maximum alignment needed for any type on this platform, rounded up to a /* maximum alignment needed for any type on this platform, rounded up to a
power of two */ power of two */

@ -73,7 +73,7 @@ size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice slice);
void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *slices, size_t n); void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *slices, size_t n);
/* add a very small (less than 8 bytes) amount of data to the end of a slice /* add a very small (less than 8 bytes) amount of data to the end of a slice
buffer: returns a pointer into which to add the data */ buffer: returns a pointer into which to add the data */
gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, int len); gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned len);
/* clear a slice buffer, unref all elements */ /* clear a slice buffer, unref all elements */
void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb); void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb);

@ -105,7 +105,7 @@ void grpc_channel_args_destroy(grpc_channel_args *a) {
} }
int grpc_channel_args_is_census_enabled(const grpc_channel_args *a) { int grpc_channel_args_is_census_enabled(const grpc_channel_args *a) {
int i; unsigned i;
if (a == NULL) return 0; if (a == NULL) return 0;
for (i = 0; i < a->num_args; i++) { for (i = 0; i < a->num_args; i++) {
if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_CENSUS)) { if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_CENSUS)) {

@ -125,7 +125,8 @@ void grpc_channel_stack_init(const grpc_channel_filter **filters,
call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data); call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data);
} }
GPR_ASSERT(user_data - (char *)stack == GPR_ASSERT(user_data > (char *)stack);
GPR_ASSERT((gpr_uintptr)(user_data - (char *)stack) ==
grpc_channel_stack_size(filters, filter_count)); grpc_channel_stack_size(filters, filter_count));
stack->call_stack_size = call_size; stack->call_stack_size = call_size;

@ -134,7 +134,7 @@ static void destroy_call_elem(grpc_call_element *elem) {
} }
static const char *scheme_from_args(const grpc_channel_args *args) { static const char *scheme_from_args(const grpc_channel_args *args) {
int i; unsigned i;
if (args != NULL) { if (args != NULL) {
for (i = 0; i < args->num_args; ++i) { for (i = 0; i < args->num_args; ++i) {
if (args->args[i].type == GRPC_ARG_STRING && if (args->args[i].type == GRPC_ARG_STRING &&

@ -319,8 +319,8 @@ static void init_channel_elem(grpc_channel_element *elem,
if (channeld->gettable_count == gettable_capacity) { if (channeld->gettable_count == gettable_capacity) {
gettable_capacity = gettable_capacity =
GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1); GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1);
channeld->gettables = channeld->gettables = gpr_realloc(channeld->gettables,
gpr_realloc(channeld->gettables, gettable_capacity * sizeof(gettable)); gettable_capacity * sizeof(gettable));
} }
g = &channeld->gettables[channeld->gettable_count++]; g = &channeld->gettables[channeld->gettable_count++];
g->path = grpc_mdelem_from_strings(mdctx, ":path", p->path); g->path = grpc_mdelem_from_strings(mdctx, ":path", p->path);
@ -328,15 +328,25 @@ static void init_channel_elem(grpc_channel_element *elem,
grpc_mdelem_from_strings(mdctx, "content-type", p->content_type); grpc_mdelem_from_strings(mdctx, "content-type", p->content_type);
slice = gpr_slice_from_copied_string(p->content); slice = gpr_slice_from_copied_string(p->content);
g->content = grpc_byte_buffer_create(&slice, 1); g->content = grpc_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
} }
} }
} }
/* Destructor for channel data */ /* Destructor for channel data */
static void destroy_channel_elem(grpc_channel_element *elem) { static void destroy_channel_elem(grpc_channel_element *elem) {
size_t i;
/* grab pointers to our data from the channel element */ /* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data; channel_data *channeld = elem->channel_data;
for (i = 0; i < channeld->gettable_count; i++) {
grpc_mdelem_unref(channeld->gettables[i].path);
grpc_mdelem_unref(channeld->gettables[i].content_type);
grpc_byte_buffer_destroy(channeld->gettables[i].content);
}
gpr_free(channeld->gettables);
grpc_mdelem_unref(channeld->te_trailers); grpc_mdelem_unref(channeld->te_trailers);
grpc_mdelem_unref(channeld->status_ok); grpc_mdelem_unref(channeld->status_ok);
grpc_mdelem_unref(channeld->status_not_found); grpc_mdelem_unref(channeld->status_not_found);
@ -350,6 +360,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
} }
const grpc_channel_filter grpc_http_server_filter = { const grpc_channel_filter grpc_http_server_filter = {
call_op, channel_op, sizeof(call_data), call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
init_call_elem, destroy_call_elem, sizeof(channel_data), sizeof(channel_data), init_channel_elem, destroy_channel_elem,
init_channel_elem, destroy_channel_elem, "http-server"}; "http-server"};

@ -47,12 +47,63 @@
enum descriptor_state { NOT_READY, READY, WAITING }; enum descriptor_state { NOT_READY, READY, WAITING };
/* We need to keep a freelist not because of any concerns of malloc performance
* but instead so that implementations with multiple threads in (for example)
* epoll_wait deal with the race between pollset removal and incoming poll
* notifications.
*
* The problem is that the poller ultimately holds a reference to this
* object, so it is very difficult to know when is safe to free it, at least
* without some expensive synchronization.
*
* If we keep the object freelisted, in the worst case losing this race just
* becomes a spurious read notification on a reused fd.
*/
/* TODO(klempner): We could use some form of polling generation count to know
* when these are safe to free. */
/* TODO(klempner): Consider disabling freelisting if we don't have multiple
* threads in poll on the same fd */
/* TODO(klempner): Batch these allocations to reduce fragmentation */
static grpc_fd *fd_freelist = NULL;
static gpr_mu fd_freelist_mu;
static void freelist_fd(grpc_fd *fd) {
gpr_free(fd->watchers);
gpr_mu_lock(&fd_freelist_mu);
fd->freelist_next = fd_freelist;
fd_freelist = fd;
gpr_mu_unlock(&fd_freelist_mu);
}
static grpc_fd *alloc_fd(int fd) {
grpc_fd *r = NULL;
gpr_mu_lock(&fd_freelist_mu);
if (fd_freelist != NULL) {
r = fd_freelist;
fd_freelist = fd_freelist->freelist_next;
}
gpr_mu_unlock(&fd_freelist_mu);
if (r == NULL) {
r = gpr_malloc(sizeof(grpc_fd));
gpr_mu_init(&r->set_state_mu);
gpr_mu_init(&r->watcher_mu);
}
gpr_atm_rel_store(&r->refst, 1);
gpr_atm_rel_store(&r->readst.state, NOT_READY);
gpr_atm_rel_store(&r->writest.state, NOT_READY);
gpr_atm_rel_store(&r->shutdown, 0);
r->fd = fd;
r->watchers = NULL;
r->watcher_count = 0;
r->watcher_capacity = 0;
r->freelist_next = NULL;
return r;
}
static void destroy(grpc_fd *fd) { static void destroy(grpc_fd *fd) {
grpc_iomgr_add_callback(fd->on_done, fd->on_done_user_data);
gpr_mu_destroy(&fd->set_state_mu); gpr_mu_destroy(&fd->set_state_mu);
gpr_free(fd->watchers); gpr_mu_destroy(&fd->watcher_mu);
gpr_free(fd); gpr_free(fd);
grpc_iomgr_unref();
} }
static void ref_by(grpc_fd *fd, int n) { static void ref_by(grpc_fd *fd, int n) {
@ -61,25 +112,30 @@ static void ref_by(grpc_fd *fd, int n) {
static void unref_by(grpc_fd *fd, int n) { static void unref_by(grpc_fd *fd, int n) {
if (gpr_atm_full_fetch_add(&fd->refst, -n) == n) { if (gpr_atm_full_fetch_add(&fd->refst, -n) == n) {
grpc_iomgr_add_callback(fd->on_done, fd->on_done_user_data);
freelist_fd(fd);
grpc_iomgr_unref();
}
}
void grpc_fd_global_init(void) {
gpr_mu_init(&fd_freelist_mu);
}
void grpc_fd_global_shutdown(void) {
while (fd_freelist != NULL) {
grpc_fd *fd = fd_freelist;
fd_freelist = fd_freelist->freelist_next;
destroy(fd); destroy(fd);
} }
gpr_mu_destroy(&fd_freelist_mu);
} }
static void do_nothing(void *ignored, int success) {} static void do_nothing(void *ignored, int success) {}
grpc_fd *grpc_fd_create(int fd) { grpc_fd *grpc_fd_create(int fd) {
grpc_fd *r = gpr_malloc(sizeof(grpc_fd)); grpc_fd *r = alloc_fd(fd);
grpc_iomgr_ref(); grpc_iomgr_ref();
gpr_atm_rel_store(&r->refst, 1);
gpr_atm_rel_store(&r->readst.state, NOT_READY);
gpr_atm_rel_store(&r->writest.state, NOT_READY);
gpr_mu_init(&r->set_state_mu);
gpr_mu_init(&r->watcher_mu);
gpr_atm_rel_store(&r->shutdown, 0);
r->fd = fd;
r->watchers = NULL;
r->watcher_count = 0;
r->watcher_capacity = 0;
grpc_pollset_add_fd(grpc_backup_pollset(), r); grpc_pollset_add_fd(grpc_backup_pollset(), r);
return r; return r;
} }

@ -69,6 +69,7 @@ typedef struct grpc_fd {
grpc_iomgr_cb_func on_done; grpc_iomgr_cb_func on_done;
void *on_done_user_data; void *on_done_user_data;
struct grpc_fd *freelist_next;
} grpc_fd; } grpc_fd;
/* Create a wrapped file descriptor. /* Create a wrapped file descriptor.
@ -135,4 +136,7 @@ void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback);
void grpc_fd_ref(grpc_fd *fd); void grpc_fd_ref(grpc_fd *fd);
void grpc_fd_unref(grpc_fd *fd); void grpc_fd_unref(grpc_fd *fd);
void grpc_fd_global_init(void);
void grpc_fd_global_shutdown(void);
#endif /* __GRPC_INTERNAL_IOMGR_FD_POSIX_H_ */ #endif /* __GRPC_INTERNAL_IOMGR_FD_POSIX_H_ */

@ -98,7 +98,6 @@ void grpc_iomgr_shutdown(void) {
gpr_timespec shutdown_deadline = gpr_timespec shutdown_deadline =
gpr_time_add(gpr_now(), gpr_time_from_seconds(10)); gpr_time_add(gpr_now(), gpr_time_from_seconds(10));
grpc_iomgr_platform_shutdown();
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_mu);
g_shutdown = 1; g_shutdown = 1;
@ -129,6 +128,7 @@ void grpc_iomgr_shutdown(void) {
gpr_event_wait(&g_background_callback_executor_done, gpr_inf_future); gpr_event_wait(&g_background_callback_executor_done, gpr_inf_future);
grpc_iomgr_platform_shutdown();
grpc_alarm_list_shutdown(); grpc_alarm_list_shutdown();
gpr_mu_destroy(&g_mu); gpr_mu_destroy(&g_mu);
gpr_cv_destroy(&g_cv); gpr_cv_destroy(&g_cv);

@ -32,7 +32,14 @@
*/ */
#include "src/core/iomgr/iomgr_posix.h" #include "src/core/iomgr/iomgr_posix.h"
#include "src/core/iomgr/fd_posix.h"
void grpc_iomgr_platform_init(void) { grpc_pollset_global_init(); } void grpc_iomgr_platform_init(void) {
grpc_fd_global_init();
grpc_pollset_global_init();
}
void grpc_iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } void grpc_iomgr_platform_shutdown(void) {
grpc_pollset_global_shutdown();
grpc_fd_global_shutdown();
}

@ -68,7 +68,7 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
The file descriptor remains owned by the server, and will be cleaned The file descriptor remains owned by the server, and will be cleaned
up when grpc_tcp_server_destroy is called. */ up when grpc_tcp_server_destroy is called. */
int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index); int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index);
void grpc_tcp_server_destroy(grpc_tcp_server *server); void grpc_tcp_server_destroy(grpc_tcp_server *server);

@ -272,7 +272,7 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
int addr_len) { int addr_len) {
int allocated_port1 = -1; int allocated_port1 = -1;
int allocated_port2 = -1; int allocated_port2 = -1;
int i; unsigned i;
int fd; int fd;
grpc_dualstack_mode dsmode; grpc_dualstack_mode dsmode;
struct sockaddr_in6 addr6_v4mapped; struct sockaddr_in6 addr6_v4mapped;
@ -345,8 +345,8 @@ done:
return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; return allocated_port1 >= 0 ? allocated_port1 : allocated_port2;
} }
int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index) { int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index) {
return (0 <= index && index < s->nports) ? s->ports[index].fd : -1; return (index < s->nports) ? s->ports[index].fd : -1;
} }
void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset, void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset,

@ -0,0 +1,64 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <grpc/support/alloc.h>
#include "src/core/json/json.h"
grpc_json *grpc_json_create(grpc_json_type type) {
grpc_json *json = gpr_malloc(sizeof(grpc_json));
memset(json, 0, sizeof(grpc_json));
json->type = type;
return json;
}
void grpc_json_destroy(grpc_json *json) {
while (json->child) {
grpc_json_destroy(json->child);
}
if (json->next) {
json->next->prev = json->prev;
}
if (json->prev) {
json->prev->next = json->next;
} else if (json->parent) {
json->parent->child = json->next;
}
gpr_free(json);
}

@ -0,0 +1,88 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPC_SRC_CORE_JSON_JSON_H__
#define __GRPC_SRC_CORE_JSON_JSON_H__
#include <stdlib.h>
#include "src/core/json/json_common.h"
/* A tree-like structure to hold json values. The key and value pointers
* are not owned by it.
*/
typedef struct grpc_json {
struct grpc_json* next;
struct grpc_json* prev;
struct grpc_json* child;
struct grpc_json* parent;
grpc_json_type type;
const char* key;
const char* value;
} grpc_json;
/* The next two functions are going to parse the input string, and
* destroy it in the process, in order to use its space to store
* all of the keys and values for the returned object tree.
*
* They assume UTF-8 input stream, and will output UTF-8 encoded
* strings in the tree. The input stream's UTF-8 isn't validated,
* as in, what you input is what you get as an output.
*
* All the keys and values in the grpc_json_t objects will be strings
* pointing at your input buffer.
*
* Delete the allocated tree afterward using grpc_json_destroy().
*/
grpc_json* grpc_json_parse_string_with_len(char* input, size_t size);
grpc_json* grpc_json_parse_string(char* input);
/* This function will create a new string using gpr_realloc, and will
* deserialize the grpc_json tree into it. It'll be zero-terminated,
* but will be allocated in chunks of 256 bytes.
*
* The indent parameter controls the way the output is formatted.
* If indent is 0, then newlines will be suppressed as well, and the
* output will be condensed at its maximum.
*/
char* grpc_json_dump_to_string(grpc_json* json, int indent);
/* Use these to create or delete a grpc_json object.
* Deletion is recursive. We will not attempt to free any of the strings
* in any of the objects of that tree.
*/
grpc_json* grpc_json_create(grpc_json_type type);
void grpc_json_destroy(grpc_json* json);
#endif /* __GRPC_SRC_CORE_JSON_JSON_H__ */

@ -0,0 +1,49 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPC_SRC_CORE_JSON_JSON_COMMON_H__
#define __GRPC_SRC_CORE_JSON_JSON_COMMON_H__
/* The various json types. */
typedef enum {
GRPC_JSON_OBJECT,
GRPC_JSON_ARRAY,
GRPC_JSON_STRING,
GRPC_JSON_NUMBER,
GRPC_JSON_TRUE,
GRPC_JSON_FALSE,
GRPC_JSON_NULL,
GRPC_JSON_TOP_LEVEL
} grpc_json_type;
#endif /* __GRPC_SRC_CORE_JSON_JSON_COMMON_H__ */

@ -0,0 +1,653 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <grpc/support/port_platform.h>
#include "src/core/json/json_reader.h"
static void json_reader_string_clear(grpc_json_reader* reader) {
reader->vtable->string_clear(reader->userdata);
}
static void json_reader_string_add_char(grpc_json_reader* reader,
gpr_uint32 c) {
reader->vtable->string_add_char(reader->userdata, c);
}
static void json_reader_string_add_utf32(grpc_json_reader* reader,
gpr_uint32 utf32) {
reader->vtable->string_add_utf32(reader->userdata, utf32);
}
static gpr_uint32
grpc_json_reader_read_char(grpc_json_reader* reader) {
return reader->vtable->read_char(reader->userdata);
}
static void json_reader_container_begins(grpc_json_reader* reader,
grpc_json_type type) {
reader->vtable->container_begins(reader->userdata, type);
}
static grpc_json_type
grpc_json_reader_container_ends(grpc_json_reader* reader) {
return reader->vtable->container_ends(reader->userdata);
}
static void json_reader_set_key(grpc_json_reader* reader) {
reader->vtable->set_key(reader->userdata);
}
static void json_reader_set_string(grpc_json_reader* reader) {
reader->vtable->set_string(reader->userdata);
}
static int json_reader_set_number(grpc_json_reader* reader) {
return reader->vtable->set_number(reader->userdata);
}
static void json_reader_set_true(grpc_json_reader* reader) {
reader->vtable->set_true(reader->userdata);
}
static void json_reader_set_false(grpc_json_reader* reader) {
reader->vtable->set_false(reader->userdata);
}
static void json_reader_set_null(grpc_json_reader* reader) {
reader->vtable->set_null(reader->userdata);
}
/* Call this function to initialize the reader structure. */
void grpc_json_reader_init(grpc_json_reader* reader,
grpc_json_reader_vtable* vtable, void* userdata) {
memset(reader, 0, sizeof(grpc_json_reader));
reader->vtable = vtable;
reader->userdata = userdata;
json_reader_string_clear(reader);
reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
}
int grpc_json_reader_is_complete(grpc_json_reader* reader) {
return ((reader->depth == 0) && ((reader->state == GRPC_JSON_STATE_END) ||
(reader->state == GRPC_JSON_STATE_VALUE_END)));
}
grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader) {
gpr_uint32 c, success;
/* This state-machine is a strict implementation of ECMA-404 */
for (;;) {
c = grpc_json_reader_read_char(reader);
switch (c) {
/* Let's process the error cases first. */
case GRPC_JSON_READ_CHAR_ERROR:
return GRPC_JSON_READ_ERROR;
case GRPC_JSON_READ_CHAR_EAGAIN:
return GRPC_JSON_EAGAIN;
case GRPC_JSON_READ_CHAR_EOF:
if (grpc_json_reader_is_complete(reader)) {
return GRPC_JSON_DONE;
} else {
return GRPC_JSON_PARSE_ERROR;
}
break;
/* Processing whitespaces. */
case ' ':
case '\t':
case '\n':
case '\r':
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
case GRPC_JSON_STATE_OBJECT_KEY_END:
case GRPC_JSON_STATE_VALUE_BEGIN:
case GRPC_JSON_STATE_VALUE_END:
case GRPC_JSON_STATE_END:
break;
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
case GRPC_JSON_STATE_VALUE_STRING:
if (c != ' ') return GRPC_JSON_PARSE_ERROR;
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
break;
case GRPC_JSON_STATE_VALUE_NUMBER:
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
success = json_reader_set_number(reader);
if (!success) return GRPC_JSON_PARSE_ERROR;
json_reader_string_clear(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
/* Value, object or array terminations. */
case ',':
case '}':
case ']':
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
case GRPC_JSON_STATE_VALUE_STRING:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
break;
case GRPC_JSON_STATE_VALUE_NUMBER:
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
success = json_reader_set_number(reader);
if (!success) return GRPC_JSON_PARSE_ERROR;
json_reader_string_clear(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
/* The missing break here is intentional. */
case GRPC_JSON_STATE_VALUE_END:
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
case GRPC_JSON_STATE_VALUE_BEGIN:
if (c == ',') {
if (reader->state != GRPC_JSON_STATE_VALUE_END) {
return GRPC_JSON_PARSE_ERROR;
}
if (reader->in_object) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
} else {
reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
}
} else {
if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR;
if ((c == '}') && !reader->in_object) {
return GRPC_JSON_PARSE_ERROR;
}
if ((c == '}') &&
(reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) &&
!reader->container_just_begun) {
return GRPC_JSON_PARSE_ERROR;
}
if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR;
if ((c == ']') &&
(reader->state == GRPC_JSON_STATE_VALUE_BEGIN) &&
!reader->container_just_begun) {
return GRPC_JSON_PARSE_ERROR;
}
reader->state = GRPC_JSON_STATE_VALUE_END;
switch (grpc_json_reader_container_ends(reader)) {
case GRPC_JSON_OBJECT:
reader->in_object = 1;
reader->in_array = 0;
break;
case GRPC_JSON_ARRAY:
reader->in_object = 0;
reader->in_array = 1;
break;
case GRPC_JSON_TOP_LEVEL:
if (reader->depth != 0) return GRPC_JSON_INTERNAL_ERROR;
reader->in_object = 0;
reader->in_array = 0;
reader->state = GRPC_JSON_STATE_END;
break;
default:
return GRPC_JSON_INTERNAL_ERROR;
}
}
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
/* In-string escaping. */
case '\\':
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
reader->escaped_string_was_key = 1;
reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
break;
case GRPC_JSON_STATE_VALUE_STRING:
reader->escaped_string_was_key = 0;
reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
break;
/* This is the \\ case. */
case GRPC_JSON_STATE_STRING_ESCAPE:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, '\\');
if (reader->escaped_string_was_key) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
} else {
reader->state = GRPC_JSON_STATE_VALUE_STRING;
}
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
default:
reader->container_just_begun = 0;
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
if (c != '"') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
break;
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
if (c == '"') {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_END;
json_reader_set_key(reader);
json_reader_string_clear(reader);
} else {
if (c <= 0x001f) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
}
break;
case GRPC_JSON_STATE_VALUE_STRING:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
if (c == '"') {
reader->state = GRPC_JSON_STATE_VALUE_END;
json_reader_set_string(reader);
json_reader_string_clear(reader);
} else {
if (c < 32) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
}
break;
case GRPC_JSON_STATE_OBJECT_KEY_END:
if (c != ':') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
break;
case GRPC_JSON_STATE_VALUE_BEGIN:
switch (c) {
case 't':
reader->state = GRPC_JSON_STATE_VALUE_TRUE_R;
break;
case 'f':
reader->state = GRPC_JSON_STATE_VALUE_FALSE_A;
break;
case 'n':
reader->state = GRPC_JSON_STATE_VALUE_NULL_U;
break;
case '"':
reader->state = GRPC_JSON_STATE_VALUE_STRING;
break;
case '0':
json_reader_string_add_char(reader, c);
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
json_reader_string_add_char(reader, c);
reader->state = GRPC_JSON_STATE_VALUE_NUMBER;
break;
case '{':
reader->container_just_begun = 1;
json_reader_container_begins(reader, GRPC_JSON_OBJECT);
reader->depth++;
reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
reader->in_object = 1;
reader->in_array = 0;
break;
case '[':
reader->container_just_begun = 1;
json_reader_container_begins(reader, GRPC_JSON_ARRAY);
reader->depth++;
reader->in_object = 0;
reader->in_array = 1;
break;
}
break;
case GRPC_JSON_STATE_STRING_ESCAPE:
if (reader->escaped_string_was_key) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
} else {
reader->state = GRPC_JSON_STATE_VALUE_STRING;
}
if (reader->unicode_high_surrogate && c != 'u')
return GRPC_JSON_PARSE_ERROR;
switch (c) {
case '"':
case '/':
json_reader_string_add_char(reader, c);
break;
case 'b':
json_reader_string_add_char(reader, '\b');
break;
case 'f':
json_reader_string_add_char(reader, '\f');
break;
case 'n':
json_reader_string_add_char(reader, '\n');
break;
case 'r':
json_reader_string_add_char(reader, '\r');
break;
case 't':
json_reader_string_add_char(reader, '\t');
break;
case 'u':
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1;
reader->unicode_char = 0;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U1:
case GRPC_JSON_STATE_STRING_ESCAPE_U2:
case GRPC_JSON_STATE_STRING_ESCAPE_U3:
case GRPC_JSON_STATE_STRING_ESCAPE_U4:
if ((c >= '0') && (c <= '9')) {
c -= '0';
} else if ((c >= 'A') && (c <= 'F')) {
c -= 'A' - 10;
} else if ((c >= 'a') && (c <= 'f')) {
c -= 'a' - 10;
} else {
return GRPC_JSON_PARSE_ERROR;
}
reader->unicode_char <<= 4;
reader->unicode_char |= c;
switch (reader->state) {
case GRPC_JSON_STATE_STRING_ESCAPE_U1:
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2;
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U2:
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3;
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U3:
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4;
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U4:
/* See grpc_json_writer_escape_string to have a description
* of what's going on here.
*/
if ((reader->unicode_char & 0xfc00) == 0xd800) {
/* high surrogate utf-16 */
if (reader->unicode_high_surrogate != 0)
return GRPC_JSON_PARSE_ERROR;
reader->unicode_high_surrogate = reader->unicode_char;
} else if ((reader->unicode_char & 0xfc00) == 0xdc00) {
/* low surrogate utf-16 */
gpr_uint32 utf32;
if (reader->unicode_high_surrogate == 0)
return GRPC_JSON_PARSE_ERROR;
utf32 = 0x10000;
utf32 += (reader->unicode_high_surrogate - 0xd800) * 0x400;
utf32 += reader->unicode_char - 0xdc00;
json_reader_string_add_utf32(reader, utf32);
reader->unicode_high_surrogate = 0;
} else {
/* anything else */
if (reader->unicode_high_surrogate != 0)
return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_utf32(reader, reader->unicode_char);
}
if (reader->escaped_string_was_key) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
} else {
reader->state = GRPC_JSON_STATE_VALUE_STRING;
}
break;
default:
return GRPC_JSON_INTERNAL_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case 'e':
case 'E':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
break;
case '.':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case 'e':
case 'E':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
if (c != '.') return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
break;
case GRPC_JSON_STATE_VALUE_NUMBER_DOT:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_E:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '+':
case '-':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_TRUE_R:
if (c != 'r') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_TRUE_U;
break;
case GRPC_JSON_STATE_VALUE_TRUE_U:
if (c != 'u') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_TRUE_E;
break;
case GRPC_JSON_STATE_VALUE_TRUE_E:
if (c != 'e') return GRPC_JSON_PARSE_ERROR;
json_reader_set_true(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
case GRPC_JSON_STATE_VALUE_FALSE_A:
if (c != 'a') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_FALSE_L;
break;
case GRPC_JSON_STATE_VALUE_FALSE_L:
if (c != 'l') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_FALSE_S;
break;
case GRPC_JSON_STATE_VALUE_FALSE_S:
if (c != 's') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_FALSE_E;
break;
case GRPC_JSON_STATE_VALUE_FALSE_E:
if (c != 'e') return GRPC_JSON_PARSE_ERROR;
json_reader_set_false(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
case GRPC_JSON_STATE_VALUE_NULL_U:
if (c != 'u') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_NULL_L1;
break;
case GRPC_JSON_STATE_VALUE_NULL_L1:
if (c != 'l') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_NULL_L2;
break;
case GRPC_JSON_STATE_VALUE_NULL_L2:
if (c != 'l') return GRPC_JSON_PARSE_ERROR;
json_reader_set_null(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
/* All of the VALUE_END cases are handled in the specialized case
* above. */
case GRPC_JSON_STATE_VALUE_END:
switch (c) {
case ',':
case '}':
case ']':
return GRPC_JSON_INTERNAL_ERROR;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_END:
return GRPC_JSON_PARSE_ERROR;
}
}
}
return GRPC_JSON_INTERNAL_ERROR;
}

@ -0,0 +1,160 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPC_SRC_CORE_JSON_JSON_READER_H__
#define __GRPC_SRC_CORE_JSON_JSON_READER_H__
#include <grpc/support/port_platform.h>
#include "src/core/json/json_common.h"
typedef enum {
GRPC_JSON_STATE_OBJECT_KEY_BEGIN,
GRPC_JSON_STATE_OBJECT_KEY_STRING,
GRPC_JSON_STATE_OBJECT_KEY_END,
GRPC_JSON_STATE_VALUE_BEGIN,
GRPC_JSON_STATE_VALUE_STRING,
GRPC_JSON_STATE_STRING_ESCAPE,
GRPC_JSON_STATE_STRING_ESCAPE_U1,
GRPC_JSON_STATE_STRING_ESCAPE_U2,
GRPC_JSON_STATE_STRING_ESCAPE_U3,
GRPC_JSON_STATE_STRING_ESCAPE_U4,
GRPC_JSON_STATE_VALUE_NUMBER,
GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL,
GRPC_JSON_STATE_VALUE_NUMBER_ZERO,
GRPC_JSON_STATE_VALUE_NUMBER_DOT,
GRPC_JSON_STATE_VALUE_NUMBER_E,
GRPC_JSON_STATE_VALUE_NUMBER_EPM,
GRPC_JSON_STATE_VALUE_TRUE_R,
GRPC_JSON_STATE_VALUE_TRUE_U,
GRPC_JSON_STATE_VALUE_TRUE_E,
GRPC_JSON_STATE_VALUE_FALSE_A,
GRPC_JSON_STATE_VALUE_FALSE_L,
GRPC_JSON_STATE_VALUE_FALSE_S,
GRPC_JSON_STATE_VALUE_FALSE_E,
GRPC_JSON_STATE_VALUE_NULL_U,
GRPC_JSON_STATE_VALUE_NULL_L1,
GRPC_JSON_STATE_VALUE_NULL_L2,
GRPC_JSON_STATE_VALUE_END,
GRPC_JSON_STATE_END
} grpc_json_reader_state;
enum {
/* The first non-unicode value is 0x110000. But let's pick
* a value high enough to start our error codes from. These
* values are safe to return from the read_char function.
*/
GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0,
GRPC_JSON_READ_CHAR_EAGAIN,
GRPC_JSON_READ_CHAR_ERROR
};
struct grpc_json_reader;
typedef struct grpc_json_reader_vtable {
/* Clears your internal string scratchpad. */
void (*string_clear)(void* userdata);
/* Adds a char to the string scratchpad. */
void (*string_add_char)(void* userdata, gpr_uint32 c);
/* Adds a utf32 char to the string scratchpad. */
void (*string_add_utf32)(void* userdata, gpr_uint32 c);
/* Reads a character from your input. May be utf-8, 16 or 32. */
gpr_uint32 (*read_char)(void* userdata);
/* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */
void (*container_begins)(void* userdata, grpc_json_type type);
/* Ends the current container. Must return the type of its parent. */
grpc_json_type (*container_ends)(void* userdata);
/* Your internal string scratchpad is an object's key. */
void (*set_key)(void* userdata);
/* Your internal string scratchpad is a string value. */
void (*set_string)(void* userdata);
/* Your internal string scratchpad is a numerical value. Return 1 if valid. */
int (*set_number)(void* userdata);
/* Sets the values true, false or null. */
void (*set_true)(void* userdata);
void (*set_false)(void* userdata);
void (*set_null)(void* userdata);
} grpc_json_reader_vtable;
typedef struct grpc_json_reader {
/* That structure is fully private, and initialized by grpc_json_reader_init.
* The definition is public so you can put it on your stack.
*/
void* userdata;
grpc_json_reader_vtable* vtable;
int depth;
int in_object;
int in_array;
int escaped_string_was_key;
int container_just_begun;
gpr_uint16 unicode_char, unicode_high_surrogate;
grpc_json_reader_state state;
} grpc_json_reader;
/* The return type of the parser. */
typedef enum {
GRPC_JSON_DONE, /* The parser finished successfully. */
GRPC_JSON_EAGAIN, /* The parser yields to get more data. */
GRPC_JSON_READ_ERROR, /* The parser passes through a read error. */
GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */
GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */
} grpc_json_reader_status;
/* Call this function to start parsing the input. It will return the following:
* . GRPC_JSON_DONE if the input got eof, and the parsing finished
* successfully.
* . GRPC_JSON_EAGAIN if the read_char function returned again. Call the
* parser again as needed. It is okay to call the parser in polling mode,
* although a bit dull.
* . GRPC_JSON_READ_ERROR if the read_char function returned an error. The
* state isn't broken however, and the function can be called again if the
* error has been corrected. But please use the EAGAIN feature instead for
* consistency.
* . GRPC_JSON_PARSE_ERROR if the input was somehow invalid.
* . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid
* internal state.
*/
grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader);
/* Call this function to initialize the reader structure. */
void grpc_json_reader_init(grpc_json_reader* reader,
grpc_json_reader_vtable* vtable, void* userdata);
/* You may call this from the read_char callback if you don't know where is the
* end of your input stream, and you'd like the json reader to hint you that it
* has completed reading its input, so you can return an EOF to it. Note that
* there might still be trailing whitespaces after that point.
*/
int grpc_json_reader_is_complete(grpc_json_reader* reader);
#endif /* __GRPC_SRC_CORE_JSON_JSON_READER_H__ */

@ -0,0 +1,391 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdlib.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/json/json.h"
#include "src/core/json/json_reader.h"
#include "src/core/json/json_writer.h"
/* The json reader will construct a bunch of grpc_json objects and
* link them all up together in a tree-like structure that will represent
* the json data in memory.
*
* It also uses its own input as a scratchpad to store all of the decoded,
* unescaped strings. So we need to keep track of all these pointers in
* that opaque structure the reader will carry for us.
*
* Note that this works because the act of parsing json always reduces its
* input size, and never expands it.
*/
typedef struct {
grpc_json* top;
grpc_json* current_container;
grpc_json* current_value;
gpr_uint8* input;
gpr_uint8* key;
gpr_uint8* string;
gpr_uint8* string_ptr;
size_t remaining_input;
} json_reader_userdata;
/* This json writer will put everything in a big string.
* The point is that we allocate that string in chunks of 256 bytes.
*/
typedef struct {
char* output;
size_t free_space;
size_t string_len;
size_t allocated;
} json_writer_userdata;
/* This function checks if there's enough space left in the output buffer,
* and will enlarge it if necessary. We're only allocating chunks of 256
* bytes at a time (or multiples thereof).
*/
static void json_writer_output_check(void* userdata, size_t needed) {
json_writer_userdata* state = userdata;
if (state->free_space >= needed) return;
needed -= state->free_space;
/* Round up by 256 bytes. */
needed = (needed + 0xff) & ~0xff;
state->output = gpr_realloc(state->output, state->allocated + needed);
state->free_space += needed;
state->allocated += needed;
}
/* These are needed by the writer's implementation. */
static void json_writer_output_char(void* userdata, char c) {
json_writer_userdata* state = userdata;
json_writer_output_check(userdata, 1);
state->output[state->string_len++] = c;
state->free_space--;
}
static void json_writer_output_string_with_len(void* userdata,
const char* str, size_t len) {
json_writer_userdata* state = userdata;
json_writer_output_check(userdata, len);
memcpy(state->output + state->string_len, str, len);
state->string_len += len;
state->free_space -= len;
}
static void json_writer_output_string(void* userdata,
const char* str) {
size_t len = strlen(str);
json_writer_output_string_with_len(userdata, str, len);
}
/* The reader asks us to clear our scratchpad. In our case, we'll simply mark
* the end of the current string, and advance our output pointer.
*/
static void json_reader_string_clear(void* userdata) {
json_reader_userdata* state = userdata;
if (state->string) {
GPR_ASSERT(state->string_ptr < state->input);
*state->string_ptr++ = 0;
}
state->string = state->string_ptr;
}
static void json_reader_string_add_char(void* userdata, gpr_uint32 c) {
json_reader_userdata* state = userdata;
GPR_ASSERT(state->string_ptr < state->input);
GPR_ASSERT(c <= 0xff);
*state->string_ptr++ = (char)c;
}
/* We are converting a UTF-32 character into UTF-8 here,
* as described by RFC3629.
*/
static void json_reader_string_add_utf32(void* userdata, gpr_uint32 c) {
if (c <= 0x7f) {
json_reader_string_add_char(userdata, c);
} else if (c <= 0x7ff) {
int b1 = 0xc0 | ((c >> 6) & 0x1f);
int b2 = 0x80 | (c & 0x3f);
json_reader_string_add_char(userdata, b1);
json_reader_string_add_char(userdata, b2);
} else if (c <= 0xffff) {
int b1 = 0xe0 | ((c >> 12) & 0x0f);
int b2 = 0x80 | ((c >> 6) & 0x3f);
int b3 = 0x80 | (c & 0x3f);
json_reader_string_add_char(userdata, b1);
json_reader_string_add_char(userdata, b2);
json_reader_string_add_char(userdata, b3);
} else if (c <= 0x1fffff) {
int b1 = 0xf0 | ((c >> 18) & 0x07);
int b2 = 0x80 | ((c >> 12) & 0x3f);
int b3 = 0x80 | ((c >> 6) & 0x3f);
int b4 = 0x80 | (c & 0x3f);
json_reader_string_add_char(userdata, b1);
json_reader_string_add_char(userdata, b2);
json_reader_string_add_char(userdata, b3);
json_reader_string_add_char(userdata, b4);
}
}
/* We consider that the input may be a zero-terminated string. So we
* can end up hitting eof before the end of the alleged string length.
*/
static gpr_uint32 json_reader_read_char(void* userdata) {
gpr_uint32 r;
json_reader_userdata* state = userdata;
if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF;
r = *state->input++;
state->remaining_input--;
if (r == 0) {
state->remaining_input = 0;
return GRPC_JSON_READ_CHAR_EOF;
}
return r;
}
/* Helper function to create a new grpc_json object and link it into
* our tree-in-progress inside our opaque structure.
*/
static grpc_json* json_create_and_link(void* userdata,
grpc_json_type type) {
json_reader_userdata* state = userdata;
grpc_json* json = grpc_json_create(type);
json->parent = state->current_container;
json->prev = state->current_value;
state->current_value = json;
if (json->prev) {
json->prev->next = json;
}
if (json->parent) {
if (!json->parent->child) {
json->parent->child = json;
}
if (json->parent->type == GRPC_JSON_OBJECT) {
json->key = (char*) state->key;
}
}
if (!state->top) {
state->top = json;
}
return json;
}
static void json_reader_container_begins(void* userdata, grpc_json_type type) {
json_reader_userdata* state = userdata;
grpc_json* container;
GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT);
container = json_create_and_link(userdata, type);
state->current_container = container;
state->current_value = NULL;
}
/* It's important to remember that the reader is mostly stateless, so it
* isn't trying to remember what the container was prior the one that just
* ends. Since we're keeping track of these for our own purpose, we are
* able to return that information back, which is useful for it to validate
* the input json stream.
*
* Also note that if we're at the top of the tree, and the last container
* ends, we have to return GRPC_JSON_TOP_LEVEL.
*/
static grpc_json_type json_reader_container_ends(void* userdata) {
grpc_json_type container_type = GRPC_JSON_TOP_LEVEL;
json_reader_userdata* state = userdata;
GPR_ASSERT(state->current_container);
state->current_value = state->current_container;
state->current_container = state->current_container->parent;
if (state->current_container) {
container_type = state->current_container->type;
}
return container_type;
}
/* The next 3 functions basically are the reader asking us to use our string
* scratchpad for one of these 3 purposes.
*
* Note that in the set_number case, we're not going to try interpreting it.
* We'll keep it as a string, and leave it to the caller to evaluate it.
*/
static void json_reader_set_key(void* userdata) {
json_reader_userdata* state = userdata;
state->key = state->string;
}
static void json_reader_set_string(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json* json = json_create_and_link(userdata, GRPC_JSON_STRING);
json->value = (char*) state->string;
}
static int json_reader_set_number(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json* json = json_create_and_link(userdata, GRPC_JSON_NUMBER);
json->value = (char*) state->string;
return 1;
}
/* The object types true, false and null are self-sufficient, and don't need
* any more information beside their type.
*/
static void json_reader_set_true(void* userdata) {
json_create_and_link(userdata, GRPC_JSON_TRUE);
}
static void json_reader_set_false(void* userdata) {
json_create_and_link(userdata, GRPC_JSON_FALSE);
}
static void json_reader_set_null(void* userdata) {
json_create_and_link(userdata, GRPC_JSON_NULL);
}
static grpc_json_reader_vtable reader_vtable = {
json_reader_string_clear,
json_reader_string_add_char,
json_reader_string_add_utf32,
json_reader_read_char,
json_reader_container_begins,
json_reader_container_ends,
json_reader_set_key,
json_reader_set_string,
json_reader_set_number,
json_reader_set_true,
json_reader_set_false,
json_reader_set_null
};
/* And finally, let's define our public API. */
grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) {
grpc_json_reader reader;
json_reader_userdata state;
grpc_json *json = NULL;
grpc_json_reader_status status;
if (!input) return NULL;
state.top = state.current_container = state.current_value = NULL;
state.string = state.key = NULL;
state.string_ptr = state.input = (gpr_uint8*) input;
state.remaining_input = size;
grpc_json_reader_init(&reader, &reader_vtable, &state);
status = grpc_json_reader_run(&reader);
json = state.top;
if ((status != GRPC_JSON_DONE) && json) {
grpc_json_destroy(json);
json = NULL;
}
return json;
}
#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff
grpc_json* grpc_json_parse_string(char* input) {
return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH);
}
static void json_dump_recursive(grpc_json_writer* writer,
grpc_json* json, int in_object) {
while (json) {
if (in_object) grpc_json_writer_object_key(writer, json->key);
switch (json->type) {
case GRPC_JSON_OBJECT:
case GRPC_JSON_ARRAY:
grpc_json_writer_container_begins(writer, json->type);
if (json->child)
json_dump_recursive(writer, json->child,
json->type == GRPC_JSON_OBJECT);
grpc_json_writer_container_ends(writer, json->type);
break;
case GRPC_JSON_STRING:
grpc_json_writer_value_string(writer, json->value);
break;
case GRPC_JSON_NUMBER:
grpc_json_writer_value_raw(writer, json->value);
break;
case GRPC_JSON_TRUE:
grpc_json_writer_value_raw_with_len(writer, "true", 4);
break;
case GRPC_JSON_FALSE:
grpc_json_writer_value_raw_with_len(writer, "false", 5);
break;
case GRPC_JSON_NULL:
grpc_json_writer_value_raw_with_len(writer, "null", 4);
break;
default:
abort();
}
json = json->next;
}
}
static grpc_json_writer_vtable writer_vtable = {
json_writer_output_char,
json_writer_output_string,
json_writer_output_string_with_len
};
char* grpc_json_dump_to_string(grpc_json* json, int indent) {
grpc_json_writer writer;
json_writer_userdata state;
state.output = NULL;
state.free_space = state.string_len = state.allocated = 0;
grpc_json_writer_init(&writer, indent, &writer_vtable, &state);
json_dump_recursive(&writer, json, 0);
json_writer_output_char(&state, 0);
return state.output;
}

@ -0,0 +1,252 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <grpc/support/port_platform.h>
#include "src/core/json/json_writer.h"
static void json_writer_output_char(grpc_json_writer* writer, char c) {
writer->vtable->output_char(writer->userdata, c);
}
static void json_writer_output_string(grpc_json_writer* writer, const char* str) {
writer->vtable->output_string(writer->userdata, str);
}
static void json_writer_output_string_with_len(grpc_json_writer* writer, const char* str, size_t len) {
writer->vtable->output_string_with_len(writer->userdata, str, len);
}
void grpc_json_writer_init(grpc_json_writer* writer, int indent,
grpc_json_writer_vtable* vtable, void* userdata) {
memset(writer, 0, sizeof(grpc_json_writer));
writer->container_empty = 1;
writer->indent = indent;
writer->vtable = vtable;
writer->userdata = userdata;
}
static void json_writer_output_indent(
grpc_json_writer* writer) {
static const char spacesstr[] =
" "
" "
" "
" ";
unsigned spaces = writer->depth * writer->indent;
if (writer->indent == 0) return;
if (writer->got_key) {
json_writer_output_char(writer, ' ');
return;
}
while (spaces >= (sizeof(spacesstr) - 1)) {
json_writer_output_string_with_len(writer, spacesstr,
sizeof(spacesstr) - 1);
spaces -= (sizeof(spacesstr) - 1);
}
if (spaces == 0) return;
json_writer_output_string_with_len(
writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces);
}
static void json_writer_value_end(grpc_json_writer* writer) {
if (writer->container_empty) {
writer->container_empty = 0;
if ((writer->indent == 0) || (writer->depth == 0)) return;
json_writer_output_char(writer, '\n');
} else {
json_writer_output_char(writer, ',');
if (writer->indent == 0) return;
json_writer_output_char(writer, '\n');
}
}
static void json_writer_escape_utf16(grpc_json_writer* writer, gpr_uint16 utf16) {
static const char hex[] = "0123456789abcdef";
json_writer_output_string_with_len(writer, "\\u", 2);
json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]);
json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]);
json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]);
json_writer_output_char(writer, hex[(utf16) & 0x0f]);
}
static void json_writer_escape_string(grpc_json_writer* writer,
const char* string) {
json_writer_output_char(writer, '"');
for (;;) {
gpr_uint8 c = (gpr_uint8)*string++;
if (c == 0) {
break;
} else if ((c >= 32) && (c <= 127)) {
if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\');
json_writer_output_char(writer, c);
} else if (c < 32) {
switch (c) {
case '\b':
json_writer_output_string_with_len(writer, "\\b", 2);
break;
case '\f':
json_writer_output_string_with_len(writer, "\\f", 2);
break;
case '\n':
json_writer_output_string_with_len(writer, "\\n", 2);
break;
case '\r':
json_writer_output_string_with_len(writer, "\\r", 2);
break;
case '\t':
json_writer_output_string_with_len(writer, "\\t", 2);
break;
default:
json_writer_escape_utf16(writer, c);
break;
}
} else {
gpr_uint32 utf32 = 0;
int extra = 0;
int i;
int valid = 1;
if ((c & 0xe0) == 0xc0) {
utf32 = c & 0x1f;
extra = 1;
} else if ((c & 0xf0) == 0xe0) {
utf32 = c & 0x0f;
extra = 2;
} else if ((c & 0xf8) == 0xf0) {
utf32 = c & 0x07;
extra = 3;
} else {
break;
}
for (i = 0; i < extra; i++) {
utf32 <<= 6;
c = *string++;
if ((c & 0xc0) != 0x80) {
valid = 0;
break;
}
utf32 |= c & 0x3f;
}
if (!valid) break;
/* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam.
* Any other range is technically reserved for future usage, so if we
* don't want the software to break in the future, we have to allow
* anything else. The first non-unicode character is 0x110000. */
if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) ||
(utf32 >= 0x110000)) break;
if (utf32 >= 0x10000) {
/* If utf32 contains a character that is above 0xffff, it needs to be
* broken down into a utf-16 surrogate pair. A surrogate pair is first
* a high surrogate, followed by a low surrogate. Each surrogate holds
* 10 bits of usable data, thus allowing a total of 20 bits of data.
* The high surrogate marker is 0xd800, while the low surrogate marker
* is 0xdc00. The low 10 bits of each will be the usable data.
*
* After re-combining the 20 bits of data, one has to add 0x10000 to
* the resulting value, in order to obtain the original character.
* This is obviously because the range 0x0000 - 0xffff can be written
* without any special trick.
*
* Since 0x10ffff is the highest allowed character, we're working in
* the range 0x00000 - 0xfffff after we decrement it by 0x10000.
* That range is exactly 20 bits.
*/
utf32 -= 0x10000;
json_writer_escape_utf16(writer, 0xd800 | (utf32 >> 10));
json_writer_escape_utf16(writer, 0xdc00 | (utf32 & 0x3ff));
} else {
json_writer_escape_utf16(writer, utf32);
}
}
}
json_writer_output_char(writer, '"');
}
void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '[');
writer->container_empty = 1;
writer->got_key = 0;
writer->depth++;
}
void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type) {
if (writer->indent && !writer->container_empty)
json_writer_output_char(writer, '\n');
writer->depth--;
if (!writer->container_empty) json_writer_output_indent(writer);
json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']');
writer->container_empty = 0;
writer->got_key = 0;
}
void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) {
json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_escape_string(writer, string);
json_writer_output_char(writer, ':');
writer->got_key = 1;
}
void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_output_string(writer, string);
writer->got_key = 0;
}
void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_output_string_with_len(writer, string, len);
writer->got_key = 0;
}
void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_escape_string(writer, string);
writer->got_key = 0;
}

@ -0,0 +1,93 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* The idea of the writer is basically symmetrical of the reader. While the
* reader emits various calls to your code, the writer takes basically the
* same calls and emit json out of it. It doesn't try to make any check on
* the order of the calls you do on it. Meaning you can theorically force
* it to generate invalid json.
*
* Also, unlike the reader, the writer expects UTF-8 encoded input strings.
* These strings will be UTF-8 validated, and any invalid character will
* cut the conversion short, before any invalid UTF-8 sequence, thus forming
* a valid UTF-8 string overall.
*/
#ifndef __GRPC_SRC_CORE_JSON_JSON_WRITER_H__
#define __GRPC_SRC_CORE_JSON_JSON_WRITER_H__
#include <stdlib.h>
#include "src/core/json/json_common.h"
typedef struct grpc_json_writer_vtable {
/* Adds a character to the output stream. */
void (*output_char)(void* userdata, char);
/* Adds a zero-terminated string to the output stream. */
void (*output_string)(void* userdata, const char* str);
/* Adds a fixed-length string to the output stream. */
void (*output_string_with_len)(void* userdata, const char* str, size_t len);
} grpc_json_writer_vtable;
typedef struct grpc_json_writer {
void* userdata;
grpc_json_writer_vtable* vtable;
int indent;
int depth;
int container_empty;
int got_key;
} grpc_json_writer;
/* Call this to initialize your writer structure. The indent parameter is
* specifying the number of spaces to use for indenting the output. If you
* use indent=0, then the output will not have any newlines either, thus
* emitting a condensed json output.
*/
void grpc_json_writer_init(grpc_json_writer* writer, int indent,
grpc_json_writer_vtable* vtable, void* userdata);
/* Signals the beginning of a container. */
void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type);
/* Signals the end of a container. */
void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type);
/* Writes down an object key for the next value. */
void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string);
/* Sets a raw value. Useful for numbers. */
void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string);
/* Sets a raw value with its length. Useful for values like true or false. */
void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len);
/* Sets a string value. It'll be escaped, and utf-8 validated. */
void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string);
#endif /* __GRPC_SRC_CORE_JSON_JSON_WRITER_H__ */

@ -113,7 +113,8 @@ char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe,
*current++ = GRPC_BASE64_PAD_CHAR; *current++ = GRPC_BASE64_PAD_CHAR;
} }
GPR_ASSERT((current - result) < result_projected_size); GPR_ASSERT(current >= result);
GPR_ASSERT((gpr_uintptr)(current - result) < result_projected_size);
result[current - result] = '\0'; result[current - result] = '\0';
return result; return result;
} }

@ -42,7 +42,7 @@
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include "third_party/cJSON/cJSON.h" #include "src/core/json/json.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -173,7 +173,9 @@ static void ssl_server_destroy(grpc_server_credentials *creds) {
gpr_free(creds); gpr_free(creds);
} }
static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; } static int ssl_has_request_metadata(const grpc_credentials *creds) {
return 0;
}
static int ssl_has_request_metadata_only(const grpc_credentials *creds) { static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
return 0; return 0;
@ -336,7 +338,7 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
char *null_terminated_body = NULL; char *null_terminated_body = NULL;
char *new_access_token = NULL; char *new_access_token = NULL;
grpc_credentials_status status = GRPC_CREDENTIALS_OK; grpc_credentials_status status = GRPC_CREDENTIALS_OK;
cJSON *json = NULL; grpc_json *json = NULL;
if (response->body_length > 0) { if (response->body_length > 0) {
null_terminated_body = gpr_malloc(response->body_length + 1); null_terminated_body = gpr_malloc(response->body_length + 1);
@ -351,41 +353,48 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
status = GRPC_CREDENTIALS_ERROR; status = GRPC_CREDENTIALS_ERROR;
goto end; goto end;
} else { } else {
cJSON *access_token = NULL; grpc_json *access_token = NULL;
cJSON *token_type = NULL; grpc_json *token_type = NULL;
cJSON *expires_in = NULL; grpc_json *expires_in = NULL;
json = cJSON_Parse(null_terminated_body); grpc_json *ptr;
json = grpc_json_parse_string(null_terminated_body);
if (json == NULL) { if (json == NULL) {
gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body); gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
status = GRPC_CREDENTIALS_ERROR; status = GRPC_CREDENTIALS_ERROR;
goto end; goto end;
} }
if (json->type != cJSON_Object) { if (json->type != GRPC_JSON_OBJECT) {
gpr_log(GPR_ERROR, "Response should be a JSON object"); gpr_log(GPR_ERROR, "Response should be a JSON object");
status = GRPC_CREDENTIALS_ERROR; status = GRPC_CREDENTIALS_ERROR;
goto end; goto end;
} }
access_token = cJSON_GetObjectItem(json, "access_token"); for (ptr = json->child; ptr; ptr = ptr->next) {
if (access_token == NULL || access_token->type != cJSON_String) { if (strcmp(ptr->key, "access_token") == 0) {
access_token = ptr;
} else if (strcmp(ptr->key, "token_type") == 0) {
token_type = ptr;
} else if (strcmp(ptr->key, "expires_in") == 0) {
expires_in = ptr;
}
}
if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON."); gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
status = GRPC_CREDENTIALS_ERROR; status = GRPC_CREDENTIALS_ERROR;
goto end; goto end;
} }
token_type = cJSON_GetObjectItem(json, "token_type"); if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
if (token_type == NULL || token_type->type != cJSON_String) {
gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON."); gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
status = GRPC_CREDENTIALS_ERROR; status = GRPC_CREDENTIALS_ERROR;
goto end; goto end;
} }
expires_in = cJSON_GetObjectItem(json, "expires_in"); if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
if (expires_in == NULL || expires_in->type != cJSON_Number) {
gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON."); gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
status = GRPC_CREDENTIALS_ERROR; status = GRPC_CREDENTIALS_ERROR;
goto end; goto end;
} }
gpr_asprintf(&new_access_token, "%s %s", token_type->valuestring, gpr_asprintf(&new_access_token, "%s %s", token_type->value,
access_token->valuestring); access_token->value);
token_lifetime->tv_sec = expires_in->valueint; token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
token_lifetime->tv_nsec = 0; token_lifetime->tv_nsec = 0;
if (*token_elem != NULL) grpc_mdelem_unref(*token_elem); if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
*token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY, *token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
@ -400,7 +409,7 @@ end:
} }
if (null_terminated_body != NULL) gpr_free(null_terminated_body); if (null_terminated_body != NULL) gpr_free(null_terminated_body);
if (new_access_token != NULL) gpr_free(new_access_token); if (new_access_token != NULL) gpr_free(new_access_token);
if (json != NULL) cJSON_Delete(json); if (json != NULL) grpc_json_destroy(json);
return status; return status;
} }
@ -896,7 +905,9 @@ static void iam_destroy(grpc_credentials *creds) {
gpr_free(c); gpr_free(c);
} }
static int iam_has_request_metadata(const grpc_credentials *creds) { return 1; } static int iam_has_request_metadata(const grpc_credentials *creds) {
return 1;
}
static int iam_has_request_metadata_only(const grpc_credentials *creds) { static int iam_has_request_metadata_only(const grpc_credentials *creds) {
return 1; return 1;

@ -44,7 +44,8 @@
#include <openssl/bio.h> #include <openssl/bio.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/pem.h> #include <openssl/pem.h>
#include "third_party/cJSON/cJSON.h"
#include "src/core/json/json.h"
/* --- Constants. --- */ /* --- Constants. --- */
@ -64,18 +65,20 @@ static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL;
/* --- grpc_auth_json_key. --- */ /* --- grpc_auth_json_key. --- */
static const char *json_get_string_property(cJSON *json, static const char *json_get_string_property(grpc_json *json,
const char *prop_name) { const char *prop_name) {
cJSON *child = NULL; grpc_json *child;
child = cJSON_GetObjectItem(json, prop_name); for (child = json->child; child != NULL; child = child->next) {
if (child == NULL || child->type != cJSON_String) { if (strcmp(child->key, prop_name) == 0) break;
}
if (child == NULL || child->type != GRPC_JSON_STRING) {
gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name); gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
return NULL; return NULL;
} }
return child->valuestring; return child->value;
} }
static int set_json_key_string_property(cJSON *json, const char *prop_name, static int set_json_key_string_property(grpc_json *json, const char *prop_name,
char **json_key_field) { char **json_key_field) {
const char *prop_value = json_get_string_property(json, prop_name); const char *prop_value = json_get_string_property(json, prop_name);
if (prop_value == NULL) return 0; if (prop_value == NULL) return 0;
@ -91,7 +94,8 @@ int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) {
grpc_auth_json_key grpc_auth_json_key_create_from_string( grpc_auth_json_key grpc_auth_json_key_create_from_string(
const char *json_string) { const char *json_string) {
grpc_auth_json_key result; grpc_auth_json_key result;
cJSON *json = cJSON_Parse(json_string); char *scratchpad = gpr_strdup(json_string);
grpc_json *json = grpc_json_parse_string(scratchpad);
BIO *bio = NULL; BIO *bio = NULL;
const char *prop_value; const char *prop_value;
int success = 0; int success = 0;
@ -100,7 +104,7 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
result.type = GRPC_AUTH_JSON_KEY_TYPE_INVALID; result.type = GRPC_AUTH_JSON_KEY_TYPE_INVALID;
if (json == NULL) { if (json == NULL) {
gpr_log(GPR_ERROR, "Invalid json string %s", json_string); gpr_log(GPR_ERROR, "Invalid json string %s", json_string);
return result; goto end;
} }
prop_value = json_get_string_property(json, "type"); prop_value = json_get_string_property(json, "type");
@ -123,7 +127,8 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
goto end; goto end;
} }
bio = BIO_new(BIO_s_mem()); bio = BIO_new(BIO_s_mem());
if (BIO_puts(bio, prop_value) != strlen(prop_value)) { success = BIO_puts(bio, prop_value);
if ((success < 0) || ((size_t)success != strlen(prop_value))) {
gpr_log(GPR_ERROR, "Could not write into openssl BIO."); gpr_log(GPR_ERROR, "Could not write into openssl BIO.");
goto end; goto end;
} }
@ -136,8 +141,9 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
end: end:
if (bio != NULL) BIO_free(bio); if (bio != NULL) BIO_free(bio);
if (json != NULL) cJSON_Delete(json); if (json != NULL) grpc_json_destroy(json);
if (!success) grpc_auth_json_key_destruct(&result); if (!success) grpc_auth_json_key_destruct(&result);
gpr_free(scratchpad);
return result; return result;
} }
@ -164,49 +170,63 @@ void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) {
/* --- jwt encoding and signature. --- */ /* --- jwt encoding and signature. --- */
static grpc_json *create_child(grpc_json *brother, grpc_json *parent,
const char *key, const char *value,
grpc_json_type type) {
grpc_json *child = grpc_json_create(type);
if (brother) brother->next = child;
if (!parent->child) parent->child = child;
child->parent = parent;
child->value = value;
child->key = key;
return child;
}
static char *encoded_jwt_header(const char *algorithm) { static char *encoded_jwt_header(const char *algorithm) {
cJSON *json = cJSON_CreateObject(); grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
cJSON *child = cJSON_CreateString(algorithm); grpc_json *child = NULL;
char *json_str = NULL; char *json_str = NULL;
char *result = NULL; char *result = NULL;
cJSON_AddItemToObject(json, "alg", child);
child = cJSON_CreateString(GRPC_JWT_TYPE); child = create_child(NULL, json, "alg", algorithm, GRPC_JSON_STRING);
cJSON_AddItemToObject(json, "typ", child); create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING);
json_str = cJSON_PrintUnformatted(json);
json_str = grpc_json_dump_to_string(json, 0);
result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
free(json_str); gpr_free(json_str);
cJSON_Delete(json); grpc_json_destroy(json);
return result; return result;
} }
static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, static char *encoded_jwt_claim(const grpc_auth_json_key *json_key,
const char *scope, gpr_timespec token_lifetime) { const char *scope, gpr_timespec token_lifetime) {
cJSON *json = cJSON_CreateObject(); grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
cJSON *child = NULL; grpc_json *child = NULL;
char *json_str = NULL; char *json_str = NULL;
char *result = NULL; char *result = NULL;
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now();
gpr_timespec expiration = gpr_time_add(now, token_lifetime); gpr_timespec expiration = gpr_time_add(now, token_lifetime);
/* log10(2^64) ~= 20 */
char now_str[24];
char expiration_str[24];
if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime) > 0) { if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime) > 0) {
gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value."); gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value.");
expiration = gpr_time_add(now, grpc_max_auth_token_lifetime); expiration = gpr_time_add(now, grpc_max_auth_token_lifetime);
} }
child = cJSON_CreateString(json_key->client_email); sprintf(now_str, "%ld", now.tv_sec);
cJSON_AddItemToObject(json, "iss", child); sprintf(expiration_str, "%ld", expiration.tv_sec);
child = cJSON_CreateString(scope);
cJSON_AddItemToObject(json, "scope", child); child = create_child(NULL, json, "iss", json_key->client_email,
child = cJSON_CreateString(GRPC_JWT_AUDIENCE); GRPC_JSON_STRING);
cJSON_AddItemToObject(json, "aud", child); child = create_child(child, json, "scope", scope, GRPC_JSON_STRING);
child = cJSON_CreateNumber(now.tv_sec); child = create_child(child, json, "aud", GRPC_JWT_AUDIENCE, GRPC_JSON_STRING);
cJSON_SetIntValue(child, now.tv_sec); child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER);
cJSON_AddItemToObject(json, "iat", child); create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER);
child = cJSON_CreateNumber(expiration.tv_sec);
cJSON_SetIntValue(child, expiration.tv_sec); json_str = grpc_json_dump_to_string(json, 0);
cJSON_AddItemToObject(json, "exp", child);
json_str = cJSON_PrintUnformatted(json);
result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
free(json_str); gpr_free(json_str);
cJSON_Delete(json); grpc_json_destroy(json);
return result; return result;
} }
@ -216,12 +236,13 @@ static char *dot_concat_and_free_strings(char *str1, char *str2) {
size_t result_len = str1_len + 1 /* dot */ + str2_len; size_t result_len = str1_len + 1 /* dot */ + str2_len;
char *result = gpr_malloc(result_len + 1 /* NULL terminated */); char *result = gpr_malloc(result_len + 1 /* NULL terminated */);
char *current = result; char *current = result;
strncpy(current, str1, str1_len); memcpy(current, str1, str1_len);
current += str1_len; current += str1_len;
*(current++) = '.'; *(current++) = '.';
strncpy(current, str2, str2_len); memcpy(current, str2, str2_len);
current += str2_len; current += str2_len;
GPR_ASSERT((current - result) == result_len); GPR_ASSERT(current >= result);
GPR_ASSERT((gpr_uintptr)(current - result) == result_len);
*current = '\0'; *current = '\0';
gpr_free(str1); gpr_free(str1);
gpr_free(str2); gpr_free(str2);

@ -111,7 +111,7 @@ static void call_read_cb(secure_endpoint *ep, gpr_slice *slices, size_t nslices,
static void on_read(void *user_data, gpr_slice *slices, size_t nslices, static void on_read(void *user_data, gpr_slice *slices, size_t nslices,
grpc_endpoint_cb_status error) { grpc_endpoint_cb_status error) {
int i = 0; unsigned i;
gpr_uint8 keep_looping = 0; gpr_uint8 keep_looping = 0;
int input_buffer_count = 0; int input_buffer_count = 0;
tsi_result result = TSI_OK; tsi_result result = TSI_OK;
@ -221,7 +221,7 @@ static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep,
size_t nslices, size_t nslices,
grpc_endpoint_write_cb cb, grpc_endpoint_write_cb cb,
void *user_data) { void *user_data) {
int i = 0; unsigned i;
int output_buffer_count = 0; int output_buffer_count = 0;
tsi_result result = TSI_OK; tsi_result result = TSI_OK;
secure_endpoint *ep = (secure_endpoint *)secure_ep; secure_endpoint *ep = (secure_endpoint *)secure_ep;

@ -92,7 +92,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
grpc_resolved_addresses *resolved = NULL; grpc_resolved_addresses *resolved = NULL;
grpc_tcp_server *tcp = NULL; grpc_tcp_server *tcp = NULL;
size_t i; size_t i;
int count = 0; unsigned count = 0;
int port_num = -1; int port_num = -1;
int port_temp; int port_temp;
@ -127,6 +127,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
if (count != resolved->naddrs) { if (count != resolved->naddrs) {
gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved", gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved",
count, resolved->naddrs); count, resolved->naddrs);
/* if it's an error, don't we want to goto error; here ? */
} }
grpc_resolved_addresses_destroy(resolved); grpc_resolved_addresses_destroy(resolved);

@ -173,7 +173,7 @@ typedef struct census_log_core_local_block {
struct census_log { struct census_log {
int discard_old_records; int discard_old_records;
/* Number of cores (aka hardware-contexts) */ /* Number of cores (aka hardware-contexts) */
int num_cores; unsigned num_cores;
/* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */ /* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */
gpr_int32 num_blocks; gpr_int32 num_blocks;
cl_block* blocks; /* Block metadata. */ cl_block* blocks; /* Block metadata. */
@ -183,7 +183,7 @@ struct census_log {
/* Keeps the state of the reader iterator. A value of 0 indicates that /* Keeps the state of the reader iterator. A value of 0 indicates that
iterator has reached the end. census_log_init_reader() resets the iterator has reached the end. census_log_init_reader() resets the
value to num_core to restart iteration. */ value to num_core to restart iteration. */
gpr_int32 read_iterator_state; gpr_uint32 read_iterator_state;
/* Points to the block being read. If non-NULL, the block is locked for /* Points to the block being read. If non-NULL, the block is locked for
reading (block_being_read_->reader_lock is held). */ reading (block_being_read_->reader_lock is held). */
cl_block* block_being_read; cl_block* block_being_read;

@ -184,7 +184,7 @@ static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) {
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_mu);
if (store != NULL) { if (store != NULL) {
size_t n; size_t n;
int i, j; unsigned i, j;
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now();
census_ht_kv* kv = census_ht_get_all_elements(store, &n); census_ht_kv* kv = census_ht_get_all_elements(store, &n);
if (kv != NULL) { if (kv != NULL) {

@ -292,7 +292,7 @@ static void ht_delete_entry_chain(const census_ht_option* options,
} }
void census_ht_destroy(census_ht* ht) { void census_ht_destroy(census_ht* ht) {
int i; unsigned i;
for (i = 0; i < ht->num_buckets; ++i) { for (i = 0; i < ht->num_buckets; ++i) {
ht_delete_entry_chain(&ht->options, ht->buckets[i].next); ht_delete_entry_chain(&ht->options, ht->buckets[i].next);
} }

@ -150,7 +150,7 @@ window_stats* census_window_stats_create(int nintervals,
is->width = size_ns / granularity; is->width = size_ns / granularity;
/* Check for possible overflow issues, and maximize interval size if the /* Check for possible overflow issues, and maximize interval size if the
user requested something large enough. */ user requested something large enough. */
if (GPR_INT64_MAX - is->width > size_ns) { if ((GPR_INT64_MAX - is->width) > size_ns) {
is->top = size_ns + is->width; is->top = size_ns + is->width;
} else { } else {
is->top = GPR_INT64_MAX; is->top = GPR_INT64_MAX;

@ -38,12 +38,12 @@
/* Return the number of CPU cores on the current system. Will return 0 if /* Return the number of CPU cores on the current system. Will return 0 if
if information is not available. */ if information is not available. */
int gpr_cpu_num_cores(void); unsigned gpr_cpu_num_cores(void);
/* Return the CPU on which the current thread is executing; N.B. This should /* Return the CPU on which the current thread is executing; N.B. This should
be considered advisory only - it is possible that the thread is switched be considered advisory only - it is possible that the thread is switched
to a different CPU at any time. Returns a value in range to a different CPU at any time. Returns a value in range
[0, gpr_cpu_num_cores() - 1] */ [0, gpr_cpu_num_cores() - 1] */
int gpr_cpu_current_cpu(void); unsigned gpr_cpu_current_cpu(void);
#endif /* __GRPC_INTERNAL_SUPPORT_CPU_H__ */ #endif /* __GRPC_INTERNAL_SUPPORT_CPU_H__ */

@ -75,8 +75,9 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
int gpr_cpu_num_cores(void) { unsigned gpr_cpu_num_cores(void) {
static int ncpus = 0; static int ncpus = 0;
/* FIXME: !threadsafe */
if (ncpus == 0) { if (ncpus == 0) {
ncpus = sysconf(_SC_NPROCESSORS_ONLN); ncpus = sysconf(_SC_NPROCESSORS_ONLN);
if (ncpus < 1) { if (ncpus < 1) {
@ -87,7 +88,7 @@ int gpr_cpu_num_cores(void) {
return ncpus; return ncpus;
} }
int gpr_cpu_current_cpu(void) { unsigned gpr_cpu_current_cpu(void) {
int cpu = sched_getcpu(); int cpu = sched_getcpu();
if (cpu < 0) { if (cpu < 0) {
gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno));

@ -45,7 +45,7 @@
static __thread char magic_thread_local; static __thread char magic_thread_local;
int gpr_cpu_num_cores(void) { unsigned gpr_cpu_num_cores(void) {
static int ncpus = 0; static int ncpus = 0;
if (ncpus == 0) { if (ncpus == 0) {
ncpus = sysconf(_SC_NPROCESSORS_ONLN); ncpus = sysconf(_SC_NPROCESSORS_ONLN);
@ -63,7 +63,7 @@ static size_t shard_ptr(const void *info) {
return ((x >> 4) ^ (x >> 9) ^ (x >> 14)) % gpr_cpu_num_cores(); return ((x >> 4) ^ (x >> 9) ^ (x >> 14)) % gpr_cpu_num_cores();
} }
int gpr_cpu_current_cpu(void) { unsigned gpr_cpu_current_cpu(void) {
/* NOTE: there's no way I know to return the actual cpu index portably... /* NOTE: there's no way I know to return the actual cpu index portably...
most code that's using this is using it to shard across work queues though, most code that's using this is using it to shard across work queues though,
so here we use thread identity instead to achieve a similar though not so here we use thread identity instead to achieve a similar though not

@ -77,7 +77,6 @@ static size_t bucket_for_unchecked(gpr_histogram *h, double x) {
/* bounds checked version of the above */ /* bounds checked version of the above */
static size_t bucket_for(gpr_histogram *h, double x) { static size_t bucket_for(gpr_histogram *h, double x) {
size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 0, h->max_possible)); size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 0, h->max_possible));
GPR_ASSERT(bucket >= 0);
GPR_ASSERT(bucket < h->num_buckets); GPR_ASSERT(bucket < h->num_buckets);
return bucket; return bucket;
} }

@ -55,7 +55,7 @@ void gpr_slice_buffer_destroy(gpr_slice_buffer *sb) {
gpr_free(sb->slices); gpr_free(sb->slices);
} }
gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, int n) { gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) {
gpr_slice *back; gpr_slice *back;
gpr_uint8 *out; gpr_uint8 *out;
@ -64,7 +64,7 @@ gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, int n) {
if (sb->count == 0) goto add_new; if (sb->count == 0) goto add_new;
back = &sb->slices[sb->count - 1]; back = &sb->slices[sb->count - 1];
if (back->refcount) goto add_new; if (back->refcount) goto add_new;
if (back->data.inlined.length + n > sizeof(back->data.inlined.bytes)) if ((back->data.inlined.length + n) > sizeof(back->data.inlined.bytes))
goto add_new; goto add_new;
out = back->data.inlined.bytes + back->data.inlined.length; out = back->data.inlined.bytes + back->data.inlined.length;
back->data.inlined.length += n; back->data.inlined.length += n;

@ -57,7 +57,7 @@ int gpr_asprintf(char **strp, const char *format, ...) {
va_start(args, format); va_start(args, format);
ret = vsnprintf(buf, sizeof(buf), format, args); ret = vsnprintf(buf, sizeof(buf), format, args);
va_end(args); va_end(args);
if (!(0 <= ret && ret < ~(size_t)0)) { if (!(0 <= ret)) {
*strp = NULL; *strp = NULL;
return -1; return -1;
} }
@ -79,7 +79,7 @@ int gpr_asprintf(char **strp, const char *format, ...) {
va_start(args, format); va_start(args, format);
ret = vsnprintf(*strp, strp_buflen, format, args); ret = vsnprintf(*strp, strp_buflen, format, args);
va_end(args); va_end(args);
if (ret == strp_buflen - 1) { if ((size_t)ret == strp_buflen - 1) {
return ret; return ret;
} }

@ -43,6 +43,7 @@
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/thd.h> #include <grpc/support/thd.h>
#include <grpc/support/useful.h>
struct thd_arg { struct thd_arg {
void (*body)(void *arg); /* body of a thread */ void (*body)(void *arg); /* body of a thread */

@ -75,7 +75,7 @@ int grpc_server_add_http2_port(grpc_server *server, const char *addr) {
grpc_resolved_addresses *resolved = NULL; grpc_resolved_addresses *resolved = NULL;
grpc_tcp_server *tcp = NULL; grpc_tcp_server *tcp = NULL;
size_t i; size_t i;
int count = 0; unsigned count = 0;
int port_num = -1; int port_num = -1;
int port_temp; int port_temp;

@ -237,6 +237,9 @@ struct transport {
/* state for a stream that's not yet been created */ /* state for a stream that's not yet been created */
grpc_stream_op_buffer new_stream_sopb; grpc_stream_op_buffer new_stream_sopb;
/* stream ops that need to be destroyed, but outside of the lock */
grpc_stream_op_buffer nuke_later_sopb;
/* active parser */ /* active parser */
void *parser_data; void *parser_data;
stream *incoming_stream; stream *incoming_stream;
@ -370,6 +373,8 @@ static void unref_transport(transport *t) {
} }
gpr_free(t->pending_goaways); gpr_free(t->pending_goaways);
grpc_sopb_destroy(&t->nuke_later_sopb);
gpr_free(t); gpr_free(t);
} }
@ -416,6 +421,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
t->cap_pending_goaways = 0; t->cap_pending_goaways = 0;
gpr_slice_buffer_init(&t->outbuf); gpr_slice_buffer_init(&t->outbuf);
gpr_slice_buffer_init(&t->qbuf); gpr_slice_buffer_init(&t->qbuf);
grpc_sopb_init(&t->nuke_later_sopb);
if (is_client) { if (is_client) {
gpr_slice_buffer_add(&t->qbuf, gpr_slice_buffer_add(&t->qbuf,
gpr_slice_from_copied_string(CLIENT_CONNECT_STRING)); gpr_slice_from_copied_string(CLIENT_CONNECT_STRING));
@ -555,6 +561,11 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs,
return 0; return 0;
} }
static void schedule_nuke_sopb(transport *t, grpc_stream_op_buffer *sopb) {
grpc_sopb_append(&t->nuke_later_sopb, sopb->ops, sopb->nops);
sopb->nops = 0;
}
static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { static void destroy_stream(grpc_transport *gt, grpc_stream *gs) {
transport *t = (transport *)gt; transport *t = (transport *)gt;
stream *s = (stream *)gs; stream *s = (stream *)gs;
@ -681,6 +692,11 @@ static void unlock(transport *t) {
int i; int i;
pending_goaway *goaways = NULL; pending_goaway *goaways = NULL;
grpc_endpoint *ep = t->ep; grpc_endpoint *ep = t->ep;
grpc_stream_op_buffer nuke_now = t->nuke_later_sopb;
if (nuke_now.nops) {
memset(&t->nuke_later_sopb, 0, sizeof(t->nuke_later_sopb));
}
/* see if we need to trigger a write - and if so, get the data ready */ /* see if we need to trigger a write - and if so, get the data ready */
if (ep && !t->writing) { if (ep && !t->writing) {
@ -750,6 +766,10 @@ static void unlock(transport *t) {
unref_transport(t); unref_transport(t);
} }
if (nuke_now.nops) {
grpc_sopb_destroy(&nuke_now);
}
gpr_free(goaways); gpr_free(goaways);
} }
@ -1006,9 +1026,9 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
if (s) { if (s) {
/* clear out any unreported input & output: nobody cares anymore */ /* clear out any unreported input & output: nobody cares anymore */
grpc_sopb_reset(&s->parser.incoming_sopb);
had_outgoing = s->outgoing_sopb.nops != 0; had_outgoing = s->outgoing_sopb.nops != 0;
grpc_sopb_reset(&s->outgoing_sopb); schedule_nuke_sopb(t, &s->parser.incoming_sopb);
schedule_nuke_sopb(t, &s->outgoing_sopb);
if (s->cancelled) { if (s->cancelled) {
send_rst = 0; send_rst = 0;
} else if (!s->read_closed || !s->sent_write_closed || had_outgoing) { } else if (!s->read_closed || !s->sent_write_closed || had_outgoing) {
@ -1518,7 +1538,7 @@ static int process_read(transport *t, gpr_slice slice) {
dts_fh_0: dts_fh_0:
case DTS_FH_0: case DTS_FH_0:
GPR_ASSERT(cur < end); GPR_ASSERT(cur < end);
t->incoming_frame_size = ((gpr_uint32) * cur) << 16; t->incoming_frame_size = ((gpr_uint32)*cur) << 16;
if (++cur == end) { if (++cur == end) {
t->deframe_state = DTS_FH_1; t->deframe_state = DTS_FH_1;
return 1; return 1;
@ -1526,7 +1546,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */ /* fallthrough */
case DTS_FH_1: case DTS_FH_1:
GPR_ASSERT(cur < end); GPR_ASSERT(cur < end);
t->incoming_frame_size |= ((gpr_uint32) * cur) << 8; t->incoming_frame_size |= ((gpr_uint32)*cur) << 8;
if (++cur == end) { if (++cur == end) {
t->deframe_state = DTS_FH_2; t->deframe_state = DTS_FH_2;
return 1; return 1;
@ -1558,7 +1578,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */ /* fallthrough */
case DTS_FH_5: case DTS_FH_5:
GPR_ASSERT(cur < end); GPR_ASSERT(cur < end);
t->incoming_stream_id = (((gpr_uint32) * cur) << 24) & 0x7f; t->incoming_stream_id = (((gpr_uint32)*cur) << 24) & 0x7f;
if (++cur == end) { if (++cur == end) {
t->deframe_state = DTS_FH_6; t->deframe_state = DTS_FH_6;
return 1; return 1;
@ -1566,7 +1586,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */ /* fallthrough */
case DTS_FH_6: case DTS_FH_6:
GPR_ASSERT(cur < end); GPR_ASSERT(cur < end);
t->incoming_stream_id |= ((gpr_uint32) * cur) << 16; t->incoming_stream_id |= ((gpr_uint32)*cur) << 16;
if (++cur == end) { if (++cur == end) {
t->deframe_state = DTS_FH_7; t->deframe_state = DTS_FH_7;
return 1; return 1;
@ -1574,7 +1594,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */ /* fallthrough */
case DTS_FH_7: case DTS_FH_7:
GPR_ASSERT(cur < end); GPR_ASSERT(cur < end);
t->incoming_stream_id |= ((gpr_uint32) * cur) << 8; t->incoming_stream_id |= ((gpr_uint32)*cur) << 8;
if (++cur == end) { if (++cur == end) {
t->deframe_state = DTS_FH_8; t->deframe_state = DTS_FH_8;
return 1; return 1;
@ -1582,7 +1602,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */ /* fallthrough */
case DTS_FH_8: case DTS_FH_8:
GPR_ASSERT(cur < end); GPR_ASSERT(cur < end);
t->incoming_stream_id |= ((gpr_uint32) * cur); t->incoming_stream_id |= ((gpr_uint32)*cur);
t->deframe_state = DTS_FRAME; t->deframe_state = DTS_FRAME;
if (!init_frame_parser(t)) { if (!init_frame_parser(t)) {
return 0; return 0;
@ -1738,9 +1758,9 @@ static void add_to_pollset(grpc_transport *gt, grpc_pollset *pollset) {
*/ */
static const grpc_transport_vtable vtable = { static const grpc_transport_vtable vtable = {
sizeof(stream), init_stream, send_batch, set_allow_window_updates, sizeof(stream), init_stream, send_batch, set_allow_window_updates,
add_to_pollset, destroy_stream, abort_stream, goaway, add_to_pollset, destroy_stream, abort_stream, goaway, close_transport,
close_transport, send_ping, destroy_transport}; send_ping, destroy_transport};
void grpc_create_chttp2_transport(grpc_transport_setup_callback setup, void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
void *arg, void *arg,

@ -38,6 +38,7 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#include <grpc/support/useful.h>
#include "src/core/tsi/transport_security.h" #include "src/core/tsi/transport_security.h"
/* --- Constants. ---*/ /* --- Constants. ---*/
@ -412,7 +413,7 @@ static tsi_result fake_handshaker_process_bytes_from_peer(
tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) { tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) {
tsi_result result = TSI_OK; tsi_result result = TSI_OK;
tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self;
int expected_msg = impl->next_message_to_send - 1; tsi_fake_handshake_message expected_msg = impl->next_message_to_send - 1;
tsi_fake_handshake_message received_msg; tsi_fake_handshake_message received_msg;
if (!impl->needs_incoming_message || impl->result == TSI_OK) { if (!impl->needs_incoming_message || impl->result == TSI_OK) {

@ -37,6 +37,7 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
#include <grpc/support/useful.h>
#include "src/core/tsi/transport_security.h" #include "src/core/tsi/transport_security.h"
#include <openssl/bio.h> #include <openssl/bio.h>
@ -565,7 +566,8 @@ static tsi_result build_alpn_protocol_name_list(
current += alpn_protocols_lengths[i]; current += alpn_protocols_lengths[i];
} }
/* Safety check. */ /* Safety check. */
if ((current - *protocol_name_list) != *protocol_name_list_length) { if ((current < *protocol_name_list) ||
((gpr_uintptr)(current - *protocol_name_list) != *protocol_name_list_length)) {
return TSI_INTERNAL_ERROR; return TSI_INTERNAL_ERROR;
} }
return TSI_OK; return TSI_OK;
@ -1063,7 +1065,8 @@ static int server_handshaker_factory_alpn_callback(
while ((client_current - in) < inlen) { while ((client_current - in) < inlen) {
unsigned char client_current_len = *(client_current++); unsigned char client_current_len = *(client_current++);
const unsigned char* server_current = factory->alpn_protocol_list; const unsigned char* server_current = factory->alpn_protocol_list;
while ((server_current - factory->alpn_protocol_list) < while ((server_current >= factory->alpn_protocol_list) &&
(gpr_uintptr)(server_current - factory->alpn_protocol_list) <
factory->alpn_protocol_list_length) { factory->alpn_protocol_list_length) {
unsigned char server_current_len = *(server_current++); unsigned char server_current_len = *(server_current++);
if ((client_current_len == server_current_len) && if ((client_current_len == server_current_len) &&

@ -0,0 +1,62 @@
// 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 examples;
// Protocol type definitions
message StockRequest {
optional string symbol = 1;
optional int32 num_trades_to_watch = 2 [default=0];
};
message StockReply {
optional float price = 1;
optional string symbol = 2;
};
// Interface exported by the server
service Stock {
// Simple blocking RPC
rpc GetLastTradePrice(StockRequest) returns (StockReply) {
};
// Bidirectional streaming RPC
rpc GetLastTradePriceMultiple(stream StockRequest) returns
(stream StockReply) {
};
// Unidirectional server-to-client streaming RPC
rpc WatchFutureTrades(StockRequest) returns (stream StockReply) {
};
// Unidirectional client-to-server streaming RPC
rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) {
};
};

@ -0,0 +1,43 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
var grpc = require('..');
var examples = grpc.load(__dirname + '/stock.proto').examples;
/**
* This exports a client constructor for the Stock service. The usage looks like
*
* var StockClient = require('stock_client.js');
* var stockClient = new StockClient(server_address);
*/
module.exports = examples.Stock;

@ -0,0 +1,83 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
var _ = require('underscore');
var grpc = require('..');
var examples = grpc.load(__dirname + '/stock.proto').examples;
var StockServer = grpc.makeServerConstructor([examples.Stock.service]);
function getLastTradePrice(call, callback) {
callback(null, {price: 88});
}
function watchFutureTrades(call) {
for (var i = 0; i < call.request.num_trades_to_watch; i++) {
call.write({price: 88.00 + i * 10.00});
}
call.end();
}
function getHighestTradePrice(call, callback) {
var trades = [];
call.on('data', function(data) {
trades.push({symbol: data.symbol, price: _.random(0, 100)});
});
call.on('end', function() {
if(_.isEmpty(trades)) {
callback(null, {});
} else {
callback(null, _.max(trades, function(trade){return trade.price;}));
}
});
}
function getLastTradePriceMultiple(call) {
call.on('data', function(data) {
call.write({price: 88});
});
call.on('end', function() {
call.end();
});
}
var stockServer = new StockServer({
'examples.Stock' : {
getLastTradePrice: getLastTradePrice,
getLastTradePriceMultiple: getLastTradePriceMultiple,
watchFutureTrades: watchFutureTrades,
getHighestTradePrice: getHighestTradePrice
}
});
exports.module = stockServer;

@ -39,13 +39,17 @@
#include "grpc/grpc.h" #include "grpc/grpc.h"
#include "grpc/support/slice.h" #include "grpc/support/slice.h"
#include "byte_buffer.h"
namespace grpc { namespace grpc {
namespace node { namespace node {
#include "byte_buffer.h"
using ::node::Buffer; using ::node::Buffer;
using v8::Context;
using v8::Function;
using v8::Handle; using v8::Handle;
using v8::Object;
using v8::Number;
using v8::Value; using v8::Value;
grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) { grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) {
@ -73,7 +77,19 @@ Handle<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) {
memcpy(result + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); memcpy(result + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next));
offset += GPR_SLICE_LENGTH(next); offset += GPR_SLICE_LENGTH(next);
} }
return NanEscapeScope(NanNewBufferHandle(result, length)); return NanEscapeScope(MakeFastBuffer(NanNewBufferHandle(result, length)));
}
Handle<Value> MakeFastBuffer(Handle<Value> slowBuffer) {
NanEscapableScope();
Handle<Object> globalObj = Context::GetCurrent()->Global();
Handle<Function> bufferConstructor = Handle<Function>::Cast(
globalObj->Get(NanNew("Buffer")));
Handle<Value> consArgs[3] = { slowBuffer,
NanNew<Number>(Buffer::Length(slowBuffer)),
NanNew<Number>(0) };
Handle<Object> fastBuffer = bufferConstructor->NewInstance(3, consArgs);
return NanEscapeScope(fastBuffer);
} }
} // namespace node } // namespace node
} // namespace grpc } // namespace grpc

@ -50,6 +50,10 @@ grpc_byte_buffer *BufferToByteBuffer(v8::Handle<v8::Value> buffer);
/* Convert a grpc_byte_buffer to a Node.js Buffer */ /* Convert a grpc_byte_buffer to a Node.js Buffer */
v8::Handle<v8::Value> ByteBufferToBuffer(grpc_byte_buffer *buffer); v8::Handle<v8::Value> ByteBufferToBuffer(grpc_byte_buffer *buffer);
/* Convert a ::node::Buffer to a fast Buffer, as defined in the Node
Buffer documentation */
v8::Handle<v8::Value> MakeFastBuffer(v8::Handle<v8::Value> slowBuffer);
} // namespace node } // namespace node
} // namespace grpc } // namespace grpc

@ -33,6 +33,7 @@
#include <node.h> #include <node.h>
#include "grpc/support/log.h"
#include "grpc/grpc.h" #include "grpc/grpc.h"
#include "grpc/support/time.h" #include "grpc/support/time.h"
#include "byte_buffer.h" #include "byte_buffer.h"
@ -173,31 +174,43 @@ NAN_METHOD(Call::AddMetadata) {
return NanThrowTypeError("addMetadata can only be called on Call objects"); return NanThrowTypeError("addMetadata can only be called on Call objects");
} }
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
for (int i = 0; !args[i]->IsUndefined(); i++) { if (!args[0]->IsObject()) {
if (!args[i]->IsObject()) { return NanThrowTypeError("addMetadata's first argument must be an object");
}
Handle<Object> metadata = args[0]->ToObject();
Handle<Array> keys(metadata->GetOwnPropertyNames());
for (unsigned int i = 0; i < keys->Length(); i++) {
Handle<String> current_key(keys->Get(i)->ToString());
if (!metadata->Get(current_key)->IsArray()) {
return NanThrowTypeError( return NanThrowTypeError(
"addMetadata arguments must be objects with key and value"); "addMetadata's first argument's values must be arrays");
} }
Handle<Object> item = args[i]->ToObject(); NanUtf8String utf8_key(current_key);
Handle<Value> key = item->Get(NanNew("key")); Handle<Array> values = Local<Array>::Cast(metadata->Get(current_key));
if (!key->IsString()) { for (unsigned int j = 0; j < values->Length(); j++) {
return NanThrowTypeError( Handle<Value> value = values->Get(j);
"objects passed to addMetadata must have key->string"); grpc_metadata metadata;
} grpc_call_error error;
Handle<Value> value = item->Get(NanNew("value")); metadata.key = *utf8_key;
if (!Buffer::HasInstance(value)) { if (Buffer::HasInstance(value)) {
return NanThrowTypeError( metadata.value = Buffer::Data(value);
"objects passed to addMetadata must have value->Buffer"); metadata.value_length = Buffer::Length(value);
} error = grpc_call_add_metadata(call->wrapped_call, &metadata, 0);
grpc_metadata metadata; } else if (value->IsString()) {
NanUtf8String utf8_key(key); Handle<String> string_value = value->ToString();
metadata.key = *utf8_key; NanUtf8String utf8_value(string_value);
metadata.value = Buffer::Data(value); metadata.value = *utf8_value;
metadata.value_length = Buffer::Length(value); metadata.value_length = string_value->Length();
grpc_call_error error = gpr_log(GPR_DEBUG, "adding metadata: %s, %s, %d", metadata.key,
grpc_call_add_metadata(call->wrapped_call, &metadata, 0); metadata.value, metadata.value_length);
if (error != GRPC_CALL_OK) { error = grpc_call_add_metadata(call->wrapped_call, &metadata, 0);
return NanThrowError("addMetadata failed", error); } else {
return NanThrowTypeError(
"addMetadata values must be strings or buffers");
}
if (error != GRPC_CALL_OK) {
return NanThrowError("addMetadata failed", error);
}
} }
} }
NanReturnUndefined(); NanReturnUndefined();

@ -31,6 +31,8 @@
* *
*/ */
#include <map>
#include <node.h> #include <node.h>
#include <nan.h> #include <nan.h>
#include "grpc/grpc.h" #include "grpc/grpc.h"
@ -43,6 +45,7 @@
namespace grpc { namespace grpc {
namespace node { namespace node {
using ::node::Buffer;
using v8::Array; using v8::Array;
using v8::Date; using v8::Date;
using v8::Handle; using v8::Handle;
@ -53,6 +56,37 @@ using v8::Persistent;
using v8::String; using v8::String;
using v8::Value; using v8::Value;
Handle<Value> ParseMetadata(grpc_metadata *metadata_elements, size_t length) {
NanEscapableScope();
std::map<char*, size_t> size_map;
std::map<char*, size_t> index_map;
for (unsigned int i = 0; i < length; i++) {
char *key = metadata_elements[i].key;
if (size_map.count(key)) {
size_map[key] += 1;
}
index_map[key] = 0;
}
Handle<Object> metadata_object = NanNew<Object>();
for (unsigned int i = 0; i < length; i++) {
grpc_metadata* elem = &metadata_elements[i];
Handle<String> key_string = String::New(elem->key);
Handle<Array> array;
if (metadata_object->Has(key_string)) {
array = Handle<Array>::Cast(metadata_object->Get(key_string));
} else {
array = NanNew<Array>(size_map[elem->key]);
metadata_object->Set(key_string, array);
}
array->Set(index_map[elem->key],
MakeFastBuffer(
NanNewBufferHandle(elem->value, elem->value_length)));
index_map[elem->key] += 1;
}
return NanEscapeScope(metadata_object);
}
Handle<Value> GetEventData(grpc_event *event) { Handle<Value> GetEventData(grpc_event *event) {
NanEscapableScope(); NanEscapableScope();
size_t count; size_t count;
@ -72,18 +106,7 @@ Handle<Value> GetEventData(grpc_event *event) {
case GRPC_CLIENT_METADATA_READ: case GRPC_CLIENT_METADATA_READ:
count = event->data.client_metadata_read.count; count = event->data.client_metadata_read.count;
items = event->data.client_metadata_read.elements; items = event->data.client_metadata_read.elements;
metadata = NanNew<Array>(static_cast<int>(count)); return NanEscapeScope(ParseMetadata(items, count));
for (unsigned int i = 0; i < count; i++) {
Handle<Object> item_obj = NanNew<Object>();
item_obj->Set(NanNew<String, const char *>("key"),
NanNew<String, char *>(items[i].key));
item_obj->Set(
NanNew<String, const char *>("value"),
NanNew<String, char *>(items[i].value,
static_cast<int>(items[i].value_length)));
metadata->Set(i, item_obj);
}
return NanEscapeScope(metadata);
case GRPC_FINISHED: case GRPC_FINISHED:
status = NanNew<Object>(); status = NanNew<Object>();
status->Set(NanNew("code"), NanNew<Number>(event->data.finished.status)); status->Set(NanNew("code"), NanNew<Number>(event->data.finished.status));
@ -93,18 +116,7 @@ Handle<Value> GetEventData(grpc_event *event) {
} }
count = event->data.finished.metadata_count; count = event->data.finished.metadata_count;
items = event->data.finished.metadata_elements; items = event->data.finished.metadata_elements;
metadata = NanNew<Array>(static_cast<int>(count)); status->Set(NanNew("metadata"), ParseMetadata(items, count));
for (unsigned int i = 0; i < count; i++) {
Handle<Object> item_obj = NanNew<Object>();
item_obj->Set(NanNew<String, const char *>("key"),
NanNew<String, char *>(items[i].key));
item_obj->Set(
NanNew<String, const char *>("value"),
NanNew<String, char *>(items[i].value,
static_cast<int>(items[i].value_length)));
metadata->Set(i, item_obj);
}
status->Set(NanNew("metadata"), metadata);
return NanEscapeScope(status); return NanEscapeScope(status);
case GRPC_SERVER_RPC_NEW: case GRPC_SERVER_RPC_NEW:
rpc_new = NanNew<Object>(); rpc_new = NanNew<Object>();
@ -133,7 +145,7 @@ Handle<Value> GetEventData(grpc_event *event) {
static_cast<int>(items[i].value_length))); static_cast<int>(items[i].value_length)));
metadata->Set(i, item_obj); metadata->Set(i, item_obj);
} }
rpc_new->Set(NanNew<String, const char *>("metadata"), metadata); rpc_new->Set(NanNew("metadata"), ParseMetadata(items, count));
return NanEscapeScope(rpc_new); return NanEscapeScope(rpc_new);
default: default:
return NanEscapeScope(NanNull()); return NanEscapeScope(NanNull());

@ -199,7 +199,6 @@ function pingPong(client, done) {
/** /**
* Run the empty_stream test. * Run the empty_stream test.
* NOTE: This does not work, but should with the new invoke API
* @param {Client} client The client to test against * @param {Client} client The client to test against
* @param {function} done Callback to call when the test is completed. Included * @param {function} done Callback to call when the test is completed. Included
* primarily for use with mocha * primarily for use with mocha
@ -218,6 +217,44 @@ function emptyStream(client, done) {
call.end(); call.end();
} }
/**
* Run the cancel_after_begin test.
* @param {Client} client The client to test against
* @param {function} done Callback to call when the test is completed. Included
* primarily for use with mocha
*/
function cancelAfterBegin(client, done) {
var call = client.streamingInputCall(function(err, resp) {
assert.strictEqual(err.code, grpc.status.CANCELLED);
done();
});
call.cancel();
}
/**
* Run the cancel_after_first_response test.
* @param {Client} client The client to test against
* @param {function} done Callback to call when the test is completed. Included
* primarily for use with mocha
*/
function cancelAfterFirstResponse(client, done) {
var call = client.fullDuplexCall();
call.write({
response_type: testProto.PayloadType.COMPRESSABLE,
response_parameters: [
{size: 31415}
],
payload: {body: zeroBuffer(27182)}
});
call.on('data', function(data) {
call.cancel();
});
call.on('status', function(status) {
assert.strictEqual(status.code, grpc.status.CANCELLED);
done();
});
}
/** /**
* Map from test case names to test functions * Map from test case names to test functions
*/ */
@ -227,7 +264,9 @@ var test_cases = {
client_streaming: clientStreaming, client_streaming: clientStreaming,
server_streaming: serverStreaming, server_streaming: serverStreaming,
ping_pong: pingPong, ping_pong: pingPong,
empty_stream: emptyStream empty_stream: emptyStream,
cancel_after_begin: cancelAfterBegin,
cancel_after_first_response: cancelAfterFirstResponse
}; };
/** /**

@ -183,7 +183,7 @@ function getServer(port, tls) {
fullDuplexCall: handleFullDuplex, fullDuplexCall: handleFullDuplex,
halfDuplexCall: handleHalfDuplex halfDuplexCall: handleHalfDuplex
} }
}, options); }, null, options);
var port_num = server.bind('0.0.0.0:' + port, tls); var port_num = server.bind('0.0.0.0:' + port, tls);
return {server: server, port: port_num}; return {server: server, port: port_num};
} }

@ -202,10 +202,13 @@ GrpcServerStream.prototype._write = function(chunk, encoding, callback) {
* Constructs a server object that stores request handlers and delegates * Constructs a server object that stores request handlers and delegates
* incoming requests to those handlers * incoming requests to those handlers
* @constructor * @constructor
* @param {Array} options Options that should be passed to the internal server * @param {function(string, Object<string, Array<Buffer>>):
Object<string, Array<Buffer|string>>=} getMetadata Callback that gets
* metatada for a given method
* @param {Object=} options Options that should be passed to the internal server
* implementation * implementation
*/ */
function Server(options) { function Server(getMetadata, options) {
this.handlers = {}; this.handlers = {};
var handlers = this.handlers; var handlers = this.handlers;
var server = new grpc.Server(options); var server = new grpc.Server(options);
@ -240,15 +243,27 @@ function Server(options) {
var handler = undefined; var handler = undefined;
var deadline = data.absolute_deadline; var deadline = data.absolute_deadline;
var cancelled = false; var cancelled = false;
if (handlers.hasOwnProperty(data.method)) {
handler = handlers[data.method];
}
call.serverAccept(function(event) { call.serverAccept(function(event) {
if (event.data.code === grpc.status.CANCELLED) { if (event.data.code === grpc.status.CANCELLED) {
cancelled = true; cancelled = true;
stream.emit('cancelled'); if (stream) {
stream.emit('cancelled');
}
} }
}, 0); }, 0);
if (handlers.hasOwnProperty(data.method)) {
handler = handlers[data.method];
} else {
call.serverEndInitialMetadata(0);
call.startWriteStatus(
grpc.status.UNIMPLEMENTED,
"This method is not available on this server.",
function() {});
return;
}
if (getMetadata) {
call.addMetadata(getMetadata(data.method, data.metadata));
}
call.serverEndInitialMetadata(0); call.serverEndInitialMetadata(0);
var stream = new GrpcServerStream(call, handler.serialize, var stream = new GrpcServerStream(call, handler.serialize,
handler.deserialize); handler.deserialize);

@ -129,16 +129,18 @@ ServerWritableObjectStream.prototype._write = _write;
/** /**
* Creates a binary stream handler function from a unary handler function * Creates a binary stream handler function from a unary handler function
* @param {function(Object, function(Error, *))} handler Unary call handler * @param {function(Object, function(Error, *), metadata=)} handler Unary call
* @return {function(stream)} Binary stream handler * handler
* @return {function(stream, metadata=)} Binary stream handler
*/ */
function makeUnaryHandler(handler) { function makeUnaryHandler(handler) {
/** /**
* Handles a stream by reading a single data value, passing it to the handler, * Handles a stream by reading a single data value, passing it to the handler,
* and writing the response back to the stream. * and writing the response back to the stream.
* @param {stream} stream Binary data stream * @param {stream} stream Binary data stream
* @param {metadata=} metadata Incoming metadata array
*/ */
return function handleUnaryCall(stream) { return function handleUnaryCall(stream, metadata) {
stream.on('data', function handleUnaryData(value) { stream.on('data', function handleUnaryData(value) {
var call = {request: value}; var call = {request: value};
Object.defineProperty(call, 'cancelled', { Object.defineProperty(call, 'cancelled', {
@ -154,7 +156,7 @@ function makeUnaryHandler(handler) {
stream.write(value); stream.write(value);
stream.end(); stream.end();
} }
}); }, metadata);
}); });
}; };
} }
@ -162,17 +164,18 @@ function makeUnaryHandler(handler) {
/** /**
* Creates a binary stream handler function from a client stream handler * Creates a binary stream handler function from a client stream handler
* function * function
* @param {function(Readable, function(Error, *))} handler Client stream call * @param {function(Readable, function(Error, *), metadata=)} handler Client
* handler * stream call handler
* @return {function(stream)} Binary stream handler * @return {function(stream, metadata=)} Binary stream handler
*/ */
function makeClientStreamHandler(handler) { function makeClientStreamHandler(handler) {
/** /**
* Handles a stream by passing a deserializing stream to the handler and * Handles a stream by passing a deserializing stream to the handler and
* writing the response back to the stream. * writing the response back to the stream.
* @param {stream} stream Binary data stream * @param {stream} stream Binary data stream
* @param {metadata=} metadata Incoming metadata array
*/ */
return function handleClientStreamCall(stream) { return function handleClientStreamCall(stream, metadata) {
var object_stream = new ServerReadableObjectStream(stream); var object_stream = new ServerReadableObjectStream(stream);
handler(object_stream, function sendClientStreamData(err, value) { handler(object_stream, function sendClientStreamData(err, value) {
if (err) { if (err) {
@ -181,35 +184,36 @@ function makeClientStreamHandler(handler) {
stream.write(value); stream.write(value);
stream.end(); stream.end();
} }
}); }, metadata);
}; };
} }
/** /**
* Creates a binary stream handler function from a server stream handler * Creates a binary stream handler function from a server stream handler
* function * function
* @param {function(Writable)} handler Server stream call handler * @param {function(Writable, metadata=)} handler Server stream call handler
* @return {function(stream)} Binary stream handler * @return {function(stream, metadata=)} Binary stream handler
*/ */
function makeServerStreamHandler(handler) { function makeServerStreamHandler(handler) {
/** /**
* Handles a stream by attaching it to a serializing stream, and passing it to * Handles a stream by attaching it to a serializing stream, and passing it to
* the handler. * the handler.
* @param {stream} stream Binary data stream * @param {stream} stream Binary data stream
* @param {metadata=} metadata Incoming metadata array
*/ */
return function handleServerStreamCall(stream) { return function handleServerStreamCall(stream, metadata) {
stream.on('data', function handleClientData(value) { stream.on('data', function handleClientData(value) {
var object_stream = new ServerWritableObjectStream(stream); var object_stream = new ServerWritableObjectStream(stream);
object_stream.request = value; object_stream.request = value;
handler(object_stream); handler(object_stream, metadata);
}); });
}; };
} }
/** /**
* Creates a binary stream handler function from a bidi stream handler function * Creates a binary stream handler function from a bidi stream handler function
* @param {function(Duplex)} handler Unary call handler * @param {function(Duplex, metadata=)} handler Unary call handler
* @return {function(stream)} Binary stream handler * @return {function(stream, metadata=)} Binary stream handler
*/ */
function makeBidiStreamHandler(handler) { function makeBidiStreamHandler(handler) {
return handler; return handler;
@ -252,10 +256,13 @@ function makeServerConstructor(services) {
* @constructor * @constructor
* @param {Object} service_handlers Map from service names to map from method * @param {Object} service_handlers Map from service names to map from method
* names to handlers * names to handlers
* @param {Object} options Options to pass to the underlying server * @param {function(string, Object<string, Array<Buffer>>):
Object<string, Array<Buffer|string>>=} getMetadata Callback that
* gets metatada for a given method
* @param {Object=} options Options to pass to the underlying server
*/ */
function SurfaceServer(service_handlers, options) { function SurfaceServer(service_handlers, getMetadata, options) {
var server = new Server(options); var server = new Server(getMetadata, options);
this.inner_server = server; this.inner_server = server;
_.each(services, function(service) { _.each(services, function(service) {
var service_name = common.fullyQualifiedName(service); var service_name = common.fullyQualifiedName(service);

@ -99,24 +99,30 @@ describe('call', function() {
}); });
}); });
describe('addMetadata', function() { describe('addMetadata', function() {
it('should succeed with objects containing keys and values', function() { it('should succeed with a map from strings to string arrays', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1)); var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.doesNotThrow(function() { assert.doesNotThrow(function() {
call.addMetadata(); call.addMetadata({'key': ['value']});
});
assert.doesNotThrow(function() {
call.addMetadata({'key1': ['value1'], 'key2': ['value2']});
}); });
});
it('should succeed with a map from strings to buffer arrays', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.doesNotThrow(function() { assert.doesNotThrow(function() {
call.addMetadata({'key' : 'key', call.addMetadata({'key': [new Buffer('value')]});
'value' : new Buffer('value')});
}); });
assert.doesNotThrow(function() { assert.doesNotThrow(function() {
call.addMetadata({'key' : 'key1', call.addMetadata({'key1': [new Buffer('value1')],
'value' : new Buffer('value1')}, 'key2': [new Buffer('value2')]});
{'key' : 'key2',
'value' : new Buffer('value2')});
}); });
}); });
it('should fail with other parameter types', function() { it('should fail with other parameter types', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1)); var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.throws(function() {
call.addMetadata();
});
assert.throws(function() { assert.throws(function() {
call.addMetadata(null); call.addMetadata(null);
}, TypeError); }, TypeError);
@ -133,7 +139,7 @@ describe('call', function() {
function() {done();}, function() {done();},
0); 0);
assert.throws(function() { assert.throws(function() {
call.addMetadata({'key' : 'key', 'value' : new Buffer('value') }); call.addMetadata({'key': ['value']});
}, function(err) { }, function(err) {
return err.code === grpc.callError.ALREADY_INVOKED; return err.code === grpc.callError.ALREADY_INVOKED;
}); });

@ -84,6 +84,10 @@ function cancelHandler(stream) {
// do nothing // do nothing
} }
function metadataHandler(stream, metadata) {
stream.end();
}
/** /**
* Serialize a string to a Buffer * Serialize a string to a Buffer
* @param {string} value The string to serialize * @param {string} value The string to serialize
@ -106,11 +110,14 @@ describe('echo client', function() {
var server; var server;
var channel; var channel;
before(function() { before(function() {
server = new Server(); server = new Server(function getMetadata(method, metadata) {
return {method: [method]};
});
var port_num = server.bind('0.0.0.0:0'); var port_num = server.bind('0.0.0.0:0');
server.register('echo', echoHandler); server.register('echo', echoHandler);
server.register('error', errorHandler); server.register('error', errorHandler);
server.register('cancellation', cancelHandler); server.register('cancellation', cancelHandler);
server.register('metadata', metadataHandler);
server.start(); server.start();
channel = new grpc.Channel('localhost:' + port_num); channel = new grpc.Channel('localhost:' + port_num);
@ -142,12 +149,19 @@ describe('echo client', function() {
done(); done();
}); });
}); });
it('should recieve metadata set by the server', function(done) {
var stream = client.makeRequest(channel, 'metadata');
stream.on('metadata', function(metadata) {
assert.strictEqual(metadata.method[0].toString(), 'metadata');
});
stream.on('status', function(status) {
assert.equal(status.code, client.status.OK);
done();
});
stream.end();
});
it('should get an error status that the server throws', function(done) { it('should get an error status that the server throws', function(done) {
var stream = client.makeRequest( var stream = client.makeRequest(channel, 'error');
channel,
'error',
null,
getDeadline(1));
stream.on('data', function() {}); stream.on('data', function() {});
stream.write(new Buffer('test')); stream.write(new Buffer('test'));
@ -171,6 +185,14 @@ describe('echo client', function() {
done(); done();
}); });
}); });
it('should get correct status for unimplemented method', function(done) {
var stream = client.makeRequest(channel, 'unimplemented_method');
stream.end();
stream.on('status', function(status) {
assert.equal(status.code, grpc.status.UNIMPLEMENTED);
done();
});
});
}); });
/* TODO(mlumish): explore options for reducing duplication between this test /* TODO(mlumish): explore options for reducing duplication between this test
* and the insecure echo client test */ * and the insecure echo client test */
@ -189,7 +211,7 @@ describe('secure echo client', function() {
key_data, key_data,
pem_data); pem_data);
server = new Server({'credentials' : server_creds}); server = new Server(null, {'credentials' : server_creds});
var port_num = server.bind('0.0.0.0:0', true); var port_num = server.bind('0.0.0.0:0', true);
server.register('echo', echoHandler); server.register('echo', echoHandler);
server.start(); server.start();

@ -68,18 +68,61 @@ describe('end-to-end', function() {
server.shutdown(); server.shutdown();
}); });
it('should start and end a request without error', function(complete) { it('should start and end a request without error', function(complete) {
var done = multiDone(function() { var done = multiDone(complete, 2);
complete();
}, 2);
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3); deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'xyz'; var status_text = 'xyz';
var call = new grpc.Call(channel, var call = new grpc.Call(channel,
'dummy_method', 'dummy_method',
deadline); deadline);
call.invoke(function(event) { call.invoke(function(event) {
assert.strictEqual(event.type,
grpc.completionType.CLIENT_METADATA_READ);
},function(event) {
assert.strictEqual(event.type, grpc.completionType.FINISHED);
var status = event.data;
assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(status.details, status_text);
done();
}, 0);
server.requestCall(function(event) {
assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW);
var server_call = event.call;
assert.notEqual(server_call, null);
server_call.serverAccept(function(event) {
assert.strictEqual(event.type, grpc.completionType.FINISHED);
}, 0);
server_call.serverEndInitialMetadata(0);
server_call.startWriteStatus(
grpc.status.OK,
status_text,
function(event) {
assert.strictEqual(event.type,
grpc.completionType.FINISH_ACCEPTED);
assert.strictEqual(event.data, grpc.opError.OK);
done();
});
});
call.writesDone(function(event) {
assert.strictEqual(event.type,
grpc.completionType.FINISH_ACCEPTED);
assert.strictEqual(event.data, grpc.opError.OK);
});
});
it('should successfully send and receive metadata', function(complete) {
var done = multiDone(complete, 2);
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'xyz';
var call = new grpc.Call(channel,
'dummy_method',
deadline);
call.addMetadata({'client_key': ['client_value']});
call.invoke(function(event) {
assert.strictEqual(event.type, assert.strictEqual(event.type,
grpc.completionType.CLIENT_METADATA_READ); grpc.completionType.CLIENT_METADATA_READ);
assert.strictEqual(event.data.server_key[0].toString(), 'server_value');
},function(event) { },function(event) {
assert.strictEqual(event.type, grpc.completionType.FINISHED); assert.strictEqual(event.type, grpc.completionType.FINISHED);
var status = event.data; var status = event.data;
@ -90,11 +133,14 @@ describe('end-to-end', function() {
server.requestCall(function(event) { server.requestCall(function(event) {
assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW);
assert.strictEqual(event.data.metadata.client_key[0].toString(),
'client_value');
var server_call = event.call; var server_call = event.call;
assert.notEqual(server_call, null); assert.notEqual(server_call, null);
server_call.serverAccept(function(event) { server_call.serverAccept(function(event) {
assert.strictEqual(event.type, grpc.completionType.FINISHED); assert.strictEqual(event.type, grpc.completionType.FINISHED);
}, 0); }, 0);
server_call.addMetadata({'server_key': ['server_value']});
server_call.serverEndInitialMetadata(0); server_call.serverEndInitialMetadata(0);
server_call.startWriteStatus( server_call.startWriteStatus(
grpc.status.OK, grpc.status.OK,
@ -115,10 +161,7 @@ describe('end-to-end', function() {
it('should send and receive data without error', function(complete) { it('should send and receive data without error', function(complete) {
var req_text = 'client_request'; var req_text = 'client_request';
var reply_text = 'server_response'; var reply_text = 'server_response';
var done = multiDone(function() { var done = multiDone(complete, 6);
complete();
server.shutdown();
}, 6);
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3); deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'success'; var status_text = 'success';

@ -71,4 +71,12 @@ describe('Interop tests', function() {
it('should pass empty_stream', function(done) { it('should pass empty_stream', function(done) {
interop_client.runTest(port, name_override, 'empty_stream', true, done); interop_client.runTest(port, name_override, 'empty_stream', true, done);
}); });
it('should pass cancel_after_begin', function(done) {
interop_client.runTest(port, name_override, 'cancel_after_begin', true,
done);
});
it('should pass cancel_after_first_response', function(done) {
interop_client.runTest(port, name_override, 'cancel_after_first_response',
true, done);
});
}); });

@ -75,6 +75,9 @@ describe('echo server', function() {
channel = new grpc.Channel('localhost:' + port_num); channel = new grpc.Channel('localhost:' + port_num);
}); });
after(function() {
server.shutdown();
});
it('should echo inputs as responses', function(done) { it('should echo inputs as responses', function(done) {
done = multiDone(done, 4); done = multiDone(done, 4);
@ -95,7 +98,6 @@ describe('echo server', function() {
var status = event.data; var status = event.data;
assert.strictEqual(status.code, grpc.status.OK); assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(status.details, status_text); assert.strictEqual(status.details, status_text);
server.shutdown();
done(); done();
}, 0); }, 0);
call.startWrite( call.startWrite(

@ -75,7 +75,7 @@ describe('Surface server constructor', function() {
}, /math.Math/); }, /math.Math/);
}); });
}); });
describe('Surface client', function() { describe('Cancelling surface client', function() {
var client; var client;
var server; var server;
before(function() { before(function() {

@ -16,9 +16,10 @@
#include "grpc/support/slice.h" #include "grpc/support/slice.h"
grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length) { grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length) {
gpr_slice slice = gpr_slice_malloc(length); gpr_slice slice = gpr_slice_from_copied_buffer(string, length);
memcpy(GPR_SLICE_START_PTR(slice), string, length); grpc_byte_buffer *buffer = grpc_byte_buffer_create(&slice, 1);
return grpc_byte_buffer_create(&slice, 1); gpr_slice_unref(slice);
return buffer;
} }
void byte_buffer_to_string(grpc_byte_buffer *buffer, char **out_string, void byte_buffer_to_string(grpc_byte_buffer *buffer, char **out_string,

@ -28,9 +28,9 @@ class ActiveCall {
$this->flags = $flags; $this->flags = $flags;
// Invoke the call. // Invoke the call.
$this->call->start_invoke($this->completion_queue, $this->call->invoke($this->completion_queue,
CLIENT_METADATA_READ, CLIENT_METADATA_READ,
FINISHED, 0); FINISHED, 0);
$metadata_event = $this->completion_queue->pluck(CLIENT_METADATA_READ, $metadata_event = $this->completion_queue->pluck(CLIENT_METADATA_READ,
Timeval::inf_future()); Timeval::inf_future());
$this->metadata = $metadata_event->data; $this->metadata = $metadata_event->data;

@ -10,8 +10,8 @@ class BaseStub {
private $channel; private $channel;
public function __construct($hostname) { public function __construct($hostname, $opts) {
$this->channel = new Channel($hostname, []); $this->channel = new Channel($hostname, $opts);
} }
/** /**
@ -33,10 +33,10 @@ class BaseStub {
* @param array $metadata A metadata map to send to the server * @param array $metadata A metadata map to send to the server
* @return SimpleSurfaceActiveCall The active call object * @return SimpleSurfaceActiveCall The active call object
*/ */
protected function _simpleRequest($method, public function _simpleRequest($method,
$argument, $argument,
callable $deserialize, callable $deserialize,
$metadata = array()) { $metadata = array()) {
return new SimpleSurfaceActiveCall($this->channel, return new SimpleSurfaceActiveCall($this->channel,
$method, $method,
$deserialize, $deserialize,
@ -55,10 +55,10 @@ class BaseStub {
* @param array $metadata A metadata map to send to the server * @param array $metadata A metadata map to send to the server
* @return ClientStreamingSurfaceActiveCall The active call object * @return ClientStreamingSurfaceActiveCall The active call object
*/ */
protected function _clientStreamRequest($method, public function _clientStreamRequest($method,
$arguments, $arguments,
callable $deserialize, callable $deserialize,
$metadata = array()) { $metadata = array()) {
return new ClientStreamingSurfaceActiveCall($this->channel, return new ClientStreamingSurfaceActiveCall($this->channel,
$method, $method,
$deserialize, $deserialize,
@ -76,10 +76,10 @@ class BaseStub {
* @param array $metadata A metadata map to send to the server * @param array $metadata A metadata map to send to the server
* @return ServerStreamingSurfaceActiveCall The active call object * @return ServerStreamingSurfaceActiveCall The active call object
*/ */
protected function _serverStreamRequest($method, public function _serverStreamRequest($method,
$argument, $argument,
callable $deserialize, callable $deserialize,
$metadata = array()) { $metadata = array()) {
return new ServerStreamingSurfaceActiveCall($this->channel, return new ServerStreamingSurfaceActiveCall($this->channel,
$method, $method,
$deserialize, $deserialize,
@ -95,9 +95,9 @@ class BaseStub {
* @param array $metadata A metadata map to send to the server * @param array $metadata A metadata map to send to the server
* @return BidiStreamingSurfaceActiveCall The active call object * @return BidiStreamingSurfaceActiveCall The active call object
*/ */
protected function _bidiRequest($method, public function _bidiRequest($method,
callable $deserialize, callable $deserialize,
$metadata = array()) { $metadata = array()) {
return new BidiStreamingSurfaceActiveCall($this->channel, return new BidiStreamingSurfaceActiveCall($this->channel,
$method, $method,
$deserialize, $deserialize,

@ -1,9 +1,9 @@
<?php <?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0 // DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: net/proto2/proto/empty.proto // Source: test/cpp/interop/empty.proto
// Date: 2014-12-03 22:02:20 // Date: 2015-01-30 23:30:46
namespace proto2 { namespace grpc\testing {
class EmptyMessage extends \DrSlump\Protobuf\Message { class EmptyMessage extends \DrSlump\Protobuf\Message {
@ -13,7 +13,7 @@ namespace proto2 {
public static function descriptor() public static function descriptor()
{ {
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'proto2.EmptyMessage'); $descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'grpc.testing.EmptyMessage');
foreach (self::$__extensions as $cb) { foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true); $descriptor->addField($cb(), true);
@ -23,4 +23,3 @@ namespace proto2 {
} }
} }
} }

@ -25,7 +25,7 @@ function hardAssert($value, $error_message) {
* @param $stub Stub object that has service methods * @param $stub Stub object that has service methods
*/ */
function emptyUnary($stub) { function emptyUnary($stub) {
list($result, $status) = $stub->EmptyCall(new proto2\EmptyMessage())->wait(); list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result != null, 'Call completed with a null response'); hardAssert($result != null, 'Call completed with a null response');
} }
@ -161,11 +161,12 @@ $server_address = $args['server_host'] . ':' . $args['server_port'];
$credentials = Grpc\Credentials::createSsl( $credentials = Grpc\Credentials::createSsl(
file_get_contents(dirname(__FILE__) . '/../data/ca.pem')); file_get_contents(dirname(__FILE__) . '/../data/ca.pem'));
$stub = new grpc\testing\TestServiceClient( $stub = new grpc\testing\TestServiceClient(
$server_address, new Grpc\BaseStub(
[ $server_address,
'grpc.ssl_target_name_override' => 'foo.test.google.com', [
'credentials' => $credentials 'grpc.ssl_target_name_override' => 'foo.test.google.com',
]); 'credentials' => $credentials
]));
echo "Connecting to $server_address\n"; echo "Connecting to $server_address\n";
echo "Running test case $args[test_case]\n"; echo "Running test case $args[test_case]\n";

@ -1,7 +1,7 @@
<?php <?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0 // DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: third_party/stubby/testing/proto/messages.proto // Source: test/cpp/interop/messages.proto
// Date: 2014-12-03 22:02:20 // Date: 2015-01-30 23:30:46
namespace grpc\testing { namespace grpc\testing {
@ -142,6 +142,12 @@ namespace grpc\testing {
/** @var \grpc\testing\Payload */ /** @var \grpc\testing\Payload */
public $payload = null; public $payload = null;
/** @var boolean */
public $fill_username = null;
/** @var boolean */
public $fill_oauth_scope = null;
/** @var \Closure[] */ /** @var \Closure[] */
protected static $__extensions = array(); protected static $__extensions = array();
@ -176,6 +182,22 @@ namespace grpc\testing {
$f->reference = '\grpc\testing\Payload'; $f->reference = '\grpc\testing\Payload';
$descriptor->addField($f); $descriptor->addField($f);
// OPTIONAL BOOL fill_username = 4
$f = new \DrSlump\Protobuf\Field();
$f->number = 4;
$f->name = "fill_username";
$f->type = \DrSlump\Protobuf::TYPE_BOOL;
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
$descriptor->addField($f);
// OPTIONAL BOOL fill_oauth_scope = 5
$f = new \DrSlump\Protobuf\Field();
$f->number = 5;
$f->name = "fill_oauth_scope";
$f->type = \DrSlump\Protobuf::TYPE_BOOL;
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
$descriptor->addField($f);
foreach (self::$__extensions as $cb) { foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true); $descriptor->addField($cb(), true);
} }
@ -293,6 +315,80 @@ namespace grpc\testing {
public function setPayload(\grpc\testing\Payload $value){ public function setPayload(\grpc\testing\Payload $value){
return $this->_set(3, $value); return $this->_set(3, $value);
} }
/**
* Check if <fill_username> has a value
*
* @return boolean
*/
public function hasFillUsername(){
return $this->_has(4);
}
/**
* Clear <fill_username> value
*
* @return \grpc\testing\SimpleRequest
*/
public function clearFillUsername(){
return $this->_clear(4);
}
/**
* Get <fill_username> value
*
* @return boolean
*/
public function getFillUsername(){
return $this->_get(4);
}
/**
* Set <fill_username> value
*
* @param boolean $value
* @return \grpc\testing\SimpleRequest
*/
public function setFillUsername( $value){
return $this->_set(4, $value);
}
/**
* Check if <fill_oauth_scope> has a value
*
* @return boolean
*/
public function hasFillOauthScope(){
return $this->_has(5);
}
/**
* Clear <fill_oauth_scope> value
*
* @return \grpc\testing\SimpleRequest
*/
public function clearFillOauthScope(){
return $this->_clear(5);
}
/**
* Get <fill_oauth_scope> value
*
* @return boolean
*/
public function getFillOauthScope(){
return $this->_get(5);
}
/**
* Set <fill_oauth_scope> value
*
* @param boolean $value
* @return \grpc\testing\SimpleRequest
*/
public function setFillOauthScope( $value){
return $this->_set(5, $value);
}
} }
} }
@ -303,8 +399,11 @@ namespace grpc\testing {
/** @var \grpc\testing\Payload */ /** @var \grpc\testing\Payload */
public $payload = null; public $payload = null;
/** @var int */ /** @var string */
public $effective_gaia_user_id = null; public $username = null;
/** @var string */
public $oauth_scope = null;
/** @var \Closure[] */ /** @var \Closure[] */
@ -323,11 +422,19 @@ namespace grpc\testing {
$f->reference = '\grpc\testing\Payload'; $f->reference = '\grpc\testing\Payload';
$descriptor->addField($f); $descriptor->addField($f);
// OPTIONAL INT64 effective_gaia_user_id = 2 // OPTIONAL STRING username = 2
$f = new \DrSlump\Protobuf\Field(); $f = new \DrSlump\Protobuf\Field();
$f->number = 2; $f->number = 2;
$f->name = "effective_gaia_user_id"; $f->name = "username";
$f->type = \DrSlump\Protobuf::TYPE_INT64; $f->type = \DrSlump\Protobuf::TYPE_STRING;
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
$descriptor->addField($f);
// OPTIONAL STRING oauth_scope = 3
$f = new \DrSlump\Protobuf\Field();
$f->number = 3;
$f->name = "oauth_scope";
$f->type = \DrSlump\Protobuf::TYPE_STRING;
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
$descriptor->addField($f); $descriptor->addField($f);
@ -376,109 +483,77 @@ namespace grpc\testing {
} }
/** /**
* Check if <effective_gaia_user_id> has a value * Check if <username> has a value
* *
* @return boolean * @return boolean
*/ */
public function hasEffectiveGaiaUserId(){ public function hasUsername(){
return $this->_has(2); return $this->_has(2);
} }
/** /**
* Clear <effective_gaia_user_id> value * Clear <username> value
* *
* @return \grpc\testing\SimpleResponse * @return \grpc\testing\SimpleResponse
*/ */
public function clearEffectiveGaiaUserId(){ public function clearUsername(){
return $this->_clear(2); return $this->_clear(2);
} }
/** /**
* Get <effective_gaia_user_id> value * Get <username> value
* *
* @return int * @return string
*/ */
public function getEffectiveGaiaUserId(){ public function getUsername(){
return $this->_get(2); return $this->_get(2);
} }
/** /**
* Set <effective_gaia_user_id> value * Set <username> value
* *
* @param int $value * @param string $value
* @return \grpc\testing\SimpleResponse * @return \grpc\testing\SimpleResponse
*/ */
public function setEffectiveGaiaUserId( $value){ public function setUsername( $value){
return $this->_set(2, $value); return $this->_set(2, $value);
} }
}
}
namespace grpc\testing {
class SimpleContext extends \DrSlump\Protobuf\Message {
/** @var string */
public $value = null;
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'grpc.testing.SimpleContext');
// OPTIONAL STRING value = 1
$f = new \DrSlump\Protobuf\Field();
$f->number = 1;
$f->name = "value";
$f->type = \DrSlump\Protobuf::TYPE_STRING;
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
$descriptor->addField($f);
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
/** /**
* Check if <value> has a value * Check if <oauth_scope> has a value
* *
* @return boolean * @return boolean
*/ */
public function hasValue(){ public function hasOauthScope(){
return $this->_has(1); return $this->_has(3);
} }
/** /**
* Clear <value> value * Clear <oauth_scope> value
* *
* @return \grpc\testing\SimpleContext * @return \grpc\testing\SimpleResponse
*/ */
public function clearValue(){ public function clearOauthScope(){
return $this->_clear(1); return $this->_clear(3);
} }
/** /**
* Get <value> value * Get <oauth_scope> value
* *
* @return string * @return string
*/ */
public function getValue(){ public function getOauthScope(){
return $this->_get(1); return $this->_get(3);
} }
/** /**
* Set <value> value * Set <oauth_scope> value
* *
* @param string $value * @param string $value
* @return \grpc\testing\SimpleContext * @return \grpc\testing\SimpleResponse
*/ */
public function setValue( $value){ public function setOauthScope( $value){
return $this->_set(1, $value); return $this->_set(3, $value);
} }
} }
} }
@ -997,15 +1072,3 @@ namespace grpc\testing {
} }
} }
namespace {
\proto2\bridge\MessageSet::extension(function(){
// OPTIONAL MESSAGE grpc\testing\SimpleContext\message_set_extension = 71139615
$f = new \DrSlump\Protobuf\Field();
$f->number = 71139615;
$f->name = "grpc\testing\SimpleContext\message_set_extension";
$f->type = \DrSlump\Protobuf::TYPE_MESSAGE;
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
$f->reference = '\grpc\testing\SimpleContext';
return $f;
});
}

@ -1,52 +1,52 @@
<?php <?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0 // DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: third_party/stubby/testing/proto/test.proto // Source: test/cpp/interop/test.proto
// Date: 2014-12-03 22:02:20 // Date: 2015-01-30 23:30:46
namespace grpc\testing { namespace grpc\testing {
class TestServiceClient extends \Grpc\BaseStub { class TestServiceClient{
private $rpc_impl;
public function __construct($rpc_impl) {
$this->rpc_impl = $rpc_impl;
}
/** /**
* @param proto2\EmptyMessage $input * @param grpc\testing\EmptyMessage $input
* @return proto2\EmptyMessage
*/ */
public function EmptyCall(\proto2\EmptyMessage $argument, $metadata = array()) { public function EmptyCall(\grpc\testing\EmptyMessage $argument, $metadata = array()) {
return $this->_simpleRequest('/TestService/EmptyCall', $argument, '\proto2\EmptyMessage::deserialize', $metadata); return $this->rpc_impl->_simpleRequest('/grpc.testing.TestService/EmptyCall', $argument, '\grpc\testing\EmptyMessage::deserialize', $metadata);
} }
/** /**
* @param grpc\testing\SimpleRequest $input * @param grpc\testing\SimpleRequest $input
* @return grpc\testing\SimpleResponse
*/ */
public function UnaryCall(\grpc\testing\SimpleRequest $argument, $metadata = array()) { public function UnaryCall(\grpc\testing\SimpleRequest $argument, $metadata = array()) {
return $this->_simpleRequest('/TestService/UnaryCall', $argument, '\grpc\testing\SimpleResponse::deserialize', $metadata); return $this->rpc_impl->_simpleRequest('/grpc.testing.TestService/UnaryCall', $argument, '\grpc\testing\SimpleResponse::deserialize', $metadata);
} }
/** /**
* @param grpc\testing\StreamingOutputCallRequest $input * @param grpc\testing\StreamingOutputCallRequest $input
* @return grpc\testing\StreamingOutputCallResponse
*/ */
public function StreamingOutputCall($argument, $metadata = array()) { public function StreamingOutputCall($argument, $metadata = array()) {
return $this->_serverStreamRequest('/TestService/StreamingOutputCall', $argument, '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata); return $this->rpc_impl->_serverStreamRequest('/grpc.testing.TestService/StreamingOutputCall', $argument, '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata);
} }
/** /**
* @param grpc\testing\StreamingInputCallRequest $input * @param grpc\testing\StreamingInputCallRequest $input
* @return grpc\testing\StreamingInputCallResponse
*/ */
public function StreamingInputCall($arguments, $metadata = array()) { public function StreamingInputCall($arguments, $metadata = array()) {
return $this->_clientStreamRequest('/TestService/StreamingInputCall', $arguments, '\grpc\testing\StreamingInputCallResponse::deserialize', $metadata); return $this->rpc_impl->_clientStreamRequest('/grpc.testing.TestService/StreamingInputCall', $arguments, '\grpc\testing\StreamingInputCallResponse::deserialize', $metadata);
} }
/** /**
* @param grpc\testing\StreamingOutputCallRequest $input * @param grpc\testing\StreamingOutputCallRequest $input
* @return grpc\testing\StreamingOutputCallResponse
*/ */
public function FullDuplexCall($metadata = array()) { public function FullDuplexCall($metadata = array()) {
return $this->_bidiRequest('/TestService/FullDuplexCall', '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata); return $this->rpc_impl->_bidiRequest('/grpc.testing.TestService/FullDuplexCall', '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata);
} }
/** /**
* @param grpc\testing\StreamingOutputCallRequest $input * @param grpc\testing\StreamingOutputCallRequest $input
* @return grpc\testing\StreamingOutputCallResponse
*/ */
public function HalfDuplexCall($metadata = array()) { public function HalfDuplexCall($metadata = array()) {
return $this->_bidiRequest('/TestService/HalfDuplexCall', '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata); return $this->rpc_impl->_bidiRequest('/grpc.testing.TestService/HalfDuplexCall', '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata);
} }
} }
} }

@ -0,0 +1,86 @@
# 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.
"""A setup module for the GRPC Python package."""
from distutils import core as _core
_EXTENSION_SOURCES = (
'src/_adapter/_c.c',
'src/_adapter/_call.c',
'src/_adapter/_channel.c',
'src/_adapter/_completion_queue.c',
'src/_adapter/_error.c',
'src/_adapter/_server.c',
)
_EXTENSION_INCLUDE_DIRECTORIES = (
'src',
# TODO(nathaniel): Can this path specification be made to work?
#'../../include',
)
_EXTENSION_LIBRARIES = (
'gpr',
'grpc',
)
_EXTENSION_LIBRARY_DIRECTORIES = (
# TODO(nathaniel): Can this path specification be made to work?
#'../../libs/dbg',
)
_EXTENSION_MODULE = _core.Extension(
'_adapter._c', sources=list(_EXTENSION_SOURCES),
include_dirs=_EXTENSION_INCLUDE_DIRECTORIES,
libraries=_EXTENSION_LIBRARIES,
library_dirs=_EXTENSION_LIBRARY_DIRECTORIES)
_PACKAGES=(
'_adapter',
'_framework',
'_framework.base',
'_framework.base.packets',
'_framework.common',
'_framework.face',
'_framework.face.testing',
'_framework.foundation',
'_junkdrawer',
)
_PACKAGE_DIRECTORIES = {
'_adapter': 'src/_adapter',
'_framework': 'src/_framework',
'_junkdrawer': 'src/_junkdrawer',
}
_core.setup(
name='grpc', version='0.0.1',
ext_modules=[_EXTENSION_MODULE], packages=_PACKAGES,
package_dir=_PACKAGE_DIRECTORIES)

@ -0,0 +1,17 @@
"""One of the tests of the Face layer of RPC Framework."""
import unittest
from _adapter import _face_test_case
from _framework.face.testing import blocking_invocation_inline_service_test_case as test_case
class BlockingInvocationInlineServiceTest(
_face_test_case.FaceTestCase,
test_case.BlockingInvocationInlineServiceTestCase,
unittest.TestCase):
pass
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,77 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <Python.h>
#include <grpc/grpc.h>
#include "_adapter/_completion_queue.h"
#include "_adapter/_channel.h"
#include "_adapter/_call.h"
#include "_adapter/_server.h"
static PyObject *init(PyObject *self, PyObject *args) {
grpc_init();
Py_RETURN_NONE;
}
static PyObject *shutdown(PyObject *self, PyObject *args) {
grpc_shutdown();
Py_RETURN_NONE;
}
static PyMethodDef _c_methods[] = {
{"init", init, METH_VARARGS, "Initialize the module's static state."},
{"shut_down", shutdown, METH_VARARGS,
"Shut down the module's static state."},
{NULL},
};
PyMODINIT_FUNC init_c(void) {
PyObject *module;
module = Py_InitModule3("_c", _c_methods,
"Wrappings of C structures and functions.");
if (pygrpc_add_completion_queue(module) == -1) {
return;
}
if (pygrpc_add_channel(module) == -1) {
return;
}
if (pygrpc_add_call(module) == -1) {
return;
}
if (pygrpc_add_server(module) == -1) {
return;
}
}

@ -0,0 +1,141 @@
# 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.
"""Tests for _adapter._c."""
import threading
import time
import unittest
from _adapter import _c
from _adapter import _datatypes
_TIMEOUT = 3
_FUTURE = time.time() + 60 * 60 * 24
_IDEMPOTENCE_DEMONSTRATION = 7
class _CTest(unittest.TestCase):
def testUpAndDown(self):
_c.init()
_c.shut_down()
def testCompletionQueue(self):
_c.init()
completion_queue = _c.CompletionQueue()
event = completion_queue.get(0)
self.assertIsNone(event)
event = completion_queue.get(time.time())
self.assertIsNone(event)
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIsNone(event)
completion_queue.stop()
for _ in range(_IDEMPOTENCE_DEMONSTRATION):
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.STOP)
del completion_queue
del event
_c.shut_down()
def testChannel(self):
_c.init()
channel = _c.Channel('test host:12345')
del channel
_c.shut_down()
def testCall(self):
method = 'test method'
host = 'test host'
_c.init()
channel = _c.Channel('%s:%d' % (host, 12345))
call = _c.Call(channel, method, host, time.time() + _TIMEOUT)
del call
del channel
_c.shut_down()
def testServer(self):
_c.init()
completion_queue = _c.CompletionQueue()
server = _c.Server(completion_queue)
server.add_http2_addr('[::]:0')
server.start()
server.stop()
completion_queue.stop()
del server
del completion_queue
service_tag = object()
completion_queue = _c.CompletionQueue()
server = _c.Server(completion_queue)
server.add_http2_addr('[::]:0')
server.start()
server.service(service_tag)
server.stop()
completion_queue.stop()
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.SERVICE_ACCEPTED)
self.assertIs(event.tag, service_tag)
self.assertIsNone(event.service_acceptance)
for _ in range(_IDEMPOTENCE_DEMONSTRATION):
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.STOP)
del server
del completion_queue
completion_queue = _c.CompletionQueue()
server = _c.Server(completion_queue)
server.add_http2_addr('[::]:0')
server.start()
thread = threading.Thread(target=completion_queue.get, args=(_FUTURE,))
thread.start()
time.sleep(1)
server.stop()
completion_queue.stop()
for _ in range(_IDEMPOTENCE_DEMONSTRATION):
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.STOP)
thread.join()
del server
del completion_queue
_c.shut_down()
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,292 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "_adapter/_call.h"
#include <math.h>
#include <Python.h>
#include <grpc/grpc.h>
#include "_adapter/_channel.h"
#include "_adapter/_completion_queue.h"
#include "_adapter/_error.h"
static int pygrpc_call_init(Call *self, PyObject *args, PyObject *kwds) {
const PyObject *channel;
const char *method;
const char *host;
const double deadline;
if (!PyArg_ParseTuple(args, "O!ssd", &pygrpc_ChannelType, &channel, &method,
&host, &deadline)) {
self->c_call = NULL;
return -1;
}
/* TODO(nathaniel): Hoist the gpr_timespec <-> PyFloat arithmetic into its own
* function with its own test coverage.
*/
self->c_call =
grpc_channel_create_call(((Channel *)channel)->c_channel, method, host,
gpr_time_from_nanos(deadline * GPR_NS_PER_SEC));
return 0;
}
static void pygrpc_call_dealloc(Call *self) {
if (self->c_call != NULL) {
grpc_call_destroy(self->c_call);
}
self->ob_type->tp_free((PyObject *)self);
}
static const PyObject *pygrpc_call_invoke(Call *self, PyObject *args) {
const PyObject *completion_queue;
const PyObject *metadata_tag;
const PyObject *finish_tag;
grpc_call_error call_error;
const PyObject *result;
if (!(PyArg_ParseTuple(args, "O!OO", &pygrpc_CompletionQueueType,
&completion_queue, &metadata_tag, &finish_tag))) {
return NULL;
}
call_error = grpc_call_invoke(
self->c_call, ((CompletionQueue *)completion_queue)->c_completion_queue,
(void *)metadata_tag, (void *)finish_tag, 0);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(metadata_tag);
Py_INCREF(finish_tag);
}
return result;
}
static const PyObject *pygrpc_call_write(Call *self, PyObject *args) {
const char *bytes;
int length;
const PyObject *tag;
gpr_slice slice;
grpc_byte_buffer *byte_buffer;
grpc_call_error call_error;
const PyObject *result;
if (!(PyArg_ParseTuple(args, "s#O", &bytes, &length, &tag))) {
return NULL;
}
slice = gpr_slice_from_copied_buffer(bytes, length);
byte_buffer = grpc_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
call_error = grpc_call_start_write(self->c_call, byte_buffer, (void *)tag, 0);
grpc_byte_buffer_destroy(byte_buffer);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(tag);
}
return result;
}
static const PyObject *pygrpc_call_complete(Call *self, PyObject *args) {
const PyObject *tag;
grpc_call_error call_error;
const PyObject *result;
if (!(PyArg_ParseTuple(args, "O", &tag))) {
return NULL;
}
call_error = grpc_call_writes_done(self->c_call, (void *)tag);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(tag);
}
return result;
}
static const PyObject *pygrpc_call_accept(Call *self, PyObject *args) {
const PyObject *completion_queue;
const PyObject *tag;
grpc_call_error call_error;
const PyObject *result;
if (!(PyArg_ParseTuple(args, "O!O", &pygrpc_CompletionQueueType,
&completion_queue, &tag))) {
return NULL;
}
call_error = grpc_call_server_accept(
self->c_call, ((CompletionQueue *)completion_queue)->c_completion_queue,
(void *)tag);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(tag);
}
return result;
}
static const PyObject *pygrpc_call_premetadata(Call *self, PyObject *args) {
/* TODO(b/18702680): Actually support metadata. */
return pygrpc_translate_call_error(
grpc_call_server_end_initial_metadata(self->c_call, 0));
}
static const PyObject *pygrpc_call_read(Call *self, PyObject *args) {
const PyObject *tag;
grpc_call_error call_error;
const PyObject *result;
if (!(PyArg_ParseTuple(args, "O", &tag))) {
return NULL;
}
call_error = grpc_call_start_read(self->c_call, (void *)tag);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(tag);
}
return result;
}
static const PyObject *pygrpc_call_status(Call *self, PyObject *args) {
PyObject *status;
PyObject *code;
PyObject *details;
const PyObject *tag;
grpc_status_code c_code;
char *c_message;
grpc_call_error call_error;
const PyObject *result;
if (!(PyArg_ParseTuple(args, "OO", &status, &tag))) {
return NULL;
}
code = PyObject_GetAttrString(status, "code");
details = PyObject_GetAttrString(status, "details");
c_code = PyInt_AsLong(code);
c_message = PyBytes_AsString(details);
Py_DECREF(code);
Py_DECREF(details);
call_error = grpc_call_start_write_status(self->c_call, c_code, c_message,
(void *)tag);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(tag);
}
return result;
}
static const PyObject *pygrpc_call_cancel(Call *self) {
return pygrpc_translate_call_error(grpc_call_cancel(self->c_call));
}
static PyMethodDef methods[] = {
{"invoke", (PyCFunction)pygrpc_call_invoke, METH_VARARGS,
"Invoke this call."},
{"write", (PyCFunction)pygrpc_call_write, METH_VARARGS,
"Write bytes to this call."},
{"complete", (PyCFunction)pygrpc_call_complete, METH_VARARGS,
"Complete writes to this call."},
{"accept", (PyCFunction)pygrpc_call_accept, METH_VARARGS, "Accept an RPC."},
{"premetadata", (PyCFunction)pygrpc_call_premetadata, METH_VARARGS,
"Indicate the end of leading metadata in the response."},
{"read", (PyCFunction)pygrpc_call_read, METH_VARARGS,
"Read bytes from this call."},
{"status", (PyCFunction)pygrpc_call_status, METH_VARARGS,
"Report this call's status."},
{"cancel", (PyCFunction)pygrpc_call_cancel, METH_NOARGS,
"Cancel this call."},
{NULL}};
PyTypeObject pygrpc_CallType = {
PyObject_HEAD_INIT(NULL)0, /*ob_size*/
"_grpc.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, /*tp_flags*/
"Wrapping of grpc_call.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
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 */
(initproc)pygrpc_call_init, /* tp_init */
};
int pygrpc_add_call(PyObject *module) {
pygrpc_CallType.tp_new = PyType_GenericNew;
if (PyType_Ready(&pygrpc_CallType) < 0) {
PyErr_SetString(PyExc_RuntimeError, "Error defining pygrpc_CallType!");
return -1;
}
if (PyModule_AddObject(module, "Call", (PyObject *)&pygrpc_CallType) == -1) {
PyErr_SetString(PyExc_ImportError, "Couldn't add Call type to module!");
}
return 0;
}

@ -0,0 +1,46 @@
/*
*
* 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 _ADAPTER__CALL_H_
#define _ADAPTER__CALL_H_
#include <Python.h>
#include <grpc/grpc.h>
typedef struct { PyObject_HEAD grpc_call *c_call; } Call;
PyTypeObject pygrpc_CallType;
int pygrpc_add_call(PyObject *module);
#endif /* _ADAPTER__CALL_H_ */

@ -0,0 +1,109 @@
/*
*
* 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 "_adapter/_channel.h"
#include <Python.h>
#include <grpc/grpc.h>
static int pygrpc_channel_init(Channel *self, PyObject *args, PyObject *kwds) {
const char *hostport;
if (!(PyArg_ParseTuple(args, "s", &hostport))) {
self->c_channel = NULL;
return -1;
}
self->c_channel = grpc_channel_create(hostport, NULL);
return 0;
}
static void pygrpc_channel_dealloc(Channel *self) {
if (self->c_channel != NULL) {
grpc_channel_destroy(self->c_channel);
}
self->ob_type->tp_free((PyObject *)self);
}
PyTypeObject pygrpc_ChannelType = {
PyObject_HEAD_INIT(NULL)0, /*ob_size*/
"_grpc.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, /*tp_flags*/
"Wrapping of grpc_channel.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* 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 */
(initproc)pygrpc_channel_init, /* tp_init */
};
int pygrpc_add_channel(PyObject *module) {
pygrpc_ChannelType.tp_new = PyType_GenericNew;
if (PyType_Ready(&pygrpc_ChannelType) < 0) {
PyErr_SetString(PyExc_RuntimeError, "Error defining pygrpc_ChannelType!");
return -1;
}
if (PyModule_AddObject(module, "Channel", (PyObject *)&pygrpc_ChannelType) ==
-1) {
PyErr_SetString(PyExc_ImportError, "Couldn't add Channel type to module!");
return -1;
}
return 0;
}

@ -0,0 +1,46 @@
/*
*
* 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 _ADAPTER__CHANNEL_H_
#define _ADAPTER__CHANNEL_H_
#include <Python.h>
#include <grpc/grpc.h>
typedef struct { PyObject_HEAD grpc_channel *c_channel; } Channel;
PyTypeObject pygrpc_ChannelType;
int pygrpc_add_channel(PyObject *module);
#endif /* _ADAPTER__CHANNEL_H_ */

@ -0,0 +1,76 @@
# 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.
"""State used by both invocation-side and service-side code."""
import enum
@enum.unique
class HighWrite(enum.Enum):
"""The possible categories of high-level write state."""
OPEN = 'OPEN'
CLOSED = 'CLOSED'
class WriteState(object):
"""A description of the state of writing to an RPC.
Attributes:
low: A side-specific value describing the low-level state of writing.
high: A HighWrite value describing the high-level state of writing.
pending: A list of bytestrings for the RPC waiting to be written to the
other side of the RPC.
"""
def __init__(self, low, high, pending):
self.low = low
self.high = high
self.pending = pending
class CommonRPCState(object):
"""A description of an RPC's state.
Attributes:
write: A WriteState describing the state of writing to the RPC.
sequence_number: The lowest-unused sequence number for use in generating
tickets locally describing the progress of the RPC.
deserializer: The behavior to be used to deserialize payload bytestreams
taken off the wire.
serializer: The behavior to be used to serialize payloads to be sent on the
wire.
"""
def __init__(self, write, sequence_number, deserializer, serializer):
self.write = write
self.sequence_number = sequence_number
self.deserializer = deserializer
self.serializer = serializer

@ -0,0 +1,541 @@
/*
*
* 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 "_adapter/_completion_queue.h"
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include "_adapter/_call.h"
static PyObject *status_class;
static PyObject *service_acceptance_class;
static PyObject *event_class;
static PyObject *ok_status_code;
static PyObject *cancelled_status_code;
static PyObject *unknown_status_code;
static PyObject *invalid_argument_status_code;
static PyObject *expired_status_code;
static PyObject *not_found_status_code;
static PyObject *already_exists_status_code;
static PyObject *permission_denied_status_code;
static PyObject *unauthenticated_status_code;
static PyObject *resource_exhausted_status_code;
static PyObject *failed_precondition_status_code;
static PyObject *aborted_status_code;
static PyObject *out_of_range_status_code;
static PyObject *unimplemented_status_code;
static PyObject *internal_error_status_code;
static PyObject *unavailable_status_code;
static PyObject *data_loss_status_code;
static PyObject *stop_event_kind;
static PyObject *write_event_kind;
static PyObject *complete_event_kind;
static PyObject *service_event_kind;
static PyObject *read_event_kind;
static PyObject *metadata_event_kind;
static PyObject *finish_event_kind;
static PyObject *pygrpc_as_py_time(gpr_timespec *timespec) {
return Py_BuildValue("f",
timespec->tv_sec + ((double)timespec->tv_nsec) / 1.0E9);
}
static PyObject *pygrpc_status_code(grpc_status_code c_status_code) {
switch (c_status_code) {
case GRPC_STATUS_OK:
return ok_status_code;
case GRPC_STATUS_CANCELLED:
return cancelled_status_code;
case GRPC_STATUS_UNKNOWN:
return unknown_status_code;
case GRPC_STATUS_INVALID_ARGUMENT:
return invalid_argument_status_code;
case GRPC_STATUS_DEADLINE_EXCEEDED:
return expired_status_code;
case GRPC_STATUS_NOT_FOUND:
return not_found_status_code;
case GRPC_STATUS_ALREADY_EXISTS:
return already_exists_status_code;
case GRPC_STATUS_PERMISSION_DENIED:
return permission_denied_status_code;
case GRPC_STATUS_UNAUTHENTICATED:
return unauthenticated_status_code;
case GRPC_STATUS_RESOURCE_EXHAUSTED:
return resource_exhausted_status_code;
case GRPC_STATUS_FAILED_PRECONDITION:
return failed_precondition_status_code;
case GRPC_STATUS_ABORTED:
return aborted_status_code;
case GRPC_STATUS_OUT_OF_RANGE:
return out_of_range_status_code;
case GRPC_STATUS_UNIMPLEMENTED:
return unimplemented_status_code;
case GRPC_STATUS_INTERNAL:
return internal_error_status_code;
case GRPC_STATUS_UNAVAILABLE:
return unavailable_status_code;
case GRPC_STATUS_DATA_LOSS:
return data_loss_status_code;
default:
return NULL;
}
}
static PyObject *pygrpc_stop_event_args(grpc_event *c_event) {
return Py_BuildValue("(OOOOOOO)", stop_event_kind, Py_None, Py_None, Py_None,
Py_None, Py_None, Py_None);
}
static PyObject *pygrpc_write_event_args(grpc_event *c_event) {
PyObject *write_accepted =
c_event->data.write_accepted == GRPC_OP_OK ? Py_True : Py_False;
return Py_BuildValue("(OOOOOOO)", write_event_kind, (PyObject *)c_event->tag,
write_accepted, Py_None, Py_None, Py_None, Py_None);
}
static PyObject *pygrpc_complete_event_args(grpc_event *c_event) {
PyObject *complete_accepted =
c_event->data.finish_accepted == GRPC_OP_OK ? Py_True : Py_False;
return Py_BuildValue("(OOOOOOO)", complete_event_kind,
(PyObject *)c_event->tag, Py_None, complete_accepted,
Py_None, Py_None, Py_None);
}
static PyObject *pygrpc_service_event_args(grpc_event *c_event) {
if (c_event->data.server_rpc_new.method == NULL) {
return Py_BuildValue("(OOOOOOO)", service_event_kind, c_event->tag,
Py_None, Py_None, Py_None, Py_None, Py_None);
} else {
PyObject *method = PyBytes_FromString(c_event->data.server_rpc_new.method);
PyObject *host = PyBytes_FromString(c_event->data.server_rpc_new.host);
PyObject *service_deadline =
pygrpc_as_py_time(&c_event->data.server_rpc_new.deadline);
Call *call;
PyObject *service_acceptance_args;
PyObject *service_acceptance;
PyObject *event_args;
call = PyObject_New(Call, &pygrpc_CallType);
call->c_call = c_event->call;
service_acceptance_args =
Py_BuildValue("(OOOO)", call, method, host, service_deadline);
Py_DECREF(call);
Py_DECREF(method);
Py_DECREF(host);
Py_DECREF(service_deadline);
service_acceptance =
PyObject_CallObject(service_acceptance_class, service_acceptance_args);
Py_DECREF(service_acceptance_args);
event_args = Py_BuildValue("(OOOOOOO)", service_event_kind,
(PyObject *)c_event->tag, Py_None, Py_None,
service_acceptance, Py_None, Py_None);
Py_DECREF(service_acceptance);
return event_args;
}
}
static PyObject *pygrpc_read_event_args(grpc_event *c_event) {
if (c_event->data.read == NULL) {
return Py_BuildValue("(OOOOOOO)", read_event_kind,
(PyObject *)c_event->tag, Py_None, Py_None, Py_None,
Py_None, Py_None);
} else {
size_t length;
size_t offset;
grpc_byte_buffer_reader *reader;
gpr_slice slice;
char *c_bytes;
PyObject *bytes;
PyObject *event_args;
length = grpc_byte_buffer_length(c_event->data.read);
reader = grpc_byte_buffer_reader_create(c_event->data.read);
c_bytes = gpr_malloc(length);
offset = 0;
while (grpc_byte_buffer_reader_next(reader, &slice)) {
memcpy(c_bytes + offset, GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice));
offset += GPR_SLICE_LENGTH(slice);
}
grpc_byte_buffer_reader_destroy(reader);
bytes = PyBytes_FromStringAndSize(c_bytes, length);
gpr_free(c_bytes);
event_args =
Py_BuildValue("(OOOOOOO)", read_event_kind, (PyObject *)c_event->tag,
Py_None, Py_None, Py_None, bytes, Py_None);
Py_DECREF(bytes);
return event_args;
}
}
static PyObject *pygrpc_metadata_event_args(grpc_event *c_event) {
/* TODO(nathaniel): Actual transmission of metadata. */
return Py_BuildValue("(OOOOOOO)", metadata_event_kind,
(PyObject *)c_event->tag, Py_None, Py_None, Py_None,
Py_None, Py_None);
}
static PyObject *pygrpc_finished_event_args(grpc_event *c_event) {
PyObject *code;
PyObject *details;
PyObject *status_args;
PyObject *status;
PyObject *event_args;
code = pygrpc_status_code(c_event->data.finished.status);
if (code == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Unrecognized status code!");
return NULL;
}
if (c_event->data.finished.details == NULL) {
details = PyBytes_FromString("");
} else {
details = PyBytes_FromString(c_event->data.finished.details);
}
status_args = Py_BuildValue("(OO)", code, details);
Py_DECREF(details);
status = PyObject_CallObject(status_class, status_args);
Py_DECREF(status_args);
event_args =
Py_BuildValue("(OOOOOOO)", finish_event_kind, (PyObject *)c_event->tag,
Py_None, Py_None, Py_None, Py_None, status);
Py_DECREF(status);
return event_args;
}
static int pygrpc_completion_queue_init(CompletionQueue *self, PyObject *args,
PyObject *kwds) {
self->c_completion_queue = grpc_completion_queue_create();
return 0;
}
static void pygrpc_completion_queue_dealloc(CompletionQueue *self) {
grpc_completion_queue_destroy(self->c_completion_queue);
self->ob_type->tp_free((PyObject *)self);
}
static PyObject *pygrpc_completion_queue_get(CompletionQueue *self,
PyObject *args) {
PyObject *deadline;
double double_deadline;
gpr_timespec deadline_timespec;
grpc_event *c_event;
PyObject *event_args;
PyObject *event;
if (!(PyArg_ParseTuple(args, "O", &deadline))) {
return NULL;
}
if (deadline == Py_None) {
deadline_timespec = gpr_inf_future;
} else {
double_deadline = PyFloat_AsDouble(deadline);
deadline_timespec = gpr_time_from_nanos((long)(double_deadline * 1.0E9));
}
/* TODO(nathaniel): Suppress clang-format in this block and remove the
unnecessary and unPythonic semicolons trailing the _ALLOW_THREADS macros.
(Right now clang-format only understands //-demarcated suppressions.) */
Py_BEGIN_ALLOW_THREADS;
c_event =
grpc_completion_queue_next(self->c_completion_queue, deadline_timespec);
Py_END_ALLOW_THREADS;
if (c_event == NULL) {
Py_RETURN_NONE;
}
switch (c_event->type) {
case GRPC_QUEUE_SHUTDOWN:
event_args = pygrpc_stop_event_args(c_event);
break;
case GRPC_WRITE_ACCEPTED:
event_args = pygrpc_write_event_args(c_event);
break;
case GRPC_FINISH_ACCEPTED:
event_args = pygrpc_complete_event_args(c_event);
break;
case GRPC_SERVER_RPC_NEW:
event_args = pygrpc_service_event_args(c_event);
break;
case GRPC_READ:
event_args = pygrpc_read_event_args(c_event);
break;
case GRPC_CLIENT_METADATA_READ:
event_args = pygrpc_metadata_event_args(c_event);
break;
case GRPC_FINISHED:
event_args = pygrpc_finished_event_args(c_event);
break;
default:
PyErr_SetString(PyExc_Exception, "Unrecognized event type!");
return NULL;
}
if (event_args == NULL) {
return NULL;
}
event = PyObject_CallObject(event_class, event_args);
Py_DECREF(event_args);
Py_XDECREF((PyObject *)c_event->tag);
grpc_event_finish(c_event);
return event;
}
static PyObject *pygrpc_completion_queue_stop(CompletionQueue *self) {
grpc_completion_queue_shutdown(self->c_completion_queue);
Py_RETURN_NONE;
}
static PyMethodDef methods[] = {
{"get", (PyCFunction)pygrpc_completion_queue_get, METH_VARARGS,
"Get the next event."},
{"stop", (PyCFunction)pygrpc_completion_queue_stop, METH_NOARGS,
"Stop this completion queue."},
{NULL}};
PyTypeObject pygrpc_CompletionQueueType = {
PyObject_HEAD_INIT(NULL)0, /*ob_size*/
"_gprc.CompletionQueue", /*tp_name*/
sizeof(CompletionQueue), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pygrpc_completion_queue_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, /*tp_flags*/
"Wrapping of grpc_completion_queue.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
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 */
(initproc)pygrpc_completion_queue_init, /* tp_init */
};
static int pygrpc_get_status_codes(PyObject *datatypes_module) {
PyObject *code_class = PyObject_GetAttrString(datatypes_module, "Code");
if (code_class == NULL) {
return -1;
}
ok_status_code = PyObject_GetAttrString(code_class, "OK");
if (ok_status_code == NULL) {
return -1;
}
cancelled_status_code = PyObject_GetAttrString(code_class, "CANCELLED");
if (cancelled_status_code == NULL) {
return -1;
}
unknown_status_code = PyObject_GetAttrString(code_class, "UNKNOWN");
if (unknown_status_code == NULL) {
return -1;
}
invalid_argument_status_code =
PyObject_GetAttrString(code_class, "INVALID_ARGUMENT");
if (invalid_argument_status_code == NULL) {
return -1;
}
expired_status_code = PyObject_GetAttrString(code_class, "EXPIRED");
if (expired_status_code == NULL) {
return -1;
}
not_found_status_code = PyObject_GetAttrString(code_class, "NOT_FOUND");
if (not_found_status_code == NULL) {
return -1;
}
already_exists_status_code =
PyObject_GetAttrString(code_class, "ALREADY_EXISTS");
if (already_exists_status_code == NULL) {
return -1;
}
permission_denied_status_code =
PyObject_GetAttrString(code_class, "PERMISSION_DENIED");
if (permission_denied_status_code == NULL) {
return -1;
}
unauthenticated_status_code =
PyObject_GetAttrString(code_class, "UNAUTHENTICATED");
if (unauthenticated_status_code == NULL) {
return -1;
}
resource_exhausted_status_code =
PyObject_GetAttrString(code_class, "RESOURCE_EXHAUSTED");
if (resource_exhausted_status_code == NULL) {
return -1;
}
failed_precondition_status_code =
PyObject_GetAttrString(code_class, "FAILED_PRECONDITION");
if (failed_precondition_status_code == NULL) {
return -1;
}
aborted_status_code = PyObject_GetAttrString(code_class, "ABORTED");
if (aborted_status_code == NULL) {
return -1;
}
out_of_range_status_code = PyObject_GetAttrString(code_class, "OUT_OF_RANGE");
if (out_of_range_status_code == NULL) {
return -1;
}
unimplemented_status_code =
PyObject_GetAttrString(code_class, "UNIMPLEMENTED");
if (unimplemented_status_code == NULL) {
return -1;
}
internal_error_status_code =
PyObject_GetAttrString(code_class, "INTERNAL_ERROR");
if (internal_error_status_code == NULL) {
return -1;
}
unavailable_status_code = PyObject_GetAttrString(code_class, "UNAVAILABLE");
if (unavailable_status_code == NULL) {
return -1;
}
data_loss_status_code = PyObject_GetAttrString(code_class, "DATA_LOSS");
if (data_loss_status_code == NULL) {
return -1;
}
Py_DECREF(code_class);
return 0;
}
static int pygrpc_get_event_kinds(PyObject *event_class) {
PyObject *kind_class = PyObject_GetAttrString(event_class, "Kind");
if (kind_class == NULL) {
return -1;
}
stop_event_kind = PyObject_GetAttrString(kind_class, "STOP");
if (stop_event_kind == NULL) {
return -1;
}
write_event_kind = PyObject_GetAttrString(kind_class, "WRITE_ACCEPTED");
if (write_event_kind == NULL) {
return -1;
}
complete_event_kind = PyObject_GetAttrString(kind_class, "COMPLETE_ACCEPTED");
if (complete_event_kind == NULL) {
return -1;
}
service_event_kind = PyObject_GetAttrString(kind_class, "SERVICE_ACCEPTED");
if (service_event_kind == NULL) {
return -1;
}
read_event_kind = PyObject_GetAttrString(kind_class, "READ_ACCEPTED");
if (read_event_kind == NULL) {
return -1;
}
metadata_event_kind = PyObject_GetAttrString(kind_class, "METADATA_ACCEPTED");
if (metadata_event_kind == NULL) {
return -1;
}
finish_event_kind = PyObject_GetAttrString(kind_class, "FINISH");
if (finish_event_kind == NULL) {
return -1;
}
Py_DECREF(kind_class);
return 0;
}
int pygrpc_add_completion_queue(PyObject *module) {
char *datatypes_module_path = "_adapter._datatypes";
PyObject *datatypes_module = PyImport_ImportModule(datatypes_module_path);
if (datatypes_module == NULL) {
PyErr_SetString(PyExc_ImportError, datatypes_module_path);
return -1;
}
status_class = PyObject_GetAttrString(datatypes_module, "Status");
service_acceptance_class =
PyObject_GetAttrString(datatypes_module, "ServiceAcceptance");
event_class = PyObject_GetAttrString(datatypes_module, "Event");
if (status_class == NULL || service_acceptance_class == NULL ||
event_class == NULL) {
PyErr_SetString(PyExc_ImportError, "Missing classes in _datatypes module!");
return -1;
}
if (pygrpc_get_status_codes(datatypes_module) == -1) {
PyErr_SetString(PyExc_ImportError, "Status codes import broken!");
return -1;
}
if (pygrpc_get_event_kinds(event_class) == -1) {
PyErr_SetString(PyExc_ImportError, "Event kinds import broken!");
return -1;
}
Py_DECREF(datatypes_module);
pygrpc_CompletionQueueType.tp_new = PyType_GenericNew;
if (PyType_Ready(&pygrpc_CompletionQueueType) < 0) {
PyErr_SetString(PyExc_RuntimeError,
"Error defining pygrpc_CompletionQueueType!");
return -1;
}
if (PyModule_AddObject(module, "CompletionQueue",
(PyObject *)&pygrpc_CompletionQueueType) == -1) {
PyErr_SetString(PyExc_ImportError,
"Couldn't add CompletionQueue type to module!");
return -1;
}
return 0;
}

@ -0,0 +1,48 @@
/*
*
* 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 _ADAPTER__COMPLETION_QUEUE_H_
#define _ADAPTER__COMPLETION_QUEUE_H_
#include <Python.h>
#include <grpc/grpc.h>
typedef struct {
PyObject_HEAD grpc_completion_queue *c_completion_queue;
} CompletionQueue;
PyTypeObject pygrpc_CompletionQueueType;
int pygrpc_add_completion_queue(PyObject *module);
#endif /* _ADAPTER__COMPLETION_QUEUE_H_ */

@ -0,0 +1,86 @@
# 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.
"""Datatypes passed between Python and C code."""
import collections
import enum
@enum.unique
class Code(enum.IntEnum):
"""One Platform error codes (see status.h and codes.proto)."""
OK = 0
CANCELLED = 1
UNKNOWN = 2
INVALID_ARGUMENT = 3
EXPIRED = 4
NOT_FOUND = 5
ALREADY_EXISTS = 6
PERMISSION_DENIED = 7
UNAUTHENTICATED = 16
RESOURCE_EXHAUSTED = 8
FAILED_PRECONDITION = 9
ABORTED = 10
OUT_OF_RANGE = 11
UNIMPLEMENTED = 12
INTERNAL_ERROR = 13
UNAVAILABLE = 14
DATA_LOSS = 15
class Status(collections.namedtuple('Status', ['code', 'details'])):
"""Describes an RPC's overall status."""
class ServiceAcceptance(
collections.namedtuple(
'ServiceAcceptance', ['call', 'method', 'host', 'deadline'])):
"""Describes an RPC on the service side at the start of service."""
class Event(
collections.namedtuple(
'Event',
['kind', 'tag', 'write_accepted', 'complete_accepted',
'service_acceptance', 'bytes', 'status'])):
"""Describes an event emitted from a completion queue."""
@enum.unique
class Kind(enum.Enum):
"""Describes the kind of an event."""
STOP = object()
WRITE_ACCEPTED = object()
COMPLETE_ACCEPTED = object()
SERVICE_ACCEPTED = object()
READ_ACCEPTED = object()
METADATA_ACCEPTED = object()
FINISH = object()

@ -0,0 +1,79 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "_adapter/_error.h"
#include <Python.h>
#include <grpc/grpc.h>
const PyObject *pygrpc_translate_call_error(grpc_call_error call_error) {
switch (call_error) {
case GRPC_CALL_OK:
Py_RETURN_NONE;
case GRPC_CALL_ERROR:
PyErr_SetString(PyExc_Exception, "Defect: unknown defect!");
return NULL;
case GRPC_CALL_ERROR_NOT_ON_SERVER:
PyErr_SetString(PyExc_Exception,
"Defect: client-only method called on server!");
return NULL;
case GRPC_CALL_ERROR_NOT_ON_CLIENT:
PyErr_SetString(PyExc_Exception,
"Defect: server-only method called on client!");
return NULL;
case GRPC_CALL_ERROR_ALREADY_ACCEPTED:
PyErr_SetString(PyExc_Exception,
"Defect: attempted to accept already-accepted call!");
return NULL;
case GRPC_CALL_ERROR_ALREADY_INVOKED:
PyErr_SetString(PyExc_Exception,
"Defect: attempted to invoke already-invoked call!");
return NULL;
case GRPC_CALL_ERROR_NOT_INVOKED:
PyErr_SetString(PyExc_Exception, "Defect: Call not yet invoked!");
return NULL;
case GRPC_CALL_ERROR_ALREADY_FINISHED:
PyErr_SetString(PyExc_Exception, "Defect: Call already finished!");
return NULL;
case GRPC_CALL_ERROR_TOO_MANY_OPERATIONS:
PyErr_SetString(PyExc_Exception,
"Defect: Attempted extra read or extra write on call!");
return NULL;
case GRPC_CALL_ERROR_INVALID_FLAGS:
PyErr_SetString(PyExc_Exception, "Defect: invalid flags!");
return NULL;
default:
PyErr_SetString(PyExc_Exception, "Defect: Unknown call error!");
return NULL;
}
}

@ -0,0 +1,42 @@
/*
*
* 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 _ADAPTER__ERROR_H_
#define _ADAPTER__ERROR_H_
#include <Python.h>
#include <grpc/grpc.h>
const PyObject *pygrpc_translate_call_error(grpc_call_error call_error);
#endif /* _ADAPTER__ERROR_H_ */

@ -0,0 +1,46 @@
# 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.
"""One of the tests of the Face layer of RPC Framework."""
import unittest
from _adapter import _face_test_case
from _framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case
class EventInvocationSynchronousEventServiceTest(
_face_test_case.FaceTestCase,
test_case.EventInvocationSynchronousEventServiceTestCase,
unittest.TestCase):
pass
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,124 @@
# 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.
"""Common construction and destruction for GRPC-backed Face-layer tests."""
import unittest
from _adapter import fore
from _adapter import rear
from _framework.base import util
from _framework.base.packets import implementations as tickets_implementations
from _framework.face import implementations as face_implementations
from _framework.face.testing import coverage
from _framework.face.testing import serial
from _framework.face.testing import test_case
from _framework.foundation import logging_pool
_TIMEOUT = 3
_MAXIMUM_TIMEOUT = 90
_MAXIMUM_POOL_SIZE = 400
class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage):
"""Provides abstract Face-layer tests a GRPC-backed implementation."""
def set_up_implementation(
self,
name,
methods,
inline_value_in_value_out_methods,
inline_value_in_stream_out_methods,
inline_stream_in_value_out_methods,
inline_stream_in_stream_out_methods,
event_value_in_value_out_methods,
event_value_in_stream_out_methods,
event_stream_in_value_out_methods,
event_stream_in_stream_out_methods,
multi_method):
pool = logging_pool.pool(_MAXIMUM_POOL_SIZE)
servicer = face_implementations.servicer(
pool,
inline_value_in_value_out_methods=inline_value_in_value_out_methods,
inline_value_in_stream_out_methods=inline_value_in_stream_out_methods,
inline_stream_in_value_out_methods=inline_stream_in_value_out_methods,
inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods,
event_value_in_value_out_methods=event_value_in_value_out_methods,
event_value_in_stream_out_methods=event_value_in_stream_out_methods,
event_stream_in_value_out_methods=event_stream_in_value_out_methods,
event_stream_in_stream_out_methods=event_stream_in_stream_out_methods,
multi_method=multi_method)
serialization = serial.serialization(methods)
fore_link = fore.ForeLink(
pool, serialization.request_deserializers,
serialization.response_serializers)
port = fore_link.start()
rear_link = rear.RearLink(
'localhost', port, pool,
serialization.request_serializers, serialization.response_deserializers)
rear_link.start()
front = tickets_implementations.front(pool, pool, pool)
back = tickets_implementations.back(
servicer, pool, pool, pool, _TIMEOUT, _MAXIMUM_TIMEOUT)
fore_link.join_rear_link(back)
back.join_fore_link(fore_link)
rear_link.join_fore_link(front)
front.join_rear_link(rear_link)
server = face_implementations.server()
stub = face_implementations.stub(front, pool)
return server, stub, (rear_link, fore_link, front, back)
def tear_down_implementation(self, memo):
rear_link, fore_link, front, back = memo
# TODO(nathaniel): Waiting for the front and back to idle possibly should
# not be necessary - investigate as part of graceful shutdown work.
util.wait_for_idle(front)
util.wait_for_idle(back)
rear_link.stop()
fore_link.stop()
@unittest.skip('Service-side failure not transmitted by GRPC.')
def testFailedUnaryRequestUnaryResponse(self):
raise NotImplementedError()
@unittest.skip('Service-side failure not transmitted by GRPC.')
def testFailedUnaryRequestStreamResponse(self):
raise NotImplementedError()
@unittest.skip('Service-side failure not transmitted by GRPC.')
def testFailedStreamRequestUnaryResponse(self):
raise NotImplementedError()
@unittest.skip('Service-side failure not transmitted by GRPC.')
def testFailedStreamRequestStreamResponse(self):
raise NotImplementedError()

@ -0,0 +1,46 @@
# 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.
"""One of the tests of the Face layer of RPC Framework."""
import unittest
from _adapter import _face_test_case
from _framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case
class FutureInvocationAsynchronousEventServiceTest(
_face_test_case.FaceTestCase,
test_case.FutureInvocationAsynchronousEventServiceTestCase,
unittest.TestCase):
pass
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,245 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Test of the GRPC-backed ForeLink and RearLink."""
import threading
import unittest
from _adapter import _proto_scenarios
from _adapter import _test_links
from _adapter import fore
from _adapter import rear
from _framework.base import interfaces
from _framework.base.packets import packets as tickets
from _framework.foundation import logging_pool
_IDENTITY = lambda x: x
_TIMEOUT = 2
class RoundTripTest(unittest.TestCase):
def setUp(self):
self.fore_link_pool = logging_pool.pool(80)
self.rear_link_pool = logging_pool.pool(80)
def tearDown(self):
self.rear_link_pool.shutdown(wait=True)
self.fore_link_pool.shutdown(wait=True)
def testZeroMessageRoundTrip(self):
test_operation_id = object()
test_method = 'test method'
test_fore_link = _test_links.ForeLink(None, None)
def rear_action(front_to_back_ticket, fore_link):
if front_to_back_ticket.kind in (
tickets.Kind.COMPLETION, tickets.Kind.ENTIRE):
back_to_front_ticket = tickets.BackToFrontPacket(
front_to_back_ticket.operation_id, 0, tickets.Kind.COMPLETION, None)
fore_link.accept_back_to_front_ticket(back_to_front_ticket)
test_rear_link = _test_links.RearLink(rear_action, None)
fore_link = fore.ForeLink(
self.fore_link_pool, {test_method: None}, {test_method: None})
fore_link.join_rear_link(test_rear_link)
test_rear_link.join_fore_link(fore_link)
port = fore_link.start()
rear_link = rear.RearLink(
'localhost', port, self.rear_link_pool, {test_method: None},
{test_method: None})
rear_link.join_fore_link(test_fore_link)
test_fore_link.join_rear_link(rear_link)
rear_link.start()
front_to_back_ticket = tickets.FrontToBackPacket(
test_operation_id, 0, tickets.Kind.ENTIRE, test_method, interfaces.FULL,
None, None, _TIMEOUT)
rear_link.accept_front_to_back_ticket(front_to_back_ticket)
with test_fore_link.condition:
while (not test_fore_link.tickets or
test_fore_link.tickets[-1].kind is tickets.Kind.CONTINUATION):
test_fore_link.condition.wait()
rear_link.stop()
fore_link.stop()
with test_fore_link.condition:
self.assertIs(test_fore_link.tickets[-1].kind, tickets.Kind.COMPLETION)
def testEntireRoundTrip(self):
test_operation_id = object()
test_method = 'test method'
test_front_to_back_datum = b'\x07'
test_back_to_front_datum = b'\x08'
test_fore_link = _test_links.ForeLink(None, None)
rear_sequence_number = [0]
def rear_action(front_to_back_ticket, fore_link):
if front_to_back_ticket.payload is None:
payload = None
else:
payload = test_back_to_front_datum
terminal = front_to_back_ticket.kind in (
tickets.Kind.COMPLETION, tickets.Kind.ENTIRE)
if payload is not None or terminal:
back_to_front_ticket = tickets.BackToFrontPacket(
front_to_back_ticket.operation_id, rear_sequence_number[0],
tickets.Kind.COMPLETION if terminal else tickets.Kind.CONTINUATION,
payload)
rear_sequence_number[0] += 1
fore_link.accept_back_to_front_ticket(back_to_front_ticket)
test_rear_link = _test_links.RearLink(rear_action, None)
fore_link = fore.ForeLink(
self.fore_link_pool, {test_method: _IDENTITY},
{test_method: _IDENTITY})
fore_link.join_rear_link(test_rear_link)
test_rear_link.join_fore_link(fore_link)
port = fore_link.start()
rear_link = rear.RearLink(
'localhost', port, self.rear_link_pool, {test_method: _IDENTITY},
{test_method: _IDENTITY})
rear_link.join_fore_link(test_fore_link)
test_fore_link.join_rear_link(rear_link)
rear_link.start()
front_to_back_ticket = tickets.FrontToBackPacket(
test_operation_id, 0, tickets.Kind.ENTIRE, test_method, interfaces.FULL,
None, test_front_to_back_datum, _TIMEOUT)
rear_link.accept_front_to_back_ticket(front_to_back_ticket)
with test_fore_link.condition:
while (not test_fore_link.tickets or
test_fore_link.tickets[-1].kind is not tickets.Kind.COMPLETION):
test_fore_link.condition.wait()
rear_link.stop()
fore_link.stop()
with test_rear_link.condition:
front_to_back_payloads = tuple(
ticket.payload for ticket in test_rear_link.tickets
if ticket.payload is not None)
with test_fore_link.condition:
back_to_front_payloads = tuple(
ticket.payload for ticket in test_fore_link.tickets
if ticket.payload is not None)
self.assertTupleEqual((test_front_to_back_datum,), front_to_back_payloads)
self.assertTupleEqual((test_back_to_front_datum,), back_to_front_payloads)
def _perform_scenario_test(self, scenario):
test_operation_id = object()
test_method = scenario.method()
test_fore_link = _test_links.ForeLink(None, None)
rear_lock = threading.Lock()
rear_sequence_number = [0]
def rear_action(front_to_back_ticket, fore_link):
with rear_lock:
if front_to_back_ticket.payload is not None:
response = scenario.response_for_request(front_to_back_ticket.payload)
else:
response = None
terminal = front_to_back_ticket.kind in (
tickets.Kind.COMPLETION, tickets.Kind.ENTIRE)
if response is not None or terminal:
back_to_front_ticket = tickets.BackToFrontPacket(
front_to_back_ticket.operation_id, rear_sequence_number[0],
tickets.Kind.COMPLETION if terminal else tickets.Kind.CONTINUATION,
response)
rear_sequence_number[0] += 1
fore_link.accept_back_to_front_ticket(back_to_front_ticket)
test_rear_link = _test_links.RearLink(rear_action, None)
fore_link = fore.ForeLink(
self.fore_link_pool, {test_method: scenario.deserialize_request},
{test_method: scenario.serialize_response})
fore_link.join_rear_link(test_rear_link)
test_rear_link.join_fore_link(fore_link)
port = fore_link.start()
rear_link = rear.RearLink(
'localhost', port, self.rear_link_pool,
{test_method: scenario.serialize_request},
{test_method: scenario.deserialize_response})
rear_link.join_fore_link(test_fore_link)
test_fore_link.join_rear_link(rear_link)
rear_link.start()
commencement_ticket = tickets.FrontToBackPacket(
test_operation_id, 0, tickets.Kind.COMMENCEMENT, test_method,
interfaces.FULL, None, None, _TIMEOUT)
fore_sequence_number = 1
rear_link.accept_front_to_back_ticket(commencement_ticket)
for request in scenario.requests():
continuation_ticket = tickets.FrontToBackPacket(
test_operation_id, fore_sequence_number, tickets.Kind.CONTINUATION,
None, None, None, request, None)
fore_sequence_number += 1
rear_link.accept_front_to_back_ticket(continuation_ticket)
completion_ticket = tickets.FrontToBackPacket(
test_operation_id, fore_sequence_number, tickets.Kind.COMPLETION, None,
None, None, None, None)
fore_sequence_number += 1
rear_link.accept_front_to_back_ticket(completion_ticket)
with test_fore_link.condition:
while (not test_fore_link.tickets or
test_fore_link.tickets[-1].kind is not tickets.Kind.COMPLETION):
test_fore_link.condition.wait()
rear_link.stop()
fore_link.stop()
with test_rear_link.condition:
requests = tuple(
ticket.payload for ticket in test_rear_link.tickets
if ticket.payload is not None)
with test_fore_link.condition:
responses = tuple(
ticket.payload for ticket in test_fore_link.tickets
if ticket.payload is not None)
self.assertTrue(scenario.verify_requests(requests))
self.assertTrue(scenario.verify_responses(responses))
def testEmptyScenario(self):
self._perform_scenario_test(_proto_scenarios.EmptyScenario())
def testBidirectionallyUnaryScenario(self):
self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario())
def testBidirectionallyStreamingScenario(self):
self._perform_scenario_test(
_proto_scenarios.BidirectionallyStreamingScenario())
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,97 @@
# 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.
"""A test of invocation-side code unconnected to an RPC server."""
import unittest
from _adapter import _test_links
from _adapter import rear
from _framework.base import interfaces
from _framework.base.packets import packets
from _framework.foundation import logging_pool
_IDENTITY = lambda x: x
_TIMEOUT = 2
class LonelyRearLinkTest(unittest.TestCase):
def setUp(self):
self.pool = logging_pool.pool(80)
def tearDown(self):
self.pool.shutdown(wait=True)
def testUpAndDown(self):
rear_link = rear.RearLink('nonexistent', 54321, self.pool, {}, {})
rear_link.start()
rear_link.stop()
def _perform_lonely_client_test_with_ticket_kind(
self, front_to_back_ticket_kind):
test_operation_id = object()
test_method = 'test method'
fore_link = _test_links.ForeLink(None, None)
rear_link = rear.RearLink(
'nonexistent', 54321, self.pool, {test_method: None},
{test_method: None})
rear_link.join_fore_link(fore_link)
rear_link.start()
front_to_back_ticket = packets.FrontToBackPacket(
test_operation_id, 0, front_to_back_ticket_kind, test_method,
interfaces.FULL, None, None, _TIMEOUT)
rear_link.accept_front_to_back_ticket(front_to_back_ticket)
with fore_link.condition:
while True:
if (fore_link.tickets and
fore_link.tickets[-1].kind is not packets.Kind.CONTINUATION):
break
fore_link.condition.wait()
rear_link.stop()
with fore_link.condition:
self.assertIsNot(fore_link.tickets[-1].kind, packets.Kind.COMPLETION)
@unittest.skip('TODO(nathaniel): This seems to have broken in the last few weeks; fix it.')
def testLonelyClientCommencementPacket(self):
self._perform_lonely_client_test_with_ticket_kind(
packets.Kind.COMMENCEMENT)
def testLonelyClientEntirePacket(self):
self._perform_lonely_client_test_with_ticket_kind(packets.Kind.ENTIRE)
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,55 @@
# 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.
"""A Python interface for GRPC C core structures and behaviors."""
import atexit
import gc
from _adapter import _c
from _adapter import _datatypes
def _shut_down():
# force garbage collection before shutting down grpc, to ensure all grpc
# objects are cleaned up
gc.collect()
_c.shut_down()
_c.init()
atexit.register(_shut_down)
# pylint: disable=invalid-name
Code = _datatypes.Code
Status = _datatypes.Status
Event = _datatypes.Event
Call = _c.Call
Channel = _c.Channel
CompletionQueue = _c.CompletionQueue
Server = _c.Server
# pylint: enable=invalid-name

@ -0,0 +1,371 @@
# 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.
"""Tests for _adapter._low."""
import time
import unittest
from _adapter import _low
_STREAM_LENGTH = 300
_TIMEOUT = 5
_AFTER_DELAY = 2
_FUTURE = time.time() + 60 * 60 * 24
_BYTE_SEQUENCE = b'\abcdefghijklmnopqrstuvwxyz0123456789' * 200
_BYTE_SEQUENCE_SEQUENCE = tuple(
bytes(bytearray((row + column) % 256 for column in range(row)))
for row in range(_STREAM_LENGTH))
class LonelyClientTest(unittest.TestCase):
def testLonelyClient(self):
host = 'nosuchhostexists'
port = 54321
method = 'test method'
deadline = time.time() + _TIMEOUT
after_deadline = deadline + _AFTER_DELAY
metadata_tag = object()
finish_tag = object()
completion_queue = _low.CompletionQueue()
channel = _low.Channel('%s:%d' % (host, port))
client_call = _low.Call(channel, method, host, deadline)
client_call.invoke(completion_queue, metadata_tag, finish_tag)
first_event = completion_queue.get(after_deadline)
self.assertIsNotNone(first_event)
second_event = completion_queue.get(after_deadline)
self.assertIsNotNone(second_event)
kinds = [event.kind for event in (first_event, second_event)]
self.assertItemsEqual(
(_low.Event.Kind.METADATA_ACCEPTED, _low.Event.Kind.FINISH),
kinds)
self.assertIsNone(completion_queue.get(after_deadline))
completion_queue.stop()
stop_event = completion_queue.get(_FUTURE)
self.assertEqual(_low.Event.Kind.STOP, stop_event.kind)
class EchoTest(unittest.TestCase):
def setUp(self):
self.host = 'localhost'
self.server_completion_queue = _low.CompletionQueue()
self.server = _low.Server(self.server_completion_queue)
port = self.server.add_http2_addr('[::]:0')
self.server.start()
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port))
def tearDown(self):
self.server.stop()
# NOTE(nathaniel): Yep, this is weird; it's a consequence of
# grpc_server_destroy's being what has the effect of telling the server's
# completion queue to pump out all pending events/tags immediately rather
# than gracefully completing all outstanding RPCs while accepting no new
# ones.
# TODO(nathaniel): Deallocation of a Python object shouldn't have this kind
# of observable side effect let alone such an important one.
del self.server
self.server_completion_queue.stop()
self.client_completion_queue.stop()
while True:
event = self.server_completion_queue.get(_FUTURE)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
while True:
event = self.client_completion_queue.get(_FUTURE)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
self.server_completion_queue = None
self.client_completion_queue = None
def _perform_echo_test(self, test_data):
method = 'test method'
details = 'test details'
deadline = _FUTURE
metadata_tag = object()
finish_tag = object()
write_tag = object()
complete_tag = object()
service_tag = object()
read_tag = object()
status_tag = object()
server_data = []
client_data = []
client_call = _low.Call(self.channel, method, self.host, deadline)
client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag)
self.server.service(service_tag)
service_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(service_accepted)
self.assertIs(service_accepted.kind, _low.Event.Kind.SERVICE_ACCEPTED)
self.assertIs(service_accepted.tag, service_tag)
self.assertEqual(method, service_accepted.service_acceptance.method)
self.assertEqual(self.host, service_accepted.service_acceptance.host)
self.assertIsNotNone(service_accepted.service_acceptance.call)
server_call = service_accepted.service_acceptance.call
server_call.accept(self.server_completion_queue, finish_tag)
server_call.premetadata()
metadata_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(metadata_accepted)
self.assertEqual(_low.Event.Kind.METADATA_ACCEPTED, metadata_accepted.kind)
self.assertEqual(metadata_tag, metadata_accepted.tag)
# TODO(nathaniel): Test transmission and reception of metadata.
for datum in test_data:
client_call.write(datum, write_tag)
write_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
self.assertIs(write_accepted.kind, _low.Event.Kind.WRITE_ACCEPTED)
self.assertIs(write_accepted.tag, write_tag)
self.assertIs(write_accepted.write_accepted, True)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNotNone(read_accepted.bytes)
server_data.append(read_accepted.bytes)
server_call.write(read_accepted.bytes, write_tag)
write_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
self.assertEqual(_low.Event.Kind.WRITE_ACCEPTED, write_accepted.kind)
self.assertEqual(write_tag, write_accepted.tag)
self.assertTrue(write_accepted.write_accepted)
client_call.read(read_tag)
read_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNotNone(read_accepted.bytes)
client_data.append(read_accepted.bytes)
client_call.complete(complete_tag)
complete_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(complete_accepted)
self.assertIs(complete_accepted.kind, _low.Event.Kind.COMPLETE_ACCEPTED)
self.assertIs(complete_accepted.tag, complete_tag)
self.assertIs(complete_accepted.complete_accepted, True)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNone(read_accepted.bytes)
server_call.status(_low.Status(_low.Code.OK, details), status_tag)
server_terminal_event_one = self.server_completion_queue.get(_FUTURE)
server_terminal_event_two = self.server_completion_queue.get(_FUTURE)
if server_terminal_event_one.kind == _low.Event.Kind.COMPLETE_ACCEPTED:
status_accepted = server_terminal_event_one
rpc_accepted = server_terminal_event_two
else:
status_accepted = server_terminal_event_two
rpc_accepted = server_terminal_event_one
self.assertIsNotNone(status_accepted)
self.assertIsNotNone(rpc_accepted)
self.assertEqual(_low.Event.Kind.COMPLETE_ACCEPTED, status_accepted.kind)
self.assertEqual(status_tag, status_accepted.tag)
self.assertTrue(status_accepted.complete_accepted)
self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind)
self.assertEqual(finish_tag, rpc_accepted.tag)
self.assertEqual(_low.Status(_low.Code.OK, ''), rpc_accepted.status)
client_call.read(read_tag)
client_terminal_event_one = self.client_completion_queue.get(_FUTURE)
client_terminal_event_two = self.client_completion_queue.get(_FUTURE)
if client_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED:
read_accepted = client_terminal_event_one
finish_accepted = client_terminal_event_two
else:
read_accepted = client_terminal_event_two
finish_accepted = client_terminal_event_one
self.assertIsNotNone(read_accepted)
self.assertIsNotNone(finish_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNone(read_accepted.bytes)
self.assertEqual(_low.Event.Kind.FINISH, finish_accepted.kind)
self.assertEqual(finish_tag, finish_accepted.tag)
self.assertEqual(_low.Status(_low.Code.OK, details), finish_accepted.status)
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data)
def testNoEcho(self):
self._perform_echo_test(())
def testOneByteEcho(self):
self._perform_echo_test([b'\x07'])
def testOneManyByteEcho(self):
self._perform_echo_test([_BYTE_SEQUENCE])
def testManyOneByteEchoes(self):
self._perform_echo_test(_BYTE_SEQUENCE)
def testManyManyByteEchoes(self):
self._perform_echo_test(_BYTE_SEQUENCE_SEQUENCE)
class CancellationTest(unittest.TestCase):
def setUp(self):
self.host = 'localhost'
self.server_completion_queue = _low.CompletionQueue()
self.server = _low.Server(self.server_completion_queue)
port = self.server.add_http2_addr('[::]:0')
self.server.start()
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port))
def tearDown(self):
self.server.stop()
del self.server
self.server_completion_queue.stop()
self.client_completion_queue.stop()
while True:
event = self.server_completion_queue.get(0)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
while True:
event = self.client_completion_queue.get(0)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
def testCancellation(self):
method = 'test method'
deadline = _FUTURE
metadata_tag = object()
finish_tag = object()
write_tag = object()
service_tag = object()
read_tag = object()
test_data = _BYTE_SEQUENCE_SEQUENCE
server_data = []
client_data = []
client_call = _low.Call(self.channel, method, self.host, deadline)
client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag)
self.server.service(service_tag)
service_accepted = self.server_completion_queue.get(_FUTURE)
server_call = service_accepted.service_acceptance.call
server_call.accept(self.server_completion_queue, finish_tag)
server_call.premetadata()
metadata_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(metadata_accepted)
for datum in test_data:
client_call.write(datum, write_tag)
write_accepted = self.client_completion_queue.get(_FUTURE)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
server_data.append(read_accepted.bytes)
server_call.write(read_accepted.bytes, write_tag)
write_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
client_call.read(read_tag)
read_accepted = self.client_completion_queue.get(_FUTURE)
client_data.append(read_accepted.bytes)
client_call.cancel()
# cancel() is idempotent.
client_call.cancel()
client_call.cancel()
client_call.cancel()
server_call.read(read_tag)
server_terminal_event_one = self.server_completion_queue.get(_FUTURE)
server_terminal_event_two = self.server_completion_queue.get(_FUTURE)
if server_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED:
read_accepted = server_terminal_event_one
rpc_accepted = server_terminal_event_two
else:
read_accepted = server_terminal_event_two
rpc_accepted = server_terminal_event_one
self.assertIsNotNone(read_accepted)
self.assertIsNotNone(rpc_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertIsNone(read_accepted.bytes)
self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind)
self.assertEqual(_low.Status(_low.Code.CANCELLED, ''), rpc_accepted.status)
finish_event = self.client_completion_queue.get(_FUTURE)
self.assertEqual(_low.Event.Kind.FINISH, finish_event.kind)
self.assertEqual(_low.Status(_low.Code.CANCELLED, ''), finish_event.status)
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data)
class ExpirationTest(unittest.TestCase):
@unittest.skip('TODO(nathaniel): Expiration test!')
def testExpiration(self):
pass
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,261 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Test scenarios using protocol buffers."""
import abc
import threading
from _junkdrawer import math_pb2
class ProtoScenario(object):
"""An RPC test scenario using protocol buffers."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def method(self):
"""Access the test method name.
Returns:
The test method name.
"""
raise NotImplementedError()
@abc.abstractmethod
def serialize_request(self, request):
"""Serialize a request protocol buffer.
Args:
request: A request protocol buffer.
Returns:
The bytestring serialization of the given request protocol buffer.
"""
raise NotImplementedError()
@abc.abstractmethod
def deserialize_request(self, request_bytestring):
"""Deserialize a request protocol buffer.
Args:
request_bytestring: The bytestring serialization of a request protocol
buffer.
Returns:
The request protocol buffer deserialized from the given byte string.
"""
raise NotImplementedError()
@abc.abstractmethod
def serialize_response(self, response):
"""Serialize a response protocol buffer.
Args:
response: A response protocol buffer.
Returns:
The bytestring serialization of the given response protocol buffer.
"""
raise NotImplementedError()
@abc.abstractmethod
def deserialize_response(self, response_bytestring):
"""Deserialize a response protocol buffer.
Args:
response_bytestring: The bytestring serialization of a response protocol
buffer.
Returns:
The response protocol buffer deserialized from the given byte string.
"""
raise NotImplementedError()
@abc.abstractmethod
def requests(self):
"""Access the sequence of requests for this scenario.
Returns:
A sequence of request protocol buffers.
"""
raise NotImplementedError()
@abc.abstractmethod
def response_for_request(self, request):
"""Access the response for a particular request.
Args:
request: A request protocol buffer.
Returns:
The response protocol buffer appropriate for the given request.
"""
raise NotImplementedError()
@abc.abstractmethod
def verify_requests(self, experimental_requests):
"""Verify the requests transmitted through the system under test.
Args:
experimental_requests: The request protocol buffers transmitted through
the system under test.
Returns:
True if the requests satisfy this test scenario; False otherwise.
"""
raise NotImplementedError()
@abc.abstractmethod
def verify_responses(self, experimental_responses):
"""Verify the responses transmitted through the system under test.
Args:
experimental_responses: The response protocol buffers transmitted through
the system under test.
Returns:
True if the responses satisfy this test scenario; False otherwise.
"""
raise NotImplementedError()
class EmptyScenario(ProtoScenario):
"""A scenario that transmits no protocol buffers in either direction."""
def method(self):
return 'DivMany'
def serialize_request(self, request):
raise ValueError('This should not be necessary to call!')
def deserialize_request(self, request_bytestring):
raise ValueError('This should not be necessary to call!')
def serialize_response(self, response):
raise ValueError('This should not be necessary to call!')
def deserialize_response(self, response_bytestring):
raise ValueError('This should not be necessary to call!')
def requests(self):
return ()
def response_for_request(self, request):
raise ValueError('This should not be necessary to call!')
def verify_requests(self, experimental_requests):
return not experimental_requests
def verify_responses(self, experimental_responses):
return not experimental_responses
class BidirectionallyUnaryScenario(ProtoScenario):
"""A scenario that transmits no protocol buffers in either direction."""
_DIVIDEND = 59
_DIVISOR = 7
_QUOTIENT = 8
_REMAINDER = 3
_REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR)
_RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER)
def method(self):
return 'Div'
def serialize_request(self, request):
return request.SerializeToString()
def deserialize_request(self, request_bytestring):
return math_pb2.DivArgs.FromString(request_bytestring)
def serialize_response(self, response):
return response.SerializeToString()
def deserialize_response(self, response_bytestring):
return math_pb2.DivReply.FromString(response_bytestring)
def requests(self):
return [self._REQUEST]
def response_for_request(self, request):
return self._RESPONSE
def verify_requests(self, experimental_requests):
return tuple(experimental_requests) == (self._REQUEST,)
def verify_responses(self, experimental_responses):
return tuple(experimental_responses) == (self._RESPONSE,)
class BidirectionallyStreamingScenario(ProtoScenario):
"""A scenario that transmits no protocol buffers in either direction."""
_STREAM_LENGTH = 200
_REQUESTS = tuple(
math_pb2.DivArgs(dividend=59 + index, divisor=7 + index)
for index in range(_STREAM_LENGTH))
def __init__(self):
self._lock = threading.Lock()
self._responses = []
def method(self):
return 'DivMany'
def serialize_request(self, request):
return request.SerializeToString()
def deserialize_request(self, request_bytestring):
return math_pb2.DivArgs.FromString(request_bytestring)
def serialize_response(self, response):
return response.SerializeToString()
def deserialize_response(self, response_bytestring):
return math_pb2.DivReply.FromString(response_bytestring)
def requests(self):
return self._REQUESTS
def response_for_request(self, request):
quotient, remainder = divmod(request.dividend, request.divisor)
response = math_pb2.DivReply(quotient=quotient, remainder=remainder)
with self._lock:
self._responses.append(response)
return response
def verify_requests(self, experimental_requests):
return tuple(experimental_requests) == self._REQUESTS
def verify_responses(self, experimental_responses):
with self._lock:
return tuple(experimental_responses) == tuple(self._responses)

@ -0,0 +1,167 @@
/*
*
* 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 "_adapter/_server.h"
#include <Python.h>
#include <grpc/grpc.h>
#include "_adapter/_completion_queue.h"
#include "_adapter/_error.h"
static int pygrpc_server_init(Server *self, PyObject *args, PyObject *kwds) {
const PyObject *completion_queue;
if (!(PyArg_ParseTuple(args, "O!", &pygrpc_CompletionQueueType,
&completion_queue))) {
self->c_server = NULL;
return -1;
}
self->c_server = grpc_server_create(
((CompletionQueue *)completion_queue)->c_completion_queue, NULL);
return 0;
}
static void pygrpc_server_dealloc(Server *self) {
if (self->c_server != NULL) {
grpc_server_destroy(self->c_server);
}
self->ob_type->tp_free((PyObject *)self);
}
static PyObject *pygrpc_server_add_http2_addr(Server *self, PyObject *args) {
const char *addr;
int port;
PyArg_ParseTuple(args, "s", &addr);
port = grpc_server_add_http2_port(self->c_server, addr);
if (port == 0) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't add port to server!");
return NULL;
}
return PyInt_FromLong(port);
}
static PyObject *pygrpc_server_start(Server *self) {
grpc_server_start(self->c_server);
Py_RETURN_NONE;
}
static const PyObject *pygrpc_server_service(Server *self, PyObject *args) {
const PyObject *tag;
grpc_call_error call_error;
const PyObject *result;
if (!(PyArg_ParseTuple(args, "O", &tag))) {
return NULL;
}
call_error = grpc_server_request_call(self->c_server, (void *)tag);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(tag);
}
return result;
}
static PyObject *pygrpc_server_stop(Server *self) {
grpc_server_shutdown(self->c_server);
Py_RETURN_NONE;
}
static PyMethodDef methods[] = {
{"add_http2_addr", (PyCFunction)pygrpc_server_add_http2_addr, METH_VARARGS,
"Add an HTTP2 address."},
{"start", (PyCFunction)pygrpc_server_start, METH_NOARGS,
"Starts the server."},
{"service", (PyCFunction)pygrpc_server_service, METH_VARARGS,
"Services a call."},
{"stop", (PyCFunction)pygrpc_server_stop, METH_NOARGS, "Stops the server."},
{NULL}};
static PyTypeObject pygrpc_ServerType = {
PyObject_HEAD_INIT(NULL)0, /*ob_size*/
"_gprc.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, /*tp_flags*/
"Wrapping of grpc_server.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
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 */
(initproc)pygrpc_server_init, /* tp_init */
};
int pygrpc_add_server(PyObject *module) {
pygrpc_ServerType.tp_new = PyType_GenericNew;
if (PyType_Ready(&pygrpc_ServerType) < 0) {
PyErr_SetString(PyExc_RuntimeError, "Error defining pygrpc_ServerType!");
return -1;
}
if (PyModule_AddObject(module, "Server", (PyObject *)&pygrpc_ServerType) ==
-1) {
PyErr_SetString(PyExc_ImportError, "Couldn't add Server type to module!");
return -1;
}
return 0;
}

@ -0,0 +1,44 @@
/*
*
* 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 _ADAPTER__SERVER_H_
#define _ADAPTER__SERVER_H_
#include <Python.h>
#include <grpc/grpc.h>
typedef struct { PyObject_HEAD grpc_server *c_server; } Server;
int pygrpc_add_server(PyObject *module);
#endif /* _ADAPTER__SERVER_H_ */

@ -0,0 +1,80 @@
# 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.
"""Links suitable for use in tests."""
import threading
from _framework.base.packets import interfaces
class ForeLink(interfaces.ForeLink):
"""A ForeLink suitable for use in tests of RearLinks."""
def __init__(self, action, rear_link):
self.condition = threading.Condition()
self.tickets = []
self.action = action
self.rear_link = rear_link
def accept_back_to_front_ticket(self, ticket):
with self.condition:
self.tickets.append(ticket)
self.condition.notify_all()
action, rear_link = self.action, self.rear_link
if action is not None:
action(ticket, rear_link)
def join_rear_link(self, rear_link):
with self.condition:
self.rear_link = rear_link
class RearLink(interfaces.RearLink):
"""A RearLink suitable for use in tests of ForeLinks."""
def __init__(self, action, fore_link):
self.condition = threading.Condition()
self.tickets = []
self.action = action
self.fore_link = fore_link
def accept_front_to_back_ticket(self, ticket):
with self.condition:
self.tickets.append(ticket)
self.condition.notify_all()
action, fore_link = self.action, self.fore_link
if action is not None:
action(ticket, fore_link)
def join_fore_link(self, fore_link):
with self.condition:
self.fore_link = fore_link

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

Loading…
Cancel
Save