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

pull/277/head
Donna Dionne 10 years ago
commit 856b0c8b5d
  1. 6
      .gitignore
  2. 3
      INSTALL
  3. 179
      Makefile
  4. 65
      build.json
  5. 32
      examples/tips/client.cc
  6. 3
      examples/tips/client.h
  7. 54
      examples/tips/client_main.cc
  8. 2
      include/grpc/support/port_platform.h
  9. 84
      src/core/iomgr/fd_posix.c
  10. 4
      src/core/iomgr/fd_posix.h
  11. 2
      src/core/iomgr/iomgr.c
  12. 11
      src/core/iomgr/iomgr_posix.c
  13. 142
      src/core/iomgr/pollset_kick.c
  14. 10
      src/core/iomgr/pollset_kick.h
  15. 10
      src/core/iomgr/pollset_kick_posix.h
  16. 4
      src/core/iomgr/pollset_kick_windows.h
  17. 82
      src/core/iomgr/wakeup_fd_eventfd.c
  18. 53
      src/core/iomgr/wakeup_fd_nospecial.c
  19. 93
      src/core/iomgr/wakeup_fd_pipe.c
  20. 41
      src/core/iomgr/wakeup_fd_pipe.h
  21. 70
      src/core/iomgr/wakeup_fd_posix.c
  22. 102
      src/core/iomgr/wakeup_fd_posix.h
  23. 64
      src/core/json/json.c
  24. 88
      src/core/json/json.h
  25. 49
      src/core/json/json_common.h
  26. 653
      src/core/json/json_reader.c
  27. 160
      src/core/json/json_reader.h
  28. 391
      src/core/json/json_string.c
  29. 252
      src/core/json/json_writer.c
  30. 93
      src/core/json/json_writer.h
  31. 41
      src/core/security/credentials.c
  32. 93
      src/core/security/json_token.c
  33. 8
      src/core/statistics/census_tracing.h
  34. 6
      src/core/support/log_android.c
  35. 3
      src/core/surface/call.c
  36. 59
      src/node/ext/call.cc
  37. 61
      src/node/ext/event.cc
  38. 24
      src/node/test/call_test.js
  39. 59
      src/node/test/end_to_end_test.js
  40. 4
      src/node/test/server_test.js
  41. 4
      test/core/echo/client.c
  42. 16
      test/core/iomgr/poll_kick_posix_test.c
  43. 261
      test/core/json/json_rewrite.c
  44. 322
      test/core/json/json_rewrite_test.c
  45. 177
      test/core/json/json_test.c
  46. 117
      test/core/json/rewrite_test_input.json
  47. 1
      test/core/json/rewrite_test_output_condensed.json
  48. 272
      test/core/json/rewrite_test_output_indented.json
  49. 26
      test/core/security/credentials_test.c
  50. 143
      test/core/security/json_token_test.c
  51. 6
      test/core/statistics/census_log_tests.c
  52. 34
      test/cpp/interop/client.cc
  53. 20
      third_party/cJSON/LICENSE
  54. 247
      third_party/cJSON/README
  55. 596
      third_party/cJSON/cJSON.c
  56. 143
      third_party/cJSON/cJSON.h
  57. 156
      third_party/cJSON/test.c
  58. 22
      third_party/cJSON/tests/test1
  59. 11
      third_party/cJSON/tests/test2
  60. 26
      third_party/cJSON/tests/test3
  61. 27
      third_party/cJSON/tests/test5
  62. 14
      tools/dockerfile/grpc_node/Dockerfile
  63. 22
      tools/dockerfile/grpc_node_base/Dockerfile
  64. 21
      tools/gce_setup/grpc_docker.sh
  65. 2
      tools/gce_setup/shared_startup_funcs.sh
  66. 4
      tools/run_tests/run_tests.py
  67. 10
      tools/run_tests/tests.json
  68. 1
      tools/run_tests/watch_dirs.py
  69. 26
      vsprojects/vs2013/grpc.vcxproj
  70. 56
      vsprojects/vs2013/grpc.vcxproj.filters
  71. 26
      vsprojects/vs2013/grpc_unsecure.vcxproj
  72. 56
      vsprojects/vs2013/grpc_unsecure.vcxproj.filters

6
.gitignore vendored

@ -21,4 +21,8 @@ coverage
.run_tests_cache .run_tests_cache
# emacs temp files # 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 itself, notably the autoconf suite, curl, and unzip. If you have apt-get, you
can install these dependencies this way: 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: Then, you can build and install protobuf 3.0.0:
$ cd third_party/protobuf $ cd third_party/protobuf
$ ./autogen.sh
$ ./configure $ ./configure
$ make $ make
# make install # make install

File diff suppressed because one or more lines are too long

@ -61,6 +61,12 @@
"src/core/iomgr/tcp_posix.h", "src/core/iomgr/tcp_posix.h",
"src/core/iomgr/tcp_server.h", "src/core/iomgr/tcp_server.h",
"src/core/iomgr/time_averaged_stats.h", "src/core/iomgr/time_averaged_stats.h",
"src/core/iomgr/wakeup_fd_posix.h",
"src/core/iomgr/wakeup_fd_pipe.h",
"src/core/json/json.h",
"src/core/json/json_common.h",
"src/core/json/json_reader.h",
"src/core/json/json_writer.h",
"src/core/statistics/census_interface.h", "src/core/statistics/census_interface.h",
"src/core/statistics/census_log.h", "src/core/statistics/census_log.h",
"src/core/statistics/census_rpc_stats.h", "src/core/statistics/census_rpc_stats.h",
@ -125,7 +131,7 @@
"src/core/iomgr/fd_posix.c", "src/core/iomgr/fd_posix.c",
"src/core/iomgr/iomgr.c", "src/core/iomgr/iomgr.c",
"src/core/iomgr/iomgr_posix.c", "src/core/iomgr/iomgr_posix.c",
"src/core/iomgr/pollset_kick_posix.c", "src/core/iomgr/pollset_kick.c",
"src/core/iomgr/pollset_multipoller_with_poll_posix.c", "src/core/iomgr/pollset_multipoller_with_poll_posix.c",
"src/core/iomgr/pollset_posix.c", "src/core/iomgr/pollset_posix.c",
"src/core/iomgr/pollset_windows.c", "src/core/iomgr/pollset_windows.c",
@ -138,6 +144,14 @@
"src/core/iomgr/tcp_posix.c", "src/core/iomgr/tcp_posix.c",
"src/core/iomgr/tcp_server_posix.c", "src/core/iomgr/tcp_server_posix.c",
"src/core/iomgr/time_averaged_stats.c", "src/core/iomgr/time_averaged_stats.c",
"src/core/iomgr/wakeup_fd_eventfd.c",
"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_init.c",
"src/core/statistics/census_log.c", "src/core/statistics/census_log.c",
"src/core/statistics/census_rpc_stats.c", "src/core/statistics/census_rpc_stats.c",
@ -178,8 +192,7 @@
"src/core/transport/chttp2_transport.c", "src/core/transport/chttp2_transport.c",
"src/core/transport/metadata.c", "src/core/transport/metadata.c",
"src/core/transport/stream_op.c", "src/core/transport/stream_op.c",
"src/core/transport/transport.c", "src/core/transport/transport.c"
"third_party/cJSON/cJSON.c"
] ]
} }
], ],
@ -1262,11 +1275,11 @@
] ]
}, },
{ {
"name": "poll_kick_test", "name": "poll_kick_posix_test",
"build": "test", "build": "test",
"language": "c", "language": "c",
"src": [ "src": [
"test/core/iomgr/poll_kick_test.c" "test/core/iomgr/poll_kick_posix_test.c"
], ],
"deps": [ "deps": [
"grpc_test_util", "grpc_test_util",
@ -1415,6 +1428,48 @@
"gpr" "gpr"
] ]
}, },
{
"name": "json_test",
"build": "test",
"language": "c",
"src": [
"test/core/json/json_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"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": "channel_arguments_test", "name": "channel_arguments_test",
"build": "test", "build": "test",

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

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

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

@ -68,6 +68,8 @@
#define GPR_GCC_ATOMIC 1 #define GPR_GCC_ATOMIC 1
#define GPR_LINUX 1 #define GPR_LINUX 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_HAS_SPECIAL_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1 #define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1 #define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_STRING 1 #define GPR_POSIX_STRING 1

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

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

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

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

@ -34,98 +34,74 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET #ifdef GPR_POSIX_SOCKET
#include "src/core/iomgr/pollset_kick.h"
#include "src/core/iomgr/pollset_kick_posix.h"
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "src/core/iomgr/socket_utils_posix.h" #include "src/core/iomgr/socket_utils_posix.h"
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
/* This implementation is based on a freelist of pipes. */ /* This implementation is based on a freelist of wakeup fds, with extra logic to
* handle kicks while there is no attached fd. */
#define GRPC_MAX_CACHED_PIPES 50
#define GRPC_PIPE_LOW_WATERMARK 25
typedef struct grpc_kick_pipe_info { #define GRPC_MAX_CACHED_WFDS 50
int pipe_read_fd; #define GRPC_WFD_LOW_WATERMARK 25
int pipe_write_fd;
struct grpc_kick_pipe_info *next;
} grpc_kick_pipe_info;
static grpc_kick_pipe_info *pipe_freelist = NULL; static grpc_kick_fd_info *fd_freelist = NULL;
static int pipe_freelist_count = 0; static int fd_freelist_count = 0;
static gpr_mu pipe_freelist_mu; static gpr_mu fd_freelist_mu;
static grpc_kick_pipe_info *allocate_pipe(void) { static grpc_kick_fd_info *allocate_wfd(void) {
grpc_kick_pipe_info *info; grpc_kick_fd_info *info;
gpr_mu_lock(&pipe_freelist_mu); gpr_mu_lock(&fd_freelist_mu);
if (pipe_freelist != NULL) { if (fd_freelist != NULL) {
info = pipe_freelist; info = fd_freelist;
pipe_freelist = pipe_freelist->next; fd_freelist = fd_freelist->next;
--pipe_freelist_count; --fd_freelist_count;
} else { } else {
int pipefd[2];
/* TODO(klempner): Make this nonfatal */
GPR_ASSERT(0 == pipe(pipefd));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1));
info = gpr_malloc(sizeof(*info)); info = gpr_malloc(sizeof(*info));
info->pipe_read_fd = pipefd[0]; grpc_wakeup_fd_create(&info->wakeup_fd);
info->pipe_write_fd = pipefd[1];
info->next = NULL; info->next = NULL;
} }
gpr_mu_unlock(&pipe_freelist_mu); gpr_mu_unlock(&fd_freelist_mu);
return info; return info;
} }
static void destroy_pipe(void) { static void destroy_wfd(void) {
/* assumes pipe_freelist_mu is held */ /* assumes fd_freelist_mu is held */
grpc_kick_pipe_info *current = pipe_freelist; grpc_kick_fd_info *current = fd_freelist;
pipe_freelist = pipe_freelist->next; fd_freelist = fd_freelist->next;
pipe_freelist_count--; fd_freelist_count--;
close(current->pipe_read_fd); grpc_wakeup_fd_destroy(&current->wakeup_fd);
close(current->pipe_write_fd);
gpr_free(current); gpr_free(current);
} }
static void free_pipe(grpc_kick_pipe_info *pipe_info) { static void free_wfd(grpc_kick_fd_info *fd_info) {
gpr_mu_lock(&pipe_freelist_mu); gpr_mu_lock(&fd_freelist_mu);
pipe_info->next = pipe_freelist; fd_info->next = fd_freelist;
pipe_freelist = pipe_info; fd_freelist = fd_info;
pipe_freelist_count++; fd_freelist_count++;
if (pipe_freelist_count > GRPC_MAX_CACHED_PIPES) { if (fd_freelist_count > GRPC_MAX_CACHED_WFDS) {
while (pipe_freelist_count > GRPC_PIPE_LOW_WATERMARK) { while (fd_freelist_count > GRPC_WFD_LOW_WATERMARK) {
destroy_pipe(); destroy_wfd();
} }
} }
gpr_mu_unlock(&pipe_freelist_mu); gpr_mu_unlock(&fd_freelist_mu);
}
void grpc_pollset_kick_global_init() {
pipe_freelist = NULL;
gpr_mu_init(&pipe_freelist_mu);
}
void grpc_pollset_kick_global_destroy() {
while (pipe_freelist != NULL) {
destroy_pipe();
}
gpr_mu_destroy(&pipe_freelist_mu);
} }
void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state) { void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state) {
gpr_mu_init(&kick_state->mu); gpr_mu_init(&kick_state->mu);
kick_state->kicked = 0; kick_state->kicked = 0;
kick_state->pipe_info = NULL; kick_state->fd_info = NULL;
} }
void grpc_pollset_kick_destroy(grpc_pollset_kick_state *kick_state) { void grpc_pollset_kick_destroy(grpc_pollset_kick_state *kick_state) {
gpr_mu_destroy(&kick_state->mu); gpr_mu_destroy(&kick_state->mu);
GPR_ASSERT(kick_state->pipe_info == NULL); GPR_ASSERT(kick_state->fd_info == NULL);
} }
int grpc_pollset_kick_pre_poll(grpc_pollset_kick_state *kick_state) { int grpc_pollset_kick_pre_poll(grpc_pollset_kick_state *kick_state) {
@ -135,49 +111,43 @@ int grpc_pollset_kick_pre_poll(grpc_pollset_kick_state *kick_state) {
gpr_mu_unlock(&kick_state->mu); gpr_mu_unlock(&kick_state->mu);
return -1; return -1;
} }
kick_state->pipe_info = allocate_pipe(); kick_state->fd_info = allocate_wfd();
gpr_mu_unlock(&kick_state->mu); gpr_mu_unlock(&kick_state->mu);
return kick_state->pipe_info->pipe_read_fd; return GRPC_WAKEUP_FD_GET_READ_FD(&kick_state->fd_info->wakeup_fd);
} }
void grpc_pollset_kick_consume(grpc_pollset_kick_state *kick_state) { void grpc_pollset_kick_consume(grpc_pollset_kick_state *kick_state) {
char buf[128]; grpc_wakeup_fd_consume_wakeup(&kick_state->fd_info->wakeup_fd);
int r;
for (;;) {
r = read(kick_state->pipe_info->pipe_read_fd, buf, sizeof(buf));
if (r > 0) continue;
if (r == 0) return;
switch (errno) {
case EAGAIN:
return;
case EINTR:
continue;
default:
gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno));
return;
}
}
} }
void grpc_pollset_kick_post_poll(grpc_pollset_kick_state *kick_state) { void grpc_pollset_kick_post_poll(grpc_pollset_kick_state *kick_state) {
gpr_mu_lock(&kick_state->mu); gpr_mu_lock(&kick_state->mu);
free_pipe(kick_state->pipe_info); free_wfd(kick_state->fd_info);
kick_state->pipe_info = NULL; kick_state->fd_info = NULL;
gpr_mu_unlock(&kick_state->mu); gpr_mu_unlock(&kick_state->mu);
} }
void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) { void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) {
gpr_mu_lock(&kick_state->mu); gpr_mu_lock(&kick_state->mu);
if (kick_state->pipe_info != NULL) { if (kick_state->fd_info != NULL) {
char c = 0; grpc_wakeup_fd_wakeup(&kick_state->fd_info->wakeup_fd);
while (write(kick_state->pipe_info->pipe_write_fd, &c, 1) != 1 &&
errno == EINTR)
;
} else { } else {
kick_state->kicked = 1; kick_state->kicked = 1;
} }
gpr_mu_unlock(&kick_state->mu); gpr_mu_unlock(&kick_state->mu);
} }
#endif void grpc_pollset_kick_global_init_fallback_fd(void) {
grpc_wakeup_fd_global_init_force_fallback();
}
void grpc_pollset_kick_global_init(void) {
grpc_wakeup_fd_global_init();
}
void grpc_pollset_kick_global_destroy(void) {
grpc_wakeup_fd_global_destroy();
}
#endif /* GPR_POSIX_SOCKET */

@ -36,9 +36,6 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
/* This is an abstraction around the typical pipe mechanism for waking up a
thread sitting in a poll() style call. */
#ifdef GPR_POSIX_SOCKET #ifdef GPR_POSIX_SOCKET
#include "src/core/iomgr/pollset_kick_posix.h" #include "src/core/iomgr/pollset_kick_posix.h"
#endif #endif
@ -47,12 +44,19 @@
#include "src/core/iomgr/pollset_kick_windows.h" #include "src/core/iomgr/pollset_kick_windows.h"
#endif #endif
/* This is an abstraction around the typical pipe mechanism for waking up a
thread sitting in a poll() style call. */
void grpc_pollset_kick_global_init(void); void grpc_pollset_kick_global_init(void);
void grpc_pollset_kick_global_destroy(void); void grpc_pollset_kick_global_destroy(void);
void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state); void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state);
void grpc_pollset_kick_destroy(grpc_pollset_kick_state *kick_state); void grpc_pollset_kick_destroy(grpc_pollset_kick_state *kick_state);
/* Guarantees a pure posix implementation rather than a specialized one, if
* applicable. Intended for testing. */
void grpc_pollset_kick_global_init_fallback_fd(void);
/* Must be called before entering poll(). If return value is -1, this consumed /* Must be called before entering poll(). If return value is -1, this consumed
an existing kick. Otherwise the return value is an FD to add to the poll set. an existing kick. Otherwise the return value is an FD to add to the poll set.
*/ */

@ -34,14 +34,18 @@
#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_ #ifndef __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_
#define __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_ #define __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
struct grpc_kick_pipe_info; typedef struct grpc_kick_fd_info {
grpc_wakeup_fd_info wakeup_fd;
struct grpc_kick_fd_info *next;
} grpc_kick_fd_info;
typedef struct grpc_pollset_kick_state { typedef struct grpc_pollset_kick_state {
gpr_mu mu; gpr_mu mu;
int kicked; int kicked;
struct grpc_kick_pipe_info *pipe_info; struct grpc_kick_fd_info *fd_info;
} grpc_pollset_kick_state; } grpc_pollset_kick_state;
#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_ */ #endif /* __GRPC_INTERNALIOMGR_POLLSET_KICK_POSIX_H_ */

@ -36,10 +36,10 @@
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
struct grpc_kick_pipe_info; struct grpc_kick_fd_info;
typedef struct grpc_pollset_kick_state { typedef struct grpc_pollset_kick_state {
int unused; int unused;
} grpc_pollset_kick_state; } grpc_pollset_kick_state;
#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_ */ #endif /* __GRPC_INTERNALIOMGR_POLLSET_KICK_WINDOWS_H_ */

@ -0,0 +1,82 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX_EVENTFD
#include <errno.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <grpc/support/log.h>
static void eventfd_create(grpc_wakeup_fd_info *fd_info) {
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
/* TODO(klempner): Handle failure more gracefully */
GPR_ASSERT(efd >= 0);
fd_info->read_fd = efd;
fd_info->write_fd = -1;
}
static void eventfd_consume(grpc_wakeup_fd_info *fd_info) {
eventfd_t value;
int err;
do {
err = eventfd_read(fd_info->read_fd, &value);
} while (err < 0 && errno == EINTR);
}
static void eventfd_wakeup(grpc_wakeup_fd_info *fd_info) {
int err;
do {
err = eventfd_write(fd_info->read_fd, 1);
} while (err < 0 && errno == EINTR);
}
static void eventfd_destroy(grpc_wakeup_fd_info *fd_info) {
close(fd_info->read_fd);
}
static int eventfd_check_availability(void) {
/* TODO(klempner): Actually check if eventfd is available */
return 1;
}
const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable = {
eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy,
eventfd_check_availability
};
#endif /* GPR_LINUX_EVENTFD */

@ -0,0 +1,53 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* This is a dummy file to provide an invalid specialized_wakeup_fd_vtable on
* systems without anything better than pipe.
*/
#include <grpc/support/port_platform.h>
#ifndef GPR_POSIX_HAS_SPECIAL_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd.h"
static int check_availability_invalid(void) {
return 0;
}
const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable = {
NULL, NULL, NULL, NULL, check_availability_invalid
};
#endif /* GPR_POSIX_HAS_SPECIAL_WAKEUP */

@ -0,0 +1,93 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* TODO(klempner): Allow this code to be disabled. */
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "src/core/iomgr/socket_utils_posix.h"
#include <grpc/support/log.h>
static void pipe_create(grpc_wakeup_fd_info *fd_info) {
int pipefd[2];
/* TODO(klempner): Make this nonfatal */
GPR_ASSERT(0 == pipe(pipefd));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1));
fd_info->read_fd = pipefd[0];
fd_info->write_fd = pipefd[1];
}
static void pipe_consume(grpc_wakeup_fd_info *fd_info) {
char buf[128];
int r;
for (;;) {
r = read(fd_info->read_fd, buf, sizeof(buf));
if (r > 0) continue;
if (r == 0) return;
switch (errno) {
case EAGAIN:
return;
case EINTR:
continue;
default:
gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno));
return;
}
}
}
static void pipe_wakeup(grpc_wakeup_fd_info *fd_info) {
char c = 0;
while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR)
;
}
static void pipe_destroy(grpc_wakeup_fd_info *fd_info) {
close(fd_info->read_fd);
close(fd_info->write_fd);
}
static int pipe_check_availability(void) {
/* Assume that pipes are always available. */
return 1;
}
const grpc_wakeup_fd_vtable pipe_wakeup_fd_vtable = {
pipe_create, pipe_consume, pipe_wakeup, pipe_destroy, pipe_check_availability
};

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

@ -0,0 +1,70 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/iomgr/wakeup_fd_posix.h"
#include "src/core/iomgr/wakeup_fd_pipe.h"
#include <stddef.h>
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;
} else {
wakeup_fd_vtable = &pipe_wakeup_fd_vtable;
}
}
void grpc_wakeup_fd_global_init_force_fallback(void) {
wakeup_fd_vtable = &pipe_wakeup_fd_vtable;
}
void grpc_wakeup_fd_global_destroy(void) {
wakeup_fd_vtable = NULL;
}
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->create(fd_info);
}
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->consume(fd_info);
}
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->wakeup(fd_info);
}
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->destroy(fd_info);
}

@ -0,0 +1,102 @@
/*
*
* 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.
*
*/
/*
* wakeup_fd abstracts the concept of a file descriptor for the purpose of
* waking up a thread in select()/poll()/epoll_wait()/etc.
* The poll() family of system calls provide a way for a thread to block until
* there is activity on one (or more) of a set of file descriptors. An
* application may wish to wake up this thread to do non file related work. The
* typical way to do this is to add a pipe to the set of file descriptors, then
* write to the pipe to wake up the thread in poll().
*
* Linux has a lighter weight eventfd specifically designed for this purpose.
* wakeup_fd abstracts the difference between the two.
*
* Setup:
* 1. Before calling anything, call global_init() at least once.
* 1. Call grpc_wakeup_fd_create() to get a wakeup_fd.
* 2. Add the result of GRPC_WAKEUP_FD_FD to the set of monitored file
* descriptors for the poll() style API you are using. Monitor the file
* descriptor for readability.
* 3. To tear down, call grpc_wakeup_fd_destroy(). This closes the underlying
* file descriptor.
*
* Usage:
* 1. To wake up a polling thread, call grpc_wakeup_fd_wakeup() on a wakeup_fd
* it is monitoring.
* 2. If the polling thread was awakened by a wakeup_fd event, call
* grpc_wakeup_fd_consume_wakeup() on it.
*/
#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_vtable {
void (*create)(grpc_wakeup_fd_info *fd_info);
void (*consume)(grpc_wakeup_fd_info *fd_info);
void (*wakeup)(grpc_wakeup_fd_info *fd_info);
void (*destroy)(grpc_wakeup_fd_info *fd_info);
/* Must be called before calling any other functions */
int (*check_availability)(void);
} grpc_wakeup_fd_vtable;
/* 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;
#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[] =
" "
" "
" "
" ";
int 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__ */

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

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

@ -34,6 +34,10 @@
#ifndef __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ #ifndef __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
#define __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ #define __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Opaque structure for trace object */ /* Opaque structure for trace object */
typedef struct trace_obj trace_obj; 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. */ /* Gets method tag name associated with the input trace object. */
const char* census_get_trace_method_name(const trace_obj* trace); const char* census_get_trace_method_name(const trace_obj* trace);
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ */ #endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ */

@ -72,17 +72,15 @@ void gpr_default_log(gpr_log_func_args *args) {
final_slash = strrchr(args->file, '/'); final_slash = strrchr(args->file, '/');
if (final_slash == NULL) if (final_slash == NULL)
display_file = file; display_file = args->file;
else else
display_file = final_slash + 1; display_file = final_slash + 1;
asprintf(&prefix, "%s:%d] %s", display_file, args->line, args->message); asprintf(&output, "%s:%d] %s", display_file, args->line, args->message);
__android_log_write(severity_to_log_priority(args->severity), "GRPC", output); __android_log_write(severity_to_log_priority(args->severity), "GRPC", output);
/* allocated by asprintf => use free, not gpr_free */ /* allocated by asprintf => use free, not gpr_free */
free(prefix);
free(suffix);
free(output); free(output);
} }

@ -880,8 +880,7 @@ void grpc_call_recv_metadata(grpc_call_element *elem, grpc_call_op *op) {
grpc_call *call = CALL_FROM_TOP_ELEM(elem); grpc_call *call = CALL_FROM_TOP_ELEM(elem);
grpc_mdelem *md = op->data.metadata; grpc_mdelem *md = op->data.metadata;
grpc_mdstr *key = md->key; grpc_mdstr *key = md->key;
gpr_log(GPR_DEBUG, "call %p got metadata %s %s", call,
grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value));
if (key == grpc_channel_get_status_string(call->channel)) { if (key == grpc_channel_get_status_string(call->channel)) {
maybe_set_status_code(call, decode_status(md)); maybe_set_status_code(call, decode_status(md));
grpc_mdelem_unref(md); grpc_mdelem_unref(md);

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

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

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

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

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

@ -78,7 +78,9 @@ int main(int argc, char **argv) {
GPR_ASSERT(argc == 2); GPR_ASSERT(argc == 2);
channel = grpc_channel_create(argv[1], NULL); channel = grpc_channel_create(argv[1], NULL);
call = grpc_channel_create_call(channel, "/foo", "localhost", gpr_inf_future); call = grpc_channel_create_call(channel, "/foo", "localhost",
gpr_time_add(gpr_time_from_seconds(5),
gpr_now()));
GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, 0) == GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, 0) ==
GRPC_CALL_OK); GRPC_CALL_OK);

@ -107,17 +107,23 @@ static void test_over_free(void) {
} }
} }
int main(int argc, char **argv) { static void run_tests(void) {
grpc_test_init(argc, argv);
grpc_pollset_kick_global_init();
test_allocation(); test_allocation();
test_basic_kick(); test_basic_kick();
test_non_poll_kick(); test_non_poll_kick();
test_non_kick(); test_non_kick();
test_over_free(); test_over_free();
}
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
grpc_pollset_kick_global_init();
run_tests();
grpc_pollset_kick_global_destroy();
grpc_pollset_kick_global_init_fallback_fd();
run_tests();
grpc_pollset_kick_global_destroy(); grpc_pollset_kick_global_destroy();
return 0; return 0;
} }

@ -0,0 +1,261 @@
/*
*
* 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 <stdio.h>
#include <stdlib.h>
#include <grpc/support/cmdline.h>
#include <grpc/support/alloc.h>
#include "src/core/json/json_reader.h"
#include "src/core/json/json_writer.h"
typedef struct json_writer_userdata {
FILE* out;
} json_writer_userdata;
typedef struct stacked_container {
grpc_json_type type;
struct stacked_container* next;
} stacked_container;
typedef struct json_reader_userdata {
FILE* in;
grpc_json_writer* writer;
char* scratchpad;
char* ptr;
size_t free_space;
size_t allocated;
size_t string_len;
stacked_container* top;
} json_reader_userdata;
static void json_writer_output_char(void* userdata, char c) {
json_writer_userdata* state = userdata;
fputc(c, state->out);
}
static void json_writer_output_string(void* userdata, const char* str) {
json_writer_userdata* state = userdata;
fputs(str, state->out);
}
static void json_writer_output_string_with_len(void* userdata, const char* str,
size_t len) {
json_writer_userdata* state = userdata;
fwrite(str, len, 1, state->out);
}
grpc_json_writer_vtable writer_vtable = {
json_writer_output_char,
json_writer_output_string,
json_writer_output_string_with_len
};
static void check_string(json_reader_userdata* state, size_t needed) {
if (state->free_space >= needed) return;
needed -= state->free_space;
needed = (needed + 0xff) & ~0xff;
state->scratchpad = gpr_realloc(state->scratchpad, state->allocated + needed);
state->free_space += needed;
state->allocated += needed;
}
static void json_reader_string_clear(void* userdata) {
json_reader_userdata* state = userdata;
state->free_space = state->allocated;
state->string_len = 0;
}
static void json_reader_string_add_char(void* userdata, gpr_uint32 c) {
json_reader_userdata* state = userdata;
check_string(state, 1);
state->scratchpad[state->string_len++] = c;
}
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);
}
}
static gpr_uint32 json_reader_read_char(void* userdata) {
gpr_uint32 r;
json_reader_userdata* state = userdata;
r = fgetc(state->in);
if (r == EOF) r = GRPC_JSON_READ_CHAR_EOF;
return r;
}
static void json_reader_container_begins(void* userdata, grpc_json_type type) {
json_reader_userdata* state = userdata;
stacked_container* container = gpr_malloc(sizeof(stacked_container));
container->type = type;
container->next = state->top;
state->top = container;
grpc_json_writer_container_begins(state->writer, type);
}
static grpc_json_type json_reader_container_ends(void* userdata) {
json_reader_userdata* state = userdata;
stacked_container* container = state->top;
grpc_json_writer_container_ends(state->writer, container->type);
state->top = container->next;
gpr_free(container);
return state->top ? state->top->type : GRPC_JSON_TOP_LEVEL;
}
static void json_reader_set_key(void* userdata) {
json_reader_userdata* state = userdata;
json_reader_string_add_char(userdata, 0);
grpc_json_writer_object_key(state->writer, state->scratchpad);
}
static void json_reader_set_string(void* userdata) {
json_reader_userdata* state = userdata;
json_reader_string_add_char(userdata, 0);
grpc_json_writer_value_string(state->writer, state->scratchpad);
}
static int json_reader_set_number(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, state->scratchpad,
state->string_len);
return 1;
}
static void json_reader_set_true(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, "true", 4);
}
static void json_reader_set_false(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, "false", 5);
}
static void json_reader_set_null(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, "null", 4);
}
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
};
int rewrite(FILE* in, FILE* out, int indent) {
grpc_json_writer writer;
grpc_json_reader reader;
grpc_json_reader_status status;
json_writer_userdata writer_user;
json_reader_userdata reader_user;
reader_user.writer = &writer;
reader_user.in = in;
reader_user.top = NULL;
reader_user.scratchpad = NULL;
reader_user.string_len = 0;
reader_user.free_space = 0;
reader_user.allocated = 0;
writer_user.out = out;
grpc_json_writer_init(&writer, indent, &writer_vtable, &writer_user);
grpc_json_reader_init(&reader, &reader_vtable, &reader_user);
status = grpc_json_reader_run(&reader);
free(reader_user.scratchpad);
while (reader_user.top) {
stacked_container* container = reader_user.top;
reader_user.top = container->next;
free(container);
}
return status == GRPC_JSON_DONE;
}
int main(int argc, char** argv) {
int indent = 2;
gpr_cmdline* cl;
cl = gpr_cmdline_create(NULL);
gpr_cmdline_add_int(cl, "indent", NULL, &indent);
gpr_cmdline_parse(cl, argc, argv);
gpr_cmdline_destroy(cl);
return rewrite(stdin, stdout, indent) ? 0 : 1;
}

@ -0,0 +1,322 @@
/*
*
* 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 <stdio.h>
#include <stdlib.h>
#include <grpc/support/alloc.h>
#include <grpc/support/useful.h>
#include <grpc/support/log.h>
#include "test/core/util/test_config.h"
#include "src/core/json/json_reader.h"
#include "src/core/json/json_writer.h"
typedef struct json_writer_userdata {
FILE* cmp;
} json_writer_userdata;
typedef struct stacked_container {
grpc_json_type type;
struct stacked_container* next;
} stacked_container;
typedef struct json_reader_userdata {
FILE* in;
grpc_json_writer* writer;
char* scratchpad;
char* ptr;
size_t free_space;
size_t allocated;
size_t string_len;
stacked_container* top;
int did_eagain;
} json_reader_userdata;
static void json_writer_output_char(void* userdata, char c) {
json_writer_userdata* state = userdata;
int cmp = fgetc(state->cmp);
GPR_ASSERT(cmp == c);
}
static void json_writer_output_string(void* userdata, const char* str) {
while (*str) {
json_writer_output_char(userdata, *str++);
}
}
static void json_writer_output_string_with_len(void* userdata, const char* str,
size_t len) {
size_t i;
for (i = 0; i < len; i++) {
json_writer_output_char(userdata, str[i]);
}
}
grpc_json_writer_vtable writer_vtable = {
json_writer_output_char,
json_writer_output_string,
json_writer_output_string_with_len
};
static void check_string(json_reader_userdata* state, size_t needed) {
if (state->free_space >= needed) return;
needed -= state->free_space;
needed = (needed + 0xff) & ~0xff;
state->scratchpad = gpr_realloc(state->scratchpad, state->allocated + needed);
state->free_space += needed;
state->allocated += needed;
}
static void json_reader_string_clear(void* userdata) {
json_reader_userdata* state = userdata;
state->free_space = state->allocated;
state->string_len = 0;
}
static void json_reader_string_add_char(void* userdata, gpr_uint32 c) {
json_reader_userdata* state = userdata;
check_string(state, 1);
state->scratchpad[state->string_len++] = c;
}
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);
}
}
static gpr_uint32 json_reader_read_char(void* userdata) {
gpr_uint32 r;
json_reader_userdata* state = userdata;
if (!state->did_eagain) {
state->did_eagain = 1;
return GRPC_JSON_READ_CHAR_EAGAIN;
}
state->did_eagain = 0;
r = fgetc(state->in);
if (r == EOF) r = GRPC_JSON_READ_CHAR_EOF;
return r;
}
static void json_reader_container_begins(void* userdata, grpc_json_type type) {
json_reader_userdata* state = userdata;
stacked_container* container = gpr_malloc(sizeof(stacked_container));
container->type = type;
container->next = state->top;
state->top = container;
grpc_json_writer_container_begins(state->writer, type);
}
static grpc_json_type json_reader_container_ends(void* userdata) {
json_reader_userdata* state = userdata;
stacked_container* container = state->top;
grpc_json_writer_container_ends(state->writer, container->type);
state->top = container->next;
gpr_free(container);
return state->top ? state->top->type : GRPC_JSON_TOP_LEVEL;
}
static void json_reader_set_key(void* userdata) {
json_reader_userdata* state = userdata;
json_reader_string_add_char(userdata, 0);
grpc_json_writer_object_key(state->writer, state->scratchpad);
}
static void json_reader_set_string(void* userdata) {
json_reader_userdata* state = userdata;
json_reader_string_add_char(userdata, 0);
grpc_json_writer_value_string(state->writer, state->scratchpad);
}
static int json_reader_set_number(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, state->scratchpad,
state->string_len);
return 1;
}
static void json_reader_set_true(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, "true", 4);
}
static void json_reader_set_false(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, "false", 5);
}
static void json_reader_set_null(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json_writer_value_raw_with_len(state->writer, "null", 4);
}
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
};
int rewrite_and_compare(FILE* in, FILE* cmp, int indent) {
grpc_json_writer writer;
grpc_json_reader reader;
grpc_json_reader_status status;
json_writer_userdata writer_user;
json_reader_userdata reader_user;
GPR_ASSERT(in);
GPR_ASSERT(cmp);
reader_user.writer = &writer;
reader_user.in = in;
reader_user.top = NULL;
reader_user.scratchpad = NULL;
reader_user.string_len = 0;
reader_user.free_space = 0;
reader_user.allocated = 0;
reader_user.did_eagain = 0;
writer_user.cmp = cmp;
grpc_json_writer_init(&writer, indent, &writer_vtable, &writer_user);
grpc_json_reader_init(&reader, &reader_vtable, &reader_user);
do {
status = grpc_json_reader_run(&reader);
} while (status == GRPC_JSON_EAGAIN);
free(reader_user.scratchpad);
while (reader_user.top) {
stacked_container* container = reader_user.top;
reader_user.top = container->next;
free(container);
}
return status == GRPC_JSON_DONE;
}
typedef struct test_file {
const char* input;
const char* cmp;
int indent;
} test_file;
static test_file test_files[] = {
{
"test/core/json/rewrite_test_input.json",
"test/core/json/rewrite_test_output_condensed.json",
0
},
{
"test/core/json/rewrite_test_input.json",
"test/core/json/rewrite_test_output_indented.json",
2
},
{
"test/core/json/rewrite_test_output_indented.json",
"test/core/json/rewrite_test_output_condensed.json",
0
},
{
"test/core/json/rewrite_test_output_condensed.json",
"test/core/json/rewrite_test_output_indented.json",
2
},
};
void test_rewrites() {
int i;
for (i = 0; i < GPR_ARRAY_SIZE(test_files); i++) {
test_file* test = test_files + i;
FILE* input = fopen(test->input, "rb");
FILE* cmp = fopen(test->cmp, "rb");
int status;
gpr_log(GPR_INFO, "Testing file %s against %s using indent=%i",
test->input, test->cmp, test->indent);
status = rewrite_and_compare(input, cmp, test->indent);
GPR_ASSERT(status);
fclose(input);
fclose(cmp);
}
}
int main(int argc, char** argv) {
grpc_test_init(argc, argv);
test_rewrites();
gpr_log(GPR_INFO, "json_rewrite_test success");
return 0;
}

@ -0,0 +1,177 @@
/*
*
* 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 <grpc/support/useful.h>
#include <grpc/support/log.h>
#include "src/core/json/json.h"
#include "src/core/support/string.h"
#include "test/core/util/test_config.h"
typedef struct testing_pair {
const char* input;
const char* output;
} testing_pair;
static testing_pair testing_pairs[] = {
/* Testing valid parsing. */
/* Testing trivial parses, with de-indentation. */
{ " 0 ", "0" },
{ " 1 ", "1" },
{ " \"a\" ", "\"a\"" },
{ " true ", "true" },
/* Testing the parser's ability to decode trivial UTF-16. */
{ "\"\\u0020\\\\\\u0010\\u000a\\u000D\"", "\" \\\\\\u0010\\n\\r\"" },
/* Testing various UTF-8 sequences. */
{ "\"ßâñć௵⇒\"", "\"\\u00df\\u00e2\\u00f1\\u0107\\u0bf5\\u21d2\"" },
{ "\"\\u00df\\u00e2\\u00f1\\u0107\\u0bf5\\u21d2\"", "\"\\u00df\\u00e2\\u00f1\\u0107\\u0bf5\\u21d2\"" },
/* Testing UTF-8 character "𝄞", U+11D1E. */
{ "\"\xf0\x9d\x84\x9e\"", "\"\\ud834\\udd1e\"" },
{ "\"\\ud834\\udd1e\"", "\"\\ud834\\udd1e\"" },
/* Testing nested empty containers. */
{ " [ [ ] , { } , [ ] ] ", "[[],{},[]]", },
/* Testing escapes and control chars in key strings. */
{ " { \"\\n\\\\a , b\": 1, \"\": 0 } ", "{\"\\n\\\\a , b\":1,\"\":0}" },
/* Testing the writer's ability to cut off invalid UTF-8 sequences. */
{ "\"abc\xf0\x9d\x24\"", "\"abc\"" },
{ "\"\xff\"", "\"\"" },
/* Testing valid number parsing. */
{ "[0, 42 , 0.0123, 123.456]", "[0,42,0.0123,123.456]"},
{ "[1e4,-53.235e-31, 0.3e+3]", "[1e4,-53.235e-31,0.3e+3]" },
/* Testing keywords parsing. */
{ "[true, false, null]", "[true,false,null]" },
/* Testing invalid parsing. */
/* Testing plain invalid things, exercising the state machine. */
{ "\\", NULL },
{ "nu ll", NULL },
{ "fals", NULL },
/* Testing unterminated string. */
{ "\"\\x", NULL },
/* Testing invalid UTF-16 number. */
{ "\"\\u123x", NULL },
/* Testing imbalanced surrogate pairs. */
{ "\"\\ud834f", NULL },
{ "\"\\ud834\\n", NULL },
{ "\"\\udd1ef", NULL },
{ "\"\\ud834\\ud834\"", NULL },
{ "\"\\ud834\\u1234\"", NULL },
/* Testing embedded invalid whitechars. */
{ "\"\n\"", NULL },
{ "\"\t\"", NULL },
/* Testing empty json data. */
{ "", NULL },
/* Testing extra characters after end of parsing. */
{ "{},", NULL },
/* Testing imbalanced containers. */
{ "{}}", NULL },
{ "[]]", NULL },
{ "{{}", NULL },
{ "[[]", NULL },
{ "[}", NULL },
{ "{]", NULL },
/*Testing trailing comma. */
{ "{,}", NULL },
{ "[1,2,3,4,]", NULL },
/* Testing having a key syntax in an array. */
{ "[\"x\":0]", NULL },
/* Testing invalid numbers. */
{ "1.", NULL },
{ "1e", NULL },
{ ".12", NULL },
{ "1.x", NULL },
{ "1.12x", NULL },
{ "1ex", NULL },
{ "1e12x", NULL },
{ ".12x", NULL },
{ "000", NULL },
};
static void test_pairs() {
int i;
for (i = 0; i < GPR_ARRAY_SIZE(testing_pairs); i++) {
testing_pair* pair = testing_pairs + i;
char* scratchpad = gpr_strdup(pair->input);
grpc_json* json;
gpr_log(GPR_INFO, "parsing string %i - should %s", i,
pair->output ? "succeed" : "fail");
json = grpc_json_parse_string(scratchpad);
if (pair->output) {
char* output;
GPR_ASSERT(json);
output = grpc_json_dump_to_string(json, 0);
GPR_ASSERT(output);
gpr_log(GPR_INFO, "succeeded with output = %s", output);
GPR_ASSERT(strcmp(output, pair->output) == 0);
grpc_json_destroy(json);
gpr_free(output);
} else {
gpr_log(GPR_INFO, "failed");
GPR_ASSERT(!json);
}
free(scratchpad);
}
}
static void test_atypical() {
char* scratchpad = gpr_strdup("[[],[]]");
grpc_json* json = grpc_json_parse_string(scratchpad);
grpc_json* brother;
GPR_ASSERT(json);
GPR_ASSERT(json->child);
brother = json->child->next;
grpc_json_destroy(json->child);
json->child = brother;
grpc_json_destroy(json);
}
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
test_pairs();
test_atypical();
gpr_log(GPR_INFO, "json_test success");
return 0;
}

@ -1,3 +1,86 @@
{
"unicode, escape and empty test": { "a\tb": "\u00eb", "empty": [{},[],{}] },
"some more unicode tests": {
"typical utf-8 input (plane 0)": "ßâñć⇒",
"atypical utf-8 input (plane 1)": "𝄞"
},
"whitespace test": { "trying" :
"to"
,
"break"
:
"the" ,
"parser": "a bit" } ,
"#": "All these examples are from http://json.org/example",
"test1":
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
},
"test2":
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}},
"test3":
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}},
"test4":
{"web-app": { {"web-app": {
"servlet": [ "servlet": [
{ {
@ -85,4 +168,36 @@
"taglib": { "taglib": {
"taglib-uri": "cofax.tld", "taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}} "taglib-location": "/WEB-INF/tlds/cofax.tld"}}},
"test5":
{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}
}

@ -0,0 +1 @@
{"unicode, escape and empty test":{"a\tb":"\u00eb","empty":[{},[],{}]},"some more unicode tests":{"typical utf-8 input (plane 0)":"\u00df\u00e2\u00f1\u0107\u21d2","atypical utf-8 input (plane 1)":"\ud834\udd1e"},"whitespace test":{"trying":"to","break":"the","parser":"a bit"},"#":"All these examples are from http://json.org/example","test1":{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}}},"test2":{"menu":{"id":"file","value":"File","popup":{"menuitem":[{"value":"New","onclick":"CreateNewDoc()"},{"value":"Open","onclick":"OpenDoc()"},{"value":"Close","onclick":"CloseDoc()"}]}}},"test3":{"widget":{"debug":"on","window":{"title":"Sample Konfabulator Widget","name":"main_window","width":500,"height":500},"image":{"src":"Images/Sun.png","name":"sun1","hOffset":250,"vOffset":250,"alignment":"center"},"text":{"data":"Click Here","size":36,"style":"bold","name":"text1","hOffset":250,"vOffset":100,"alignment":"center","onMouseUp":"sun1.opacity = (sun1.opacity / 100) * 90;"}}},"test4":{"web-app":{"servlet":[{"servlet-name":"cofaxCDS","servlet-class":"org.cofax.cds.CDSServlet","init-param":{"configGlossary:installationAt":"Philadelphia, PA","configGlossary:adminEmail":"ksm@pobox.com","configGlossary:poweredBy":"Cofax","configGlossary:poweredByIcon":"/images/cofax.gif","configGlossary:staticPath":"/content/static","templateProcessorClass":"org.cofax.WysiwygTemplate","templateLoaderClass":"org.cofax.FilesTemplateLoader","templatePath":"templates","templateOverridePath":"","defaultListTemplate":"listTemplate.htm","defaultFileTemplate":"articleTemplate.htm","useJSP":false,"jspListTemplate":"listTemplate.jsp","jspFileTemplate":"articleTemplate.jsp","cachePackageTagsTrack":200,"cachePackageTagsStore":200,"cachePackageTagsRefresh":60,"cacheTemplatesTrack":100,"cacheTemplatesStore":50,"cacheTemplatesRefresh":15,"cachePagesTrack":200,"cachePagesStore":100,"cachePagesRefresh":10,"cachePagesDirtyRead":10,"searchEngineListTemplate":"forSearchEnginesList.htm","searchEngineFileTemplate":"forSearchEngines.htm","searchEngineRobotsDb":"WEB-INF/robots.db","useDataStore":true,"dataStoreClass":"org.cofax.SqlDataStore","redirectionClass":"org.cofax.SqlRedirection","dataStoreName":"cofax","dataStoreDriver":"com.microsoft.jdbc.sqlserver.SQLServerDriver","dataStoreUrl":"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon","dataStoreUser":"sa","dataStorePassword":"dataStoreTestQuery","dataStoreTestQuery":"SET NOCOUNT ON;select test='test';","dataStoreLogFile":"/usr/local/tomcat/logs/datastore.log","dataStoreInitConns":10,"dataStoreMaxConns":100,"dataStoreConnUsageLimit":100,"dataStoreLogLevel":"debug","maxUrlLength":500}},{"servlet-name":"cofaxEmail","servlet-class":"org.cofax.cds.EmailServlet","init-param":{"mailHost":"mail1","mailHostOverride":"mail2"}},{"servlet-name":"cofaxAdmin","servlet-class":"org.cofax.cds.AdminServlet"},{"servlet-name":"fileServlet","servlet-class":"org.cofax.cds.FileServlet"},{"servlet-name":"cofaxTools","servlet-class":"org.cofax.cms.CofaxToolsServlet","init-param":{"templatePath":"toolstemplates/","log":1,"logLocation":"/usr/local/tomcat/logs/CofaxTools.log","logMaxSize":"","dataLog":1,"dataLogLocation":"/usr/local/tomcat/logs/dataLog.log","dataLogMaxSize":"","removePageCache":"/content/admin/remove?cache=pages&id=","removeTemplateCache":"/content/admin/remove?cache=templates&id=","fileTransferFolder":"/usr/local/tomcat/webapps/content/fileTransferFolder","lookInContext":1,"adminGroupID":4,"betaServer":true}}],"servlet-mapping":{"cofaxCDS":"/","cofaxEmail":"/cofaxutil/aemail/*","cofaxAdmin":"/admin/*","fileServlet":"/static/*","cofaxTools":"/tools/*"},"taglib":{"taglib-uri":"cofax.tld","taglib-location":"/WEB-INF/tlds/cofax.tld"}}},"test5":{"menu":{"header":"SVG Viewer","items":[{"id":"Open"},{"id":"OpenNew","label":"Open New"},null,{"id":"ZoomIn","label":"Zoom In"},{"id":"ZoomOut","label":"Zoom Out"},{"id":"OriginalView","label":"Original View"},null,{"id":"Quality"},{"id":"Pause"},{"id":"Mute"},null,{"id":"Find","label":"Find..."},{"id":"FindAgain","label":"Find Again"},{"id":"Copy"},{"id":"CopyAgain","label":"Copy Again"},{"id":"CopySVG","label":"Copy SVG"},{"id":"ViewSVG","label":"View SVG"},{"id":"ViewSource","label":"View Source"},{"id":"SaveAs","label":"Save As"},null,{"id":"Help"},{"id":"About","label":"About Adobe CVG Viewer..."}]}}}

@ -0,0 +1,272 @@
{
"unicode, escape and empty test": {
"a\tb": "\u00eb",
"empty": [
{},
[],
{}
]
},
"some more unicode tests": {
"typical utf-8 input (plane 0)": "\u00df\u00e2\u00f1\u0107\u21d2",
"atypical utf-8 input (plane 1)": "\ud834\udd1e"
},
"whitespace test": {
"trying": "to",
"break": "the",
"parser": "a bit"
},
"#": "All these examples are from http://json.org/example",
"test1": {
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"GlossSee": "markup"
}
}
}
}
},
"test2": {
"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{
"value": "New",
"onclick": "CreateNewDoc()"
},
{
"value": "Open",
"onclick": "OpenDoc()"
},
{
"value": "Close",
"onclick": "CloseDoc()"
}
]
}
}
},
"test3": {
"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}
},
"test4": {
"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500
}
},
{
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"
}
},
{
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"
},
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"
},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true
}
}
],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"
},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"
}
}
},
"test5": {
"menu": {
"header": "SVG Viewer",
"items": [
{
"id": "Open"
},
{
"id": "OpenNew",
"label": "Open New"
},
null,
{
"id": "ZoomIn",
"label": "Zoom In"
},
{
"id": "ZoomOut",
"label": "Zoom Out"
},
{
"id": "OriginalView",
"label": "Original View"
},
null,
{
"id": "Quality"
},
{
"id": "Pause"
},
{
"id": "Mute"
},
null,
{
"id": "Find",
"label": "Find..."
},
{
"id": "FindAgain",
"label": "Find Again"
},
{
"id": "Copy"
},
{
"id": "CopyAgain",
"label": "Copy Again"
},
{
"id": "CopySVG",
"label": "Copy SVG"
},
{
"id": "ViewSVG",
"label": "View SVG"
},
{
"id": "ViewSource",
"label": "View Source"
},
{
"id": "SaveAs",
"label": "Save As"
},
null,
{
"id": "Help"
},
{
"id": "About",
"label": "About Adobe CVG Viewer..."
}
]
}
}
}

@ -55,23 +55,23 @@ static const char test_root_cert[] = "I am the root!";
Maximum size for a string literal is 509 chars in C89, yay! */ Maximum size for a string literal is 509 chars in C89, yay! */
static const char test_json_key_str_part1[] = static const char test_json_key_str_part1[] =
"{ \"private_key\": \"-----BEGIN PRIVATE KEY-----" "{ \"private_key\": \"-----BEGIN PRIVATE KEY-----"
"\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\n7mJEqg" "\\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\\n7mJEqg"
"WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\nyjSeg/" "WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\\nyjSeg/"
"rWBQvS4hle4LfijkP3J5BG+" "rWBQvS4hle4LfijkP3J5BG+"
"IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\nOnVF6N7dL3nTYZg+" "IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\\nOnVF6N7dL3nTYZg+"
"uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\nDZgSE6Bu/" "uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\\nDZgSE6Bu/"
"zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\n/" "zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\\n/"
"8HpCqFYM9V8f34SBWfD4fRFT+n/" "8HpCqFYM9V8f34SBWfD4fRFT+n/"
"73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\ngqXjDvpkypEusgXAykECQQD+"; "73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\\ngqXjDvpkypEusgXAykECQQD+";
static const char test_json_key_str_part2[] = static const char test_json_key_str_part2[] =
"53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\nCslxoHQM8s+" "53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\\nCslxoHQM8s+"
"dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\nEkoy2L/" "dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\\nEkoy2L/"
"XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\nAARh2QJBAMKeDAG" "XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\\nAARh2QJBAMKeDAG"
"W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\n8FZi5c8idxiwC36kbAL6HzA" "W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\\n8FZi5c8idxiwC36kbAL6HzA"
"ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\n6z8RJm0+" "ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\\n6z8RJm0+"
"6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/" "6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/"
"5nZ68ECQQDvYuI3\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZY" "5nZ68ECQQDvYuI3\\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZY"
"Ap6LI9W\nIqv4vr6y38N79TTC\n-----END PRIVATE KEY-----\n\", "; "Ap6LI9W\\nIqv4vr6y38N79TTC\\n-----END PRIVATE KEY-----\\n\", ";
static const char test_json_key_str_part3[] = static const char test_json_key_str_part3[] =
"\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", " "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
"\"client_email\": " "\"client_email\": "

@ -41,7 +41,7 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/slice.h> #include <grpc/support/slice.h>
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
#include "third_party/cJSON/cJSON.h" #include "src/core/json/json.h"
#include <openssl/evp.h> #include <openssl/evp.h>
/* This JSON key was generated with the GCE console and revoked immediately. /* This JSON key was generated with the GCE console and revoked immediately.
@ -49,23 +49,23 @@
Maximum size for a string literal is 509 chars in C89, yay! */ Maximum size for a string literal is 509 chars in C89, yay! */
static const char test_json_key_str_part1[] = static const char test_json_key_str_part1[] =
"{ \"private_key\": \"-----BEGIN PRIVATE KEY-----" "{ \"private_key\": \"-----BEGIN PRIVATE KEY-----"
"\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\n7mJEqg" "\\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\\n7mJEqg"
"WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\nyjSeg/" "WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\\nyjSeg/"
"rWBQvS4hle4LfijkP3J5BG+" "rWBQvS4hle4LfijkP3J5BG+"
"IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\nOnVF6N7dL3nTYZg+" "IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\\nOnVF6N7dL3nTYZg+"
"uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\nDZgSE6Bu/" "uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\\nDZgSE6Bu/"
"zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\n/" "zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\\n/"
"8HpCqFYM9V8f34SBWfD4fRFT+n/" "8HpCqFYM9V8f34SBWfD4fRFT+n/"
"73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\ngqXjDvpkypEusgXAykECQQD+"; "73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\\ngqXjDvpkypEusgXAykECQQD+";
static const char test_json_key_str_part2[] = static const char test_json_key_str_part2[] =
"53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\nCslxoHQM8s+" "53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\\nCslxoHQM8s+"
"dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\nEkoy2L/" "dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\\nEkoy2L/"
"XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\nAARh2QJBAMKeDAG" "XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\\nAARh2QJBAMKeDAG"
"W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\n8FZi5c8idxiwC36kbAL6HzA" "W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\\n8FZi5c8idxiwC36kbAL6HzA"
"ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\n6z8RJm0+" "ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\\n6z8RJm0+"
"6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/" "6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/"
"5nZ68ECQQDvYuI3\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZY" "5nZ68ECQQDvYuI3\\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZY"
"Ap6LI9W\nIqv4vr6y38N79TTC\n-----END PRIVATE KEY-----\n\", "; "Ap6LI9W\\nIqv4vr6y38N79TTC\\n-----END PRIVATE KEY-----\\n\", ";
static const char test_json_key_str_part3[] = static const char test_json_key_str_part3[] =
"\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", " "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
"\"client_email\": " "\"client_email\": "
@ -203,10 +203,11 @@ static void test_parse_json_key_failure_no_private_key(void) {
grpc_auth_json_key_destruct(&json_key); grpc_auth_json_key_destruct(&json_key);
} }
static cJSON *parse_json_part_from_jwt(const char *str, size_t len) { static grpc_json *parse_json_part_from_jwt(const char *str, size_t len,
char **scratchpad) {
char *b64; char *b64;
char *decoded; char *decoded;
cJSON *json; grpc_json *json;
gpr_slice slice; gpr_slice slice;
b64 = gpr_malloc(len + 1); b64 = gpr_malloc(len + 1);
strncpy(b64, str, len); strncpy(b64, str, len);
@ -217,59 +218,84 @@ static cJSON *parse_json_part_from_jwt(const char *str, size_t len) {
strncpy(decoded, (const char *)GPR_SLICE_START_PTR(slice), strncpy(decoded, (const char *)GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice)); GPR_SLICE_LENGTH(slice));
decoded[GPR_SLICE_LENGTH(slice)] = '\0'; decoded[GPR_SLICE_LENGTH(slice)] = '\0';
json = cJSON_Parse(decoded); json = grpc_json_parse_string(decoded);
gpr_free(b64); gpr_free(b64);
gpr_free(decoded); *scratchpad = decoded;
gpr_slice_unref(slice); gpr_slice_unref(slice);
return json; return json;
} }
static void check_jwt_header(cJSON *header) { static void check_jwt_header(grpc_json *header) {
cJSON *child = cJSON_GetObjectItem(header, "alg"); grpc_json *ptr;
GPR_ASSERT(child != NULL); grpc_json *alg = NULL;
GPR_ASSERT(child->type == cJSON_String); grpc_json *typ = NULL;
GPR_ASSERT(!strcmp(child->valuestring, "RS256"));
for (ptr = header->child; ptr; ptr = ptr->next) {
child = cJSON_GetObjectItem(header, "typ"); if (strcmp(ptr->key, "alg") == 0) {
GPR_ASSERT(child != NULL); alg = ptr;
GPR_ASSERT(child->type == cJSON_String); } else if (strcmp(ptr->key, "typ") == 0) {
GPR_ASSERT(!strcmp(child->valuestring, "JWT")); typ = ptr;
}
}
GPR_ASSERT(alg != NULL);
GPR_ASSERT(alg->type == GRPC_JSON_STRING);
GPR_ASSERT(!strcmp(alg->value, "RS256"));
GPR_ASSERT(typ != NULL);
GPR_ASSERT(typ->type == GRPC_JSON_STRING);
GPR_ASSERT(!strcmp(typ->value, "JWT"));
} }
static void check_jwt_claim(cJSON *claim) { static void check_jwt_claim(grpc_json *claim) {
gpr_timespec exp = {0, 0}; gpr_timespec expiration = {0, 0};
gpr_timespec issue_time = {0, 0}; gpr_timespec issue_time = {0, 0};
gpr_timespec parsed_lifetime; gpr_timespec parsed_lifetime;
cJSON *child = cJSON_GetObjectItem(claim, "iss"); grpc_json *iss = NULL;
GPR_ASSERT(child != NULL); grpc_json *scope = NULL;
GPR_ASSERT(child->type == cJSON_String); grpc_json *aud = NULL;
grpc_json *exp = NULL;
grpc_json *iat = NULL;
grpc_json *ptr;
for (ptr = claim->child; ptr; ptr = ptr->next) {
if (strcmp(ptr->key, "iss") == 0) {
iss = ptr;
} else if (strcmp(ptr->key, "scope") == 0) {
scope = ptr;
} else if (strcmp(ptr->key, "aud") == 0) {
aud = ptr;
} else if (strcmp(ptr->key, "exp") == 0) {
exp = ptr;
} else if (strcmp(ptr->key, "iat") == 0) {
iat = ptr;
}
}
GPR_ASSERT(iss != NULL);
GPR_ASSERT(iss->type == GRPC_JSON_STRING);
GPR_ASSERT( GPR_ASSERT(
!strcmp( !strcmp(
child->valuestring, iss->value,
"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount.com")); "777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount.com"));
child = cJSON_GetObjectItem(claim, "scope"); GPR_ASSERT(scope != NULL);
GPR_ASSERT(child != NULL); GPR_ASSERT(scope->type == GRPC_JSON_STRING);
GPR_ASSERT(child->type == cJSON_String); GPR_ASSERT(!strcmp(scope->value, test_scope));
GPR_ASSERT(!strcmp(child->valuestring, test_scope));
child = cJSON_GetObjectItem(claim, "aud"); GPR_ASSERT(aud != NULL);
GPR_ASSERT(child != NULL); GPR_ASSERT(aud->type == GRPC_JSON_STRING);
GPR_ASSERT(child->type == cJSON_String); GPR_ASSERT(!strcmp(aud->value,
GPR_ASSERT(!strcmp(child->valuestring,
"https://www.googleapis.com/oauth2/v3/token")); "https://www.googleapis.com/oauth2/v3/token"));
child = cJSON_GetObjectItem(claim, "exp"); GPR_ASSERT(exp != NULL);
GPR_ASSERT(child != NULL); GPR_ASSERT(exp->type == GRPC_JSON_NUMBER);
GPR_ASSERT(child->type == cJSON_Number); expiration.tv_sec = strtol(exp->value, NULL, 10);
exp.tv_sec = child->valueint;
child = cJSON_GetObjectItem(claim, "iat"); GPR_ASSERT(iat != NULL);
GPR_ASSERT(child != NULL); GPR_ASSERT(iat->type == GRPC_JSON_NUMBER);
GPR_ASSERT(child->type == cJSON_Number); issue_time.tv_sec = strtol(iat->value, NULL, 10);
issue_time.tv_sec = child->valueint;
parsed_lifetime = gpr_time_sub(exp, issue_time); parsed_lifetime = gpr_time_sub(expiration, issue_time);
GPR_ASSERT(parsed_lifetime.tv_sec == grpc_max_auth_token_lifetime.tv_sec); GPR_ASSERT(parsed_lifetime.tv_sec == grpc_max_auth_token_lifetime.tv_sec);
} }
@ -300,8 +326,9 @@ static void check_jwt_signature(const char *b64_signature, RSA *rsa_key,
static void test_jwt_encode_and_sign(void) { static void test_jwt_encode_and_sign(void) {
char *json_string = test_json_key_str(NULL); char *json_string = test_json_key_str(NULL);
cJSON *parsed_header = NULL; grpc_json *parsed_header = NULL;
cJSON *parsed_claim = NULL; grpc_json *parsed_claim = NULL;
char *scratchpad;
grpc_auth_json_key json_key = grpc_auth_json_key json_key =
grpc_auth_json_key_create_from_string(json_string); grpc_auth_json_key_create_from_string(json_string);
const char *b64_signature; const char *b64_signature;
@ -310,17 +337,21 @@ static void test_jwt_encode_and_sign(void) {
grpc_max_auth_token_lifetime); grpc_max_auth_token_lifetime);
const char *dot = strchr(jwt, '.'); const char *dot = strchr(jwt, '.');
GPR_ASSERT(dot != NULL); GPR_ASSERT(dot != NULL);
parsed_header = parse_json_part_from_jwt(jwt, dot - jwt); parsed_header = parse_json_part_from_jwt(jwt, dot - jwt, &scratchpad);
GPR_ASSERT(parsed_header != NULL); GPR_ASSERT(parsed_header != NULL);
check_jwt_header(parsed_header); check_jwt_header(parsed_header);
offset = dot - jwt + 1; offset = dot - jwt + 1;
grpc_json_destroy(parsed_header);
gpr_free(scratchpad);
dot = strchr(jwt + offset, '.'); dot = strchr(jwt + offset, '.');
GPR_ASSERT(dot != NULL); GPR_ASSERT(dot != NULL);
parsed_claim = parse_json_part_from_jwt(jwt + offset, dot - (jwt + offset)); parsed_claim = parse_json_part_from_jwt(jwt + offset, dot - (jwt + offset), &scratchpad);
GPR_ASSERT(parsed_claim != NULL); GPR_ASSERT(parsed_claim != NULL);
check_jwt_claim(parsed_claim); check_jwt_claim(parsed_claim);
offset = dot - jwt + 1; offset = dot - jwt + 1;
grpc_json_destroy(parsed_claim);
gpr_free(scratchpad);
dot = strchr(jwt + offset, '.'); dot = strchr(jwt + offset, '.');
GPR_ASSERT(dot == NULL); /* no more part. */ GPR_ASSERT(dot == NULL); /* no more part. */
@ -328,8 +359,6 @@ static void test_jwt_encode_and_sign(void) {
check_jwt_signature(b64_signature, json_key.private_key, jwt, offset - 1); check_jwt_signature(b64_signature, json_key.private_key, jwt, offset - 1);
gpr_free(json_string); gpr_free(json_string);
cJSON_Delete(parsed_header);
cJSON_Delete(parsed_claim);
grpc_auth_json_key_destruct(&json_key); grpc_auth_json_key_destruct(&json_key);
gpr_free(jwt); gpr_free(jwt);
} }

@ -123,11 +123,11 @@ static void assert_log_empty(void) {
/* Given log size and record size, computes the minimum usable space. */ /* Given log size and record size, computes the minimum usable space. */
static gpr_int32 min_usable_space(size_t log_size, size_t record_size) { static gpr_int32 min_usable_space(size_t log_size, size_t record_size) {
gpr_int32 usable_space; gpr_int32 usable_space;
gpr_int32 num_blocks = log_size / CENSUS_LOG_MAX_RECORD_SIZE; gpr_int32 num_blocks = GPR_MAX(log_size / CENSUS_LOG_MAX_RECORD_SIZE,
gpr_cpu_num_cores());
gpr_int32 waste_per_block = CENSUS_LOG_MAX_RECORD_SIZE % record_size; gpr_int32 waste_per_block = CENSUS_LOG_MAX_RECORD_SIZE % record_size;
/* In the worst case, all except one core-local block is full. */ /* In the worst case, all except one core-local block is full. */
gpr_int32 num_full_blocks = GPR_MAX(gpr_cpu_num_cores() - 2, 2); gpr_int32 num_full_blocks = num_blocks - 1;
GPR_ASSERT(num_blocks >= num_full_blocks);
usable_space = (gpr_int32)log_size - usable_space = (gpr_int32)log_size -
(num_full_blocks * CENSUS_LOG_MAX_RECORD_SIZE) - (num_full_blocks * CENSUS_LOG_MAX_RECORD_SIZE) -
((num_blocks - num_full_blocks) * waste_per_block); ((num_blocks - num_full_blocks) * waste_per_block);

@ -70,7 +70,10 @@ DEFINE_string(test_case, "large_unary",
"half_duplex : half-duplex streaming; " "half_duplex : half-duplex streaming; "
"ping_pong : full-duplex streaming; " "ping_pong : full-duplex streaming; "
"service_account_creds : large_unary with service_account auth; " "service_account_creds : large_unary with service_account auth; "
"compute_engine_creds: large_unary with compute engine auth; "
"all : all of above."); "all : all of above.");
DEFINE_string(default_service_account, "",
"Email of GCE default service account");
DEFINE_string(service_account_key_file, "", DEFINE_string(service_account_key_file, "",
"Path to service account json key file."); "Path to service account json key file.");
DEFINE_string(oauth_scope, "", "Scope for OAuth tokens."); DEFINE_string(oauth_scope, "", "Scope for OAuth tokens.");
@ -127,6 +130,12 @@ std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
json_key, FLAGS_oauth_scope, std::chrono::hours(1)); json_key, FLAGS_oauth_scope, std::chrono::hours(1));
return CreateTestChannel(host_port, FLAGS_server_host_override, return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds); FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else if (test_case == "compute_engine_creds") {
std::unique_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_enable_ssl);
creds = CredentialsFactory::ComputeEngineCredentials();
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
} else { } else {
return CreateTestChannel(host_port, FLAGS_server_host_override, return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_enable_ssl, FLAGS_use_prod_roots); FLAGS_enable_ssl, FLAGS_use_prod_roots);
@ -169,6 +178,26 @@ void PerformLargeUnary(std::shared_ptr<ChannelInterface> channel,
grpc::string(kLargeResponseSize, '\0')); grpc::string(kLargeResponseSize, '\0'));
} }
void DoComputeEngineCreds() {
gpr_log(GPR_INFO,
"Sending a large unary rpc with compute engine credentials ...");
std::shared_ptr<ChannelInterface> channel =
CreateChannelForTestCase("compute_engine_creds");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
request.set_fill_oauth_scope(true);
PerformLargeUnary(channel, &request, &response);
gpr_log(GPR_INFO, "Got username %s", response.username().c_str());
gpr_log(GPR_INFO, "Got oauth_scope %s", response.oauth_scope().c_str());
GPR_ASSERT(!response.username().empty());
GPR_ASSERT(response.username() == FLAGS_default_service_account);
GPR_ASSERT(!response.oauth_scope().empty());
GPR_ASSERT(
FLAGS_oauth_scope.find(response.oauth_scope()) != grpc::string::npos);
gpr_log(GPR_INFO, "Large unary with compute engine creds done.");
}
void DoServiceAccountCreds() { void DoServiceAccountCreds() {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"Sending a large unary rpc with service account credentials ..."); "Sending a large unary rpc with service account credentials ...");
@ -375,6 +404,8 @@ int main(int argc, char** argv) {
DoPingPong(); DoPingPong();
} else if (FLAGS_test_case == "service_account_creds") { } else if (FLAGS_test_case == "service_account_creds") {
DoServiceAccountCreds(); DoServiceAccountCreds();
} else if (FLAGS_test_case == "compute_engine_creds") {
DoComputeEngineCreds();
} else if (FLAGS_test_case == "all") { } else if (FLAGS_test_case == "all") {
DoEmpty(); DoEmpty();
DoLargeUnary(); DoLargeUnary();
@ -386,12 +417,13 @@ int main(int argc, char** argv) {
if (FLAGS_enable_ssl) { if (FLAGS_enable_ssl) {
DoServiceAccountCreds(); DoServiceAccountCreds();
} }
// compute_engine_creds only runs in GCE.
} else { } else {
gpr_log( gpr_log(
GPR_ERROR, GPR_ERROR,
"Unsupported test case %s. Valid options are all|empty_unary|" "Unsupported test case %s. Valid options are all|empty_unary|"
"large_unary|client_streaming|server_streaming|half_duplex|ping_pong|" "large_unary|client_streaming|server_streaming|half_duplex|ping_pong|"
"service_account_creds", "service_account_creds|compute_engine_creds",
FLAGS_test_case.c_str()); FLAGS_test_case.c_str());
} }

@ -1,20 +0,0 @@
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -1,247 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
Welcome to cJSON.
cJSON aims to be the dumbest possible parser that you can get your job done with.
It's a single file of C, and a single header file.
JSON is described best here: http://www.json.org/
It's like XML, but fat-free. You use it to move data around, store things, or just
generally represent your program's state.
First up, how do I build?
Add cJSON.c to your project, and put cJSON.h somewhere in the header search path.
For example, to build the test app:
gcc cJSON.c test.c -o test -lm
./test
As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
in one of two modes: Auto and Manual. Let's have a quick run-through.
I lifted some JSON from this page: http://www.json.org/fatfree.html
That page inspired me to write cJSON, which is a parser that tries to share the same
philosophy as JSON itself. Simple, dumb, out of the way.
Some JSON:
{
"name": "Jack (\"Bee\") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}
Assume that you got this from a file, a webserver, or magic JSON elves, whatever,
you have a char * to it. Everything is a cJSON struct.
Get it parsed:
cJSON *root = cJSON_Parse(my_json_string);
This is an object. We're in C. We don't have objects. But we do have structs.
What's the framerate?
cJSON *format = cJSON_GetObjectItem(root,"format");
int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
Want to change the framerate?
cJSON_GetObjectItem(format,"frame rate")->valueint=25;
Back to disk?
char *rendered=cJSON_Print(root);
Finished? Delete the root (this takes care of everything else).
cJSON_Delete(root);
That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers
before you dereference them. If you want to see how you'd build this struct in code?
cJSON *root,*fmt;
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
cJSON_AddStringToObject(fmt,"type", "rect");
cJSON_AddNumberToObject(fmt,"width", 1920);
cJSON_AddNumberToObject(fmt,"height", 1080);
cJSON_AddFalseToObject (fmt,"interlace");
cJSON_AddNumberToObject(fmt,"frame rate", 24);
Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup.
Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and
a few from elsewhere.
What about manual mode? First up you need some detail.
Let's cover how the cJSON objects represent the JSON data.
cJSON doesn't distinguish arrays from objects in handling; just type.
Each cJSON has, potentially, a child, siblings, value, a name.
The root object has: Object Type and a Child
The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling:
Sibling has type Object, name "format", and a child.
That child has type String, name "type", value "rect", and a sibling:
Sibling has type Number, name "width", value 1920, and a sibling:
Sibling has type Number, name "height", value 1080, and a sibling:
Sibling hs type False, name "interlace", and a sibling:
Sibling has type Number, name "frame rate", value 24
Here's the structure:
typedef struct cJSON {
struct cJSON *next,*prev;
struct cJSON *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
} cJSON;
By default all values are 0 unless set by virtue of being meaningful.
next/prev is a doubly linked list of siblings. next takes you to your sibling,
prev takes you back from your sibling to you.
Only objects and arrays have a "child", and it's the head of the doubly linked list.
A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0.
The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in
cJSON.h
A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read
valuedouble.
Any entry which is in the linked list which is the child of an object will have a "string"
which is the "name" of the entry. When I said "name" in the above example, that's "string".
"string" is the JSON name for the 'variable name' if you will.
Now you can trivially walk the lists, recursively, and parse as you please.
You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take
the root object, and traverse the structure (which is, formally, an N-tree),
and tokenise as you please. If you wanted to build a callback style parser, this is how
you'd do it (just an example, since these things are very specific):
void parse_and_callback(cJSON *item,const char *prefix)
{
while (item)
{
char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2);
sprintf(newprefix,"%s/%s",prefix,item->name);
int dorecurse=callback(newprefix, item->type, item);
if (item->child && dorecurse) parse_and_callback(item->child,newprefix);
item=item->next;
free(newprefix);
}
}
The prefix process will build you a separated list, to simplify your callback handling.
The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or
let you invoke it per-item. For the item above, your callback might look like this:
int callback(const char *name,int type,cJSON *item)
{
if (!strcmp(name,"name")) { /* populate name */ }
else if (!strcmp(name,"format/type") { /* handle "rect" */ }
else if (!strcmp(name,"format/width") { /* 800 */ }
else if (!strcmp(name,"format/height") { /* 600 */ }
else if (!strcmp(name,"format/interlace") { /* false */ }
else if (!strcmp(name,"format/frame rate") { /* 24 */ }
return 1;
}
Alternatively, you might like to parse iteratively.
You'd use:
void parse_object(cJSON *item)
{
int i; for (i=0;i<cJSON_GetArraySize(item);i++)
{
cJSON *subitem=cJSON_GetArrayItem(item,i);
// handle subitem.
}
}
Or, for PROPER manual mode:
void parse_object(cJSON *item)
{
cJSON *subitem=item->child;
while (subitem)
{
// handle subitem
if (subitem->child) parse_object(subitem->child);
subitem=subitem->next;
}
}
Of course, this should look familiar, since this is just a stripped-down version
of the callback-parser.
This should cover most uses you'll find for parsing. The rest should be possible
to infer.. and if in doubt, read the source! There's not a lot of it! ;)
In terms of constructing JSON data, the example code above is the right way to do it.
You can, of course, hand your sub-objects to other functions to populate.
Also, if you find a use for it, you can manually build the objects.
For instance, suppose you wanted to build an array of objects?
cJSON *objects[24];
cJSON *Create_array_of_anything(cJSON **items,int num)
{
int i;cJSON *prev, *root=cJSON_CreateArray();
for (i=0;i<24;i++)
{
if (!i) root->child=objects[i];
else prev->next=objects[i], objects[i]->prev=prev;
prev=objects[i];
}
return root;
}
and simply: Create_array_of_anything(objects,24);
cJSON doesn't make any assumptions about what order you create things in.
You can attach the objects, as above, and later add children to each
of those objects.
As soon as you call cJSON_Print, it renders the structure to text.
The test.c code shows how to handle a bunch of typical cases. If you uncomment
the code, it'll load, parse and print a bunch of test files, also from json.org,
which are more complex than I'd care to try and stash into a const char array[].
Enjoy cJSON!
- Dave Gamble, Aug 2009

@ -1,596 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item)
{
char *str;
double d=item->valuedouble;
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
static unsigned parse_hex4(const char *str)
{
unsigned h=0;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
uc2=parse_hex4(ptr+3);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str)
{
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
if (!str) return cJSON_strdup("");
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt)
{
char *out=0;
if (!item) return 0;
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item);break;
case cJSON_String: out=print_string(item);break;
case cJSON_Array: out=print_array(item,depth,fmt);break;
case cJSON_Object: out=print_object(item,depth,fmt);break;
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to malloc the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
out=(char*)cJSON_malloc(fmt?depth+4:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string);
entries[i++]=ret=print_value(child,depth,fmt);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}
void cJSON_Minify(char *json)
{
char *into=json;
while (*json)
{
if (*json==' ') json++;
else if (*json=='\t') json++; /* Whitespace characters. */
else if (*json=='\r') json++;
else if (*json=='\n') json++;
else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */
else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */
else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */
else *into++=*json++; /* All other characters. */
}
*into=0; /* and null-terminate. */
}

@ -1,143 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *json);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif

@ -1,156 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
/* Parse text to JSON, then render back to text, and print! */
void doit(char *text)
{
char *out;cJSON *json;
json=cJSON_Parse(text);
if (!json) {printf("Error before: [%s]\n",cJSON_GetErrorPtr());}
else
{
out=cJSON_Print(json);
cJSON_Delete(json);
printf("%s\n",out);
free(out);
}
}
/* Read a file, parse, render back, etc. */
void dofile(char *filename)
{
FILE *f=fopen(filename,"rb");fseek(f,0,SEEK_END);long len=ftell(f);fseek(f,0,SEEK_SET);
char *data=(char*)malloc(len+1);fread(data,1,len,f);fclose(f);
doit(data);
free(data);
}
/* Used by some code below as an example datatype. */
struct record {const char *precision;double lat,lon;const char *address,*city,*state,*zip,*country; };
/* Create a bunch of objects as demonstration. */
void create_objects()
{
cJSON *root,*fmt,*img,*thm,*fld;char *out;int i; /* declare a few. */
/* Here we construct some JSON standards, from the JSON site. */
/* Our "Video" datatype: */
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
cJSON_AddStringToObject(fmt,"type", "rect");
cJSON_AddNumberToObject(fmt,"width", 1920);
cJSON_AddNumberToObject(fmt,"height", 1080);
cJSON_AddFalseToObject (fmt,"interlace");
cJSON_AddNumberToObject(fmt,"frame rate", 24);
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out); /* Print to text, Delete the cJSON, print it, release the string. */
/* Our "days of the week" array: */
const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
root=cJSON_CreateStringArray(strings,7);
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
/* Our matrix: */
int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}};
root=cJSON_CreateArray();
for (i=0;i<3;i++) cJSON_AddItemToArray(root,cJSON_CreateIntArray(numbers[i],3));
/* cJSON_ReplaceItemInArray(root,1,cJSON_CreateString("Replacement")); */
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
/* Our "gallery" item: */
int ids[4]={116,943,234,38793};
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "Image", img=cJSON_CreateObject());
cJSON_AddNumberToObject(img,"Width",800);
cJSON_AddNumberToObject(img,"Height",600);
cJSON_AddStringToObject(img,"Title","View from 15th Floor");
cJSON_AddItemToObject(img, "Thumbnail", thm=cJSON_CreateObject());
cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");
cJSON_AddNumberToObject(thm,"Height",125);
cJSON_AddStringToObject(thm,"Width","100");
cJSON_AddItemToObject(img,"IDs", cJSON_CreateIntArray(ids,4));
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
/* Our array of "records": */
struct record fields[2]={
{"zip",37.7668,-1.223959e+2,"","SAN FRANCISCO","CA","94107","US"},
{"zip",37.371991,-1.22026e+2,"","SUNNYVALE","CA","94085","US"}};
root=cJSON_CreateArray();
for (i=0;i<2;i++)
{
cJSON_AddItemToArray(root,fld=cJSON_CreateObject());
cJSON_AddStringToObject(fld, "precision", fields[i].precision);
cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);
cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);
cJSON_AddStringToObject(fld, "Address", fields[i].address);
cJSON_AddStringToObject(fld, "City", fields[i].city);
cJSON_AddStringToObject(fld, "State", fields[i].state);
cJSON_AddStringToObject(fld, "Zip", fields[i].zip);
cJSON_AddStringToObject(fld, "Country", fields[i].country);
}
/* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root,1),"City",cJSON_CreateIntArray(ids,4)); */
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);
}
int main (int argc, const char * argv[]) {
/* a bunch of json: */
char text1[]="{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\": \"rect\", \n\"width\": 1920, \n\"height\": 1080, \n\"interlace\": false,\"frame rate\": 24\n}\n}";
char text2[]="[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
char text3[]="[\n [0, -1, 0],\n [1, 0, 0],\n [0, 0, 1]\n ]\n";
char text4[]="{\n \"Image\": {\n \"Width\": 800,\n \"Height\": 600,\n \"Title\": \"View from 15th Floor\",\n \"Thumbnail\": {\n \"Url\": \"http:/*www.example.com/image/481989943\",\n \"Height\": 125,\n \"Width\": \"100\"\n },\n \"IDs\": [116, 943, 234, 38793]\n }\n }";
char text5[]="[\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.7668,\n \"Longitude\": -122.3959,\n \"Address\": \"\",\n \"City\": \"SAN FRANCISCO\",\n \"State\": \"CA\",\n \"Zip\": \"94107\",\n \"Country\": \"US\"\n },\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.371991,\n \"Longitude\": -122.026020,\n \"Address\": \"\",\n \"City\": \"SUNNYVALE\",\n \"State\": \"CA\",\n \"Zip\": \"94085\",\n \"Country\": \"US\"\n }\n ]";
/* Process each json textblock by parsing, then rebuilding: */
doit(text1);
doit(text2);
doit(text3);
doit(text4);
doit(text5);
/* Parse standard testfiles: */
/* dofile("../../tests/test1"); */
/* dofile("../../tests/test2"); */
/* dofile("../../tests/test3"); */
/* dofile("../../tests/test4"); */
/* dofile("../../tests/test5"); */
/* Now some samplecode for building objects concisely: */
create_objects();
return 0;
}

@ -1,22 +0,0 @@
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}

@ -1,11 +0,0 @@
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}

@ -1,26 +0,0 @@
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

@ -1,27 +0,0 @@
{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}

@ -0,0 +1,14 @@
# Dockerfile for gRPC Node
FROM grpc/node_base
# Update the C libary
RUN cd /var/local/git/grpc \
&& git pull --recurse-submodules \
&& git submodule update --init --recursive
# Install the C core.
RUN make install_c -C /var/local/git/grpc
RUN cd /var/local/git/grpc/src/node && npm install && node-gyp rebuild
CMD ["/usr/bin/nodejs", "/var/local/git/grpc/src/node/interop/interop_server.js", "--use_tls=true", "--port 8040"]

@ -0,0 +1,22 @@
# Base Dockerfile for gRPC Node.
#
# Includes Node installation dependencies
FROM grpc/base
RUN curl -sL https://deb.nodesource.com/setup | bash -
RUN apt-get update && apt-get install -y nodejs
RUN npm install -g node-gyp
# Get the source from GitHub, this gets the protobuf library as well
RUN git clone git@github.com:google/grpc.git /var/local/git/grpc
RUN cd /var/local/git/grpc && \
git pull --recurse-submodules && \
git submodule update --init --recursive
# Build the C core
RUN make static_c shared_c -j12 -C /var/local/git/grpc
# Define the default command.
CMD ["bash"]

@ -350,7 +350,7 @@ grpc_interop_test_args() {
[[ -n $1 ]] && { # client_type [[ -n $1 ]] && { # client_type
case $1 in case $1 in
cxx|go|java|nodejs|php|python|ruby) cxx|go|java|node|php|python|ruby)
grpc_gen_test_cmd="grpc_interop_gen_$1_cmd" grpc_gen_test_cmd="grpc_interop_gen_$1_cmd"
declare -F $grpc_gen_test_cmd >> /dev/null || { declare -F $grpc_gen_test_cmd >> /dev/null || {
echo "-f: test_func for $1 => $grpc_gen_test_cmd is not defined" 1>&2 echo "-f: test_func for $1 => $grpc_gen_test_cmd is not defined" 1>&2
@ -381,7 +381,7 @@ grpc_interop_test_args() {
cxx) grpc_port=8010 ;; cxx) grpc_port=8010 ;;
go) grpc_port=8020 ;; go) grpc_port=8020 ;;
java) grpc_port=8030 ;; java) grpc_port=8030 ;;
nodejs) grpc_port=8040 ;; node) grpc_port=8040 ;;
python) grpc_port=8050 ;; python) grpc_port=8050 ;;
ruby) grpc_port=8060 ;; ruby) grpc_port=8060 ;;
*) echo "bad server_type: $1" 1>&2; return 1 ;; *) echo "bad server_type: $1" 1>&2; return 1 ;;
@ -421,7 +421,7 @@ grpc_cloud_prod_test_args() {
[[ -n $1 ]] && { # client_type [[ -n $1 ]] && { # client_type
case $1 in case $1 in
cxx|go|java|nodejs|php|python|ruby) cxx|go|java|node|php|python|ruby)
grpc_gen_test_cmd="grpc_cloud_prod_gen_$1_cmd" grpc_gen_test_cmd="grpc_cloud_prod_gen_$1_cmd"
declare -F $grpc_gen_test_cmd >> /dev/null || { declare -F $grpc_gen_test_cmd >> /dev/null || {
echo "-f: test_func for $1 => $grpc_gen_test_cmd is not defined" 1>&2 echo "-f: test_func for $1 => $grpc_gen_test_cmd is not defined" 1>&2
@ -604,7 +604,7 @@ grpc_launch_server_args() {
cxx) grpc_port=8010 ;; cxx) grpc_port=8010 ;;
go) grpc_port=8020 ;; go) grpc_port=8020 ;;
java) grpc_port=8030 ;; java) grpc_port=8030 ;;
nodejs) grpc_port=8040 ;; node) grpc_port=8040 ;;
python) grpc_port=8050 ;; python) grpc_port=8050 ;;
ruby) grpc_port=8060 ;; ruby) grpc_port=8060 ;;
*) echo "bad server_type: $1" 1>&2; return 1 ;; *) echo "bad server_type: $1" 1>&2; return 1 ;;
@ -676,7 +676,7 @@ grpc_launch_server() {
# cxx: 8010 # cxx: 8010
# go: 8020 # go: 8020
# java: 8030 # java: 8030
# nodejs: 8040 # node: 8040
# python: 8050 # python: 8050
# ruby: 8060 # ruby: 8060
# #
@ -922,6 +922,13 @@ grpc_interop_gen_cxx_cmd() {
echo $the_cmd echo $the_cmd
} }
grpc_interop_gen_node_cmd() {
local cmd_prefix="sudo docker run grpc/node";
local test_script="/usr/bin/nodejs /var/local/git/grpc/src/node/interop/interop_client.js --use_tls=true";
local the_cmd="$cmd_prefix $test_script $@";
echo $the_cmd
}
# constructs the full dockerized cpp interop test cmd. # constructs the full dockerized cpp interop test cmd.
# #
# #
@ -936,6 +943,7 @@ grpc_cloud_prod_gen_cxx_cmd() {
echo $the_cmd echo $the_cmd
} }
<<<<<<< HEAD
# constructs the full dockerized cpp interop test cmd. # constructs the full dockerized cpp interop test cmd.
# #
# #
@ -967,3 +975,6 @@ grpc_cloud_prod_auth_compute_engine_creds_gen_cxx_cmd() {
} }
# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs # TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs
=======
# TODO(grpc-team): add grpc_interop_gen_python_cmd
>>>>>>> 415bca7939809aa5156c7163defc89712b800cb7

@ -367,7 +367,7 @@ grpc_docker_launch_registry() {
grpc_docker_pull_known() { grpc_docker_pull_known() {
local addr=$1 local addr=$1
[[ -n $addr ]] || addr="0.0.0.0:5000" [[ -n $addr ]] || addr="0.0.0.0:5000"
local known="base cxx php_base php ruby_base ruby java_base java go" local known="base cxx php_base php ruby_base ruby java_base java go node_base node"
echo "... pulling docker images for '$known'" echo "... pulling docker images for '$known'"
for i in $known for i in $known
do do

@ -19,7 +19,7 @@ class SimpleConfig(object):
def __init__(self, config): def __init__(self, config):
self.build_config = config self.build_config = config
self.maxjobs = 32 * multiprocessing.cpu_count() self.maxjobs = 2 * multiprocessing.cpu_count()
self.allow_hashing = (config != 'gcov') self.allow_hashing = (config != 'gcov')
def run_command(self, binary): def run_command(self, binary):
@ -32,7 +32,7 @@ class ValgrindConfig(object):
def __init__(self, config, tool): def __init__(self, config, tool):
self.build_config = config self.build_config = config
self.tool = tool self.tool = tool
self.maxjobs = 4 * multiprocessing.cpu_count() self.maxjobs = 2 * multiprocessing.cpu_count()
self.allow_hashing = False self.allow_hashing = False
def run_command(self, binary): def run_command(self, binary):

@ -207,7 +207,7 @@
}, },
{ {
"language": "c", "language": "c",
"name": "poll_kick_test" "name": "poll_kick_posix_test"
}, },
{ {
"language": "c", "language": "c",
@ -249,6 +249,14 @@
"language": "c", "language": "c",
"name": "transport_metadata_test" "name": "transport_metadata_test"
}, },
{
"language": "c",
"name": "json_test"
},
{
"language": "c",
"name": "json_rewrite_test"
},
{ {
"language": "c++", "language": "c++",
"name": "channel_arguments_test" "name": "channel_arguments_test"

@ -25,6 +25,7 @@ class DirWatcher(object):
continue continue
for root, _, files in os.walk(path): for root, _, files in os.walk(path):
for f in files: for f in files:
if f and f[0] == '.': continue
try: try:
st = os.stat(os.path.join(root, f)) st = os.stat(os.path.join(root, f))
except OSError as e: except OSError as e:

@ -134,6 +134,12 @@
<ClInclude Include="..\..\src\core\iomgr\tcp_posix.h" /> <ClInclude Include="..\..\src\core\iomgr\tcp_posix.h" />
<ClInclude Include="..\..\src\core\iomgr\tcp_server.h" /> <ClInclude Include="..\..\src\core\iomgr\tcp_server.h" />
<ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h" /> <ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h" />
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_posix.h" />
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_pipe.h" />
<ClInclude Include="..\..\src\core\json\json.h" />
<ClInclude Include="..\..\src\core\json\json_common.h" />
<ClInclude Include="..\..\src\core\json\json_reader.h" />
<ClInclude Include="..\..\src\core\json\json_writer.h" />
<ClInclude Include="..\..\src\core\statistics\census_interface.h" /> <ClInclude Include="..\..\src\core\statistics\census_interface.h" />
<ClInclude Include="..\..\src\core\statistics\census_log.h" /> <ClInclude Include="..\..\src\core\statistics\census_log.h" />
<ClInclude Include="..\..\src\core\statistics\census_rpc_stats.h" /> <ClInclude Include="..\..\src\core\statistics\census_rpc_stats.h" />
@ -250,7 +256,7 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c"> <ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_kick_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_kick.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c">
</ClCompile> </ClCompile>
@ -276,6 +282,22 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c"> <ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_eventfd.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_nospecial.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_pipe.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_posix.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_reader.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_string.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_writer.c">
</ClCompile>
<ClCompile Include="..\..\src\core\statistics\census_init.c"> <ClCompile Include="..\..\src\core\statistics\census_init.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\statistics\census_log.c"> <ClCompile Include="..\..\src\core\statistics\census_log.c">
@ -358,8 +380,6 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\transport\transport.c"> <ClCompile Include="..\..\src\core\transport\transport.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\third_party\cJSON\cJSON.c">
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="gpr.vcxproj"> <ProjectReference Include="gpr.vcxproj">

@ -118,7 +118,7 @@
<ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c"> <ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_kick_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_kick.c">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c">
@ -157,6 +157,30 @@
<ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c"> <ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_eventfd.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_nospecial.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_pipe.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_posix.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_reader.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_string.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_writer.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\statistics\census_init.c"> <ClCompile Include="..\..\src\core\statistics\census_init.c">
<Filter>src\core\statistics</Filter> <Filter>src\core\statistics</Filter>
</ClCompile> </ClCompile>
@ -280,9 +304,6 @@
<ClCompile Include="..\..\src\core\transport\transport.c"> <ClCompile Include="..\..\src\core\transport\transport.c">
<Filter>src\core\transport</Filter> <Filter>src\core\transport</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\third_party\cJSON\cJSON.c">
<Filter>third_party\cJSON</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\include\grpc\grpc_security.h"> <ClInclude Include="..\..\include\grpc\grpc_security.h">
@ -464,6 +485,24 @@
<ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h"> <ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_posix.h">
<Filter>src\core\iomgr</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_pipe.h">
<Filter>src\core\iomgr</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json_common.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json_reader.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json_writer.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\statistics\census_interface.h"> <ClInclude Include="..\..\src\core\statistics\census_interface.h">
<Filter>src\core\statistics</Filter> <Filter>src\core\statistics</Filter>
</ClInclude> </ClInclude>
@ -599,6 +638,9 @@
<Filter Include="src\core\iomgr"> <Filter Include="src\core\iomgr">
<UniqueIdentifier>{1baf3894-af37-e647-bdbc-95dc17ed0073}</UniqueIdentifier> <UniqueIdentifier>{1baf3894-af37-e647-bdbc-95dc17ed0073}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="src\core\json">
<UniqueIdentifier>{e665cc0e-b994-d7c5-cc18-2007392019f0}</UniqueIdentifier>
</Filter>
<Filter Include="src\core\security"> <Filter Include="src\core\security">
<UniqueIdentifier>{1d850ac6-e639-4eab-5338-4ba40272fcc9}</UniqueIdentifier> <UniqueIdentifier>{1d850ac6-e639-4eab-5338-4ba40272fcc9}</UniqueIdentifier>
</Filter> </Filter>
@ -617,12 +659,6 @@
<Filter Include="src\core\tsi"> <Filter Include="src\core\tsi">
<UniqueIdentifier>{0b0f9ab1-efa4-7f03-e446-6fb9b5227e84}</UniqueIdentifier> <UniqueIdentifier>{0b0f9ab1-efa4-7f03-e446-6fb9b5227e84}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="third_party">
<UniqueIdentifier>{aaab30a4-2a15-732e-c141-3fbc0f0f5a7a}</UniqueIdentifier>
</Filter>
<Filter Include="third_party\cJSON">
<UniqueIdentifier>{332d0840-2c7a-bb09-8e58-585a6fb3959f}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -134,6 +134,12 @@
<ClInclude Include="..\..\src\core\iomgr\tcp_posix.h" /> <ClInclude Include="..\..\src\core\iomgr\tcp_posix.h" />
<ClInclude Include="..\..\src\core\iomgr\tcp_server.h" /> <ClInclude Include="..\..\src\core\iomgr\tcp_server.h" />
<ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h" /> <ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h" />
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_posix.h" />
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_pipe.h" />
<ClInclude Include="..\..\src\core\json\json.h" />
<ClInclude Include="..\..\src\core\json\json_common.h" />
<ClInclude Include="..\..\src\core\json\json_reader.h" />
<ClInclude Include="..\..\src\core\json\json_writer.h" />
<ClInclude Include="..\..\src\core\statistics\census_interface.h" /> <ClInclude Include="..\..\src\core\statistics\census_interface.h" />
<ClInclude Include="..\..\src\core\statistics\census_log.h" /> <ClInclude Include="..\..\src\core\statistics\census_log.h" />
<ClInclude Include="..\..\src\core\statistics\census_rpc_stats.h" /> <ClInclude Include="..\..\src\core\statistics\census_rpc_stats.h" />
@ -250,7 +256,7 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c"> <ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_kick_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_kick.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c">
</ClCompile> </ClCompile>
@ -276,6 +282,22 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c"> <ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_eventfd.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_nospecial.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_pipe.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_posix.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_reader.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_string.c">
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_writer.c">
</ClCompile>
<ClCompile Include="..\..\src\core\statistics\census_init.c"> <ClCompile Include="..\..\src\core\statistics\census_init.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\statistics\census_log.c"> <ClCompile Include="..\..\src\core\statistics\census_log.c">
@ -358,8 +380,6 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\transport\transport.c"> <ClCompile Include="..\..\src\core\transport\transport.c">
</ClCompile> </ClCompile>
<ClCompile Include="..\..\third_party\cJSON\cJSON.c">
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="gpr.vcxproj"> <ProjectReference Include="gpr.vcxproj">

@ -79,7 +79,7 @@
<ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c"> <ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_kick_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_kick.c">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c"> <ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c">
@ -118,6 +118,30 @@
<ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c"> <ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_eventfd.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_nospecial.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_pipe.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\wakeup_fd_posix.c">
<Filter>src\core\iomgr</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_reader.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_string.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\json\json_writer.c">
<Filter>src\core\json</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\statistics\census_init.c"> <ClCompile Include="..\..\src\core\statistics\census_init.c">
<Filter>src\core\statistics</Filter> <Filter>src\core\statistics</Filter>
</ClCompile> </ClCompile>
@ -241,9 +265,6 @@
<ClCompile Include="..\..\src\core\transport\transport.c"> <ClCompile Include="..\..\src\core\transport\transport.c">
<Filter>src\core\transport</Filter> <Filter>src\core\transport</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\third_party\cJSON\cJSON.c">
<Filter>third_party\cJSON</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\include\grpc\byte_buffer.h"> <ClInclude Include="..\..\include\grpc\byte_buffer.h">
@ -389,6 +410,24 @@
<ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h"> <ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h">
<Filter>src\core\iomgr</Filter> <Filter>src\core\iomgr</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_posix.h">
<Filter>src\core\iomgr</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\iomgr\wakeup_fd_pipe.h">
<Filter>src\core\iomgr</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json_common.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json_reader.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\json\json_writer.h">
<Filter>src\core\json</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\statistics\census_interface.h"> <ClInclude Include="..\..\src\core\statistics\census_interface.h">
<Filter>src\core\statistics</Filter> <Filter>src\core\statistics</Filter>
</ClInclude> </ClInclude>
@ -524,6 +563,9 @@
<Filter Include="src\core\iomgr"> <Filter Include="src\core\iomgr">
<UniqueIdentifier>{a9df8b24-ecea-ff6d-8999-d8fa54cd70bf}</UniqueIdentifier> <UniqueIdentifier>{a9df8b24-ecea-ff6d-8999-d8fa54cd70bf}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="src\core\json">
<UniqueIdentifier>{443ffc61-1bea-2477-6e54-1ddf8c139264}</UniqueIdentifier>
</Filter>
<Filter Include="src\core\statistics"> <Filter Include="src\core\statistics">
<UniqueIdentifier>{e084164c-a069-00e3-db35-4e0b1cd6f0b7}</UniqueIdentifier> <UniqueIdentifier>{e084164c-a069-00e3-db35-4e0b1cd6f0b7}</UniqueIdentifier>
</Filter> </Filter>
@ -536,12 +578,6 @@
<Filter Include="src\core\transport\chttp2"> <Filter Include="src\core\transport\chttp2">
<UniqueIdentifier>{5fcd6206-f774-9ae6-4b85-305d6a723843}</UniqueIdentifier> <UniqueIdentifier>{5fcd6206-f774-9ae6-4b85-305d6a723843}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="third_party">
<UniqueIdentifier>{025c051e-8eba-125b-67f9-173f95176eb2}</UniqueIdentifier>
</Filter>
<Filter Include="third_party\cJSON">
<UniqueIdentifier>{7d75397e-988a-baac-897e-2ea7b43d5dd9}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
</Project> </Project>

Loading…
Cancel
Save