aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Tim Emiola <temiola@google.com>2015-01-21 16:26:34 -0800
committerGravatar Tim Emiola <temiola@google.com>2015-01-21 16:26:34 -0800
commitec12a7d972f1b9a64961efde535f466db9226d78 (patch)
tree183d04da35fe09f1d8d199c29746119cada11adb /src
parent8a3ca244b789a36edfcfaa7cf1c5f4a5ece1427a (diff)
parent2021d031687418fe4fe09d2e2efcdc4b3e833ffc (diff)
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src')
-rw-r--r--src/core/iomgr/pollset_kick_posix.c30
-rw-r--r--src/core/security/credentials.c113
-rw-r--r--src/core/security/credentials.h13
-rw-r--r--src/core/security/security_context.c19
-rw-r--r--src/core/security/security_context.h2
-rw-r--r--src/core/security/server_secure_chttp2.c16
-rw-r--r--src/cpp/client/credentials.cc22
-rw-r--r--src/cpp/server/server_credentials.cc23
-rw-r--r--src/node/binding.gyp3
-rw-r--r--src/node/common.js5
-rw-r--r--src/node/credentials.cc24
-rw-r--r--src/node/examples/math_server.js8
-rw-r--r--src/node/interop/empty.proto19
-rw-r--r--src/node/interop/interop_client.js274
-rw-r--r--src/node/interop/interop_server.js203
-rw-r--r--src/node/interop/messages.proto94
-rw-r--r--src/node/interop/test.proto42
-rw-r--r--src/node/main.js12
-rw-r--r--src/node/package.json6
-rw-r--r--src/node/server.cc4
-rw-r--r--src/node/server.js4
-rw-r--r--src/node/server_credentials.cc18
-rw-r--r--src/node/surface_client.js12
-rw-r--r--src/node/surface_server.js13
-rw-r--r--src/node/test/client_server_test.js159
-rw-r--r--src/node/test/end_to_end_test.js239
-rw-r--r--src/node/test/interop_sanity_test.js (renamed from src/node/port_picker.js)53
-rw-r--r--src/node/test/math_client_test.js18
-rw-r--r--src/node/test/server_test.js87
-rw-r--r--src/node/test/surface_test.js6
-rw-r--r--src/php/ext/grpc/credentials.c13
-rw-r--r--src/php/ext/grpc/server_credentials.c14
-rwxr-xr-xsrc/ruby/bin/interop/interop_client.rb16
-rwxr-xr-xsrc/ruby/bin/interop/interop_server.rb2
-rw-r--r--src/ruby/ext/grpc/extconf.rb2
-rw-r--r--src/ruby/ext/grpc/rb_credentials.c19
-rw-r--r--src/ruby/ext/grpc/rb_server_credentials.c14
37 files changed, 1154 insertions, 467 deletions
diff --git a/src/core/iomgr/pollset_kick_posix.c b/src/core/iomgr/pollset_kick_posix.c
index d16e49e459..9f85b6137a 100644
--- a/src/core/iomgr/pollset_kick_posix.c
+++ b/src/core/iomgr/pollset_kick_posix.c
@@ -43,6 +43,9 @@
/* This implementation is based on a freelist of pipes. */
+#define GRPC_MAX_CACHED_PIPES 50
+#define GRPC_PIPE_LOW_WATERMARK 25
+
typedef struct grpc_kick_pipe_info {
int pipe_read_fd;
int pipe_write_fd;
@@ -50,14 +53,16 @@ typedef struct grpc_kick_pipe_info {
} grpc_kick_pipe_info;
static grpc_kick_pipe_info *pipe_freelist = NULL;
+static int pipe_freelist_count = 0;
static gpr_mu pipe_freelist_mu;
-static grpc_kick_pipe_info *allocate_pipe() {
+static grpc_kick_pipe_info *allocate_pipe(void) {
grpc_kick_pipe_info *info;
gpr_mu_lock(&pipe_freelist_mu);
if (pipe_freelist != NULL) {
info = pipe_freelist;
pipe_freelist = pipe_freelist->next;
+ --pipe_freelist_count;
} else {
int pipefd[2];
/* TODO(klempner): Make this nonfatal */
@@ -73,11 +78,26 @@ static grpc_kick_pipe_info *allocate_pipe() {
return info;
}
+static void destroy_pipe(void) {
+ /* assumes pipe_freelist_mu is held */
+ grpc_kick_pipe_info *current = pipe_freelist;
+ pipe_freelist = pipe_freelist->next;
+ pipe_freelist_count--;
+ close(current->pipe_read_fd);
+ close(current->pipe_write_fd);
+ gpr_free(current);
+}
+
static void free_pipe(grpc_kick_pipe_info *pipe_info) {
- /* TODO(klempner): Start closing pipes if the free list gets too large */
gpr_mu_lock(&pipe_freelist_mu);
pipe_info->next = pipe_freelist;
pipe_freelist = pipe_info;
+ pipe_freelist_count++;
+ if (pipe_freelist_count > GRPC_MAX_CACHED_PIPES) {
+ while (pipe_freelist_count > GRPC_PIPE_LOW_WATERMARK) {
+ destroy_pipe();
+ }
+ }
gpr_mu_unlock(&pipe_freelist_mu);
}
@@ -88,11 +108,7 @@ void grpc_pollset_kick_global_init() {
void grpc_pollset_kick_global_destroy() {
while (pipe_freelist != NULL) {
- grpc_kick_pipe_info *current = pipe_freelist;
- pipe_freelist = pipe_freelist->next;
- close(current->pipe_read_fd);
- close(current->pipe_write_fd);
- gpr_free(current);
+ destroy_pipe();
}
gpr_mu_destroy(&pipe_freelist_mu);
}
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
index 006d863e27..628963e46c 100644
--- a/src/core/security/credentials.c
+++ b/src/core/security/credentials.c
@@ -139,7 +139,7 @@ typedef struct {
typedef struct {
grpc_server_credentials base;
- grpc_ssl_config config;
+ grpc_ssl_server_config config;
} grpc_ssl_server_credentials;
static void ssl_destroy(grpc_credentials *creds) {
@@ -152,9 +152,24 @@ static void ssl_destroy(grpc_credentials *creds) {
static void ssl_server_destroy(grpc_server_credentials *creds) {
grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
+ size_t i;
+ for (i = 0; i < c->config.num_key_cert_pairs; i++) {
+ if (c->config.pem_private_keys[i] != NULL) {
+ gpr_free(c->config.pem_private_keys[i]);
+ }
+ if (c->config.pem_cert_chains[i]!= NULL) {
+ gpr_free(c->config.pem_cert_chains[i]);
+ }
+ }
+ if (c->config.pem_private_keys != NULL) gpr_free(c->config.pem_private_keys);
+ if (c->config.pem_private_keys_sizes != NULL) {
+ gpr_free(c->config.pem_private_keys_sizes);
+ }
+ if (c->config.pem_cert_chains != NULL) gpr_free(c->config.pem_cert_chains);
+ if (c->config.pem_cert_chains_sizes != NULL) {
+ gpr_free(c->config.pem_cert_chains_sizes);
+ }
if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
- if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
- if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
gpr_free(creds);
}
@@ -179,7 +194,7 @@ const grpc_ssl_config *grpc_ssl_credentials_get_config(
}
}
-const grpc_ssl_config *grpc_ssl_server_credentials_get_config(
+const grpc_ssl_server_config *grpc_ssl_server_credentials_get_config(
const grpc_server_credentials *creds) {
if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
return NULL;
@@ -189,57 +204,89 @@ const grpc_ssl_config *grpc_ssl_server_credentials_get_config(
}
}
-static void ssl_build_config(const unsigned char *pem_root_certs,
- size_t pem_root_certs_size,
- const unsigned char *pem_private_key,
- size_t pem_private_key_size,
- const unsigned char *pem_cert_chain,
- size_t pem_cert_chain_size,
+static void ssl_copy_key_material(const char *input, unsigned char **output,
+ size_t *output_size) {
+ *output_size = strlen(input);
+ *output = gpr_malloc(*output_size);
+ memcpy(*output, input, *output_size);
+}
+
+static void ssl_build_config(const char *pem_root_certs,
+ grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
grpc_ssl_config *config) {
+ if (pem_root_certs == NULL) {
+ /* TODO(jboeuf): Get them from the environment. */
+ gpr_log(GPR_ERROR, "Default SSL roots not yet implemented.");
+ } else {
+ ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
+ &config->pem_root_certs_size);
+ }
+
+ if (pem_key_cert_pair != NULL) {
+ GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
+ GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
+ ssl_copy_key_material(pem_key_cert_pair->private_key,
+ &config->pem_private_key,
+ &config->pem_private_key_size);
+ ssl_copy_key_material(pem_key_cert_pair->cert_chain,
+ &config->pem_cert_chain,
+ &config->pem_cert_chain_size);
+ }
+}
+
+static void ssl_build_server_config(
+ const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+ size_t num_key_cert_pairs, grpc_ssl_server_config *config) {
+ size_t i;
if (pem_root_certs != NULL) {
- config->pem_root_certs = gpr_malloc(pem_root_certs_size);
- memcpy(config->pem_root_certs, pem_root_certs, pem_root_certs_size);
- config->pem_root_certs_size = pem_root_certs_size;
+ ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
+ &config->pem_root_certs_size);
}
- if (pem_private_key != NULL) {
- config->pem_private_key = gpr_malloc(pem_private_key_size);
- memcpy(config->pem_private_key, pem_private_key, pem_private_key_size);
- config->pem_private_key_size = pem_private_key_size;
+ if (num_key_cert_pairs > 0) {
+ GPR_ASSERT(pem_key_cert_pairs != NULL);
+ config->pem_private_keys =
+ gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
+ config->pem_cert_chains =
+ gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
+ config->pem_private_keys_sizes =
+ gpr_malloc(num_key_cert_pairs * sizeof(size_t));
+ config->pem_cert_chains_sizes =
+ gpr_malloc(num_key_cert_pairs * sizeof(size_t));
}
- if (pem_cert_chain != NULL) {
- config->pem_cert_chain = gpr_malloc(pem_cert_chain_size);
- memcpy(config->pem_cert_chain, pem_cert_chain, pem_cert_chain_size);
- config->pem_cert_chain_size = pem_cert_chain_size;
+ config->num_key_cert_pairs = num_key_cert_pairs;
+ for (i = 0; i < num_key_cert_pairs; i++) {
+ GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL);
+ GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL);
+ ssl_copy_key_material(pem_key_cert_pairs[i].private_key,
+ &config->pem_private_keys[i],
+ &config->pem_private_keys_sizes[i]);
+ ssl_copy_key_material(pem_key_cert_pairs[i].cert_chain,
+ &config->pem_cert_chains[i],
+ &config->pem_cert_chains_sizes[i]);
}
}
grpc_credentials *grpc_ssl_credentials_create(
- const unsigned char *pem_root_certs, size_t pem_root_certs_size,
- const unsigned char *pem_private_key, size_t pem_private_key_size,
- const unsigned char *pem_cert_chain, size_t pem_cert_chain_size) {
+ const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) {
grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
memset(c, 0, sizeof(grpc_ssl_credentials));
c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
c->base.vtable = &ssl_vtable;
gpr_ref_init(&c->base.refcount, 1);
- ssl_build_config(pem_root_certs, pem_root_certs_size, pem_private_key,
- pem_private_key_size, pem_cert_chain, pem_cert_chain_size,
- &c->config);
+ ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config);
return &c->base;
}
grpc_server_credentials *grpc_ssl_server_credentials_create(
- const unsigned char *pem_root_certs, size_t pem_root_certs_size,
- const unsigned char *pem_private_key, size_t pem_private_key_size,
- const unsigned char *pem_cert_chain, size_t pem_cert_chain_size) {
+ const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+ size_t num_key_cert_pairs) {
grpc_ssl_server_credentials *c =
gpr_malloc(sizeof(grpc_ssl_server_credentials));
memset(c, 0, sizeof(grpc_ssl_server_credentials));
c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
c->base.vtable = &ssl_server_vtable;
- ssl_build_config(pem_root_certs, pem_root_certs_size, pem_private_key,
- pem_private_key_size, pem_cert_chain, pem_cert_chain_size,
- &c->config);
+ ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
+ num_key_cert_pairs, &c->config);
return &c->base;
}
diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h
index 4a2532d7c4..8a9ff41e10 100644
--- a/src/core/security/credentials.h
+++ b/src/core/security/credentials.h
@@ -137,10 +137,17 @@ struct grpc_server_credentials {
const char *type;
};
-/* TODO(jboeuf): Have an ssl_server_config that can contain multiple key/cert
- pairs. */
+typedef struct {
+ unsigned char **pem_private_keys;
+ size_t *pem_private_keys_sizes;
+ unsigned char **pem_cert_chains;
+ size_t *pem_cert_chains_sizes;
+ size_t num_key_cert_pairs;
+ unsigned char *pem_root_certs;
+ size_t pem_root_certs_size;
+} grpc_ssl_server_config;
-const grpc_ssl_config *grpc_ssl_server_credentials_get_config(
+const grpc_ssl_server_config *grpc_ssl_server_credentials_get_config(
const grpc_server_credentials *ssl_creds);
#endif /* __GRPC_INTERNAL_SECURITY_CREDENTIALS_H__ */
diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c
index 3a70f44a0a..cce3c7fe04 100644
--- a/src/core/security/security_context.c
+++ b/src/core/security/security_context.c
@@ -382,7 +382,7 @@ error:
}
grpc_security_status grpc_ssl_server_security_context_create(
- const grpc_ssl_config *config, grpc_security_context **ctx) {
+ const grpc_ssl_server_config *config, grpc_security_context **ctx) {
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const unsigned char **alpn_protocol_strings =
gpr_malloc(sizeof(const char *) * num_alpn_protocols);
@@ -399,8 +399,7 @@ grpc_security_status grpc_ssl_server_security_context_create(
strlen(grpc_chttp2_get_alpn_version_index(i));
}
- if (config == NULL || config->pem_private_key == NULL ||
- config->pem_cert_chain == NULL) {
+ if (config == NULL || config->num_key_cert_pairs == 0) {
gpr_log(GPR_ERROR, "An SSL server needs a key and a cert.");
goto error;
}
@@ -410,13 +409,13 @@ grpc_security_status grpc_ssl_server_security_context_create(
gpr_ref_init(&c->base.refcount, 1);
c->base.vtable = &ssl_server_vtable;
result = tsi_create_ssl_server_handshaker_factory(
- (const unsigned char **)&config->pem_private_key,
- &config->pem_private_key_size,
- (const unsigned char **)&config->pem_cert_chain,
- &config->pem_cert_chain_size, 1, config->pem_root_certs,
- config->pem_root_certs_size, GRPC_SSL_CIPHER_SUITES,
- alpn_protocol_strings, alpn_protocol_string_lengths, num_alpn_protocols,
- &c->handshaker_factory);
+ (const unsigned char **)config->pem_private_keys,
+ config->pem_private_keys_sizes,
+ (const unsigned char **)config->pem_cert_chains,
+ config->pem_cert_chains_sizes, config->num_key_cert_pairs,
+ config->pem_root_certs, config->pem_root_certs_size,
+ GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings,
+ alpn_protocol_string_lengths, num_alpn_protocols, &c->handshaker_factory);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
tsi_result_to_string(result));
diff --git a/src/core/security/security_context.h b/src/core/security/security_context.h
index 9ace7f1ccb..2caa2d3690 100644
--- a/src/core/security/security_context.h
+++ b/src/core/security/security_context.h
@@ -157,7 +157,7 @@ grpc_security_status grpc_ssl_channel_security_context_create(
specific error code otherwise.
*/
grpc_security_status grpc_ssl_server_security_context_create(
- const grpc_ssl_config *config, grpc_security_context **ctx);
+ const grpc_ssl_server_config *config, grpc_security_context **ctx);
/* --- Creation of high level objects. --- */
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index 931fa95651..9dd4327822 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -93,6 +93,8 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
grpc_tcp_server *tcp = NULL;
size_t i;
int count = 0;
+ int port_num = -1;
+ int port_temp;
resolved = grpc_blocking_resolve_address(addr, "https");
if (!resolved) {
@@ -105,9 +107,15 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
}
for (i = 0; i < resolved->naddrs; i++) {
- if (grpc_tcp_server_add_port(tcp,
- (struct sockaddr *)&resolved->addrs[i].addr,
- resolved->addrs[i].len)) {
+ port_temp = grpc_tcp_server_add_port(
+ tcp, (struct sockaddr *)&resolved->addrs[i].addr,
+ resolved->addrs[i].len);
+ if (port_temp >= 0) {
+ if (port_num == -1) {
+ port_num = port_temp;
+ } else {
+ GPR_ASSERT(port_num == port_temp);
+ }
count++;
}
}
@@ -125,7 +133,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
/* Register with the server only upon success */
grpc_server_add_listener(server, tcp, start, destroy);
- return 1;
+ return port_num;
/* Error path: cleanup and return */
error:
diff --git a/src/cpp/client/credentials.cc b/src/cpp/client/credentials.cc
index 0955fa28ae..8e3a988477 100644
--- a/src/cpp/client/credentials.cc
+++ b/src/cpp/client/credentials.cc
@@ -54,26 +54,12 @@ std::unique_ptr<Credentials> CredentialsFactory::DefaultCredentials() {
// Builds SSL Credentials given SSL specific options
std::unique_ptr<Credentials> CredentialsFactory::SslCredentials(
const SslCredentialsOptions &options) {
- const unsigned char *pem_root_certs =
- options.pem_root_certs.empty() ? nullptr
- : reinterpret_cast<const unsigned char *>(
- options.pem_root_certs.c_str());
- if (pem_root_certs == nullptr) {
- return std::unique_ptr<Credentials>();
- }
- const unsigned char *pem_private_key =
- options.pem_private_key.empty() ? nullptr
- : reinterpret_cast<const unsigned char *>(
- options.pem_private_key.c_str());
- const unsigned char *pem_cert_chain =
- options.pem_cert_chain.empty() ? nullptr
- : reinterpret_cast<const unsigned char *>(
- options.pem_cert_chain.c_str());
+ grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
+ options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
grpc_credentials *c_creds = grpc_ssl_credentials_create(
- pem_root_certs, options.pem_root_certs.size(), pem_private_key,
- options.pem_private_key.size(), pem_cert_chain,
- options.pem_cert_chain.size());
+ options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
+ options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair);
std::unique_ptr<Credentials> cpp_creds(
c_creds == nullptr ? nullptr : new Credentials(c_creds));
return cpp_creds;
diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc
index b82a2d821a..ce0271b6a0 100644
--- a/src/cpp/server/server_credentials.cc
+++ b/src/cpp/server/server_credentials.cc
@@ -48,23 +48,14 @@ grpc_server_credentials *ServerCredentials::GetRawCreds() { return creds_; }
std::shared_ptr<ServerCredentials> ServerCredentialsFactory::SslCredentials(
const SslServerCredentialsOptions &options) {
- const unsigned char *pem_root_certs =
- options.pem_root_certs.empty() ? nullptr
- : reinterpret_cast<const unsigned char *>(
- options.pem_root_certs.c_str());
- const unsigned char *pem_private_key =
- options.pem_private_key.empty() ? nullptr
- : reinterpret_cast<const unsigned char *>(
- options.pem_private_key.c_str());
- const unsigned char *pem_cert_chain =
- options.pem_cert_chain.empty() ? nullptr
- : reinterpret_cast<const unsigned char *>(
- options.pem_cert_chain.c_str());
-
+ std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
+ for (const auto &key_cert_pair : options.pem_key_cert_pairs) {
+ pem_key_cert_pairs.push_back(
+ {key_cert_pair.private_key.c_str(), key_cert_pair.cert_chain.c_str()});
+ }
grpc_server_credentials *c_creds = grpc_ssl_server_credentials_create(
- pem_root_certs, options.pem_root_certs.size(), pem_private_key,
- options.pem_private_key.size(), pem_cert_chain,
- options.pem_cert_chain.size());
+ options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
+ &pem_key_cert_pairs[0], pem_key_cert_pairs.size());
return std::shared_ptr<ServerCredentials>(new ServerCredentials(c_creds));
}
diff --git a/src/node/binding.gyp b/src/node/binding.gyp
index 4a1fd7aaf0..da4a943491 100644
--- a/src/node/binding.gyp
+++ b/src/node/binding.gyp
@@ -19,9 +19,6 @@
'link_settings': {
'libraries': [
'-lgrpc',
- '-levent',
- '-levent_pthreads',
- '-levent_core',
'-lrt',
'-lgpr',
'-lpthread'
diff --git a/src/node/common.js b/src/node/common.js
index 656a4aca95..54247e3fa1 100644
--- a/src/node/common.js
+++ b/src/node/common.js
@@ -31,6 +31,8 @@
*
*/
+var capitalize = require('underscore.string/capitalize');
+
/**
* Get a function that deserializes a specific type of protobuf.
* @param {function()} cls The constructor of the message type to deserialize
@@ -73,6 +75,9 @@ function fullyQualifiedName(value) {
return '';
}
var name = value.name;
+ if (value.className === 'Service.RPCMethod') {
+ name = capitalize(name);
+ }
if (value.hasOwnProperty('parent')) {
var parent_name = fullyQualifiedName(value.parent);
if (parent_name !== '') {
diff --git a/src/node/credentials.cc b/src/node/credentials.cc
index d58b7eda89..f9cd2fcfe0 100644
--- a/src/node/credentials.cc
+++ b/src/node/credentials.cc
@@ -136,33 +136,29 @@ NAN_METHOD(Credentials::CreateDefault) {
NAN_METHOD(Credentials::CreateSsl) {
NanScope();
- char *root_certs;
- char *private_key = NULL;
- char *cert_chain = NULL;
- int root_certs_length, private_key_length = 0, cert_chain_length = 0;
- if (!Buffer::HasInstance(args[0])) {
+ char *root_certs = NULL;
+ grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
+ if (Buffer::HasInstance(args[0])) {
+ root_certs = Buffer::Data(args[0]);
+ } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) {
return NanThrowTypeError("createSsl's first argument must be a Buffer");
}
- root_certs = Buffer::Data(args[0]);
- root_certs_length = Buffer::Length(args[0]);
if (Buffer::HasInstance(args[1])) {
- private_key = Buffer::Data(args[1]);
- private_key_length = Buffer::Length(args[1]);
+ key_cert_pair.private_key = Buffer::Data(args[1]);
} else if (!(args[1]->IsNull() || args[1]->IsUndefined())) {
return NanThrowTypeError(
"createSSl's second argument must be a Buffer if provided");
}
if (Buffer::HasInstance(args[2])) {
- cert_chain = Buffer::Data(args[2]);
- cert_chain_length = Buffer::Length(args[2]);
+ key_cert_pair.cert_chain = Buffer::Data(args[2]);
} else if (!(args[2]->IsNull() || args[2]->IsUndefined())) {
return NanThrowTypeError(
"createSSl's third argument must be a Buffer if provided");
}
+
NanReturnValue(WrapStruct(grpc_ssl_credentials_create(
- reinterpret_cast<unsigned char *>(root_certs), root_certs_length,
- reinterpret_cast<unsigned char *>(private_key), private_key_length,
- reinterpret_cast<unsigned char *>(cert_chain), cert_chain_length)));
+ root_certs,
+ key_cert_pair.private_key == NULL ? NULL : &key_cert_pair)));
}
NAN_METHOD(Credentials::CreateComposite) {
diff --git a/src/node/examples/math_server.js b/src/node/examples/math_server.js
index 366513dc17..d649b4fd6d 100644
--- a/src/node/examples/math_server.js
+++ b/src/node/examples/math_server.js
@@ -119,10 +119,10 @@ function mathDivMany(stream) {
var server = new Server({
'math.Math' : {
- Div: mathDiv,
- Fib: mathFib,
- Sum: mathSum,
- DivMany: mathDivMany
+ div: mathDiv,
+ fib: mathFib,
+ sum: mathSum,
+ divMany: mathDivMany
}
});
diff --git a/src/node/interop/empty.proto b/src/node/interop/empty.proto
new file mode 100644
index 0000000000..c9920a22ee
--- /dev/null
+++ b/src/node/interop/empty.proto
@@ -0,0 +1,19 @@
+syntax = "proto2";
+
+package grpc.testing;
+
+// An empty message that you can re-use to avoid defining duplicated empty
+// messages in your project. A typical example is to use it as argument or the
+// return value of a service API. For instance:
+//
+// service Foo {
+// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
+// };
+//
+// MOE:begin_strip
+// The difference between this one and net/rpc/empty-message.proto is that
+// 1) The generated message here is in proto2 C++ API.
+// 2) The proto2.Empty has minimum dependencies
+// (no message_set or net/rpc dependencies)
+// MOE:end_strip
+message Empty {}
diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js
new file mode 100644
index 0000000000..cf75b9a77a
--- /dev/null
+++ b/src/node/interop/interop_client.js
@@ -0,0 +1,274 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+var fs = require('fs');
+var path = require('path');
+var grpc = require('..');
+var testProto = grpc.load(__dirname + '/test.proto').grpc.testing;
+
+var assert = require('assert');
+
+/**
+ * Create a buffer filled with size zeroes
+ * @param {number} size The length of the buffer
+ * @return {Buffer} The new buffer
+ */
+function zeroBuffer(size) {
+ var zeros = new Buffer(size);
+ zeros.fill(0);
+ return zeros;
+}
+
+/**
+ * Run the empty_unary test
+ * @param {Client} client The client to test against
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function emptyUnary(client, done) {
+ var call = client.emptyCall({}, function(err, resp) {
+ assert.ifError(err);
+ });
+ call.on('status', function(status) {
+ assert.strictEqual(status.code, grpc.status.OK);
+ if (done) {
+ done();
+ }
+ });
+}
+
+/**
+ * Run the large_unary test
+ * @param {Client} client The client to test against
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function largeUnary(client, done) {
+ var arg = {
+ response_type: testProto.PayloadType.COMPRESSABLE,
+ response_size: 314159,
+ payload: {
+ body: zeroBuffer(271828)
+ }
+ };
+ var call = client.unaryCall(arg, function(err, resp) {
+ assert.ifError(err);
+ assert.strictEqual(resp.payload.type, testProto.PayloadType.COMPRESSABLE);
+ assert.strictEqual(resp.payload.body.limit - resp.payload.body.offset,
+ 314159);
+ });
+ call.on('status', function(status) {
+ assert.strictEqual(status.code, grpc.status.OK);
+ if (done) {
+ done();
+ }
+ });
+}
+
+/**
+ * Run the client_streaming test
+ * @param {Client} client The client to test against
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function clientStreaming(client, done) {
+ var call = client.streamingInputCall(function(err, resp) {
+ assert.ifError(err);
+ assert.strictEqual(resp.aggregated_payload_size, 74922);
+ });
+ call.on('status', function(status) {
+ assert.strictEqual(status.code, grpc.status.OK);
+ if (done) {
+ done();
+ }
+ });
+ var payload_sizes = [27182, 8, 1828, 45904];
+ for (var i = 0; i < payload_sizes.length; i++) {
+ call.write({payload: {body: zeroBuffer(payload_sizes[i])}});
+ }
+ call.end();
+}
+
+/**
+ * Run the server_streaming test
+ * @param {Client} client The client to test against
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function serverStreaming(client, done) {
+ var arg = {
+ response_type: testProto.PayloadType.COMPRESSABLE,
+ response_parameters: [
+ {size: 31415},
+ {size: 9},
+ {size: 2653},
+ {size: 58979}
+ ]
+ };
+ var call = client.streamingOutputCall(arg);
+ var resp_index = 0;
+ call.on('data', function(value) {
+ assert(resp_index < 4);
+ assert.strictEqual(value.payload.type, testProto.PayloadType.COMPRESSABLE);
+ assert.strictEqual(value.payload.body.limit - value.payload.body.offset,
+ arg.response_parameters[resp_index].size);
+ resp_index += 1;
+ });
+ call.on('status', function(status) {
+ assert.strictEqual(resp_index, 4);
+ assert.strictEqual(status.code, grpc.status.OK);
+ if (done) {
+ done();
+ }
+ });
+}
+
+/**
+ * Run the ping_pong test
+ * @param {Client} client The client to test against
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function pingPong(client, done) {
+ var payload_sizes = [27182, 8, 1828, 45904];
+ var response_sizes = [31415, 9, 2653, 58979];
+ var call = client.fullDuplexCall();
+ call.on('status', function(status) {
+ assert.strictEqual(status.code, grpc.status.OK);
+ if (done) {
+ done();
+ }
+ });
+ var index = 0;
+ call.write({
+ response_type: testProto.PayloadType.COMPRESSABLE,
+ response_parameters: [
+ {size: response_sizes[index]}
+ ],
+ payload: {body: zeroBuffer(payload_sizes[index])}
+ });
+ call.on('data', function(response) {
+ assert.strictEqual(response.payload.type,
+ testProto.PayloadType.COMPRESSABLE);
+ assert.equal(response.payload.body.limit - response.payload.body.offset,
+ response_sizes[index]);
+ index += 1;
+ if (index == 4) {
+ call.end();
+ } else {
+ call.write({
+ response_type: testProto.PayloadType.COMPRESSABLE,
+ response_parameters: [
+ {size: response_sizes[index]}
+ ],
+ payload: {body: zeroBuffer(payload_sizes[index])}
+ });
+ }
+ });
+}
+
+/**
+ * Run the empty_stream test.
+ * NOTE: This does not work, but should with the new invoke API
+ * @param {Client} client The client to test against
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function emptyStream(client, done) {
+ var call = client.fullDuplexCall();
+ call.on('status', function(status) {
+ assert.strictEqual(status.code, grpc.status.OK);
+ if (done) {
+ done();
+ }
+ });
+ call.on('data', function(value) {
+ assert.fail(value, null, 'No data should have been received', '!==');
+ });
+ call.end();
+}
+
+/**
+ * Map from test case names to test functions
+ */
+var test_cases = {
+ empty_unary: emptyUnary,
+ large_unary: largeUnary,
+ client_streaming: clientStreaming,
+ server_streaming: serverStreaming,
+ ping_pong: pingPong,
+ empty_stream: emptyStream
+};
+
+/**
+ * Execute a single test case.
+ * @param {string} address The address of the server to connect to, in the
+ * format "hostname:port"
+ * @param {string} host_overrirde The hostname of the server to use as an SSL
+ * override
+ * @param {string} test_case The name of the test case to run
+ * @param {bool} tls Indicates that a secure channel should be used
+ * @param {function} done Callback to call when the test is completed. Included
+ * primarily for use with mocha
+ */
+function runTest(address, host_override, test_case, tls, done) {
+ // TODO(mlumish): enable TLS functionality
+ var options = {};
+ if (tls) {
+ var ca_path = path.join(__dirname, '../test/data/ca.pem');
+ var ca_data = fs.readFileSync(ca_path);
+ var creds = grpc.Credentials.createSsl(ca_data);
+ options.credentials = creds;
+ if (host_override) {
+ options['grpc.ssl_target_name_override'] = host_override;
+ }
+ }
+ var client = new testProto.TestService(address, options);
+
+ test_cases[test_case](client, done);
+}
+
+if (require.main === module) {
+ var parseArgs = require('minimist');
+ var argv = parseArgs(process.argv, {
+ string: ['server_host', 'server_host_override', 'server_port', 'test_case',
+ 'use_tls', 'use_test_ca']
+ });
+ runTest(argv.server_host + ':' + argv.server_port, argv.server_host_override,
+ argv.test_case, argv.use_tls === 'true');
+}
+
+/**
+ * See docs for runTest
+ */
+exports.runTest = runTest;
diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js
new file mode 100644
index 0000000000..6d2bd7ae0d
--- /dev/null
+++ b/src/node/interop/interop_server.js
@@ -0,0 +1,203 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+var fs = require('fs');
+var path = require('path');
+var _ = require('underscore');
+var grpc = require('..');
+var testProto = grpc.load(__dirname + '/test.proto').grpc.testing;
+var Server = grpc.buildServer([testProto.TestService.service]);
+
+/**
+ * Create a buffer filled with size zeroes
+ * @param {number} size The length of the buffer
+ * @return {Buffer} The new buffer
+ */
+function zeroBuffer(size) {
+ var zeros = new Buffer(size);
+ zeros.fill(0);
+ return zeros;
+}
+
+/**
+ * Respond to an empty parameter with an empty response.
+ * NOTE: this currently does not work due to issue #137
+ * @param {Call} call Call to handle
+ * @param {function(Error, Object)} callback Callback to call with result
+ * or error
+ */
+function handleEmpty(call, callback) {
+ callback(null, {});
+}
+
+/**
+ * Handle a unary request by sending the requested payload
+ * @param {Call} call Call to handle
+ * @param {function(Error, Object)} callback Callback to call with result or
+ * error
+ */
+function handleUnary(call, callback) {
+ var req = call.request;
+ var zeros = zeroBuffer(req.response_size);
+ var payload_type = req.response_type;
+ if (payload_type === testProto.PayloadType.RANDOM) {
+ payload_type = [
+ testProto.PayloadType.COMPRESSABLE,
+ testProto.PayloadType.UNCOMPRESSABLE][Math.random() < 0.5 ? 0 : 1];
+ }
+ callback(null, {payload: {type: payload_type, body: zeros}});
+}
+
+/**
+ * Respond to a streaming call with the total size of all payloads
+ * @param {Call} call Call to handle
+ * @param {function(Error, Object)} callback Callback to call with result or
+ * error
+ */
+function handleStreamingInput(call, callback) {
+ var aggregate_size = 0;
+ call.on('data', function(value) {
+ aggregate_size += value.payload.body.limit - value.payload.body.offset;
+ });
+ call.on('end', function() {
+ callback(null, {aggregated_payload_size: aggregate_size});
+ });
+}
+
+/**
+ * Respond to a payload request with a stream of the requested payloads
+ * @param {Call} call Call to handle
+ */
+function handleStreamingOutput(call) {
+ var req = call.request;
+ var payload_type = req.response_type;
+ if (payload_type === testProto.PayloadType.RANDOM) {
+ payload_type = [
+ testProto.PayloadType.COMPRESSABLE,
+ testProto.PayloadType.UNCOMPRESSABLE][Math.random() < 0.5 ? 0 : 1];
+ }
+ _.each(req.response_parameters, function(resp_param) {
+ call.write({
+ payload: {
+ body: zeroBuffer(resp_param.size),
+ type: payload_type
+ }
+ });
+ });
+ call.end();
+}
+
+/**
+ * Respond to a stream of payload requests with a stream of payload responses as
+ * they arrive.
+ * @param {Call} call Call to handle
+ */
+function handleFullDuplex(call) {
+ call.on('data', function(value) {
+ var payload_type = value.response_type;
+ if (payload_type === testProto.PayloadType.RANDOM) {
+ payload_type = [
+ testProto.PayloadType.COMPRESSABLE,
+ testProto.PayloadType.UNCOMPRESSABLE][Math.random() < 0.5 ? 0 : 1];
+ }
+ _.each(value.response_parameters, function(resp_param) {
+ call.write({
+ payload: {
+ body: zeroBuffer(resp_param.size),
+ type: payload_type
+ }
+ });
+ });
+ });
+ call.on('end', function() {
+ call.end();
+ });
+}
+
+/**
+ * Respond to a stream of payload requests with a stream of payload responses
+ * after all requests have arrived
+ * @param {Call} call Call to handle
+ */
+function handleHalfDuplex(call) {
+ throw new Error('HalfDuplexCall not yet implemented');
+}
+
+/**
+ * Get a server object bound to the given port
+ * @param {string} port Port to which to bind
+ * @param {boolean} tls Indicates that the bound port should use TLS
+ * @return {{server: Server, port: number}} Server object bound to the support,
+ * and port number that the server is bound to
+ */
+function getServer(port, tls) {
+ // TODO(mlumish): enable TLS functionality
+ var options = {};
+ if (tls) {
+ var key_path = path.join(__dirname, '../test/data/server1.key');
+ var pem_path = path.join(__dirname, '../test/data/server1.pem');
+
+ var key_data = fs.readFileSync(key_path);
+ var pem_data = fs.readFileSync(pem_path);
+ var server_creds = grpc.ServerCredentials.createSsl(null,
+ key_data,
+ pem_data);
+ options.credentials = server_creds;
+ }
+ var server = new Server({
+ 'grpc.testing.TestService' : {
+ emptyCall: handleEmpty,
+ unaryCall: handleUnary,
+ streamingOutputCall: handleStreamingOutput,
+ streamingInputCall: handleStreamingInput,
+ fullDuplexCall: handleFullDuplex,
+ halfDuplexCall: handleHalfDuplex
+ }
+ }, options);
+ var port_num = server.bind('0.0.0.0:' + port, tls);
+ return {server: server, port: port_num};
+}
+
+if (require.main === module) {
+ var parseArgs = require('minimist');
+ var argv = parseArgs(process.argv, {
+ string: ['port', 'use_tls']
+ });
+ var server_obj = getServer(argv.port, argv.use_tls === 'true');
+ server_obj.server.start();
+}
+
+/**
+ * See docs for getServer
+ */
+exports.getServer = getServer;
diff --git a/src/node/interop/messages.proto b/src/node/interop/messages.proto
new file mode 100644
index 0000000000..29db0dd8b1
--- /dev/null
+++ b/src/node/interop/messages.proto
@@ -0,0 +1,94 @@
+// Message definitions to be used by integration test service definitions.
+
+syntax = "proto2";
+
+package grpc.testing;
+
+// The type of payload that should be returned.
+enum PayloadType {
+ // Compressable text format.
+ COMPRESSABLE = 0;
+
+ // Uncompressable binary format.
+ UNCOMPRESSABLE = 1;
+
+ // Randomly chosen from all other formats defined in this enum.
+ RANDOM = 2;
+}
+
+// A block of data, to simply increase gRPC message size.
+message Payload {
+ // The type of data in body.
+ optional PayloadType type = 1;
+ // Primary contents of payload.
+ optional bytes body = 2;
+}
+
+// Unary request.
+message SimpleRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ optional PayloadType response_type = 1;
+
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 response_size = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+}
+
+// Unary response, as configured by the request.
+message SimpleResponse {
+ // Payload to increase message size.
+ optional Payload payload = 1;
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ optional int64 effective_gaia_user_id = 2;
+}
+
+// Client-streaming request.
+message StreamingInputCallRequest {
+ // Optional input payload sent along with the request.
+ optional Payload payload = 1;
+
+ // Not expecting any payload from the response.
+}
+
+// Client-streaming response.
+message StreamingInputCallResponse {
+ // Aggregated size of payloads received from the client.
+ optional int32 aggregated_payload_size = 1;
+}
+
+// Configuration for a particular response.
+message ResponseParameters {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 size = 1;
+
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ optional int32 interval_us = 2;
+}
+
+// Server-streaming request.
+message StreamingOutputCallRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ optional PayloadType response_type = 1;
+
+ // Configuration for each expected response message.
+ repeated ResponseParameters response_parameters = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+}
+
+// Server-streaming response, as configured by the request and parameters.
+message StreamingOutputCallResponse {
+ // Payload to increase response size.
+ optional Payload payload = 1;
+}
diff --git a/src/node/interop/test.proto b/src/node/interop/test.proto
new file mode 100644
index 0000000000..8380ebb31d
--- /dev/null
+++ b/src/node/interop/test.proto
@@ -0,0 +1,42 @@
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+syntax = "proto2";
+
+import "empty.proto";
+import "messages.proto";
+
+package grpc.testing;
+
+// A simple service to test the various types of RPCs and experiment with
+// performance with various types of payload.
+service TestService {
+ // One empty request followed by one empty response.
+ rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ rpc StreamingOutputCall(StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ rpc StreamingInputCall(stream StreamingInputCallRequest)
+ returns (StreamingInputCallResponse);
+
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ rpc FullDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ rpc HalfDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+}
diff --git a/src/node/main.js b/src/node/main.js
index a8dfa20024..751c3525d3 100644
--- a/src/node/main.js
+++ b/src/node/main.js
@@ -55,7 +55,7 @@ function loadObject(value) {
return result;
} else if (value.className === 'Service') {
return surface_client.makeClientConstructor(value);
- } else if (value.className === 'Service.Message') {
+ } else if (value.className === 'Message' || value.className === 'Enum') {
return value.build();
} else {
return value;
@@ -96,3 +96,13 @@ exports.status = grpc.status;
* Call error name to code number mapping
*/
exports.callError = grpc.callError;
+
+/**
+ * Credentials factories
+ */
+exports.Credentials = grpc.Credentials;
+
+/**
+ * ServerCredentials factories
+ */
+exports.ServerCredentials = grpc.ServerCredentials;
diff --git a/src/node/package.json b/src/node/package.json
index ed93c4ff41..5f3c6fa345 100644
--- a/src/node/package.json
+++ b/src/node/package.json
@@ -8,12 +8,14 @@
"dependencies": {
"bindings": "^1.2.1",
"nan": "~1.3.0",
+ "protobufjs": "murgatroid99/ProtoBuf.js",
"underscore": "^1.7.0",
- "protobufjs": "murgatroid99/ProtoBuf.js"
+ "underscore.string": "^3.0.0"
},
"devDependencies": {
+ "highland": "~2.2.0",
"mocha": "~1.21.0",
- "highland": "~2.0.0"
+ "minimist": "^1.1.0"
},
"main": "main.js"
}
diff --git a/src/node/server.cc b/src/node/server.cc
index 64826897cd..b102775d33 100644
--- a/src/node/server.cc
+++ b/src/node/server.cc
@@ -194,7 +194,7 @@ NAN_METHOD(Server::AddHttp2Port) {
return NanThrowTypeError("addHttp2Port's argument must be a String");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
- NanReturnValue(NanNew<Boolean>(grpc_server_add_http2_port(
+ NanReturnValue(NanNew<Number>(grpc_server_add_http2_port(
server->wrapped_server, *NanUtf8String(args[0]))));
}
@@ -208,7 +208,7 @@ NAN_METHOD(Server::AddSecureHttp2Port) {
return NanThrowTypeError("addSecureHttp2Port's argument must be a String");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
- NanReturnValue(NanNew<Boolean>(grpc_server_add_secure_http2_port(
+ NanReturnValue(NanNew<Number>(grpc_server_add_secure_http2_port(
server->wrapped_server, *NanUtf8String(args[0]))));
}
diff --git a/src/node/server.js b/src/node/server.js
index e947032b29..9cee1dc57e 100644
--- a/src/node/server.js
+++ b/src/node/server.js
@@ -256,9 +256,9 @@ Server.prototype.register = function(name, handler) {
*/
Server.prototype.bind = function(port, secure) {
if (secure) {
- this._server.addSecureHttp2Port(port);
+ return this._server.addSecureHttp2Port(port);
} else {
- this._server.addHttp2Port(port);
+ return this._server.addHttp2Port(port);
}
};
diff --git a/src/node/server_credentials.cc b/src/node/server_credentials.cc
index 38df547527..393f3a6305 100644
--- a/src/node/server_credentials.cc
+++ b/src/node/server_credentials.cc
@@ -123,14 +123,12 @@ NAN_METHOD(ServerCredentials::New) {
}
NAN_METHOD(ServerCredentials::CreateSsl) {
+ // TODO: have the node API support multiple key/cert pairs.
NanScope();
char *root_certs = NULL;
- char *private_key;
- char *cert_chain;
- int root_certs_length = 0, private_key_length, cert_chain_length;
+ grpc_ssl_pem_key_cert_pair key_cert_pair;
if (Buffer::HasInstance(args[0])) {
root_certs = Buffer::Data(args[0]);
- root_certs_length = Buffer::Length(args[0]);
} else if (!(args[0]->IsNull() || args[0]->IsUndefined())) {
return NanThrowTypeError(
"createSSl's first argument must be a Buffer if provided");
@@ -138,17 +136,13 @@ NAN_METHOD(ServerCredentials::CreateSsl) {
if (!Buffer::HasInstance(args[1])) {
return NanThrowTypeError("createSsl's second argument must be a Buffer");
}
- private_key = Buffer::Data(args[1]);
- private_key_length = Buffer::Length(args[1]);
+ key_cert_pair.private_key = Buffer::Data(args[1]);
if (!Buffer::HasInstance(args[2])) {
return NanThrowTypeError("createSsl's third argument must be a Buffer");
}
- cert_chain = Buffer::Data(args[2]);
- cert_chain_length = Buffer::Length(args[2]);
- NanReturnValue(WrapStruct(grpc_ssl_server_credentials_create(
- reinterpret_cast<unsigned char *>(root_certs), root_certs_length,
- reinterpret_cast<unsigned char *>(private_key), private_key_length,
- reinterpret_cast<unsigned char *>(cert_chain), cert_chain_length)));
+ key_cert_pair.cert_chain = Buffer::Data(args[2]);
+ NanReturnValue(WrapStruct(
+ grpc_ssl_server_credentials_create(root_certs, &key_cert_pair, 1)));
}
NAN_METHOD(ServerCredentials::CreateFake) {
diff --git a/src/node/surface_client.js b/src/node/surface_client.js
index 77dab5ca6f..996e3d101f 100644
--- a/src/node/surface_client.js
+++ b/src/node/surface_client.js
@@ -33,6 +33,9 @@
var _ = require('underscore');
+var capitalize = require('underscore.string/capitalize');
+var decapitalize = require('underscore.string/decapitalize');
+
var client = require('./client.js');
var common = require('./common.js');
@@ -352,10 +355,11 @@ function makeClientConstructor(service) {
method_type = 'unary';
}
}
- SurfaceClient.prototype[method.name] = requester_makers[method_type](
- prefix + method.name,
- common.serializeCls(method.resolvedRequestType.build()),
- common.deserializeCls(method.resolvedResponseType.build()));
+ SurfaceClient.prototype[decapitalize(method.name)] =
+ requester_makers[method_type](
+ prefix + capitalize(method.name),
+ common.serializeCls(method.resolvedRequestType.build()),
+ common.deserializeCls(method.resolvedResponseType.build()));
});
SurfaceClient.service = service;
diff --git a/src/node/surface_server.js b/src/node/surface_server.js
index b6e0c37b4c..bc688839fe 100644
--- a/src/node/surface_server.js
+++ b/src/node/surface_server.js
@@ -33,6 +33,9 @@
var _ = require('underscore');
+var capitalize = require('underscore.string/capitalize');
+var decapitalize = require('underscore.string/decapitalize');
+
var Server = require('./server.js');
var stream = require('stream');
@@ -332,15 +335,16 @@ function makeServerConstructor(services) {
method_type = 'unary';
}
}
- if (service_handlers[service_name][method.name] === undefined) {
+ if (service_handlers[service_name][decapitalize(method.name)] ===
+ undefined) {
throw new Error('Method handler for ' +
common.fullyQualifiedName(method) + ' not provided.');
}
var binary_handler = handler_makers[method_type](
- service_handlers[service_name][method.name],
+ service_handlers[service_name][decapitalize(method.name)],
common.serializeCls(method.resolvedResponseType.build()),
common.deserializeCls(method.resolvedRequestType.build()));
- server.register(prefix + method.name, binary_handler);
+ server.register(prefix + capitalize(method.name), binary_handler);
});
}, this);
}
@@ -353,8 +357,7 @@ function makeServerConstructor(services) {
* @return {SurfaceServer} this
*/
SurfaceServer.prototype.bind = function(port, secure) {
- this.inner_server.bind(port, secure);
- return this;
+ return this.inner_server.bind(port, secure);
};
/**
diff --git a/src/node/test/client_server_test.js b/src/node/test/client_server_test.js
index 534a5c464f..2a25908684 100644
--- a/src/node/test/client_server_test.js
+++ b/src/node/test/client_server_test.js
@@ -37,7 +37,6 @@ var path = require('path');
var grpc = require('bindings')('grpc.node');
var Server = require('../server');
var client = require('../client');
-var port_picker = require('../port_picker');
var common = require('../common');
var _ = require('highland');
@@ -80,55 +79,50 @@ function errorHandler(stream) {
describe('echo client', function() {
it('should receive echo responses', function(done) {
- port_picker.nextAvailablePort(function(port) {
- var server = new Server();
- server.bind(port);
- server.register('echo', echoHandler);
- server.start();
-
- var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
- var channel = new grpc.Channel(port);
- var stream = client.makeRequest(
- channel,
- 'echo');
- _(messages).map(function(val) {
- return new Buffer(val);
- }).pipe(stream);
- var index = 0;
- stream.on('data', function(chunk) {
- assert.equal(messages[index], chunk.toString());
- index += 1;
- });
- stream.on('end', function() {
- server.shutdown();
- done();
- });
+ var server = new Server();
+ var port_num = server.bind('0.0.0.0:0');
+ server.register('echo', echoHandler);
+ server.start();
+
+ var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
+ var channel = new grpc.Channel('localhost:' + port_num);
+ var stream = client.makeRequest(
+ channel,
+ 'echo');
+ _(messages).map(function(val) {
+ return new Buffer(val);
+ }).pipe(stream);
+ var index = 0;
+ stream.on('data', function(chunk) {
+ assert.equal(messages[index], chunk.toString());
+ index += 1;
+ });
+ stream.on('end', function() {
+ server.shutdown();
+ done();
});
});
it('should get an error status that the server throws', function(done) {
- port_picker.nextAvailablePort(function(port) {
- var server = new Server();
- server.bind(port);
- server.register('error', errorHandler);
- server.start();
-
- var channel = new grpc.Channel(port);
- var stream = client.makeRequest(
- channel,
- 'error',
- null,
- getDeadline(1));
-
- stream.on('data', function() {});
- stream.write(new Buffer('test'));
- stream.end();
- stream.on('status', function(status) {
- assert.equal(status.code, grpc.status.UNIMPLEMENTED);
- assert.equal(status.details, 'error details');
- server.shutdown();
- done();
- });
-
+ var server = new Server();
+ var port_num = server.bind('0.0.0.0:0');
+ server.register('error', errorHandler);
+ server.start();
+
+ var channel = new grpc.Channel('localhost:' + port_num);
+ var stream = client.makeRequest(
+ channel,
+ 'error',
+ null,
+ getDeadline(1));
+
+ stream.on('data', function() {});
+ stream.write(new Buffer('test'));
+ stream.end();
+ stream.on('status', function(status) {
+ assert.equal(status.code, grpc.status.UNIMPLEMENTED);
+ assert.equal(status.details, 'error details');
+ server.shutdown();
+ done();
});
});
});
@@ -136,46 +130,43 @@ describe('echo client', function() {
* and the insecure echo client test */
describe('secure echo client', function() {
it('should recieve echo responses', function(done) {
- port_picker.nextAvailablePort(function(port) {
- fs.readFile(ca_path, function(err, ca_data) {
+ fs.readFile(ca_path, function(err, ca_data) {
+ assert.ifError(err);
+ fs.readFile(key_path, function(err, key_data) {
assert.ifError(err);
- fs.readFile(key_path, function(err, key_data) {
+ fs.readFile(pem_path, function(err, pem_data) {
assert.ifError(err);
- fs.readFile(pem_path, function(err, pem_data) {
- assert.ifError(err);
- var creds = grpc.Credentials.createSsl(ca_data);
- var server_creds = grpc.ServerCredentials.createSsl(null,
- key_data,
- pem_data);
-
- var server = new Server({'credentials' : server_creds});
- server.bind(port, true);
- server.register('echo', echoHandler);
- server.start();
-
- var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
- var channel = new grpc.Channel(port, {
- 'grpc.ssl_target_name_override' : 'foo.test.google.com',
- 'credentials' : creds
- });
- var stream = client.makeRequest(
- channel,
- 'echo');
-
- _(messages).map(function(val) {
- return new Buffer(val);
- }).pipe(stream);
- var index = 0;
- stream.on('data', function(chunk) {
- assert.equal(messages[index], chunk.toString());
- index += 1;
- });
- stream.on('end', function() {
- server.shutdown();
- done();
- });
+ var creds = grpc.Credentials.createSsl(ca_data);
+ var server_creds = grpc.ServerCredentials.createSsl(null,
+ key_data,
+ pem_data);
+
+ var server = new Server({'credentials' : server_creds});
+ var port_num = server.bind('0.0.0.0:0', true);
+ server.register('echo', echoHandler);
+ server.start();
+
+ var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
+ var channel = new grpc.Channel('localhost:' + port_num, {
+ 'grpc.ssl_target_name_override' : 'foo.test.google.com',
+ 'credentials' : creds
+ });
+ var stream = client.makeRequest(
+ channel,
+ 'echo');
+
+ _(messages).map(function(val) {
+ return new Buffer(val);
+ }).pipe(stream);
+ var index = 0;
+ stream.on('data', function(chunk) {
+ assert.equal(messages[index], chunk.toString());
+ index += 1;
+ });
+ stream.on('end', function() {
+ server.shutdown();
+ done();
});
-
});
});
});
diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js
index 40bb5f3bbd..db3834dbba 100644
--- a/src/node/test/end_to_end_test.js
+++ b/src/node/test/end_to_end_test.js
@@ -33,7 +33,6 @@
var assert = require('assert');
var grpc = require('bindings')('grpc.node');
-var port_picker = require('../port_picker');
/**
* This is used for testing functions with multiple asynchronous calls that
@@ -58,143 +57,139 @@ function multiDone(done, count) {
describe('end-to-end', function() {
it('should start and end a request without error', function(complete) {
- port_picker.nextAvailablePort(function(port) {
- var server = new grpc.Server();
- var done = multiDone(function() {
- complete();
- server.shutdown();
- }, 2);
- server.addHttp2Port(port);
- var channel = new grpc.Channel(port);
- var deadline = new Date();
- deadline.setSeconds(deadline.getSeconds() + 3);
- var status_text = 'xyz';
- var call = new grpc.Call(channel,
- 'dummy_method',
- deadline);
- call.startInvoke(function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.INVOKE_ACCEPTED);
+ var server = new grpc.Server();
+ var done = multiDone(function() {
+ complete();
+ server.shutdown();
+ }, 2);
+ var port_num = server.addHttp2Port('0.0.0.0:0');
+ var channel = new grpc.Channel('localhost:' + port_num);
+ var deadline = new Date();
+ deadline.setSeconds(deadline.getSeconds() + 3);
+ var status_text = 'xyz';
+ var call = new grpc.Call(channel,
+ 'dummy_method',
+ deadline);
+ call.startInvoke(function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.INVOKE_ACCEPTED);
- call.writesDone(function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.FINISH_ACCEPTED);
- assert.strictEqual(event.data, grpc.opError.OK);
- });
- },function(event) {
+ call.writesDone(function(event) {
assert.strictEqual(event.type,
- grpc.completionType.CLIENT_METADATA_READ);
- },function(event) {
+ grpc.completionType.FINISH_ACCEPTED);
+ assert.strictEqual(event.data, grpc.opError.OK);
+ });
+ },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.start();
+ 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);
- var status = event.data;
- assert.strictEqual(status.code, grpc.status.OK);
- assert.strictEqual(status.details, status_text);
- done();
}, 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();
+ });
+ });
+ });
- server.start();
- 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) {
+ it('should send and receive data without error', function(complete) {
+ var req_text = 'client_request';
+ var reply_text = 'server_response';
+ var server = new grpc.Server();
+ var done = multiDone(function() {
+ complete();
+ server.shutdown();
+ }, 6);
+ var port_num = server.addHttp2Port('0.0.0.0:0');
+ var channel = new grpc.Channel('localhost:' + port_num);
+ var deadline = new Date();
+ deadline.setSeconds(deadline.getSeconds() + 3);
+ var status_text = 'success';
+ var call = new grpc.Call(channel,
+ 'dummy_method',
+ deadline);
+ call.startInvoke(function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.INVOKE_ACCEPTED);
+ call.startWrite(
+ new Buffer(req_text),
+ function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.WRITE_ACCEPTED);
+ assert.strictEqual(event.data, grpc.opError.OK);
+ call.writesDone(function(event) {
assert.strictEqual(event.type,
grpc.completionType.FINISH_ACCEPTED);
assert.strictEqual(event.data, grpc.opError.OK);
done();
});
+ }, 0);
+ call.startRead(function(event) {
+ assert.strictEqual(event.type, grpc.completionType.READ);
+ assert.strictEqual(event.data.toString(), reply_text);
+ done();
});
- });
- });
+ },function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.CLIENT_METADATA_READ);
+ done();
+ },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);
- it('should send and receive data without error', function(complete) {
- port_picker.nextAvailablePort(function(port) {
- var req_text = 'client_request';
- var reply_text = 'server_response';
- var server = new grpc.Server();
- var done = multiDone(function() {
- complete();
- server.shutdown();
- }, 6);
- server.addHttp2Port(port);
- var channel = new grpc.Channel(port);
- var deadline = new Date();
- deadline.setSeconds(deadline.getSeconds() + 3);
- var status_text = 'success';
- var call = new grpc.Call(channel,
- 'dummy_method',
- deadline);
- call.startInvoke(function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.INVOKE_ACCEPTED);
- call.startWrite(
- new Buffer(req_text),
+ server.start();
+ 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);
+ done();
+ });
+ server_call.serverEndInitialMetadata(0);
+ server_call.startRead(function(event) {
+ assert.strictEqual(event.type, grpc.completionType.READ);
+ assert.strictEqual(event.data.toString(), req_text);
+ server_call.startWrite(
+ new Buffer(reply_text),
function(event) {
assert.strictEqual(event.type,
grpc.completionType.WRITE_ACCEPTED);
- assert.strictEqual(event.data, grpc.opError.OK);
- call.writesDone(function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.FINISH_ACCEPTED);
- assert.strictEqual(event.data, grpc.opError.OK);
- done();
- });
+ assert.strictEqual(event.data,
+ grpc.opError.OK);
+ 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();
+ });
}, 0);
- call.startRead(function(event) {
- assert.strictEqual(event.type, grpc.completionType.READ);
- assert.strictEqual(event.data.toString(), reply_text);
- done();
- });
- },function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.CLIENT_METADATA_READ);
- done();
- },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.start();
- 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);
- done();
- });
- server_call.serverEndInitialMetadata(0);
- server_call.startRead(function(event) {
- assert.strictEqual(event.type, grpc.completionType.READ);
- assert.strictEqual(event.data.toString(), req_text);
- server_call.startWrite(
- new Buffer(reply_text),
- function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.WRITE_ACCEPTED);
- assert.strictEqual(event.data,
- grpc.opError.OK);
- 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();
- });
- }, 0);
- });
});
});
});
diff --git a/src/node/port_picker.js b/src/node/test/interop_sanity_test.js
index ad82f2a7f8..b0fc8f8200 100644
--- a/src/node/port_picker.js
+++ b/src/node/test/interop_sanity_test.js
@@ -31,22 +31,41 @@
*
*/
-var net = require('net');
+var interop_server = require('../interop/interop_server.js');
+var interop_client = require('../interop/interop_client.js');
-/**
- * Finds a free port that a server can bind to, in the format
- * "address:port"
- * @param {function(string)} cb The callback that should execute when the port
- * is available
- */
-function nextAvailablePort(cb) {
- var server = net.createServer();
- server.listen(function() {
- var address = server.address();
- server.close(function() {
- cb(address.address + ':' + address.port.toString());
- });
- });
-}
+var server;
+
+var port;
-exports.nextAvailablePort = nextAvailablePort;
+var name_override = 'foo.test.google.com';
+
+describe('Interop tests', function() {
+ before(function(done) {
+ var server_obj = interop_server.getServer(0, true);
+ server = server_obj.server;
+ server.listen();
+ port = 'localhost:' + server_obj.port;
+ done();
+ });
+ // This depends on not using a binary stream
+ it.skip('should pass empty_unary', function(done) {
+ interop_client.runTest(port, name_override, 'empty_unary', true, done);
+ });
+ it('should pass large_unary', function(done) {
+ interop_client.runTest(port, name_override, 'large_unary', true, done);
+ });
+ it('should pass client_streaming', function(done) {
+ interop_client.runTest(port, name_override, 'client_streaming', true, done);
+ });
+ it('should pass server_streaming', function(done) {
+ interop_client.runTest(port, name_override, 'server_streaming', true, done);
+ });
+ it('should pass ping_pong', function(done) {
+ interop_client.runTest(port, name_override, 'ping_pong', true, done);
+ });
+ // This depends on the new invoke API
+ it.skip('should pass empty_stream', function(done) {
+ interop_client.runTest(port, name_override, 'empty_stream', true, done);
+ });
+});
diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js
index 45c956d179..0e365bf870 100644
--- a/src/node/test/math_client_test.js
+++ b/src/node/test/math_client_test.js
@@ -32,7 +32,6 @@
*/
var assert = require('assert');
-var port_picker = require('../port_picker');
var grpc = require('..');
var math = grpc.load(__dirname + '/../examples/math.proto').math;
@@ -50,18 +49,17 @@ var server = require('../examples/math_server.js');
describe('Math client', function() {
before(function(done) {
- port_picker.nextAvailablePort(function(port) {
- server.bind(port).listen();
- math_client = new math.Math(port);
- done();
- });
+ var port_num = server.bind('0.0.0.0:0');
+ server.listen();
+ math_client = new math.Math('localhost:' + port_num);
+ done();
});
after(function() {
server.shutdown();
});
it('should handle a single request', function(done) {
var arg = {dividend: 7, divisor: 4};
- var call = math_client.Div(arg, function handleDivResult(err, value) {
+ var call = math_client.div(arg, function handleDivResult(err, value) {
assert.ifError(err);
assert.equal(value.quotient, 1);
assert.equal(value.remainder, 3);
@@ -72,7 +70,7 @@ describe('Math client', function() {
});
});
it('should handle a server streaming request', function(done) {
- var call = math_client.Fib({limit: 7});
+ var call = math_client.fib({limit: 7});
var expected_results = [1, 1, 2, 3, 5, 8, 13];
var next_expected = 0;
call.on('data', function checkResponse(value) {
@@ -85,7 +83,7 @@ describe('Math client', function() {
});
});
it('should handle a client streaming request', function(done) {
- var call = math_client.Sum(function handleSumResult(err, value) {
+ var call = math_client.sum(function handleSumResult(err, value) {
assert.ifError(err);
assert.equal(value.num, 21);
});
@@ -103,7 +101,7 @@ describe('Math client', function() {
assert.equal(value.quotient, index);
assert.equal(value.remainder, 1);
}
- var call = math_client.DivMany();
+ var call = math_client.divMany();
var response_index = 0;
call.on('data', function(value) {
checkResponse(response_index, value);
diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js
index 79f7b32948..61aef4677e 100644
--- a/src/node/test/server_test.js
+++ b/src/node/test/server_test.js
@@ -34,7 +34,6 @@
var assert = require('assert');
var grpc = require('bindings')('grpc.node');
var Server = require('../server');
-var port_picker = require('../port_picker');
/**
* This is used for testing functions with multiple asynchronous calls that
@@ -68,54 +67,52 @@ function echoHandler(stream) {
describe('echo server', function() {
it('should echo inputs as responses', function(done) {
done = multiDone(done, 4);
- port_picker.nextAvailablePort(function(port) {
- var server = new Server();
- server.bind(port);
- server.register('echo', echoHandler);
- server.start();
+ var server = new Server();
+ var port_num = server.bind('[::]:0');
+ server.register('echo', echoHandler);
+ server.start();
- var req_text = 'echo test string';
- var status_text = 'OK';
+ var req_text = 'echo test string';
+ var status_text = 'OK';
- var channel = new grpc.Channel(port);
- var deadline = new Date();
- deadline.setSeconds(deadline.getSeconds() + 3);
- var call = new grpc.Call(channel,
- 'echo',
- deadline);
- call.startInvoke(function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.INVOKE_ACCEPTED);
- call.startWrite(
- new Buffer(req_text),
- function(event) {
+ var channel = new grpc.Channel('localhost:' + port_num);
+ var deadline = new Date();
+ deadline.setSeconds(deadline.getSeconds() + 3);
+ var call = new grpc.Call(channel,
+ 'echo',
+ deadline);
+ call.startInvoke(function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.INVOKE_ACCEPTED);
+ call.startWrite(
+ new Buffer(req_text),
+ function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.WRITE_ACCEPTED);
+ assert.strictEqual(event.data, grpc.opError.OK);
+ call.writesDone(function(event) {
assert.strictEqual(event.type,
- grpc.completionType.WRITE_ACCEPTED);
+ grpc.completionType.FINISH_ACCEPTED);
assert.strictEqual(event.data, grpc.opError.OK);
- call.writesDone(function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.FINISH_ACCEPTED);
- assert.strictEqual(event.data, grpc.opError.OK);
- done();
- });
- }, 0);
- call.startRead(function(event) {
- assert.strictEqual(event.type, grpc.completionType.READ);
- assert.strictEqual(event.data.toString(), req_text);
- done();
- });
- },function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.CLIENT_METADATA_READ);
+ done();
+ });
+ }, 0);
+ call.startRead(function(event) {
+ assert.strictEqual(event.type, grpc.completionType.READ);
+ assert.strictEqual(event.data.toString(), req_text);
done();
- },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);
- server.shutdown();
- done();
- }, 0);
- });
+ });
+ },function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.CLIENT_METADATA_READ);
+ done();
+ },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);
+ server.shutdown();
+ done();
+ }, 0);
});
});
diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js
index 8d0d8ec3bc..34f1a156eb 100644
--- a/src/node/test/surface_test.js
+++ b/src/node/test/surface_test.js
@@ -59,9 +59,9 @@ describe('Surface server constructor', function() {
assert.throws(function() {
new Server({
'math.Math': {
- 'Div': function() {},
- 'DivMany': function() {},
- 'Fib': function() {}
+ 'div': function() {},
+ 'divMany': function() {},
+ 'fib': function() {}
}
});
}, /math.Math.Sum/);
diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c
index 2a83d1cbc1..c63196bf90 100644
--- a/src/php/ext/grpc/credentials.c
+++ b/src/php/ext/grpc/credentials.c
@@ -77,24 +77,23 @@ PHP_METHOD(Credentials, createDefault) {
*/
PHP_METHOD(Credentials, createSsl) {
char *pem_root_certs;
- char *pem_private_key = NULL;
- char *pem_cert_chain = NULL;
+ grpc_ssl_pem_key_cert_pair pem_key_cert_pair;
int root_certs_length, private_key_length = 0, cert_chain_length = 0;
/* "s|s!s! == 1 string, 2 optional nullable strings */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!",
&pem_root_certs, &root_certs_length,
- &pem_private_key, &private_key_length,
- &pem_cert_chain, &cert_chain_length) == FAILURE) {
+ &pem_key_cert_pair.private_key, &private_key_length,
+ &pem_key_cert_pair.cert_chain,
+ &cert_chain_length) == FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"createSsl expects 1 to 3 strings", 1 TSRMLS_CC);
return;
}
grpc_credentials *creds = grpc_ssl_credentials_create(
- (unsigned char *)pem_root_certs, (size_t)root_certs_length,
- (unsigned char *)pem_private_key, (size_t)private_key_length,
- (unsigned char *)pem_cert_chain, (size_t)cert_chain_length);
+ pem_root_certs,
+ pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair);
zval *creds_object = grpc_php_wrap_credentials(creds);
RETURN_DESTROY_ZVAL(creds_object);
}
diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c
index 1f8e58aa4d..3d43d6a78c 100644
--- a/src/php/ext/grpc/server_credentials.c
+++ b/src/php/ext/grpc/server_credentials.c
@@ -66,24 +66,22 @@ zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped) {
*/
PHP_METHOD(ServerCredentials, createSsl) {
char *pem_root_certs = 0;
- char *pem_private_key;
- char *pem_cert_chain;
+ grpc_ssl_pem_key_cert_pair pem_key_cert_pair;
int root_certs_length = 0, private_key_length, cert_chain_length;
/* "s!ss" == 1 nullable string, 2 strings */
+ /* TODO: support multiple key cert pairs. */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!ss", &pem_root_certs,
- &root_certs_length, &pem_private_key,
- &private_key_length, &pem_cert_chain,
+ &root_certs_length, &pem_key_cert_pair.private_key,
+ &private_key_length, &pem_key_cert_pair.cert_chain,
&cert_chain_length) == FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"createSsl expects 3 strings", 1 TSRMLS_CC);
return;
}
- grpc_server_credentials *creds = grpc_ssl_server_credentials_create(
- (unsigned char *)pem_root_certs, (size_t)root_certs_length,
- (unsigned char *)pem_private_key, (size_t)private_key_length,
- (unsigned char *)pem_cert_chain, (size_t)cert_chain_length);
+ grpc_server_credentials *creds =
+ grpc_ssl_server_credentials_create(pem_root_certs, &pem_key_cert_pair, 1);
zval *creds_object = grpc_php_wrap_server_credentials(creds);
RETURN_DESTROY_ZVAL(creds_object);
}
diff --git a/src/ruby/bin/interop/interop_client.rb b/src/ruby/bin/interop/interop_client.rb
index 0ce10d9e30..0ea7f376be 100755
--- a/src/ruby/bin/interop/interop_client.rb
+++ b/src/ruby/bin/interop/interop_client.rb
@@ -107,11 +107,11 @@ class PingPongPlayer
@msg_sizes.each do |m|
req_size, resp_size = m
req = req_cls.new(payload: Payload.new(body: nulls(req_size)),
- response_type: COMPRESSABLE,
+ response_type: :COMPRESSABLE,
response_parameters: [p_cls.new(size: resp_size)])
yield req
resp = @queue.pop
- assert_equal(PayloadType.lookup(COMPRESSABLE), resp.payload.type,
+ assert_equal(:COMPRESSABLE, resp.payload.type,
'payload type is wrong')
assert_equal(resp_size, resp.payload.body.length,
'payload body #{i} has the wrong length')
@@ -149,11 +149,13 @@ class NamedTests
# FAILED
def large_unary
req_size, wanted_response_size = 271_828, 314_159
- payload = Payload.new(type: COMPRESSABLE, body: nulls(req_size))
- req = SimpleRequest.new(response_type: COMPRESSABLE,
+ payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
+ req = SimpleRequest.new(response_type: :COMPRESSABLE,
response_size: wanted_response_size,
payload: payload)
resp = @stub.unary_call(req)
+ assert_equal(:COMPRESSABLE, resp.payload.type,
+ 'large_unary: payload had the wrong type')
assert_equal(wanted_response_size, resp.payload.body.length,
'large_unary: payload had the wrong length')
assert_equal(nulls(wanted_response_size), resp.payload.body,
@@ -185,12 +187,12 @@ class NamedTests
def server_streaming
msg_sizes = [31_415, 9, 2653, 58_979]
response_spec = msg_sizes.map { |s| ResponseParameters.new(size: s) }
- req = StreamingOutputCallRequest.new(response_type: COMPRESSABLE,
+ req = StreamingOutputCallRequest.new(response_type: :COMPRESSABLE,
response_parameters: response_spec)
resps = @stub.streaming_output_call(req)
resps.each_with_index do |r, i|
assert i < msg_sizes.length, 'too many responses'
- assert_equal(PayloadType.lookup(COMPRESSABLE), r.payload.type,
+ assert_equal(:COMPRESSABLE, r.payload.type,
'payload type is wrong')
assert_equal(msg_sizes[i], r.payload.body.length,
'payload body #{i} has the wrong length')
@@ -235,7 +237,7 @@ def parse_options
end
end.parse!
- %w(server_host, server_port, test_case).each do |arg|
+ %w(server_host server_port test_case).each do |arg|
if options[arg].nil?
fail(OptionParser::MissingArgument, "please specify --#{arg}")
end
diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb
index 9273dcdf91..1a08eb97df 100755
--- a/src/ruby/bin/interop/interop_server.rb
+++ b/src/ruby/bin/interop/interop_server.rb
@@ -104,7 +104,7 @@ class TestTarget < Grpc::Testing::TestService::Service
def unary_call(simple_req, _call)
req_size = simple_req.response_size
- SimpleResponse.new(payload: Payload.new(type: COMPRESSABLE,
+ SimpleResponse.new(payload: Payload.new(type: :COMPRESSABLE,
body: nulls(req_size)))
end
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index e948504e9e..a6dbbf3aca 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -68,7 +68,7 @@ $CFLAGS << ' -Wno-return-type '
$CFLAGS << ' -Wall '
$CFLAGS << ' -pedantic '
-$LDFLAGS << ' -lgrpc -lgpr -levent -levent_pthreads -levent_core'
+$LDFLAGS << ' -lgrpc -lgpr'
# crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
#
diff --git a/src/ruby/ext/grpc/rb_credentials.c b/src/ruby/ext/grpc/rb_credentials.c
index 5dec51824d..31f47f3b76 100644
--- a/src/ruby/ext/grpc/rb_credentials.c
+++ b/src/ruby/ext/grpc/rb_credentials.c
@@ -214,6 +214,7 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
VALUE pem_cert_chain = Qnil;
grpc_rb_credentials *wrapper = NULL;
grpc_credentials *creds = NULL;
+ /* TODO: Remove mandatory arg when we support default roots. */
/* "12" == 1 mandatory arg, 2 (credentials) is optional */
rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key,
&pem_cert_chain);
@@ -225,22 +226,12 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
return Qnil;
}
if (pem_private_key == Qnil && pem_cert_chain == Qnil) {
- creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs),
- RSTRING_LEN(pem_root_certs), NULL, 0,
- NULL, 0);
- } else if (pem_cert_chain == Qnil) {
- creds = grpc_ssl_credentials_create(
- RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
- RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
- RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
- } else if (pem_private_key == Qnil) {
- creds = grpc_ssl_credentials_create(
- RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs), NULL, 0,
- RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+ creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL);
} else {
+ grpc_ssl_pem_key_cert_pair key_cert_pair = {RSTRING_PTR(pem_private_key),
+ RSTRING_PTR(pem_cert_chain)};
creds = grpc_ssl_credentials_create(
- RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
- RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key), NULL, 0);
+ RSTRING_PTR(pem_root_certs), &key_cert_pair);
}
if (creds == NULL) {
rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c
index e534c11444..4f6c67ea5e 100644
--- a/src/ruby/ext/grpc/rb_server_credentials.c
+++ b/src/ruby/ext/grpc/rb_server_credentials.c
@@ -145,8 +145,10 @@ static ID id_pem_cert_chain;
static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
VALUE pem_private_key,
VALUE pem_cert_chain) {
+ /* TODO support multiple key cert pairs in the ruby API. */
grpc_rb_server_credentials *wrapper = NULL;
grpc_server_credentials *creds = NULL;
+ grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
Data_Get_Struct(self, grpc_rb_server_credentials, wrapper);
if (pem_cert_chain == Qnil) {
rb_raise(rb_eRuntimeError,
@@ -157,15 +159,13 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
"could not create a server credential: nil pem_private_key");
return Qnil;
}
+ key_cert_pair.private_key = RSTRING_PTR(pem_private_key);
+ key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain);
if (pem_root_certs == Qnil) {
- creds = grpc_ssl_server_credentials_create(
- NULL, 0, RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
- RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+ creds = grpc_ssl_server_credentials_create(NULL, &key_cert_pair, 1);
} else {
- creds = grpc_ssl_server_credentials_create(
- RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
- RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
- RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+ creds = grpc_ssl_server_credentials_create(RSTRING_PTR(pem_root_certs),
+ &key_cert_pair, 1);
}
if (creds == NULL) {
rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");