Merge remote-tracking branch 'upstream/master'

pull/341/head
Chen Wang 10 years ago
commit 4428596d10
  1. 7
      .editorconfig
  2. 6
      .gitignore
  3. 3
      INSTALL
  4. 207
      Makefile
  5. 6
      README.md
  6. 90
      build.json
  7. 2
      include/grpc/byte_buffer_reader.h
  8. 47
      include/grpc/grpc.h
  9. 8
      include/grpc/support/port_platform.h
  10. 2
      include/grpc/support/slice_buffer.h
  11. 2
      src/core/channel/channel_args.c
  12. 3
      src/core/channel/channel_stack.c
  13. 2
      src/core/channel/http_client_filter.c
  14. 20
      src/core/channel/http_server_filter.c
  15. 2
      src/core/httpcli/format_request.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. 3
      src/core/iomgr/pollset_kick.c
  21. 7
      src/core/iomgr/pollset_multipoller_with_poll_posix.c
  22. 14
      src/core/iomgr/pollset_posix.c
  23. 6
      src/core/iomgr/pollset_posix.h
  24. 2
      src/core/iomgr/tcp_server.h
  25. 6
      src/core/iomgr/tcp_server_posix.c
  26. 2
      src/core/iomgr/wakeup_fd_eventfd.c
  27. 9
      src/core/iomgr/wakeup_fd_nospecial.c
  28. 8
      src/core/iomgr/wakeup_fd_pipe.c
  29. 2
      src/core/iomgr/wakeup_fd_pipe.h
  30. 14
      src/core/iomgr/wakeup_fd_posix.c
  31. 31
      src/core/iomgr/wakeup_fd_posix.h
  32. 64
      src/core/json/json.c
  33. 88
      src/core/json/json.h
  34. 49
      src/core/json/json_common.h
  35. 653
      src/core/json/json_reader.c
  36. 160
      src/core/json/json_reader.h
  37. 391
      src/core/json/json_string.c
  38. 252
      src/core/json/json_writer.c
  39. 93
      src/core/json/json_writer.h
  40. 3
      src/core/security/base64.c
  41. 49
      src/core/security/credentials.c
  42. 99
      src/core/security/json_token.c
  43. 4
      src/core/security/secure_endpoint.c
  44. 3
      src/core/security/server_secure_chttp2.c
  45. 4
      src/core/statistics/census_log.c
  46. 2
      src/core/statistics/census_rpc_stats.c
  47. 8
      src/core/statistics/census_tracing.h
  48. 2
      src/core/statistics/hash_table.c
  49. 2
      src/core/statistics/window_stats.c
  50. 4
      src/core/support/cpu.h
  51. 5
      src/core/support/cpu_linux.c
  52. 4
      src/core/support/cpu_posix.c
  53. 1
      src/core/support/histogram.c
  54. 2
      src/core/support/log_posix.c
  55. 4
      src/core/support/slice_buffer.c
  56. 4
      src/core/support/string_posix.c
  57. 1
      src/core/support/thd_posix.c
  58. 38
      src/core/surface/call.c
  59. 6
      src/core/surface/channel.c
  60. 3
      src/core/surface/server.c
  61. 2
      src/core/surface/server_chttp2.c
  62. 42
      src/core/transport/chttp2_transport.c
  63. 3
      src/core/tsi/fake_transport_security.c
  64. 7
      src/core/tsi/ssl_transport_security.c
  65. 16
      src/cpp/client/channel.cc
  66. 2
      src/cpp/server/async_server.cc
  67. 11
      src/cpp/server/async_server_context.cc
  68. 2
      src/cpp/server/server.cc
  69. 7
      src/cpp/server/thread_pool.cc
  70. 12
      src/cpp/stream/stream_context.cc
  71. 2
      src/node/.gitignore
  72. 62
      src/node/examples/stock.proto
  73. 43
      src/node/examples/stock_client.js
  74. 83
      src/node/examples/stock_server.js
  75. 22
      src/node/ext/byte_buffer.cc
  76. 4
      src/node/ext/byte_buffer.h
  77. 81
      src/node/ext/call.cc
  78. 62
      src/node/ext/event.cc
  79. 2
      src/node/ext/server.cc
  80. 43
      src/node/interop/interop_client.js
  81. 2
      src/node/interop/interop_server.js
  82. 27
      src/node/src/server.js
  83. 43
      src/node/src/surface_server.js
  84. 24
      src/node/test/call_test.js
  85. 36
      src/node/test/client_server_test.js
  86. 59
      src/node/test/end_to_end_test.js
  87. 8
      src/node/test/interop_sanity_test.js
  88. 4
      src/node/test/server_test.js
  89. 2
      src/node/test/surface_test.js
  90. 27
      src/php/README.md
  91. 7
      src/php/ext/grpc/byte_buffer.c
  92. 30
      src/php/ext/grpc/call.c
  93. 2
      src/php/ext/grpc/server.c
  94. 2
      src/php/lib/Grpc/AbstractSurfaceActiveCall.php
  95. 13
      src/php/lib/Grpc/ActiveCall.php
  96. 34
      src/php/lib/Grpc/BaseStub.php
  97. 2
      src/php/lib/Grpc/ServerStreamingSurfaceActiveCall.php
  98. 20
      src/php/tests/generated_code/GeneratedCodeTest.php
  99. 9
      src/php/tests/interop/empty.php
  100. 47
      src/php/tests/interop/interop_client.php
  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
# emacs temp files
*~
*~
# vim temp files
.*.swp

@ -85,11 +85,12 @@ Compiling and installing protobuf 3.0.0 requires a few more dependencies in
itself, notably the autoconf suite, curl, and unzip. If you have apt-get, you
can install these dependencies this way:
# apt-get install unzip curl autotools-dev
# apt-get install unzip curl autoconf libtool
Then, you can build and install protobuf 3.0.0:
$ cd third_party/protobuf
$ ./autogen.sh
$ ./configure
$ make
# make install

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
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
and the structure of the payload messages. It is possible to use other
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`).
## 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
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_server.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_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_log.h",
"src/core/statistics/census_rpc_stats.h",
@ -144,6 +148,10 @@
"src/core/iomgr/wakeup_fd_nospecial.c",
"src/core/iomgr/wakeup_fd_pipe.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_log.c",
"src/core/statistics/census_rpc_stats.c",
@ -184,8 +192,7 @@
"src/core/transport/chttp2_transport.c",
"src/core/transport/metadata.c",
"src/core/transport/stream_op.c",
"src/core/transport/transport.c",
"third_party/cJSON/cJSON.c"
"src/core/transport/transport.c"
]
}
],
@ -1186,6 +1193,48 @@
"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",
"build": "test",
@ -1667,6 +1716,41 @@
"gpr_test_util",
"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"
]
}
]
}

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

@ -275,8 +275,9 @@ void grpc_completion_queue_destroy(grpc_completion_queue *cq);
/* Create a call given a grpc_channel, in order to call 'method'. The request
is not sent until grpc_call_invoke is called. All completions are sent to
'completion_queue'. */
grpc_call *grpc_channel_create_call(grpc_channel *channel, const char *method,
const char *host, gpr_timespec deadline);
grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec deadline);
/* Create a client channel */
grpc_channel *grpc_channel_create(const char *target,
@ -307,8 +308,9 @@ void grpc_channel_destroy(grpc_channel *channel);
REQUIRES: grpc_call_start_invoke/grpc_call_server_end_initial_metadata have
not been called on this call.
Produces no events. */
grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata,
gpr_uint32 flags);
grpc_call_error grpc_call_add_metadata_old(grpc_call *call,
grpc_metadata *metadata,
gpr_uint32 flags);
/* Invoke the RPC. Starts sending metadata and request headers on the wire.
flags is a bit-field combination of the write flags defined above.
@ -319,9 +321,9 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata,
Produces a GRPC_FINISHED event with finished_tag when the call has been
completed (there may be other events for the call pending at this
time) */
grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag, void *finished_tag,
gpr_uint32 flags);
grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag,
void *finished_tag, gpr_uint32 flags);
/* Accept an incoming RPC, binding a completion queue to it.
To be called before sending or receiving messages.
@ -330,9 +332,9 @@ grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
Produces a GRPC_FINISHED event with finished_tag when the call has been
completed (there may be other events for the call pending at this
time) */
grpc_call_error grpc_call_server_accept(grpc_call *call,
grpc_completion_queue *cq,
void *finished_tag);
grpc_call_error grpc_call_server_accept_old(grpc_call *call,
grpc_completion_queue *cq,
void *finished_tag);
/* Start sending metadata.
To be called before sending messages.
@ -340,8 +342,8 @@ grpc_call_error grpc_call_server_accept(grpc_call *call,
REQUIRES: Can be called at most once per call.
Can only be called on the server.
Must be called after grpc_call_server_accept */
grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call,
gpr_uint32 flags);
grpc_call_error grpc_call_server_end_initial_metadata_old(grpc_call *call,
gpr_uint32 flags);
/* Called by clients to cancel an RPC on the server.
Can be called multiple times, from any thread. */
@ -370,9 +372,9 @@ grpc_call_error grpc_call_cancel_with_status(grpc_call *call,
grpc_call_server_end_of_initial_metadata must have been called
successfully.
Produces a GRPC_WRITE_ACCEPTED event. */
grpc_call_error grpc_call_start_write(grpc_call *call,
grpc_byte_buffer *byte_buffer, void *tag,
gpr_uint32 flags);
grpc_call_error grpc_call_start_write_old(grpc_call *call,
grpc_byte_buffer *byte_buffer,
void *tag, gpr_uint32 flags);
/* Queue a status for writing.
REQUIRES: No other writes are pending on the call.
@ -380,17 +382,17 @@ grpc_call_error grpc_call_start_write(grpc_call *call,
call prior to calling this.
Only callable on the server.
Produces a GRPC_FINISH_ACCEPTED event when the status is sent. */
grpc_call_error grpc_call_start_write_status(grpc_call *call,
grpc_status_code status_code,
const char *status_message,
void *tag);
grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
grpc_status_code status_code,
const char *status_message,
void *tag);
/* No more messages to send.
REQUIRES: No other writes are pending on the call.
Only callable on the client.
Produces a GRPC_FINISH_ACCEPTED event when all bytes for the call have passed
outgoing flow control. */
grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag);
grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag);
/* Initiate a read on a call. Output event contains a byte buffer with the
result of the read.
@ -402,7 +404,7 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag);
On the server:
grpc_call_server_accept must be called before calling this.
Produces a single GRPC_READ event. */
grpc_call_error grpc_call_start_read(grpc_call *call, void *tag);
grpc_call_error grpc_call_start_read_old(grpc_call *call, void *tag);
/* Destroy a call. */
void grpc_call_destroy(grpc_call *call);
@ -414,7 +416,8 @@ void grpc_call_destroy(grpc_call *call);
tag_cancel.
REQUIRES: Server must not have been shutdown.
NOTE: calling this is the only way to obtain GRPC_SERVER_RPC_NEW events. */
grpc_call_error grpc_server_request_call(grpc_server *server, void *tag_new);
grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new);
/* Create a server */
grpc_server *grpc_server_create(grpc_completion_queue *cq,

@ -56,6 +56,8 @@
#define GPR_CPU_LINUX 1
#define GPR_GCC_SYNC 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
@ -68,7 +70,7 @@
#define GPR_GCC_ATOMIC 1
#define GPR_LINUX 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_HAS_SPECIAL_WAKEUP_FD 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
@ -86,6 +88,8 @@
#define GPR_GCC_ATOMIC 1
#define GPR_POSIX_LOG 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
@ -155,7 +159,7 @@ typedef uintmax_t gpr_uintmax;
typedef uintptr_t gpr_uintptr;
/* 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
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);
/* 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 */
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 */
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 i;
unsigned i;
if (a == NULL) return 0;
for (i = 0; i < a->num_args; i++) {
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);
}
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));
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) {
int i;
unsigned i;
if (args != NULL) {
for (i = 0; i < args->num_args; ++i) {
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) {
gettable_capacity =
GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1);
channeld->gettables =
gpr_realloc(channeld->gettables, gettable_capacity * sizeof(gettable));
channeld->gettables = gpr_realloc(channeld->gettables,
gettable_capacity * sizeof(gettable));
}
g = &channeld->gettables[channeld->gettable_count++];
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);
slice = gpr_slice_from_copied_string(p->content);
g->content = grpc_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
}
}
}
/* Destructor for channel data */
static void destroy_channel_elem(grpc_channel_element *elem) {
size_t i;
/* grab pointers to our data from the channel element */
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->status_ok);
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 = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "http-server"};
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem,
"http-server"};

@ -105,6 +105,8 @@ gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
}
gpr_strvec_add(&out, gpr_strdup("\r\n"));
tmp = gpr_strvec_flatten(&out, &out_len);
gpr_strvec_destroy(&out);
if (body_bytes) {
tmp = gpr_realloc(tmp, out_len + body_size);
memcpy(tmp + out_len, body_bytes, body_size);

@ -47,12 +47,63 @@
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) {
grpc_iomgr_add_callback(fd->on_done, fd->on_done_user_data);
gpr_mu_destroy(&fd->set_state_mu);
gpr_free(fd->watchers);
gpr_mu_destroy(&fd->watcher_mu);
gpr_free(fd);
grpc_iomgr_unref();
}
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) {
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);
}
gpr_mu_destroy(&fd_freelist_mu);
}
static void do_nothing(void *ignored, int success) {}
grpc_fd *grpc_fd_create(int fd) {
grpc_fd *r = gpr_malloc(sizeof(grpc_fd));
grpc_fd *r = alloc_fd(fd);
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);
return r;
}

@ -69,6 +69,7 @@ typedef struct grpc_fd {
grpc_iomgr_cb_func on_done;
void *on_done_user_data;
struct grpc_fd *freelist_next;
} grpc_fd;
/* Create a wrapped file descriptor.
@ -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_unref(grpc_fd *fd);
void grpc_fd_global_init(void);
void grpc_fd_global_shutdown(void);
#endif /* __GRPC_INTERNAL_IOMGR_FD_POSIX_H_ */

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

@ -32,7 +32,14 @@
*/
#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();
}

@ -138,15 +138,18 @@ void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) {
}
void grpc_pollset_kick_global_init_fallback_fd(void) {
gpr_mu_init(&fd_freelist_mu);
grpc_wakeup_fd_global_init_force_fallback();
}
void grpc_pollset_kick_global_init(void) {
gpr_mu_init(&fd_freelist_mu);
grpc_wakeup_fd_global_init();
}
void grpc_pollset_kick_global_destroy(void) {
grpc_wakeup_fd_global_destroy();
gpr_mu_destroy(&fd_freelist_mu);
}

@ -147,8 +147,6 @@ static int multipoll_with_poll_pollset_maybe_work(
grpc_fd_unref(h->fds[i]);
} else {
h->fds[nf++] = h->fds[i];
h->pfds[np].events =
grpc_fd_begin_poll(h->fds[i], pollset, POLLIN, POLLOUT);
h->selfds[np] = h->fds[i];
h->pfds[np].fd = h->fds[i]->fd;
h->pfds[np].revents = 0;
@ -168,6 +166,11 @@ static int multipoll_with_poll_pollset_maybe_work(
pollset->counter = 1;
gpr_mu_unlock(&pollset->mu);
for (i = 1; i < np; i++) {
h->pfds[i].events =
grpc_fd_begin_poll(h->selfds[i], pollset, POLLIN, POLLOUT);
}
r = poll(h->pfds, h->pfd_count, timeout);
if (r < 0) {
if (errno != EINTR) {

@ -75,11 +75,14 @@ static void backup_poller(void *p) {
}
void grpc_pollset_kick(grpc_pollset *p) {
if (!p->counter) return;
grpc_pollset_kick_kick(&p->kick_state);
if (p->counter) {
grpc_pollset_kick_kick(&p->kick_state);
}
}
void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick(p); }
void grpc_pollset_force_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
/* global state management */
@ -244,11 +247,12 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
pfd[0].events = POLLIN;
pfd[0].revents = 0;
pfd[1].fd = fd->fd;
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT);
pfd[1].revents = 0;
pollset->counter = 1;
gpr_mu_unlock(&pollset->mu);
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT);
r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout);
if (r < 0) {
if (errno != EINTR) {
@ -269,9 +273,9 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
}
grpc_pollset_kick_post_poll(&pollset->kick_state);
grpc_fd_end_poll(fd, pollset);
gpr_mu_lock(&pollset->mu);
grpc_fd_end_poll(fd, pollset);
pollset->counter = 0;
gpr_cv_broadcast(&pollset->cv);
return 1;

@ -78,7 +78,11 @@ void grpc_pollset_add_fd(grpc_pollset *pollset, struct grpc_fd *fd);
poll after an fd is orphaned) */
void grpc_pollset_del_fd(grpc_pollset *pollset, struct grpc_fd *fd);
/* Force any current pollers to break polling */
/* Force any current pollers to break polling: it's the callers responsibility
to ensure that the pollset indeed needs to be kicked - no verification that
the pollset is actually performing polling work is done. At worst this will
result in spurious wakeups if performed at the wrong moment.
Does not touch pollset->mu. */
void grpc_pollset_force_kick(grpc_pollset *pollset);
/* Returns the fd to listen on for kicks */
int grpc_kick_read_fd(grpc_pollset *p);

@ -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
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);

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

@ -74,7 +74,7 @@ static int eventfd_check_availability(void) {
return 1;
}
const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable = {
const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy,
eventfd_check_availability
};

@ -38,16 +38,17 @@
#include <grpc/support/port_platform.h>
#ifndef GPR_POSIX_HAS_SPECIAL_WAKEUP_FD
#ifdef GPR_POSIX_NO_SPECIAL_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd.h"
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <stddef.h>
static int check_availability_invalid(void) {
return 0;
}
const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable = {
const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
NULL, NULL, NULL, NULL, check_availability_invalid
};
#endif /* GPR_POSIX_HAS_SPECIAL_WAKEUP */
#endif /* GPR_POSIX_NO_SPECIAL_WAKEUP_FD */

@ -31,7 +31,10 @@
*
*/
/* TODO(klempner): Allow this code to be disabled. */
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <errno.h>
@ -87,7 +90,8 @@ static int pipe_check_availability(void) {
return 1;
}
const grpc_wakeup_fd_vtable pipe_wakeup_fd_vtable = {
const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = {
pipe_create, pipe_consume, pipe_wakeup, pipe_destroy, pipe_check_availability
};
#endif /* GPR_POSIX_WAKUP_FD */

@ -36,6 +36,6 @@
#include "src/core/iomgr/wakeup_fd_posix.h"
extern grpc_wakeup_fd_vtable pipe_wakeup_fd_vtable;
extern grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable;
#endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_PIPE_H_ */

@ -31,6 +31,10 @@
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd_posix.h"
#include "src/core/iomgr/wakeup_fd_pipe.h"
#include <stddef.h>
@ -38,15 +42,15 @@
static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL;
void grpc_wakeup_fd_global_init(void) {
if (specialized_wakeup_fd_vtable.check_availability()) {
wakeup_fd_vtable = &specialized_wakeup_fd_vtable;
if (grpc_specialized_wakeup_fd_vtable.check_availability()) {
wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable;
} else {
wakeup_fd_vtable = &pipe_wakeup_fd_vtable;
wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable;
}
}
void grpc_wakeup_fd_global_init_force_fallback(void) {
wakeup_fd_vtable = &pipe_wakeup_fd_vtable;
wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable;
}
void grpc_wakeup_fd_global_destroy(void) {
@ -68,3 +72,5 @@ void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info) {
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->destroy(fd_info);
}
#endif /* GPR_POSIX_WAKEUP_FD */

@ -62,29 +62,14 @@
#ifndef __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_
#define __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_
typedef struct grpc_wakeup_fd_info grpc_wakeup_fd_info;
void grpc_wakeup_fd_global_init(void);
void grpc_wakeup_fd_global_destroy(void);
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info);
#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
/* Force using the fallback implementation. This is intended for testing
* purposes only.*/
void grpc_wakeup_fd_global_init_force_fallback(void);
/* Private structures; don't access their fields directly outside of wakeup fd
* code. */
struct grpc_wakeup_fd_info {
int read_fd;
int write_fd;
};
typedef struct grpc_wakeup_fd_info grpc_wakeup_fd_info;
typedef struct grpc_wakeup_fd_vtable {
void (*create)(grpc_wakeup_fd_info *fd_info);
@ -95,8 +80,20 @@ typedef struct grpc_wakeup_fd_vtable {
int (*check_availability)(void);
} grpc_wakeup_fd_vtable;
struct grpc_wakeup_fd_info {
int read_fd;
int write_fd;
};
#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info);
/* Defined in some specialized implementation's .c file, or by
* wakeup_fd_nospecial.c if no such implementation exists. */
extern const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable;
extern const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable;
#endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ */

@ -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;
}
GPR_ASSERT((current - result) < result_projected_size);
GPR_ASSERT(current >= result);
GPR_ASSERT((gpr_uintptr)(current - result) < result_projected_size);
result[current - result] = '\0';
return result;
}

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

@ -44,7 +44,8 @@
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include "third_party/cJSON/cJSON.h"
#include "src/core/json/json.h"
/* --- Constants. --- */
@ -64,18 +65,20 @@ static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL;
/* --- 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) {
cJSON *child = NULL;
child = cJSON_GetObjectItem(json, prop_name);
if (child == NULL || child->type != cJSON_String) {
grpc_json *child;
for (child = json->child; child != NULL; child = child->next) {
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);
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) {
const char *prop_value = json_get_string_property(json, prop_name);
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(
const char *json_string) {
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;
const char *prop_value;
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;
if (json == NULL) {
gpr_log(GPR_ERROR, "Invalid json string %s", json_string);
return result;
goto end;
}
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;
}
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.");
goto end;
}
@ -136,8 +141,9 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
end:
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);
gpr_free(scratchpad);
return result;
}
@ -164,49 +170,63 @@ void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) {
/* --- 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) {
cJSON *json = cJSON_CreateObject();
cJSON *child = cJSON_CreateString(algorithm);
grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
grpc_json *child = NULL;
char *json_str = NULL;
char *result = NULL;
cJSON_AddItemToObject(json, "alg", child);
child = cJSON_CreateString(GRPC_JWT_TYPE);
cJSON_AddItemToObject(json, "typ", child);
json_str = cJSON_PrintUnformatted(json);
child = create_child(NULL, json, "alg", algorithm, GRPC_JSON_STRING);
create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING);
json_str = grpc_json_dump_to_string(json, 0);
result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
free(json_str);
cJSON_Delete(json);
gpr_free(json_str);
grpc_json_destroy(json);
return result;
}
static char *encoded_jwt_claim(const grpc_auth_json_key *json_key,
const char *scope, gpr_timespec token_lifetime) {
cJSON *json = cJSON_CreateObject();
cJSON *child = NULL;
grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
grpc_json *child = NULL;
char *json_str = NULL;
char *result = NULL;
gpr_timespec now = gpr_now();
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) {
gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value.");
expiration = gpr_time_add(now, grpc_max_auth_token_lifetime);
}
child = cJSON_CreateString(json_key->client_email);
cJSON_AddItemToObject(json, "iss", child);
child = cJSON_CreateString(scope);
cJSON_AddItemToObject(json, "scope", child);
child = cJSON_CreateString(GRPC_JWT_AUDIENCE);
cJSON_AddItemToObject(json, "aud", child);
child = cJSON_CreateNumber(now.tv_sec);
cJSON_SetIntValue(child, now.tv_sec);
cJSON_AddItemToObject(json, "iat", child);
child = cJSON_CreateNumber(expiration.tv_sec);
cJSON_SetIntValue(child, expiration.tv_sec);
cJSON_AddItemToObject(json, "exp", child);
json_str = cJSON_PrintUnformatted(json);
sprintf(now_str, "%ld", now.tv_sec);
sprintf(expiration_str, "%ld", expiration.tv_sec);
child = create_child(NULL, json, "iss", json_key->client_email,
GRPC_JSON_STRING);
child = create_child(child, json, "scope", scope, GRPC_JSON_STRING);
child = create_child(child, json, "aud", GRPC_JWT_AUDIENCE, GRPC_JSON_STRING);
child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER);
create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER);
json_str = grpc_json_dump_to_string(json, 0);
result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
free(json_str);
cJSON_Delete(json);
gpr_free(json_str);
grpc_json_destroy(json);
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;
char *result = gpr_malloc(result_len + 1 /* NULL terminated */);
char *current = result;
strncpy(current, str1, str1_len);
memcpy(current, str1, str1_len);
current += str1_len;
*(current++) = '.';
strncpy(current, str2, str2_len);
memcpy(current, str2, 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';
gpr_free(str1);
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,
grpc_endpoint_cb_status error) {
int i = 0;
unsigned i;
gpr_uint8 keep_looping = 0;
int input_buffer_count = 0;
tsi_result result = TSI_OK;
@ -221,7 +221,7 @@ static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep,
size_t nslices,
grpc_endpoint_write_cb cb,
void *user_data) {
int i = 0;
unsigned i;
int output_buffer_count = 0;
tsi_result result = TSI_OK;
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_tcp_server *tcp = NULL;
size_t i;
int count = 0;
unsigned count = 0;
int port_num = -1;
int port_temp;
@ -127,6 +127,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
if (count != resolved->naddrs) {
gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved",
count, resolved->naddrs);
/* if it's an error, don't we want to goto error; here ? */
}
grpc_resolved_addresses_destroy(resolved);

@ -173,7 +173,7 @@ typedef struct census_log_core_local_block {
struct census_log {
int discard_old_records;
/* Number of cores (aka hardware-contexts) */
int num_cores;
unsigned num_cores;
/* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */
gpr_int32 num_blocks;
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
iterator has reached the end. census_log_init_reader() resets the
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
reading (block_being_read_->reader_lock is held). */
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);
if (store != NULL) {
size_t n;
int i, j;
unsigned i, j;
gpr_timespec now = gpr_now();
census_ht_kv* kv = census_ht_get_all_elements(store, &n);
if (kv != NULL) {

@ -34,6 +34,10 @@
#ifndef __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
#define __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Opaque structure for trace object */
typedef struct trace_obj trace_obj;
@ -56,4 +60,8 @@ void census_internal_unlock_trace_store(void);
/* Gets method tag name associated with the input trace object. */
const char* census_get_trace_method_name(const trace_obj* trace);
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ */

@ -292,7 +292,7 @@ static void ht_delete_entry_chain(const census_ht_option* options,
}
void census_ht_destroy(census_ht* ht) {
int i;
unsigned i;
for (i = 0; i < ht->num_buckets; ++i) {
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;
/* Check for possible overflow issues, and maximize interval size if the
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;
} else {
is->top = GPR_INT64_MAX;

@ -38,12 +38,12 @@
/* Return the number of CPU cores on the current system. Will return 0 if
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
be considered advisory only - it is possible that the thread is switched
to a different CPU at any time. Returns a value in range
[0, gpr_cpu_num_cores() - 1] */
int gpr_cpu_current_cpu(void);
unsigned gpr_cpu_current_cpu(void);
#endif /* __GRPC_INTERNAL_SUPPORT_CPU_H__ */

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

@ -45,7 +45,7 @@
static __thread char magic_thread_local;
int gpr_cpu_num_cores(void) {
unsigned gpr_cpu_num_cores(void) {
static int ncpus = 0;
if (ncpus == 0) {
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();
}
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...
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

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

@ -64,7 +64,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity,
va_end(args);
if (ret < 0) {
message = NULL;
} else if (ret <= sizeof(buf) - 1) {
} else if ((size_t)ret <= sizeof(buf) - 1) {
message = buf;
} else {
message = allocated = gpr_malloc(ret + 1);

@ -55,7 +55,7 @@ void gpr_slice_buffer_destroy(gpr_slice_buffer *sb) {
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_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;
back = &sb->slices[sb->count - 1];
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;
out = back->data.inlined.bytes + back->data.inlined.length;
back->data.inlined.length += n;

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

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

@ -348,8 +348,9 @@ void grpc_call_add_mdelem(grpc_call *call, grpc_mdelem *mdelem,
elem->filter->call_op(elem, NULL, &op);
}
grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata,
gpr_uint32 flags) {
grpc_call_error grpc_call_add_metadata_old(grpc_call *call,
grpc_metadata *metadata,
gpr_uint32 flags) {
grpc_mdelem *mdelem;
if (call->is_client) {
@ -455,9 +456,9 @@ static void call_started(void *user_data, grpc_op_error error) {
grpc_call_internal_unref(call);
}
grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag, void *finished_tag,
gpr_uint32 flags) {
grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag,
void *finished_tag, gpr_uint32 flags) {
grpc_call_element *elem;
grpc_call_op op;
@ -527,9 +528,9 @@ grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
return GRPC_CALL_OK;
}
grpc_call_error grpc_call_server_accept(grpc_call *call,
grpc_completion_queue *cq,
void *finished_tag) {
grpc_call_error grpc_call_server_accept_old(grpc_call *call,
grpc_completion_queue *cq,
void *finished_tag) {
/* validate preconditions */
if (call->is_client) {
gpr_log(GPR_ERROR, "can only call %s on servers", __FUNCTION__);
@ -563,8 +564,8 @@ grpc_call_error grpc_call_server_accept(grpc_call *call,
return GRPC_CALL_OK;
}
grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call,
gpr_uint32 flags) {
grpc_call_error grpc_call_server_end_initial_metadata_old(grpc_call *call,
gpr_uint32 flags) {
grpc_call_element *elem;
grpc_call_op op;
@ -634,7 +635,7 @@ static void request_more_data(grpc_call *call) {
elem->filter->call_op(elem, NULL, &op);
}
grpc_call_error grpc_call_start_read(grpc_call *call, void *tag) {
grpc_call_error grpc_call_start_read_old(grpc_call *call, void *tag) {
gpr_uint8 request_more = 0;
switch (call->state) {
@ -677,9 +678,9 @@ grpc_call_error grpc_call_start_read(grpc_call *call, void *tag) {
return GRPC_CALL_OK;
}
grpc_call_error grpc_call_start_write(grpc_call *call,
grpc_byte_buffer *byte_buffer, void *tag,
gpr_uint32 flags) {
grpc_call_error grpc_call_start_write_old(grpc_call *call,
grpc_byte_buffer *byte_buffer,
void *tag, gpr_uint32 flags) {
grpc_call_element *elem;
grpc_call_op op;
@ -732,7 +733,7 @@ grpc_call_error grpc_call_start_write(grpc_call *call,
return GRPC_CALL_OK;
}
grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) {
grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag) {
grpc_call_element *elem;
grpc_call_op op;
@ -780,9 +781,10 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) {
return GRPC_CALL_OK;
}
grpc_call_error grpc_call_start_write_status(grpc_call *call,
grpc_status_code status,
const char *details, void *tag) {
grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
grpc_status_code status,
const char *details,
void *tag) {
grpc_call_element *elem;
grpc_call_op op;

@ -74,9 +74,9 @@ grpc_channel *grpc_channel_create_from_filters(
static void do_nothing(void *ignored, grpc_op_error error) {}
grpc_call *grpc_channel_create_call(grpc_channel *channel, const char *method,
const char *host,
gpr_timespec absolute_deadline) {
grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec absolute_deadline) {
grpc_call *call;
grpc_mdelem *path_mdelem;
grpc_mdelem *authority_mdelem;

@ -625,7 +625,8 @@ void grpc_server_add_listener(grpc_server *server, void *arg,
server->listeners = l;
}
grpc_call_error grpc_server_request_call(grpc_server *server, void *tag_new) {
grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new) {
call_data *calld;
grpc_cq_begin_op(server->cq, NULL, GRPC_SERVER_RPC_NEW);

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

@ -237,6 +237,9 @@ struct transport {
/* state for a stream that's not yet been created */
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 */
void *parser_data;
stream *incoming_stream;
@ -370,6 +373,8 @@ static void unref_transport(transport *t) {
}
gpr_free(t->pending_goaways);
grpc_sopb_destroy(&t->nuke_later_sopb);
gpr_free(t);
}
@ -416,6 +421,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
t->cap_pending_goaways = 0;
gpr_slice_buffer_init(&t->outbuf);
gpr_slice_buffer_init(&t->qbuf);
grpc_sopb_init(&t->nuke_later_sopb);
if (is_client) {
gpr_slice_buffer_add(&t->qbuf,
gpr_slice_from_copied_string(CLIENT_CONNECT_STRING));
@ -555,6 +561,11 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs,
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) {
transport *t = (transport *)gt;
stream *s = (stream *)gs;
@ -681,6 +692,11 @@ static void unlock(transport *t) {
int i;
pending_goaway *goaways = NULL;
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 */
if (ep && !t->writing) {
@ -750,6 +766,10 @@ static void unlock(transport *t) {
unref_transport(t);
}
if (nuke_now.nops) {
grpc_sopb_destroy(&nuke_now);
}
gpr_free(goaways);
}
@ -1006,9 +1026,9 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
if (s) {
/* clear out any unreported input & output: nobody cares anymore */
grpc_sopb_reset(&s->parser.incoming_sopb);
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) {
send_rst = 0;
} 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:
case DTS_FH_0:
GPR_ASSERT(cur < end);
t->incoming_frame_size = ((gpr_uint32) * cur) << 16;
t->incoming_frame_size = ((gpr_uint32)*cur) << 16;
if (++cur == end) {
t->deframe_state = DTS_FH_1;
return 1;
@ -1526,7 +1546,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */
case DTS_FH_1:
GPR_ASSERT(cur < end);
t->incoming_frame_size |= ((gpr_uint32) * cur) << 8;
t->incoming_frame_size |= ((gpr_uint32)*cur) << 8;
if (++cur == end) {
t->deframe_state = DTS_FH_2;
return 1;
@ -1558,7 +1578,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */
case DTS_FH_5:
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) {
t->deframe_state = DTS_FH_6;
return 1;
@ -1566,7 +1586,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */
case DTS_FH_6:
GPR_ASSERT(cur < end);
t->incoming_stream_id |= ((gpr_uint32) * cur) << 16;
t->incoming_stream_id |= ((gpr_uint32)*cur) << 16;
if (++cur == end) {
t->deframe_state = DTS_FH_7;
return 1;
@ -1574,7 +1594,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */
case DTS_FH_7:
GPR_ASSERT(cur < end);
t->incoming_stream_id |= ((gpr_uint32) * cur) << 8;
t->incoming_stream_id |= ((gpr_uint32)*cur) << 8;
if (++cur == end) {
t->deframe_state = DTS_FH_8;
return 1;
@ -1582,7 +1602,7 @@ static int process_read(transport *t, gpr_slice slice) {
/* fallthrough */
case DTS_FH_8:
GPR_ASSERT(cur < end);
t->incoming_stream_id |= ((gpr_uint32) * cur);
t->incoming_stream_id |= ((gpr_uint32)*cur);
t->deframe_state = DTS_FRAME;
if (!init_frame_parser(t)) {
return 0;
@ -1738,9 +1758,9 @@ static void add_to_pollset(grpc_transport *gt, grpc_pollset *pollset) {
*/
static const grpc_transport_vtable vtable = {
sizeof(stream), init_stream, send_batch, set_allow_window_updates,
add_to_pollset, destroy_stream, abort_stream, goaway,
close_transport, send_ping, destroy_transport};
sizeof(stream), init_stream, send_batch, set_allow_window_updates,
add_to_pollset, destroy_stream, abort_stream, goaway, close_transport,
send_ping, destroy_transport};
void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
void *arg,

@ -38,6 +38,7 @@
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/useful.h>
#include "src/core/tsi/transport_security.h"
/* --- 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_result result = TSI_OK;
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;
if (!impl->needs_incoming_message || impl->result == TSI_OK) {

@ -37,6 +37,7 @@
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/useful.h>
#include "src/core/tsi/transport_security.h"
#include <openssl/bio.h>
@ -565,7 +566,8 @@ static tsi_result build_alpn_protocol_name_list(
current += alpn_protocols_lengths[i];
}
/* 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_OK;
@ -1063,7 +1065,8 @@ static int server_handshaker_factory_alpn_callback(
while ((client_current - in) < inlen) {
unsigned char client_current_len = *(client_current++);
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) {
unsigned char server_current_len = *(server_current++);
if ((client_current_len == server_current_len) &&

@ -99,7 +99,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
const google::protobuf::Message &request,
google::protobuf::Message *result) {
Status status;
grpc_call *call = grpc_channel_create_call(
grpc_call *call = grpc_channel_create_call_old(
c_channel_, method.name(), target_.c_str(), context->RawDeadline());
context->set_call(call);
grpc_event *ev;
@ -114,8 +114,8 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
// add_metadata from context
//
// invoke
GPR_ASSERT(grpc_call_invoke(call, cq, metadata_read_tag, finished_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_invoke_old(call, cq, metadata_read_tag, finished_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
// write request
grpc_byte_buffer *write_buffer = nullptr;
bool success = SerializeProto(request, &write_buffer);
@ -126,8 +126,8 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
GetFinalStatus(cq, finished_tag, nullptr);
return status;
}
GPR_ASSERT(grpc_call_start_write(call, write_buffer, write_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_start_write_old(call, write_buffer, write_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
grpc_byte_buffer_destroy(write_buffer);
ev = grpc_completion_queue_pluck(cq, write_tag, gpr_inf_future);
@ -138,7 +138,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
return status;
}
// writes done
GPR_ASSERT(grpc_call_writes_done(call, halfclose_tag) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_writes_done_old(call, halfclose_tag) == GRPC_CALL_OK);
ev = grpc_completion_queue_pluck(cq, halfclose_tag, gpr_inf_future);
grpc_event_finish(ev);
// start read metadata
@ -146,7 +146,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
ev = grpc_completion_queue_pluck(cq, metadata_read_tag, gpr_inf_future);
grpc_event_finish(ev);
// start read
GPR_ASSERT(grpc_call_start_read(call, read_tag) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_start_read_old(call, read_tag) == GRPC_CALL_OK);
ev = grpc_completion_queue_pluck(cq, read_tag, gpr_inf_future);
if (ev->data.read) {
if (!DeserializeProto(ev->data.read, result)) {
@ -167,7 +167,7 @@ StreamContextInterface *Channel::CreateStream(
const RpcMethod &method, ClientContext *context,
const google::protobuf::Message *request,
google::protobuf::Message *result) {
grpc_call *call = grpc_channel_create_call(
grpc_call *call = grpc_channel_create_call_old(
c_channel_, method.name(), target_.c_str(), context->RawDeadline());
context->set_call(call);
grpc_completion_queue *cq = grpc_completion_queue_create();

@ -72,7 +72,7 @@ void AsyncServer::RequestOneRpc() {
return;
}
lock.unlock();
grpc_call_error err = grpc_server_request_call(server_, nullptr);
grpc_call_error err = grpc_server_request_call_old(server_, nullptr);
GPR_ASSERT(err == GRPC_CALL_OK);
}

@ -53,14 +53,15 @@ AsyncServerContext::AsyncServerContext(
AsyncServerContext::~AsyncServerContext() { grpc_call_destroy(call_); }
void AsyncServerContext::Accept(grpc_completion_queue *cq) {
GPR_ASSERT(grpc_call_server_accept(call_, cq, this) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_server_end_initial_metadata(call_, 0) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_server_accept_old(call_, cq, this) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_server_end_initial_metadata_old(call_, 0) ==
GRPC_CALL_OK);
}
bool AsyncServerContext::StartRead(google::protobuf::Message *request) {
GPR_ASSERT(request);
request_ = request;
grpc_call_error err = grpc_call_start_read(call_, this);
grpc_call_error err = grpc_call_start_read_old(call_, this);
return err == GRPC_CALL_OK;
}
@ -70,13 +71,13 @@ bool AsyncServerContext::StartWrite(const google::protobuf::Message &response,
if (!SerializeProto(response, &buffer)) {
return false;
}
grpc_call_error err = grpc_call_start_write(call_, buffer, this, flags);
grpc_call_error err = grpc_call_start_write_old(call_, buffer, this, flags);
grpc_byte_buffer_destroy(buffer);
return err == GRPC_CALL_OK;
}
bool AsyncServerContext::StartWriteStatus(const Status &status) {
grpc_call_error err = grpc_call_start_write_status(
grpc_call_error err = grpc_call_start_write_status_old(
call_, static_cast<grpc_status_code>(status.code()),
status.details().empty() ? nullptr
: const_cast<char *>(status.details().c_str()),

@ -111,7 +111,7 @@ void Server::Start() {
void Server::AllowOneRpc() {
GPR_ASSERT(started_);
grpc_call_error err = grpc_server_request_call(server_, nullptr);
grpc_call_error err = grpc_server_request_call_old(server_, nullptr);
GPR_ASSERT(err == GRPC_CALL_OK);
}

@ -41,7 +41,10 @@ ThreadPool::ThreadPool(int num_threads) {
for (;;) {
std::unique_lock<std::mutex> lock(mu_);
// Wait until work is available or we are shutting down.
cv_.wait(lock, [=]() { return shutdown_ || !callbacks_.empty(); });
auto have_work = [=]() { return shutdown_ || !callbacks_.empty(); };
if (!have_work()) {
cv_.wait(lock, have_work);
}
// Drain callbacks before considering shutdown to ensure all work
// gets completed.
if (!callbacks_.empty()) {
@ -71,7 +74,7 @@ ThreadPool::~ThreadPool() {
void ThreadPool::ScheduleCallback(const std::function<void()> &callback) {
std::lock_guard<std::mutex> lock(mu_);
callbacks_.push(callback);
cv_.notify_all();
cv_.notify_one();
}
} // namespace grpc

@ -80,22 +80,22 @@ void StreamContext::Start(bool buffered) {
if (is_client_) {
// TODO(yangg) handle metadata send path
int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0;
grpc_call_error error = grpc_call_invoke(
grpc_call_error error = grpc_call_invoke_old(
call(), cq(), client_metadata_read_tag(), finished_tag(), flag);
GPR_ASSERT(GRPC_CALL_OK == error);
} else {
// TODO(yangg) metadata needs to be added before accept
// TODO(yangg) correctly set flag to accept
GPR_ASSERT(grpc_call_server_accept(call(), cq(), finished_tag()) ==
GPR_ASSERT(grpc_call_server_accept_old(call(), cq(), finished_tag()) ==
GRPC_CALL_OK);
GPR_ASSERT(grpc_call_server_end_initial_metadata(call(), 0) ==
GPR_ASSERT(grpc_call_server_end_initial_metadata_old(call(), 0) ==
GRPC_CALL_OK);
}
}
bool StreamContext::Read(google::protobuf::Message *msg) {
// TODO(yangg) check peer_halfclosed_ here for possible early return.
grpc_call_error err = grpc_call_start_read(call(), read_tag());
grpc_call_error err = grpc_call_start_read_old(call(), read_tag());
GPR_ASSERT(err == GRPC_CALL_OK);
grpc_event *read_ev =
grpc_completion_queue_pluck(cq(), read_tag(), gpr_inf_future);
@ -129,7 +129,7 @@ bool StreamContext::Write(const google::protobuf::Message *msg, bool is_last) {
}
int flag = is_last ? GRPC_WRITE_BUFFER_HINT : 0;
grpc_call_error err =
grpc_call_start_write(call(), out_buf, write_tag(), flag);
grpc_call_start_write_old(call(), out_buf, write_tag(), flag);
grpc_byte_buffer_destroy(out_buf);
GPR_ASSERT(err == GRPC_CALL_OK);
@ -140,7 +140,7 @@ bool StreamContext::Write(const google::protobuf::Message *msg, bool is_last) {
grpc_event_finish(ev);
}
if (ret && is_last) {
grpc_call_error err = grpc_call_writes_done(call(), halfclose_tag());
grpc_call_error err = grpc_call_writes_done_old(call(), halfclose_tag());
GPR_ASSERT(err == GRPC_CALL_OK);
ev = grpc_completion_queue_pluck(cq(), halfclose_tag(), gpr_inf_future);
GPR_ASSERT(ev->type == GRPC_FINISH_ACCEPTED);

@ -0,0 +1,2 @@
build
node_modules

@ -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/support/slice.h"
#include "byte_buffer.h"
namespace grpc {
namespace node {
#include "byte_buffer.h"
using ::node::Buffer;
using v8::Context;
using v8::Function;
using v8::Handle;
using v8::Object;
using v8::Number;
using v8::Value;
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));
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 grpc

@ -50,6 +50,10 @@ grpc_byte_buffer *BufferToByteBuffer(v8::Handle<v8::Value> buffer);
/* Convert a grpc_byte_buffer to a Node.js 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 grpc

@ -33,6 +33,7 @@
#include <node.h>
#include "grpc/support/log.h"
#include "grpc/grpc.h"
#include "grpc/support/time.h"
#include "byte_buffer.h"
@ -151,9 +152,9 @@ NAN_METHOD(Call::New) {
NanUtf8String method(args[1]);
double deadline = args[2]->NumberValue();
grpc_channel *wrapped_channel = channel->GetWrappedChannel();
grpc_call *wrapped_call =
grpc_channel_create_call(wrapped_channel, *method, channel->GetHost(),
MillisecondsToTimespec(deadline));
grpc_call *wrapped_call = grpc_channel_create_call_old(
wrapped_channel, *method, channel->GetHost(),
MillisecondsToTimespec(deadline));
call = new Call(wrapped_call);
args.This()->SetHiddenValue(String::NewSymbol("channel_"),
channel_object);
@ -173,31 +174,43 @@ NAN_METHOD(Call::AddMetadata) {
return NanThrowTypeError("addMetadata can only be called on Call objects");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
for (int i = 0; !args[i]->IsUndefined(); i++) {
if (!args[i]->IsObject()) {
if (!args[0]->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(
"addMetadata arguments must be objects with key and value");
"addMetadata's first argument's values must be arrays");
}
Handle<Object> item = args[i]->ToObject();
Handle<Value> key = item->Get(NanNew("key"));
if (!key->IsString()) {
return NanThrowTypeError(
"objects passed to addMetadata must have key->string");
}
Handle<Value> value = item->Get(NanNew("value"));
if (!Buffer::HasInstance(value)) {
return NanThrowTypeError(
"objects passed to addMetadata must have value->Buffer");
}
grpc_metadata metadata;
NanUtf8String utf8_key(key);
metadata.key = *utf8_key;
metadata.value = Buffer::Data(value);
metadata.value_length = Buffer::Length(value);
grpc_call_error error =
grpc_call_add_metadata(call->wrapped_call, &metadata, 0);
if (error != GRPC_CALL_OK) {
return NanThrowError("addMetadata failed", error);
NanUtf8String utf8_key(current_key);
Handle<Array> values = Local<Array>::Cast(metadata->Get(current_key));
for (unsigned int j = 0; j < values->Length(); j++) {
Handle<Value> value = values->Get(j);
grpc_metadata metadata;
grpc_call_error error;
metadata.key = *utf8_key;
if (Buffer::HasInstance(value)) {
metadata.value = Buffer::Data(value);
metadata.value_length = Buffer::Length(value);
error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0);
} else if (value->IsString()) {
Handle<String> string_value = value->ToString();
NanUtf8String utf8_value(string_value);
metadata.value = *utf8_value;
metadata.value_length = string_value->Length();
gpr_log(GPR_DEBUG, "adding metadata: %s, %s, %d", metadata.key,
metadata.value, metadata.value_length);
error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0);
} else {
return NanThrowTypeError(
"addMetadata values must be strings or buffers");
}
if (error != GRPC_CALL_OK) {
return NanThrowError("addMetadata failed", error);
}
}
}
NanReturnUndefined();
@ -219,7 +232,7 @@ NAN_METHOD(Call::Invoke) {
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
unsigned int flags = args[3]->Uint32Value();
grpc_call_error error = grpc_call_invoke(
grpc_call_error error = grpc_call_invoke_old(
call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(),
CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), flags);
if (error == GRPC_CALL_OK) {
@ -240,7 +253,7 @@ NAN_METHOD(Call::ServerAccept) {
return NanThrowTypeError("accept's first argument must be a function");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error = grpc_call_server_accept(
grpc_call_error error = grpc_call_server_accept_old(
call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(),
CreateTag(args[0], args.This()));
if (error == GRPC_CALL_OK) {
@ -264,7 +277,7 @@ NAN_METHOD(Call::ServerEndInitialMetadata) {
Call *call = ObjectWrap::Unwrap<Call>(args.This());
unsigned int flags = args[1]->Uint32Value();
grpc_call_error error =
grpc_call_server_end_initial_metadata(call->wrapped_call, flags);
grpc_call_server_end_initial_metadata_old(call->wrapped_call, flags);
if (error != GRPC_CALL_OK) {
return NanThrowError("serverEndInitialMetadata failed", error);
}
@ -302,7 +315,7 @@ NAN_METHOD(Call::StartWrite) {
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]);
unsigned int flags = args[2]->Uint32Value();
grpc_call_error error = grpc_call_start_write(
grpc_call_error error = grpc_call_start_write_old(
call->wrapped_call, buffer, CreateTag(args[1], args.This()), flags);
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
@ -332,7 +345,7 @@ NAN_METHOD(Call::StartWriteStatus) {
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
NanUtf8String details(args[1]);
grpc_call_error error = grpc_call_start_write_status(
grpc_call_error error = grpc_call_start_write_status_old(
call->wrapped_call, (grpc_status_code)args[0]->Uint32Value(), *details,
CreateTag(args[2], args.This()));
if (error == GRPC_CALL_OK) {
@ -352,7 +365,7 @@ NAN_METHOD(Call::WritesDone) {
return NanThrowTypeError("writesDone's first argument must be a function");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error = grpc_call_writes_done(
grpc_call_error error = grpc_call_writes_done_old(
call->wrapped_call, CreateTag(args[0], args.This()));
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
@ -371,8 +384,8 @@ NAN_METHOD(Call::StartRead) {
return NanThrowTypeError("startRead's first argument must be a function");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error =
grpc_call_start_read(call->wrapped_call, CreateTag(args[0], args.This()));
grpc_call_error error = grpc_call_start_read_old(
call->wrapped_call, CreateTag(args[0], args.This()));
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
} else {

@ -31,6 +31,8 @@
*
*/
#include <map>
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
@ -43,6 +45,7 @@
namespace grpc {
namespace node {
using ::node::Buffer;
using v8::Array;
using v8::Date;
using v8::Handle;
@ -53,6 +56,37 @@ using v8::Persistent;
using v8::String;
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) {
NanEscapableScope();
size_t count;
@ -72,18 +106,7 @@ Handle<Value> GetEventData(grpc_event *event) {
case GRPC_CLIENT_METADATA_READ:
count = event->data.client_metadata_read.count;
items = event->data.client_metadata_read.elements;
metadata = NanNew<Array>(static_cast<int>(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);
return NanEscapeScope(ParseMetadata(items, count));
case GRPC_FINISHED:
status = NanNew<Object>();
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;
items = event->data.finished.metadata_elements;
metadata = NanNew<Array>(static_cast<int>(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);
status->Set(NanNew("metadata"), ParseMetadata(items, count));
return NanEscapeScope(status);
case GRPC_SERVER_RPC_NEW:
rpc_new = NanNew<Object>();
@ -133,7 +145,7 @@ Handle<Value> GetEventData(grpc_event *event) {
static_cast<int>(items[i].value_length)));
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);
default:
return NanEscapeScope(NanNull());

@ -175,7 +175,7 @@ NAN_METHOD(Server::RequestCall) {
return NanThrowTypeError("requestCall can only be called on a Server");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
grpc_call_error error = grpc_server_request_call(
grpc_call_error error = grpc_server_request_call_old(
server->wrapped_server, CreateTag(args[0], NanNull()));
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();

@ -199,7 +199,6 @@ function pingPong(client, done) {
/**
* 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 {function} done Callback to call when the test is completed. Included
* primarily for use with mocha
@ -218,6 +217,44 @@ function emptyStream(client, done) {
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
*/
@ -227,7 +264,9 @@ var test_cases = {
client_streaming: clientStreaming,
server_streaming: serverStreaming,
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,
halfDuplexCall: handleHalfDuplex
}
}, options);
}, null, options);
var port_num = server.bind('0.0.0.0:' + port, tls);
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
* incoming requests to those handlers
* @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
*/
function Server(options) {
function Server(getMetadata, options) {
this.handlers = {};
var handlers = this.handlers;
var server = new grpc.Server(options);
@ -240,15 +243,27 @@ function Server(options) {
var handler = undefined;
var deadline = data.absolute_deadline;
var cancelled = false;
if (handlers.hasOwnProperty(data.method)) {
handler = handlers[data.method];
}
call.serverAccept(function(event) {
if (event.data.code === grpc.status.CANCELLED) {
cancelled = true;
stream.emit('cancelled');
if (stream) {
stream.emit('cancelled');
}
}
}, 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);
var stream = new GrpcServerStream(call, handler.serialize,
handler.deserialize);

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

@ -99,24 +99,30 @@ describe('call', 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));
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() {
call.addMetadata({'key' : 'key',
'value' : new Buffer('value')});
call.addMetadata({'key': [new Buffer('value')]});
});
assert.doesNotThrow(function() {
call.addMetadata({'key' : 'key1',
'value' : new Buffer('value1')},
{'key' : 'key2',
'value' : new Buffer('value2')});
call.addMetadata({'key1': [new Buffer('value1')],
'key2': [new Buffer('value2')]});
});
});
it('should fail with other parameter types', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.throws(function() {
call.addMetadata();
});
assert.throws(function() {
call.addMetadata(null);
}, TypeError);
@ -133,7 +139,7 @@ describe('call', function() {
function() {done();},
0);
assert.throws(function() {
call.addMetadata({'key' : 'key', 'value' : new Buffer('value') });
call.addMetadata({'key': ['value']});
}, function(err) {
return err.code === grpc.callError.ALREADY_INVOKED;
});

@ -84,6 +84,10 @@ function cancelHandler(stream) {
// do nothing
}
function metadataHandler(stream, metadata) {
stream.end();
}
/**
* Serialize a string to a Buffer
* @param {string} value The string to serialize
@ -106,11 +110,14 @@ describe('echo client', function() {
var server;
var channel;
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');
server.register('echo', echoHandler);
server.register('error', errorHandler);
server.register('cancellation', cancelHandler);
server.register('metadata', metadataHandler);
server.start();
channel = new grpc.Channel('localhost:' + port_num);
@ -142,12 +149,19 @@ describe('echo client', function() {
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) {
var stream = client.makeRequest(
channel,
'error',
null,
getDeadline(1));
var stream = client.makeRequest(channel, 'error');
stream.on('data', function() {});
stream.write(new Buffer('test'));
@ -171,6 +185,14 @@ describe('echo client', function() {
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
* and the insecure echo client test */
@ -189,7 +211,7 @@ describe('secure echo client', function() {
key_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);
server.register('echo', echoHandler);
server.start();

@ -68,18 +68,61 @@ describe('end-to-end', function() {
server.shutdown();
});
it('should start and end a request without error', function(complete) {
var done = multiDone(function() {
complete();
}, 2);
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.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,
grpc.completionType.CLIENT_METADATA_READ);
assert.strictEqual(event.data.server_key[0].toString(), 'server_value');
},function(event) {
assert.strictEqual(event.type, grpc.completionType.FINISHED);
var status = event.data;
@ -90,11 +133,14 @@ describe('end-to-end', function() {
server.requestCall(function(event) {
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;
assert.notEqual(server_call, null);
server_call.serverAccept(function(event) {
assert.strictEqual(event.type, grpc.completionType.FINISHED);
}, 0);
server_call.addMetadata({'server_key': ['server_value']});
server_call.serverEndInitialMetadata(0);
server_call.startWriteStatus(
grpc.status.OK,
@ -115,10 +161,7 @@ describe('end-to-end', function() {
it('should send and receive data without error', function(complete) {
var req_text = 'client_request';
var reply_text = 'server_response';
var done = multiDone(function() {
complete();
server.shutdown();
}, 6);
var done = multiDone(complete, 6);
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'success';

@ -71,4 +71,12 @@ describe('Interop tests', function() {
it('should pass empty_stream', function(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);
});
after(function() {
server.shutdown();
});
it('should echo inputs as responses', function(done) {
done = multiDone(done, 4);
@ -95,7 +98,6 @@ describe('echo server', function() {
var status = event.data;
assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(status.details, status_text);
server.shutdown();
done();
}, 0);
call.startWrite(

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

@ -7,31 +7,25 @@ Directory structure is as generated by the PHP utility
## ENVIRONMENT
To build a PHP environment that works with this extension, download and extract
PHP 5.5 (5.6 may also work), configure it, and install it:
Install `php5` and `php5-dev`.
```bash
apt-get install libxml2 libxml2-dev
curl http://php.net/get/php-5.5.16.tar.gz
tar -xf php-5.5.16.tar.gz
cd php-5.5.16
./configure --with-zlib=/usr --with-libxml-dir=ext/libxml --with-openssl=/usr/local/ssl
make
make install
```
To run the tests, additionally install `php5-readline` and `phpunit`.
Alternatively, build and install PHP 5.5 or later from source with standard
configuration options.
To also download and install the patched protoc and PHP code generator:
To also download and install protoc and the PHP code generator.
```bash
apt-get install -y procps
curl -sSL https://get.rvm.io | sudo bash -s stable --ruby
git clone sso://team/one-platform-grpc-team/protobuf
git clone git@github.com:google/protobuf.git
cd protobuf
./configure
make
make install
git clone sso://team/one-platform-grpc-team/grpc-php-protobuf-php
cd grpc-php-protobuf-php
git clone git@github.com:murgatroid99/Protobuf-PHP.git
cd Protobuf-PHP
rake pear:package version=1.0
pear install Protobuf-1.0.tgz
```
@ -52,5 +46,4 @@ This repo now has PHPUnit tests, which can by run by executing
There is also a generated code test (`./bin/run_gen_code_test.sh`), which tests
the stub `./tests/generated_code/math.php` against a running localhost server
serving the math service. That stub is generated from
`./tests/generated_code/math.proto` with the head of the repo
`sso://team/one-platform-grpc-team/grpc-php-protobuf-php`.
`./tests/generated_code/math.proto`.

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

@ -135,7 +135,7 @@ int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, int num_args,
metadata.key = (char *)key;
metadata.value = Z_STRVAL_P(*data);
metadata.value_length = Z_STRLEN_P(*data);
error_code = grpc_call_add_metadata(call, &metadata, 0u);
error_code = grpc_call_add_metadata_old(call, &metadata, 0u);
MAYBE_THROW_CALL_ERROR(add_metadata, error_code);
break;
case IS_ARRAY:
@ -188,8 +188,8 @@ PHP_METHOD(Call, __construct) {
wrapped_grpc_timeval *deadline =
(wrapped_grpc_timeval *)zend_object_store_get_object(
deadline_obj TSRMLS_CC);
call->wrapped = grpc_channel_create_call(channel->wrapped, method,
channel->target, deadline->wrapped);
call->wrapped = grpc_channel_create_call_old(
channel->wrapped, method, channel->target, deadline->wrapped);
}
/**
@ -252,8 +252,8 @@ PHP_METHOD(Call, invoke) {
wrapped_grpc_completion_queue *queue =
(wrapped_grpc_completion_queue *)zend_object_store_get_object(
queue_obj TSRMLS_CC);
error_code = grpc_call_invoke(call->wrapped, queue->wrapped, (void *)tag1,
(void *)tag2, (gpr_uint32)flags);
error_code = grpc_call_invoke_old(call->wrapped, queue->wrapped, (void *)tag1,
(void *)tag2, (gpr_uint32)flags);
MAYBE_THROW_CALL_ERROR(invoke, error_code);
}
@ -287,7 +287,7 @@ PHP_METHOD(Call, server_accept) {
(wrapped_grpc_completion_queue *)zend_object_store_get_object(
queue_obj TSRMLS_CC);
error_code =
grpc_call_server_accept(call->wrapped, queue->wrapped, (void *)tag);
grpc_call_server_accept_old(call->wrapped, queue->wrapped, (void *)tag);
MAYBE_THROW_CALL_ERROR(server_accept, error_code);
}
@ -303,7 +303,7 @@ PHP_METHOD(Call, server_end_initial_metadata) {
}
wrapped_grpc_call *call =
(wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC);
error_code = grpc_call_server_end_initial_metadata(call->wrapped, flags);
error_code = grpc_call_server_end_initial_metadata_old(call->wrapped, flags);
MAYBE_THROW_CALL_ERROR(server_end_initial_metadata, error_code);
}
@ -342,9 +342,9 @@ PHP_METHOD(Call, start_write) {
1 TSRMLS_CC);
return;
}
error_code = grpc_call_start_write(call->wrapped,
string_to_byte_buffer(buffer, buffer_len),
(void *)tag, (gpr_uint32)flags);
error_code = grpc_call_start_write_old(
call->wrapped, string_to_byte_buffer(buffer, buffer_len), (void *)tag,
(gpr_uint32)flags);
MAYBE_THROW_CALL_ERROR(start_write, error_code);
}
@ -372,9 +372,9 @@ PHP_METHOD(Call, start_write_status) {
"start_write_status expects a long, a string, and a long", 1 TSRMLS_CC);
return;
}
error_code =
grpc_call_start_write_status(call->wrapped, (grpc_status_code)status_code,
status_details, (void *)tag);
error_code = grpc_call_start_write_status_old(call->wrapped,
(grpc_status_code)status_code,
status_details, (void *)tag);
MAYBE_THROW_CALL_ERROR(start_write_status, error_code);
}
@ -393,7 +393,7 @@ PHP_METHOD(Call, writes_done) {
"writes_done expects a long", 1 TSRMLS_CC);
return;
}
error_code = grpc_call_writes_done(call->wrapped, (void *)tag);
error_code = grpc_call_writes_done_old(call->wrapped, (void *)tag);
MAYBE_THROW_CALL_ERROR(writes_done, error_code);
}
@ -414,7 +414,7 @@ PHP_METHOD(Call, start_read) {
"start_read expects a long", 1 TSRMLS_CC);
return;
}
error_code = grpc_call_start_read(call->wrapped, (void *)tag);
error_code = grpc_call_start_read_old(call->wrapped, (void *)tag);
MAYBE_THROW_CALL_ERROR(start_read, error_code);
}

@ -125,7 +125,7 @@ PHP_METHOD(Server, request_call) {
"request_call expects a long", 1 TSRMLS_CC);
return;
}
error_code = grpc_server_request_call(server->wrapped, (void *)tag_new);
error_code = grpc_server_request_call_old(server->wrapped, (void *)tag_new);
MAYBE_THROW_CALL_ERROR(request_call, error_code);
}

@ -44,7 +44,7 @@ abstract class AbstractSurfaceActiveCall {
protected function _read() {
$response = $this->active_call->read();
if ($response == null) {
if ($response === null) {
return null;
}
return call_user_func($this->deserialize, $response);

@ -28,9 +28,9 @@ class ActiveCall {
$this->flags = $flags;
// Invoke the call.
$this->call->start_invoke($this->completion_queue,
CLIENT_METADATA_READ,
FINISHED, 0);
$this->call->invoke($this->completion_queue,
CLIENT_METADATA_READ,
FINISHED, 0);
$metadata_event = $this->completion_queue->pluck(CLIENT_METADATA_READ,
Timeval::inf_future());
$this->metadata = $metadata_event->data;
@ -66,12 +66,7 @@ class ActiveCall {
* @param ByteBuffer $data The data to write
*/
public function write($data) {
if($this->call->start_write($data,
WRITE_ACCEPTED,
$this->flags) != OP_OK) {
// TODO(mlumish): more useful error
throw new \Exception("Cannot call write after writesDone");
}
$this->call->start_write($data, WRITE_ACCEPTED, $this->flags);
$this->completion_queue->pluck(WRITE_ACCEPTED, Timeval::inf_future());
}

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

@ -31,7 +31,7 @@ class ServerStreamingSurfaceActiveCall extends AbstractSurfaceActiveCall {
* @return An iterator of response values
*/
public function responses() {
while(($response = $this->_read()) != null) {
while(($response = $this->_read()) !== null) {
yield $response;
}
}

@ -17,9 +17,9 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
list($response, $status) = self::$client->Div($div_arg)->wait();
$this->assertEquals(1, $response->getQuotient());
$this->assertEquals(3, $response->getRemainder());
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
$this->assertSame(1, $response->getQuotient());
$this->assertSame(3, $response->getRemainder());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testServerStreaming() {
@ -31,9 +31,9 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
return $num->getNum();
};
$values = array_map($extract_num, $result_array);
$this->assertEquals([1, 1, 2, 3, 5, 8, 13], $values);
$this->assertSame([1, 1, 2, 3, 5, 8, 13], $values);
$status = $call->getStatus();
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testClientStreaming() {
@ -46,8 +46,8 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
};
$call = self::$client->Sum($num_iter());
list($response, $status) = $call->wait();
$this->assertEquals(21, $response->getNum());
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
$this->assertSame(21, $response->getNum());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testBidiStreaming() {
@ -58,11 +58,11 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
$div_arg->setDivisor(2);
$call->write($div_arg);
$response = $call->read();
$this->assertEquals($i, $response->getQuotient());
$this->assertEquals(1, $response->getRemainder());
$this->assertSame($i, $response->getQuotient());
$this->assertSame(1, $response->getRemainder());
}
$call->writesDone();
$status = $call->getStatus();
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
}

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

@ -25,9 +25,9 @@ function hardAssert($value, $error_message) {
* @param $stub Stub object that has service methods
*/
function emptyUnary($stub) {
list($result, $status) = $stub->EmptyCall(new proto2\EmptyMessage())->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result != null, 'Call completed with a null response');
list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result !== null, 'Call completed with a null response');
}
/**
@ -49,14 +49,14 @@ function largeUnary($stub) {
$request->setPayload($payload);
list($result, $status) = $stub->UnaryCall($request)->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result != null, 'Call returned a null response');
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result !== null, 'Call returned a null response');
$payload = $result->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload had the wrong type');
hardAssert(strlen($payload->getBody()) == $response_len,
hardAssert(strlen($payload->getBody()) === $response_len,
'Payload had the wrong length');
hardAssert($payload->getBody() == str_repeat("\0", $response_len),
hardAssert($payload->getBody() === str_repeat("\0", $response_len),
'Payload had the wrong content');
}
@ -78,8 +78,8 @@ function clientStreaming($stub) {
}, $request_lengths);
list($result, $status) = $stub->StreamingInputCall($requests)->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result->getAggregatedPayloadSize() == 74922,
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result->getAggregatedPayloadSize() === 74922,
'aggregated_payload_size was incorrect');
}
@ -100,15 +100,15 @@ function serverStreaming($stub) {
}
$call = $stub->StreamingOutputCall($request);
hardAssert($call->getStatus()->code == Grpc\STATUS_OK,
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully');
$i = 0;
foreach($call->responses() as $value) {
hardAssert($i < 4, 'Too many responses');
$payload = $value->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) == $sizes[$i],
hardAssert(strlen($payload->getBody()) === $sizes[$i],
'Response ' . $i . ' had the wrong length');
}
}
@ -136,16 +136,16 @@ function pingPong($stub) {
$call->write($request);
$response = $call->read();
hardAssert($response != null, 'Server returned too few responses');
hardAssert($response !== null, 'Server returned too few responses');
$payload = $response->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) == $response_lengths[$i],
hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
'Payload ' . $i . ' had the wrong length');
}
$call->writesDone();
hardAssert($call->read() == null, 'Server returned too many responses');
hardAssert($call->getStatus()->code == Grpc\STATUS_OK,
hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully');
}
@ -161,11 +161,12 @@ $server_address = $args['server_host'] . ':' . $args['server_port'];
$credentials = Grpc\Credentials::createSsl(
file_get_contents(dirname(__FILE__) . '/../data/ca.pem'));
$stub = new grpc\testing\TestServiceClient(
$server_address,
[
'grpc.ssl_target_name_override' => 'foo.test.google.com',
'credentials' => $credentials
]);
new Grpc\BaseStub(
$server_address,
[
'grpc.ssl_target_name_override' => 'foo.test.google.com',
'credentials' => $credentials
]));
echo "Connecting to $server_address\n";
echo "Running test case $args[test_case]\n";

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

Loading…
Cancel
Save