diff options
author | Tim Emiola <temiola@google.com> | 2015-01-21 16:26:34 -0800 |
---|---|---|
committer | Tim Emiola <temiola@google.com> | 2015-01-21 16:26:34 -0800 |
commit | ec12a7d972f1b9a64961efde535f466db9226d78 (patch) | |
tree | 183d04da35fe09f1d8d199c29746119cada11adb /src | |
parent | 8a3ca244b789a36edfcfaa7cf1c5f4a5ece1427a (diff) | |
parent | 2021d031687418fe4fe09d2e2efcdc4b3e833ffc (diff) |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src')
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"); |