sync up with master

pull/209/head
Yang Gao 10 years ago
commit 3ed874c861
  1. 1
      Makefile
  2. 2
      build.json
  3. 5
      examples/tips/client.h
  4. 17
      examples/tips/client_main.cc
  5. 67
      include/grpc/grpc_http.h
  6. 99
      src/core/channel/call_op_string.c
  7. 8
      src/core/channel/channel_args.c
  8. 22
      src/core/channel/channel_stack.c
  9. 5
      src/core/channel/channel_stack.h
  10. 3
      src/core/channel/connected_channel.c
  11. 12
      src/core/channel/http_client_filter.c
  12. 148
      src/core/channel/http_server_filter.c
  13. 82
      src/core/httpcli/format_request.c
  14. 2
      src/core/security/auth.c
  15. 48
      src/core/support/string.c
  16. 21
      src/core/support/string.h
  17. 5
      src/core/surface/call.c
  18. 2
      src/core/surface/call.h
  19. 80
      src/core/surface/event_string.c
  20. 22
      src/node/binding.gyp
  21. 7
      src/node/examples/math_server.js
  22. 0
      src/node/ext/byte_buffer.cc
  23. 0
      src/node/ext/byte_buffer.h
  24. 0
      src/node/ext/call.cc
  25. 0
      src/node/ext/call.h
  26. 0
      src/node/ext/channel.cc
  27. 0
      src/node/ext/channel.h
  28. 0
      src/node/ext/completion_queue_async_worker.cc
  29. 0
      src/node/ext/completion_queue_async_worker.h
  30. 0
      src/node/ext/credentials.cc
  31. 0
      src/node/ext/credentials.h
  32. 0
      src/node/ext/event.cc
  33. 0
      src/node/ext/event.h
  34. 0
      src/node/ext/node_grpc.cc
  35. 0
      src/node/ext/server.cc
  36. 0
      src/node/ext/server.h
  37. 0
      src/node/ext/server_credentials.cc
  38. 0
      src/node/ext/server_credentials.h
  39. 0
      src/node/ext/tag.cc
  40. 0
      src/node/ext/tag.h
  41. 0
      src/node/ext/timeval.cc
  42. 0
      src/node/ext/timeval.h
  43. 4
      src/node/index.js
  44. 2
      src/node/interop/interop_client.js
  45. 2
      src/node/package.json
  46. 14
      src/node/src/client.js
  47. 0
      src/node/src/common.js
  48. 5
      src/node/src/server.js
  49. 132
      src/node/src/surface_client.js
  50. 145
      src/node/src/surface_server.js
  51. 13
      src/node/test/call_test.js
  52. 98
      src/node/test/client_server_test.js
  53. 21
      src/node/test/end_to_end_test.js
  54. 5
      src/node/test/interop_sanity_test.js
  55. 15
      src/node/test/server_test.js
  56. 55
      src/node/test/surface_test.js
  57. 27
      src/ruby/README.md
  58. 99
      src/ruby/bin/interop/interop_client.rb
  59. 19
      src/ruby/bin/interop/interop_server.rb
  60. 17
      src/ruby/ext/grpc/rb_credentials.c
  61. 12
      src/ruby/ext/grpc/rb_server.c
  62. 1
      src/ruby/grpc.gemspec
  63. 2
      src/ruby/lib/grpc/generic/active_call.rb
  64. 26
      src/ruby/spec/call_spec.rb
  65. 21
      src/ruby/spec/channel_spec.rb
  66. 102
      src/ruby/spec/client_server_spec.rb
  67. 10
      src/ruby/spec/generic/active_call_spec.rb
  68. 144
      src/ruby/spec/generic/client_stub_spec.rb
  69. 7
      src/ruby/spec/generic/rpc_server_spec.rb
  70. 45
      src/ruby/spec/port_picker.rb
  71. 5
      src/ruby/spec/server_spec.rb
  72. 9
      test/core/channel/metadata_buffer_test.c
  73. 69
      test/core/echo/server.c
  74. 126
      test/core/end2end/cq_verifier.c
  75. 6
      test/core/end2end/tests/census_simple_request.c
  76. 6
      test/core/end2end/tests/simple_request.c
  77. 6
      test/core/security/credentials_test.c
  78. 7
      test/core/statistics/hash_table_test.c
  79. 21
      test/core/transport/chttp2/hpack_table_test.c
  80. 17
      test/core/transport/chttp2/stream_encoder_test.c
  81. 19
      test/core/transport/chttp2/timeout_encoding_test.c
  82. 19
      test/core/transport/metadata_test.c
  83. 9
      tools/clang-format/clang-format-all.sh
  84. 11
      tools/clang-format/config.sh
  85. 5
      tools/dockerfile/grpc_java/Dockerfile
  86. 18
      tools/dockerfile/grpc_java_base/Dockerfile
  87. 8
      tools/dockerfile/grpc_ruby/Dockerfile
  88. 24
      tools/gce_setup/cloud_prod_runner.sh
  89. 144
      tools/gce_setup/grpc_docker.sh
  90. 28
      tools/gce_setup/interop_test_runner.sh
  91. 38
      tools/gce_setup/shared_startup_funcs.sh
  92. 7
      tools/run_tests/build_node.sh
  93. 8
      tools/run_tests/build_php.sh
  94. 2
      tools/run_tests/run_lcov.sh
  95. 10
      tools/run_tests/run_node.sh
  96. 24
      tools/run_tests/run_tests.py
  97. 2
      vsprojects/vs2013/gpr.vcxproj
  98. 6
      vsprojects/vs2013/gpr.vcxproj.filters

@ -1233,7 +1233,6 @@ PUBLIC_HEADERS_C += \
include/grpc/support/port_platform.h \ include/grpc/support/port_platform.h \
include/grpc/support/slice.h \ include/grpc/support/slice.h \
include/grpc/support/slice_buffer.h \ include/grpc/support/slice_buffer.h \
include/grpc/support/string.h \
include/grpc/support/sync.h \ include/grpc/support/sync.h \
include/grpc/support/sync_generic.h \ include/grpc/support/sync_generic.h \
include/grpc/support/sync_posix.h \ include/grpc/support/sync_posix.h \

@ -202,7 +202,6 @@
"include/grpc/support/port_platform.h", "include/grpc/support/port_platform.h",
"include/grpc/support/slice.h", "include/grpc/support/slice.h",
"include/grpc/support/slice_buffer.h", "include/grpc/support/slice_buffer.h",
"include/grpc/support/string.h",
"include/grpc/support/sync.h", "include/grpc/support/sync.h",
"include/grpc/support/sync_generic.h", "include/grpc/support/sync_generic.h",
"include/grpc/support/sync_posix.h", "include/grpc/support/sync_posix.h",
@ -218,6 +217,7 @@
"headers": [ "headers": [
"src/core/support/cpu.h", "src/core/support/cpu.h",
"src/core/support/murmur_hash.h", "src/core/support/murmur_hash.h",
"src/core/support/string.h",
"src/core/support/thd_internal.h" "src/core/support/thd_internal.h"
], ],
"src": [ "src": [

@ -31,6 +31,9 @@
* *
*/ */
#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
#include <grpc++/channel_interface.h> #include <grpc++/channel_interface.h>
#include <grpc++/status.h> #include <grpc++/status.h>
@ -52,3 +55,5 @@ class Client {
} // namespace tips } // namespace tips
} // namespace examples } // namespace examples
} // namespace grpc } // namespace grpc
#endif // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_

@ -41,30 +41,29 @@
#include "examples/tips/client.h" #include "examples/tips/client.h"
#include "test/cpp/util/create_test_channel.h" #include "test/cpp/util/create_test_channel.h"
DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls."); DEFINE_int32(server_port, 443, "Server port.");
DEFINE_bool(use_prod_roots, true, "True to use SSL roots for production GFE"); DEFINE_string(server_host,
DEFINE_int32(server_port, 0, "Server port."); "pubsub-staging.googleapis.com", "Server host to connect to");
DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
DEFINE_string(server_host_override, "foo.test.google.com",
"Override the server host which is sent in HTTP header");
int main(int argc, char** argv) { int main(int argc, char** argv) {
grpc_init(); grpc_init();
google::ParseCommandLineFlags(&argc, &argv, true); google::ParseCommandLineFlags(&argc, &argv, true);
gpr_log(GPR_INFO, "Start TIPS client"); gpr_log(GPR_INFO, "Start TIPS client");
GPR_ASSERT(FLAGS_server_port);
const int host_port_buf_size = 1024; const int host_port_buf_size = 1024;
char host_port[host_port_buf_size]; char host_port[host_port_buf_size];
snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(), snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
FLAGS_server_port); FLAGS_server_port);
std::shared_ptr<grpc::ChannelInterface> channel( std::shared_ptr<grpc::ChannelInterface> channel(
grpc::CreateTestChannel(host_port, FLAGS_server_host_override, grpc::CreateTestChannel(host_port,
FLAGS_enable_ssl, FLAGS_use_prod_roots)); FLAGS_server_host,
true, // enable SSL
true)); // use prod roots
grpc::examples::tips::Client client(channel); grpc::examples::tips::Client client(channel);
grpc::Status s = client.CreateTopic("test"); grpc::Status s = client.CreateTopic("test");
gpr_log(GPR_INFO, "return code %d", s.code());
GPR_ASSERT(s.IsOk()); GPR_ASSERT(s.IsOk());
channel.reset(); channel.reset();

@ -0,0 +1,67 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPC_GRPC_HTTP_H__
#define __GRPC_GRPC_HTTP_H__
#ifdef __cplusplus
extern "C" {
#endif
/* HTTP GET support.
HTTP2 servers can publish statically generated text content served
via HTTP2 GET queries by publishing one or more grpc_http_server_page
elements via repeated GRPC_ARG_SERVE_OVER_HTTP elements in the servers
channel_args.
This is not:
- a general purpose web server
- particularly fast
It's useful for being able to serve up some static content (maybe some
javascript to be able to interact with your GRPC server?) */
typedef struct {
const char *path;
const char *content_type;
const char *content;
} grpc_http_server_page;
#define GRPC_ARG_SERVE_OVER_HTTP "grpc.serve_over_http"
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_GRPC_HTTP_H__ */

@ -41,110 +41,89 @@
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
#define MAX_APPEND 1024 static void put_metadata(gpr_strvec *b, grpc_mdelem *md) {
gpr_strvec_add(b, gpr_strdup(" key="));
gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice),
GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT));
typedef struct { gpr_strvec_add(b, gpr_strdup(" value="));
size_t cap; gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->value->slice),
size_t len; GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT));
char *buffer;
} buf;
static void bprintf(buf *b, const char *fmt, ...) {
va_list arg;
if (b->len + MAX_APPEND > b->cap) {
b->cap = GPR_MAX(b->len + MAX_APPEND, b->cap * 3 / 2);
b->buffer = gpr_realloc(b->buffer, b->cap);
}
va_start(arg, fmt);
b->len += vsprintf(b->buffer + b->len, fmt, arg);
va_end(arg);
}
static void bputs(buf *b, const char *s) {
size_t slen = strlen(s);
if (b->len + slen + 1 > b->cap) {
b->cap = GPR_MAX(b->len + slen + 1, b->cap * 3 / 2);
b->buffer = gpr_realloc(b->buffer, b->cap);
}
strcat(b->buffer, s);
b->len += slen;
}
static void put_metadata(buf *b, grpc_mdelem *md) {
char *txt;
txt = gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice),
GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT);
bputs(b, " key=");
bputs(b, txt);
gpr_free(txt);
txt = gpr_hexdump((char *)GPR_SLICE_START_PTR(md->value->slice),
GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT);
bputs(b, " value=");
bputs(b, txt);
gpr_free(txt);
} }
char *grpc_call_op_string(grpc_call_op *op) { char *grpc_call_op_string(grpc_call_op *op) {
buf b = {0, 0, 0}; char *tmp;
char *out;
gpr_strvec b;
gpr_strvec_init(&b);
switch (op->dir) { switch (op->dir) {
case GRPC_CALL_DOWN: case GRPC_CALL_DOWN:
bprintf(&b, ">"); gpr_strvec_add(&b, gpr_strdup(">"));
break; break;
case GRPC_CALL_UP: case GRPC_CALL_UP:
bprintf(&b, "<"); gpr_strvec_add(&b, gpr_strdup("<"));
break; break;
} }
switch (op->type) { switch (op->type) {
case GRPC_SEND_METADATA: case GRPC_SEND_METADATA:
bprintf(&b, "SEND_METADATA"); gpr_strvec_add(&b, gpr_strdup("SEND_METADATA"));
put_metadata(&b, op->data.metadata); put_metadata(&b, op->data.metadata);
break; break;
case GRPC_SEND_DEADLINE: case GRPC_SEND_DEADLINE:
bprintf(&b, "SEND_DEADLINE %d.%09d", op->data.deadline.tv_sec, gpr_asprintf(&tmp, "SEND_DEADLINE %d.%09d", op->data.deadline.tv_sec,
op->data.deadline.tv_nsec); op->data.deadline.tv_nsec);
gpr_strvec_add(&b, tmp);
break; break;
case GRPC_SEND_START: case GRPC_SEND_START:
bprintf(&b, "SEND_START pollset=%p", op->data.start.pollset); gpr_asprintf(&tmp, "SEND_START pollset=%p", op->data.start.pollset);
gpr_strvec_add(&b, tmp);
break; break;
case GRPC_SEND_MESSAGE: case GRPC_SEND_MESSAGE:
bprintf(&b, "SEND_MESSAGE"); gpr_strvec_add(&b, gpr_strdup("SEND_MESSAGE"));
break;
case GRPC_SEND_PREFORMATTED_MESSAGE:
gpr_strvec_add(&b, gpr_strdup("SEND_PREFORMATTED_MESSAGE"));
break; break;
case GRPC_SEND_FINISH: case GRPC_SEND_FINISH:
bprintf(&b, "SEND_FINISH"); gpr_strvec_add(&b, gpr_strdup("SEND_FINISH"));
break; break;
case GRPC_REQUEST_DATA: case GRPC_REQUEST_DATA:
bprintf(&b, "REQUEST_DATA"); gpr_strvec_add(&b, gpr_strdup("REQUEST_DATA"));
break; break;
case GRPC_RECV_METADATA: case GRPC_RECV_METADATA:
bprintf(&b, "RECV_METADATA"); gpr_strvec_add(&b, gpr_strdup("RECV_METADATA"));
put_metadata(&b, op->data.metadata); put_metadata(&b, op->data.metadata);
break; break;
case GRPC_RECV_DEADLINE: case GRPC_RECV_DEADLINE:
bprintf(&b, "RECV_DEADLINE %d.%09d", op->data.deadline.tv_sec, gpr_asprintf(&tmp, "RECV_DEADLINE %d.%09d", op->data.deadline.tv_sec,
op->data.deadline.tv_nsec); op->data.deadline.tv_nsec);
gpr_strvec_add(&b, tmp);
break; break;
case GRPC_RECV_END_OF_INITIAL_METADATA: case GRPC_RECV_END_OF_INITIAL_METADATA:
bprintf(&b, "RECV_END_OF_INITIAL_METADATA"); gpr_strvec_add(&b, gpr_strdup("RECV_END_OF_INITIAL_METADATA"));
break; break;
case GRPC_RECV_MESSAGE: case GRPC_RECV_MESSAGE:
bprintf(&b, "RECV_MESSAGE"); gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE"));
break; break;
case GRPC_RECV_HALF_CLOSE: case GRPC_RECV_HALF_CLOSE:
bprintf(&b, "RECV_HALF_CLOSE"); gpr_strvec_add(&b, gpr_strdup("RECV_HALF_CLOSE"));
break; break;
case GRPC_RECV_FINISH: case GRPC_RECV_FINISH:
bprintf(&b, "RECV_FINISH"); gpr_strvec_add(&b, gpr_strdup("RECV_FINISH"));
break; break;
case GRPC_CANCEL_OP: case GRPC_CANCEL_OP:
bprintf(&b, "CANCEL_OP"); gpr_strvec_add(&b, gpr_strdup("CANCEL_OP"));
break; break;
} }
bprintf(&b, " flags=0x%08x", op->flags); gpr_asprintf(&tmp, " flags=0x%08x", op->flags);
gpr_strvec_add(&b, tmp);
out = gpr_strvec_flatten(&b, NULL);
gpr_strvec_destroy(&b);
return b.buffer; return out;
} }
void grpc_call_log_op(char *file, int line, gpr_log_severity severity, void grpc_call_log_op(char *file, int line, gpr_log_severity severity,

@ -52,7 +52,9 @@ static grpc_arg copy_arg(const grpc_arg *src) {
break; break;
case GRPC_ARG_POINTER: case GRPC_ARG_POINTER:
dst.value.pointer = src->value.pointer; dst.value.pointer = src->value.pointer;
dst.value.pointer.p = src->value.pointer.copy(src->value.pointer.p); dst.value.pointer.p = src->value.pointer.copy
? src->value.pointer.copy(src->value.pointer.p)
: src->value.pointer.p;
break; break;
} }
return dst; return dst;
@ -91,7 +93,9 @@ void grpc_channel_args_destroy(grpc_channel_args *a) {
case GRPC_ARG_INTEGER: case GRPC_ARG_INTEGER:
break; break;
case GRPC_ARG_POINTER: case GRPC_ARG_POINTER:
a->args[i].value.pointer.destroy(a->args[i].value.pointer.p); if (a->args[i].value.pointer.destroy) {
a->args[i].value.pointer.destroy(a->args[i].value.pointer.p);
}
break; break;
} }
gpr_free(a->args[i].key); gpr_free(a->args[i].key);

@ -202,6 +202,17 @@ grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) {
static void do_nothing(void *user_data, grpc_op_error error) {} static void do_nothing(void *user_data, grpc_op_error error) {}
void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
grpc_mdelem *mdelem) {
grpc_call_op metadata_op;
metadata_op.type = GRPC_RECV_METADATA;
metadata_op.dir = GRPC_CALL_UP;
metadata_op.done_cb = do_nothing;
metadata_op.user_data = NULL;
metadata_op.data.metadata = mdelem;
grpc_call_next_op(cur_elem, &metadata_op);
}
void grpc_call_element_send_metadata(grpc_call_element *cur_elem, void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
grpc_mdelem *mdelem) { grpc_mdelem *mdelem) {
grpc_call_op metadata_op; grpc_call_op metadata_op;
@ -209,7 +220,7 @@ void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
metadata_op.dir = GRPC_CALL_DOWN; metadata_op.dir = GRPC_CALL_DOWN;
metadata_op.done_cb = do_nothing; metadata_op.done_cb = do_nothing;
metadata_op.user_data = NULL; metadata_op.user_data = NULL;
metadata_op.data.metadata = grpc_mdelem_ref(mdelem); metadata_op.data.metadata = mdelem;
grpc_call_next_op(cur_elem, &metadata_op); grpc_call_next_op(cur_elem, &metadata_op);
} }
@ -221,3 +232,12 @@ void grpc_call_element_send_cancel(grpc_call_element *cur_elem) {
cancel_op.user_data = NULL; cancel_op.user_data = NULL;
grpc_call_next_op(cur_elem, &cancel_op); grpc_call_next_op(cur_elem, &cancel_op);
} }
void grpc_call_element_send_finish(grpc_call_element *cur_elem) {
grpc_call_op cancel_op;
cancel_op.type = GRPC_SEND_FINISH;
cancel_op.dir = GRPC_CALL_DOWN;
cancel_op.done_cb = do_nothing;
cancel_op.user_data = NULL;
grpc_call_next_op(cur_elem, &cancel_op);
}

@ -69,6 +69,8 @@ typedef enum {
GRPC_SEND_START, GRPC_SEND_START,
/* send a message to the channels peer */ /* send a message to the channels peer */
GRPC_SEND_MESSAGE, GRPC_SEND_MESSAGE,
/* send a pre-formatted message to the channels peer */
GRPC_SEND_PREFORMATTED_MESSAGE,
/* send half-close to the channels peer */ /* send half-close to the channels peer */
GRPC_SEND_FINISH, GRPC_SEND_FINISH,
/* request that more data be allowed through flow control */ /* request that more data be allowed through flow control */
@ -294,7 +296,10 @@ void grpc_call_log_op(char *file, int line, gpr_log_severity severity,
void grpc_call_element_send_metadata(grpc_call_element *cur_elem, void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
grpc_mdelem *elem); grpc_mdelem *elem);
void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
grpc_mdelem *elem);
void grpc_call_element_send_cancel(grpc_call_element *cur_elem); void grpc_call_element_send_cancel(grpc_call_element *cur_elem);
void grpc_call_element_send_finish(grpc_call_element *cur_elem);
#ifdef GRPC_CHANNEL_STACK_TRACE #ifdef GRPC_CHANNEL_STACK_TRACE
#define GRPC_CALL_LOG_OP(sev, elem, op) grpc_call_log_op(sev, elem, op) #define GRPC_CALL_LOG_OP(sev, elem, op) grpc_call_log_op(sev, elem, op)

@ -140,6 +140,8 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_sopb_add_begin_message(&calld->outgoing_sopb, grpc_sopb_add_begin_message(&calld->outgoing_sopb,
grpc_byte_buffer_length(op->data.message), grpc_byte_buffer_length(op->data.message),
op->flags); op->flags);
/* fall-through */
case GRPC_SEND_PREFORMATTED_MESSAGE:
copy_byte_buffer_to_stream_ops(op->data.message, &calld->outgoing_sopb); copy_byte_buffer_to_stream_ops(op->data.message, &calld->outgoing_sopb);
calld->outgoing_buffer_length_estimate += calld->outgoing_buffer_length_estimate +=
(5 + grpc_byte_buffer_length(op->data.message)); (5 + grpc_byte_buffer_length(op->data.message));
@ -445,6 +447,7 @@ static void recv_batch(void *user_data, grpc_transport *transport,
(int)calld->incoming_message.length, (int)calld->incoming_message.length,
(int)calld->incoming_message_length); (int)calld->incoming_message_length);
recv_error(chand, calld, __LINE__, message); recv_error(chand, calld, __LINE__, message);
gpr_free(message);
} }
call_op.type = GRPC_RECV_HALF_CLOSE; call_op.type = GRPC_RECV_HALF_CLOSE;
call_op.dir = GRPC_CALL_UP; call_op.dir = GRPC_CALL_UP;

@ -69,8 +69,8 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
/* Send : prefixed headers, which have to be before any application /* Send : prefixed headers, which have to be before any application
* layer headers. */ * layer headers. */
calld->sent_headers = 1; calld->sent_headers = 1;
grpc_call_element_send_metadata(elem, channeld->method); grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->method));
grpc_call_element_send_metadata(elem, channeld->scheme); grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->scheme));
} }
grpc_call_next_op(elem, op); grpc_call_next_op(elem, op);
break; break;
@ -78,12 +78,12 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
if (!calld->sent_headers) { if (!calld->sent_headers) {
/* Send : prefixed headers, if we haven't already */ /* Send : prefixed headers, if we haven't already */
calld->sent_headers = 1; calld->sent_headers = 1;
grpc_call_element_send_metadata(elem, channeld->method); grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->method));
grpc_call_element_send_metadata(elem, channeld->scheme); grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->scheme));
} }
/* Send non : prefixed headers */ /* Send non : prefixed headers */
grpc_call_element_send_metadata(elem, channeld->te_trailers); grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->te_trailers));
grpc_call_element_send_metadata(elem, channeld->content_type); grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->content_type));
grpc_call_next_op(elem, op); grpc_call_next_op(elem, op);
break; break;
default: default:

@ -34,29 +34,80 @@
#include "src/core/channel/http_server_filter.h" #include "src/core/channel/http_server_filter.h"
#include <string.h> #include <string.h>
#include <grpc/grpc_http.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
typedef enum { NOT_RECEIVED, POST, GET } known_method_type;
typedef struct {
grpc_mdelem *path;
grpc_mdelem *content_type;
grpc_byte_buffer *content;
} gettable;
typedef struct call_data { typedef struct call_data {
int sent_status; known_method_type seen_method;
int seen_scheme; gpr_uint8 sent_status;
int seen_method; gpr_uint8 seen_scheme;
int seen_te_trailers; gpr_uint8 seen_te_trailers;
grpc_mdelem *path;
} call_data; } call_data;
typedef struct channel_data { typedef struct channel_data {
grpc_mdelem *te_trailers; grpc_mdelem *te_trailers;
grpc_mdelem *method; grpc_mdelem *method_get;
grpc_mdelem *method_post;
grpc_mdelem *http_scheme; grpc_mdelem *http_scheme;
grpc_mdelem *https_scheme; grpc_mdelem *https_scheme;
/* TODO(klempner): Remove this once we stop using it */ /* TODO(klempner): Remove this once we stop using it */
grpc_mdelem *grpc_scheme; grpc_mdelem *grpc_scheme;
grpc_mdelem *content_type; grpc_mdelem *content_type;
grpc_mdelem *status; grpc_mdelem *status_ok;
grpc_mdelem *status_not_found;
grpc_mdstr *path_key;
size_t gettable_count;
gettable *gettables;
} channel_data; } channel_data;
/* used to silence 'variable not used' warnings */ /* used to silence 'variable not used' warnings */
static void ignore_unused(void *ignored) {} static void ignore_unused(void *ignored) {}
/* Handle 'GET': not technically grpc, so probably a web browser hitting
us */
static void payload_done(void *elem, grpc_op_error error) {
if (error == GRPC_OP_OK) {
grpc_call_element_send_finish(elem);
}
}
static void handle_get(grpc_call_element *elem) {
channel_data *channeld = elem->channel_data;
call_data *calld = elem->call_data;
grpc_call_op op;
size_t i;
for (i = 0; i < channeld->gettable_count; i++) {
if (channeld->gettables[i].path == calld->path) {
grpc_call_element_send_metadata(elem,
grpc_mdelem_ref(channeld->status_ok));
grpc_call_element_send_metadata(
elem, grpc_mdelem_ref(channeld->gettables[i].content_type));
op.type = GRPC_SEND_PREFORMATTED_MESSAGE;
op.dir = GRPC_CALL_DOWN;
op.flags = 0;
op.data.message = channeld->gettables[i].content;
op.done_cb = payload_done;
op.user_data = elem;
grpc_call_next_op(elem, &op);
}
}
grpc_call_element_send_metadata(elem,
grpc_mdelem_ref(channeld->status_not_found));
grpc_call_element_send_finish(elem);
}
/* Called either: /* Called either:
- in response to an API call (or similar) from above, to send something - in response to an API call (or similar) from above, to send something
- a network event (or similar) from below, to receive something - a network event (or similar) from below, to receive something
@ -73,14 +124,17 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
case GRPC_RECV_METADATA: case GRPC_RECV_METADATA:
/* Check if it is one of the headers we care about. */ /* Check if it is one of the headers we care about. */
if (op->data.metadata == channeld->te_trailers || if (op->data.metadata == channeld->te_trailers ||
op->data.metadata == channeld->method || op->data.metadata == channeld->method_get ||
op->data.metadata == channeld->method_post ||
op->data.metadata == channeld->http_scheme || op->data.metadata == channeld->http_scheme ||
op->data.metadata == channeld->https_scheme || op->data.metadata == channeld->https_scheme ||
op->data.metadata == channeld->grpc_scheme || op->data.metadata == channeld->grpc_scheme ||
op->data.metadata == channeld->content_type) { op->data.metadata == channeld->content_type) {
/* swallow it */ /* swallow it */
if (op->data.metadata == channeld->method) { if (op->data.metadata == channeld->method_get) {
calld->seen_method = 1; calld->seen_method = GET;
} else if (op->data.metadata == channeld->method_post) {
calld->seen_method = POST;
} else if (op->data.metadata->key == channeld->http_scheme->key) { } else if (op->data.metadata->key == channeld->http_scheme->key) {
calld->seen_scheme = 1; calld->seen_scheme = 1;
} else if (op->data.metadata == channeld->te_trailers) { } else if (op->data.metadata == channeld->te_trailers) {
@ -108,7 +162,7 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_mdelem_unref(op->data.metadata); grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK); op->done_cb(op->user_data, GRPC_OP_OK);
} else if (op->data.metadata->key == channeld->te_trailers->key || } else if (op->data.metadata->key == channeld->te_trailers->key ||
op->data.metadata->key == channeld->method->key || op->data.metadata->key == channeld->method_post->key ||
op->data.metadata->key == channeld->http_scheme->key || op->data.metadata->key == channeld->http_scheme->key ||
op->data.metadata->key == channeld->content_type->key) { op->data.metadata->key == channeld->content_type->key) {
gpr_log(GPR_ERROR, "Invalid %s: header: '%s'", gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
@ -120,6 +174,13 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_mdelem_unref(op->data.metadata); grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK); op->done_cb(op->user_data, GRPC_OP_OK);
grpc_call_element_send_cancel(elem); grpc_call_element_send_cancel(elem);
} else if (op->data.metadata->key == channeld->path_key) {
if (calld->path != NULL) {
gpr_log(GPR_ERROR, "Received :path twice");
grpc_mdelem_unref(calld->path);
}
calld->path = op->data.metadata;
op->done_cb(op->user_data, GRPC_OP_OK);
} else { } else {
/* pass the event up */ /* pass the event up */
grpc_call_next_op(elem, op); grpc_call_next_op(elem, op);
@ -129,14 +190,21 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
/* Have we seen the required http2 transport headers? /* Have we seen the required http2 transport headers?
(:method, :scheme, content-type, with :path and :authority covered (:method, :scheme, content-type, with :path and :authority covered
at the channel level right now) */ at the channel level right now) */
if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers) { if (calld->seen_method == POST && calld->seen_scheme &&
calld->seen_te_trailers && calld->path) {
grpc_call_element_recv_metadata(elem, calld->path);
calld->path = NULL;
grpc_call_next_op(elem, op); grpc_call_next_op(elem, op);
} else if (calld->seen_method == GET) {
handle_get(elem);
} else { } else {
if (!calld->seen_method) { if (calld->seen_method == NOT_RECEIVED) {
gpr_log(GPR_ERROR, "Missing :method header"); gpr_log(GPR_ERROR, "Missing :method header");
} else if (!calld->seen_scheme) { }
if (!calld->seen_scheme) {
gpr_log(GPR_ERROR, "Missing :scheme header"); gpr_log(GPR_ERROR, "Missing :scheme header");
} else if (!calld->seen_te_trailers) { }
if (!calld->seen_te_trailers) {
gpr_log(GPR_ERROR, "Missing te trailers header"); gpr_log(GPR_ERROR, "Missing te trailers header");
} }
/* Error this call out */ /* Error this call out */
@ -151,7 +219,8 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
if (!calld->sent_status) { if (!calld->sent_status) {
calld->sent_status = 1; calld->sent_status = 1;
/* status is reffed by grpc_call_element_send_metadata */ /* status is reffed by grpc_call_element_send_metadata */
grpc_call_element_send_metadata(elem, channeld->status); grpc_call_element_send_metadata(elem,
grpc_mdelem_ref(channeld->status_ok));
} }
grpc_call_next_op(elem, op); grpc_call_next_op(elem, op);
break; break;
@ -189,9 +258,10 @@ static void init_call_elem(grpc_call_element *elem,
ignore_unused(channeld); ignore_unused(channeld);
/* initialize members */ /* initialize members */
calld->path = NULL;
calld->sent_status = 0; calld->sent_status = 0;
calld->seen_scheme = 0; calld->seen_scheme = 0;
calld->seen_method = 0; calld->seen_method = NOT_RECEIVED;
calld->seen_te_trailers = 0; calld->seen_te_trailers = 0;
} }
@ -201,14 +271,20 @@ static void destroy_call_elem(grpc_call_element *elem) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data; channel_data *channeld = elem->channel_data;
ignore_unused(calld);
ignore_unused(channeld); ignore_unused(channeld);
if (calld->path) {
grpc_mdelem_unref(calld->path);
}
} }
/* Constructor for channel_data */ /* Constructor for channel_data */
static void init_channel_elem(grpc_channel_element *elem, static void init_channel_elem(grpc_channel_element *elem,
const grpc_channel_args *args, grpc_mdctx *mdctx, const grpc_channel_args *args, grpc_mdctx *mdctx,
int is_first, int is_last) { int is_first, int is_last) {
size_t i;
size_t gettable_capacity = 0;
/* grab pointers to our data from the channel element */ /* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data; channel_data *channeld = elem->channel_data;
@ -220,13 +296,40 @@ static void init_channel_elem(grpc_channel_element *elem,
/* initialize members */ /* initialize members */
channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers"); channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200"); channeld->status_ok = grpc_mdelem_from_strings(mdctx, ":status", "200");
channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST"); channeld->status_not_found =
grpc_mdelem_from_strings(mdctx, ":status", "404");
channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST");
channeld->method_get = grpc_mdelem_from_strings(mdctx, ":method", "GET");
channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http"); channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https"); channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc"); channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
channeld->path_key = grpc_mdstr_from_string(mdctx, ":path");
channeld->content_type = channeld->content_type =
grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc"); grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
/* initialize http download support */
channeld->gettable_count = 0;
channeld->gettables = NULL;
for (i = 0; i < args->num_args; i++) {
if (0 == strcmp(args->args[i].key, GRPC_ARG_SERVE_OVER_HTTP)) {
gettable *g;
gpr_slice slice;
grpc_http_server_page *p = args->args[i].value.pointer.p;
if (channeld->gettable_count == gettable_capacity) {
gettable_capacity =
GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1);
channeld->gettables =
gpr_realloc(channeld->gettables, gettable_capacity * sizeof(gettable));
}
g = &channeld->gettables[channeld->gettable_count++];
g->path = grpc_mdelem_from_strings(mdctx, ":path", p->path);
g->content_type =
grpc_mdelem_from_strings(mdctx, "content-type", p->content_type);
slice = gpr_slice_from_copied_string(p->content);
g->content = grpc_byte_buffer_create(&slice, 1);
}
}
} }
/* Destructor for channel data */ /* Destructor for channel data */
@ -235,12 +338,15 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
channel_data *channeld = elem->channel_data; channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers); grpc_mdelem_unref(channeld->te_trailers);
grpc_mdelem_unref(channeld->status); grpc_mdelem_unref(channeld->status_ok);
grpc_mdelem_unref(channeld->method); grpc_mdelem_unref(channeld->status_not_found);
grpc_mdelem_unref(channeld->method_post);
grpc_mdelem_unref(channeld->method_get);
grpc_mdelem_unref(channeld->http_scheme); grpc_mdelem_unref(channeld->http_scheme);
grpc_mdelem_unref(channeld->https_scheme); grpc_mdelem_unref(channeld->https_scheme);
grpc_mdelem_unref(channeld->grpc_scheme); grpc_mdelem_unref(channeld->grpc_scheme);
grpc_mdelem_unref(channeld->content_type); grpc_mdelem_unref(channeld->content_type);
grpc_mdstr_unref(channeld->path_key);
} }
const grpc_channel_filter grpc_http_server_filter = { const grpc_channel_filter grpc_http_server_filter = {

@ -37,67 +37,57 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "src/core/support/string.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/slice.h> #include <grpc/support/slice.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
typedef struct { static void fill_common_header(const grpc_httpcli_request *request, gpr_strvec *buf) {
size_t length;
size_t capacity;
char *data;
} sbuf;
static void sbuf_append(sbuf *buf, const char *bytes, size_t len) {
if (buf->length + len > buf->capacity) {
buf->capacity = GPR_MAX(buf->length + len, buf->capacity * 3 / 2);
buf->data = gpr_realloc(buf->data, buf->capacity);
}
memcpy(buf->data + buf->length, bytes, len);
buf->length += len;
}
static void sbprintf(sbuf *buf, const char *fmt, ...) {
char temp[GRPC_HTTPCLI_MAX_HEADER_LENGTH];
size_t len;
va_list args;
va_start(args, fmt);
len = vsprintf(temp, fmt, args);
va_end(args);
sbuf_append(buf, temp, len);
}
static void fill_common_header(const grpc_httpcli_request *request, sbuf *buf) {
size_t i; size_t i;
sbprintf(buf, "%s HTTP/1.0\r\n", request->path); gpr_strvec_add(buf, gpr_strdup(request->path));
gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n"));
/* just in case some crazy server really expects HTTP/1.1 */ /* just in case some crazy server really expects HTTP/1.1 */
sbprintf(buf, "Host: %s\r\n", request->host); gpr_strvec_add(buf, gpr_strdup("Host: "));
sbprintf(buf, "Connection: close\r\n"); gpr_strvec_add(buf, gpr_strdup(request->host));
sbprintf(buf, "User-Agent: %s\r\n", GRPC_HTTPCLI_USER_AGENT); gpr_strvec_add(buf, gpr_strdup("\r\n"));
gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n"));
gpr_strvec_add(buf, gpr_strdup("User-Agent: "GRPC_HTTPCLI_USER_AGENT"\r\n"));
/* user supplied headers */ /* user supplied headers */
for (i = 0; i < request->hdr_count; i++) { for (i = 0; i < request->hdr_count; i++) {
sbprintf(buf, "%s: %s\r\n", request->hdrs[i].key, request->hdrs[i].value); gpr_strvec_add(buf, gpr_strdup(request->hdrs[i].key));
gpr_strvec_add(buf, gpr_strdup(": "));
gpr_strvec_add(buf, gpr_strdup(request->hdrs[i].value));
gpr_strvec_add(buf, gpr_strdup("\r\n"));
} }
} }
gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request) { gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request) {
sbuf out = {0, 0, NULL}; gpr_strvec out;
char *flat;
size_t flat_len;
sbprintf(&out, "GET "); gpr_strvec_init(&out);
gpr_strvec_add(&out, gpr_strdup("GET "));
fill_common_header(request, &out); fill_common_header(request, &out);
sbprintf(&out, "\r\n"); gpr_strvec_add(&out, gpr_strdup("\r\n"));
return gpr_slice_new(out.data, out.length, gpr_free); flat = gpr_strvec_flatten(&out, &flat_len);
gpr_strvec_destroy(&out);
return gpr_slice_new(flat, flat_len, gpr_free);
} }
gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
const char *body_bytes, const char *body_bytes,
size_t body_size) { size_t body_size) {
sbuf out = {0, 0, NULL}; gpr_strvec out;
char *tmp;
size_t out_len;
size_t i; size_t i;
sbprintf(&out, "POST "); gpr_strvec_init(&out);
gpr_strvec_add(&out, gpr_strdup("POST "));
fill_common_header(request, &out); fill_common_header(request, &out);
if (body_bytes) { if (body_bytes) {
gpr_uint8 has_content_type = 0; gpr_uint8 has_content_type = 0;
@ -108,14 +98,18 @@ gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
} }
} }
if (!has_content_type) { if (!has_content_type) {
sbprintf(&out, "Content-Type: text/plain\r\n"); gpr_strvec_add(&out, gpr_strdup("Content-Type: text/plain\r\n"));
} }
sbprintf(&out, "Content-Length: %lu\r\n", (unsigned long)body_size); gpr_asprintf(&tmp, "Content-Length: %lu\r\n", (unsigned long)body_size);
gpr_strvec_add(&out, tmp);
} }
sbprintf(&out, "\r\n"); gpr_strvec_add(&out, gpr_strdup("\r\n"));
tmp = gpr_strvec_flatten(&out, &out_len);
if (body_bytes) { if (body_bytes) {
sbuf_append(&out, body_bytes, body_size); tmp = gpr_realloc(tmp, out_len + body_size);
memcpy(tmp + out_len, body_bytes, body_size);
out_len += body_size;
} }
return gpr_slice_new(out.data, out.length, gpr_free); return gpr_slice_new(tmp, out_len, gpr_free);
} }

@ -57,7 +57,7 @@ static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
grpc_call_element *elem = (grpc_call_element *)user_data; grpc_call_element *elem = (grpc_call_element *)user_data;
size_t i; size_t i;
for (i = 0; i < num_md; i++) { for (i = 0; i < num_md; i++) {
grpc_call_element_send_metadata(elem, md_elems[i]); grpc_call_element_send_metadata(elem, grpc_mdelem_ref(md_elems[i]));
} }
grpc_call_next_op(elem, &((call_data *)elem->call_data)->op); grpc_call_next_op(elem, &((call_data *)elem->call_data)->op);
} }

@ -14,7 +14,7 @@
* in the documentation and/or other materials provided with the * in the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Google Inc. nor the names of its * * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@ -152,3 +152,49 @@ int gpr_ltoa(long value, char *string) {
string[i] = 0; string[i] = 0;
return i; return i;
} }
char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) {
size_t out_length = 0;
size_t i;
char *out;
for (i = 0; i < nstrs; i++) {
out_length += strlen(strs[i]);
}
out_length += 1; /* null terminator */
out = gpr_malloc(out_length);
out_length = 0;
for (i = 0; i < nstrs; i++) {
size_t slen = strlen(strs[i]);
memcpy(out + out_length, strs[i], slen);
out_length += slen;
}
out[out_length] = 0;
if (final_length != NULL) {
*final_length = out_length;
}
return out;
}
void gpr_strvec_init(gpr_strvec *sv) {
memset(sv, 0, sizeof(*sv));
}
void gpr_strvec_destroy(gpr_strvec *sv) {
size_t i;
for (i = 0; i < sv->count; i++) {
gpr_free(sv->strs[i]);
}
gpr_free(sv->strs);
}
void gpr_strvec_add(gpr_strvec *sv, char *str) {
if (sv->count == sv->capacity) {
sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2);
sv->strs = gpr_realloc(sv->strs, sizeof(char*) * sv->capacity);
}
sv->strs[sv->count++] = str;
}
char *gpr_strvec_flatten(gpr_strvec *sv, size_t *final_length) {
return gpr_strjoin((const char**)sv->strs, sv->count, final_length);
}

@ -81,6 +81,27 @@ void gpr_reverse_bytes(char *str, int len);
the result is undefined. */ the result is undefined. */
int gpr_asprintf(char **strp, const char *format, ...); int gpr_asprintf(char **strp, const char *format, ...);
/* Join a set of strings, returning the resulting string.
Total combined length (excluding null terminator) is returned in total_length
if it is non-null. */
char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length);
/* A vector of strings... for building up a final string one piece at a time */
typedef struct {
char **strs;
size_t count;
size_t capacity;
} gpr_strvec;
/* Initialize/destroy */
void gpr_strvec_init(gpr_strvec *strs);
void gpr_strvec_destroy(gpr_strvec *strs);
/* Add a string to a strvec, takes ownership of the string */
void gpr_strvec_add(gpr_strvec *strs, char *add);
/* Return a joined string with all added substrings, optionally setting
total_length as per gpr_strjoin */
char *gpr_strvec_flatten(gpr_strvec *strs, size_t *total_length);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -981,3 +981,8 @@ void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) {
call->have_alarm = 1; call->have_alarm = 1;
grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now()); grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now());
} }
grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) {
return CALL_STACK_FROM_CALL(call);
}

@ -64,6 +64,8 @@ void grpc_call_client_initial_metadata_complete(
void grpc_call_set_deadline(grpc_call_element *surface_element, void grpc_call_set_deadline(grpc_call_element *surface_element,
gpr_timespec deadline); gpr_timespec deadline);
grpc_call_stack *grpc_call_get_call_stack(grpc_call *call);
/* Given the top call_element, get the call object. */ /* Given the top call_element, get the call object. */
grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element); grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element);

@ -38,8 +38,10 @@
#include "src/core/support/string.h" #include "src/core/support/string.h"
#include <grpc/byte_buffer.h> #include <grpc/byte_buffer.h>
static size_t addhdr(char *p, grpc_event *ev) { static void addhdr(gpr_strvec *buf, grpc_event *ev) {
return sprintf(p, "tag:%p call:%p", ev->tag, (void *)ev->call); char *tmp;
gpr_asprintf(&tmp, "tag:%p call:%p", ev->tag, (void *)ev->call);
gpr_strvec_add(buf, tmp);
} }
static const char *errstr(grpc_op_error err) { static const char *errstr(grpc_op_error err) {
@ -52,72 +54,84 @@ static const char *errstr(grpc_op_error err) {
return "UNKNOWN_UNKNOWN"; return "UNKNOWN_UNKNOWN";
} }
static size_t adderr(char *p, grpc_op_error err) { static void adderr(gpr_strvec *buf, grpc_op_error err) {
return sprintf(p, " err=%s", errstr(err)); char *tmp;
gpr_asprintf(&tmp, " err=%s", errstr(err));
gpr_strvec_add(buf, tmp);
} }
char *grpc_event_string(grpc_event *ev) { char *grpc_event_string(grpc_event *ev) {
char buffer[1024]; char *out;
char *p = buffer; char *tmp;
gpr_strvec buf;
if (ev == NULL) return gpr_strdup("null"); if (ev == NULL) return gpr_strdup("null");
gpr_strvec_init(&buf);
switch (ev->type) { switch (ev->type) {
case GRPC_SERVER_SHUTDOWN: case GRPC_SERVER_SHUTDOWN:
p += sprintf(p, "SERVER_SHUTDOWN"); gpr_strvec_add(&buf, gpr_strdup("SERVER_SHUTDOWN"));
break; break;
case GRPC_QUEUE_SHUTDOWN: case GRPC_QUEUE_SHUTDOWN:
p += sprintf(p, "QUEUE_SHUTDOWN"); gpr_strvec_add(&buf, gpr_strdup("QUEUE_SHUTDOWN"));
break; break;
case GRPC_READ: case GRPC_READ:
p += sprintf(p, "READ: "); gpr_strvec_add(&buf, gpr_strdup("READ: "));
p += addhdr(p, ev); addhdr(&buf, ev);
if (ev->data.read) { if (ev->data.read) {
p += sprintf(p, " %d bytes", gpr_asprintf(&tmp, " %d bytes",
(int)grpc_byte_buffer_length(ev->data.read)); (int)grpc_byte_buffer_length(ev->data.read));
gpr_strvec_add(&buf, tmp);
} else { } else {
p += sprintf(p, " end-of-stream"); gpr_strvec_add(&buf, gpr_strdup(" end-of-stream"));
} }
break; break;
case GRPC_INVOKE_ACCEPTED: case GRPC_INVOKE_ACCEPTED:
p += sprintf(p, "INVOKE_ACCEPTED: "); gpr_strvec_add(&buf, gpr_strdup("INVOKE_ACCEPTED: "));
p += addhdr(p, ev); addhdr(&buf, ev);
p += adderr(p, ev->data.invoke_accepted); adderr(&buf, ev->data.invoke_accepted);
break; break;
case GRPC_WRITE_ACCEPTED: case GRPC_WRITE_ACCEPTED:
p += sprintf(p, "WRITE_ACCEPTED: "); gpr_strvec_add(&buf, gpr_strdup("WRITE_ACCEPTED: "));
p += addhdr(p, ev); addhdr(&buf, ev);
p += adderr(p, ev->data.write_accepted); adderr(&buf, ev->data.write_accepted);
break; break;
case GRPC_FINISH_ACCEPTED: case GRPC_FINISH_ACCEPTED:
p += sprintf(p, "FINISH_ACCEPTED: "); gpr_strvec_add(&buf, gpr_strdup("FINISH_ACCEPTED: "));
p += addhdr(p, ev); addhdr(&buf, ev);
p += adderr(p, ev->data.write_accepted); adderr(&buf, ev->data.write_accepted);
break; break;
case GRPC_CLIENT_METADATA_READ: case GRPC_CLIENT_METADATA_READ:
p += sprintf(p, "CLIENT_METADATA_READ: "); gpr_strvec_add(&buf, gpr_strdup("CLIENT_METADATA_READ: "));
p += addhdr(p, ev); addhdr(&buf, ev);
p += sprintf(p, " %d elements", (int)ev->data.client_metadata_read.count); gpr_asprintf(&tmp, " %d elements",
(int)ev->data.client_metadata_read.count);
gpr_strvec_add(&buf, tmp);
break; break;
case GRPC_FINISHED: case GRPC_FINISHED:
p += sprintf(p, "FINISHED: "); gpr_strvec_add(&buf, gpr_strdup("FINISHED: "));
p += addhdr(p, ev); addhdr(&buf, ev);
p += sprintf(p, " status=%d details='%s' %d metadata elements", gpr_asprintf(&tmp, " status=%d details='%s' %d metadata elements",
ev->data.finished.status, ev->data.finished.details, ev->data.finished.status, ev->data.finished.details,
(int)ev->data.finished.metadata_count); (int)ev->data.finished.metadata_count);
gpr_strvec_add(&buf, tmp);
break; break;
case GRPC_SERVER_RPC_NEW: case GRPC_SERVER_RPC_NEW:
p += sprintf(p, "SERVER_RPC_NEW: "); gpr_strvec_add(&buf, gpr_strdup("SERVER_RPC_NEW: "));
p += addhdr(p, ev); addhdr(&buf, ev);
p += sprintf(p, " method='%s' host='%s' %d metadata elements", gpr_asprintf(&tmp, " method='%s' host='%s' %d metadata elements",
ev->data.server_rpc_new.method, ev->data.server_rpc_new.host, ev->data.server_rpc_new.method, ev->data.server_rpc_new.host,
(int)ev->data.server_rpc_new.metadata_count); (int)ev->data.server_rpc_new.metadata_count);
gpr_strvec_add(&buf, tmp);
break; break;
case GRPC_COMPLETION_DO_NOT_USE: case GRPC_COMPLETION_DO_NOT_USE:
p += sprintf(p, "DO_NOT_USE (this is a bug)"); gpr_strvec_add(&buf, gpr_strdup("DO_NOT_USE (this is a bug)"));
p += addhdr(p, ev); addhdr(&buf, ev);
break; break;
} }
return gpr_strdup(buffer); out = gpr_strvec_flatten(&buf, NULL);
gpr_strvec_destroy(&buf);
return out;
} }

@ -28,17 +28,17 @@
}, },
"target_name": "grpc", "target_name": "grpc",
"sources": [ "sources": [
"byte_buffer.cc", "ext/byte_buffer.cc",
"call.cc", "ext/call.cc",
"channel.cc", "ext/channel.cc",
"completion_queue_async_worker.cc", "ext/completion_queue_async_worker.cc",
"credentials.cc", "ext/credentials.cc",
"event.cc", "ext/event.cc",
"node_grpc.cc", "ext/node_grpc.cc",
"server.cc", "ext/server.cc",
"server_credentials.cc", "ext/server_credentials.cc",
"tag.cc", "ext/tag.cc",
"timeval.cc" "ext/timeval.cc"
], ],
'conditions' : [ 'conditions' : [
['no_install=="yes"', { ['no_install=="yes"', {

@ -52,7 +52,8 @@ var Server = grpc.buildServer([math.Math.service]);
*/ */
function mathDiv(call, cb) { function mathDiv(call, cb) {
var req = call.request; var req = call.request;
if (req.divisor == 0) { // Unary + is explicit coersion to integer
if (+req.divisor === 0) {
cb(new Error('cannot divide by zero')); cb(new Error('cannot divide by zero'));
} }
cb(null, { cb(null, {
@ -89,7 +90,7 @@ function mathSum(call, cb) {
// Here, call is a standard readable Node object Stream // Here, call is a standard readable Node object Stream
var sum = 0; var sum = 0;
call.on('data', function(data) { call.on('data', function(data) {
sum += data.num | 0; sum += (+data.num);
}); });
call.on('end', function() { call.on('end', function() {
cb(null, {num: sum}); cb(null, {num: sum});
@ -104,7 +105,7 @@ function mathDivMany(stream) {
Transform.call(this, options); Transform.call(this, options);
} }
DivTransform.prototype._transform = function(div_args, encoding, callback) { DivTransform.prototype._transform = function(div_args, encoding, callback) {
if (div_args.divisor == 0) { if (+div_args.divisor === 0) {
callback(new Error('cannot divide by zero')); callback(new Error('cannot divide by zero'));
} }
callback(null, { callback(null, {

@ -35,9 +35,9 @@ var _ = require('underscore');
var ProtoBuf = require('protobufjs'); var ProtoBuf = require('protobufjs');
var surface_client = require('./surface_client.js'); var surface_client = require('./src/surface_client.js');
var surface_server = require('./surface_server.js'); var surface_server = require('./src/surface_server.js');
var grpc = require('bindings')('grpc'); var grpc = require('bindings')('grpc');

@ -183,7 +183,7 @@ function pingPong(client, done) {
assert.equal(response.payload.body.limit - response.payload.body.offset, assert.equal(response.payload.body.limit - response.payload.body.offset,
response_sizes[index]); response_sizes[index]);
index += 1; index += 1;
if (index == 4) { if (index === 4) {
call.end(); call.end();
} else { } else {
call.write({ call.write({

@ -17,5 +17,5 @@
"mocha": "~1.21.0", "mocha": "~1.21.0",
"minimist": "^1.1.0" "minimist": "^1.1.0"
}, },
"main": "main.js" "main": "index.js"
} }

@ -105,7 +105,7 @@ function GrpcClientStream(call, serialize, deserialize) {
return; return;
} }
var data = event.data; var data = event.data;
if (self.push(data) && data != null) { if (self.push(self.deserialize(data)) && data != null) {
self._call.startRead(readCallback); self._call.startRead(readCallback);
} else { } else {
reading = false; reading = false;
@ -155,11 +155,19 @@ GrpcClientStream.prototype._read = function(size) {
*/ */
GrpcClientStream.prototype._write = function(chunk, encoding, callback) { GrpcClientStream.prototype._write = function(chunk, encoding, callback) {
var self = this; var self = this;
self._call.startWrite(chunk, function(event) { self._call.startWrite(self.serialize(chunk), function(event) {
callback(); callback();
}, 0); }, 0);
}; };
/**
* Cancel the ongoing call. If the call has not already finished, it will finish
* with status CANCELLED.
*/
GrpcClientStream.prototype.cancel = function() {
this._call.cancel();
};
/** /**
* Make a request on the channel to the given method with the given arguments * Make a request on the channel to the given method with the given arguments
* @param {grpc.Channel} channel The channel on which to make the request * @param {grpc.Channel} channel The channel on which to make the request
@ -185,7 +193,7 @@ function makeRequest(channel,
if (metadata) { if (metadata) {
call.addMetadata(metadata); call.addMetadata(metadata);
} }
return new GrpcClientStream(call); return new GrpcClientStream(call, serialize, deserialize);
} }
/** /**

@ -151,7 +151,7 @@ function GrpcServerStream(call, serialize, deserialize) {
return; return;
} }
var data = event.data; var data = event.data;
if (self.push(deserialize(data)) && data != null) { if (self.push(self.deserialize(data)) && data != null) {
self._call.startRead(readCallback); self._call.startRead(readCallback);
} else { } else {
reading = false; reading = false;
@ -233,7 +233,7 @@ function Server(options) {
function handleNewCall(event) { function handleNewCall(event) {
var call = event.call; var call = event.call;
var data = event.data; var data = event.data;
if (data == null) { if (data === null) {
return; return;
} }
server.requestCall(handleNewCall); server.requestCall(handleNewCall);
@ -246,6 +246,7 @@ function Server(options) {
call.serverAccept(function(event) { call.serverAccept(function(event) {
if (event.data.code === grpc.status.CANCELLED) { if (event.data.code === grpc.status.CANCELLED) {
cancelled = true; cancelled = true;
stream.emit('cancelled');
} }
}, 0); }, 0);
call.serverEndInitialMetadata(0); call.serverEndInitialMetadata(0);

@ -63,114 +63,80 @@ util.inherits(ClientReadableObjectStream, Readable);
* client side. Extends from stream.Readable. * client side. Extends from stream.Readable.
* @constructor * @constructor
* @param {stream} stream Underlying binary Duplex stream for the call * @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/ */
function ClientReadableObjectStream(stream, deserialize, options) { function ClientReadableObjectStream(stream) {
options = _.extend(options, {objectMode: true}); var options = {objectMode: true};
Readable.call(this, options); Readable.call(this, options);
this._stream = stream; this._stream = stream;
var self = this; var self = this;
forwardEvent(stream, this, 'status'); forwardEvent(stream, this, 'status');
forwardEvent(stream, this, 'metadata'); forwardEvent(stream, this, 'metadata');
this._stream.on('data', function forwardData(chunk) { this._stream.on('data', function forwardData(chunk) {
if (!self.push(deserialize(chunk))) { if (!self.push(chunk)) {
self._stream.pause(); self._stream.pause();
} }
}); });
this._stream.pause(); this._stream.pause();
} }
util.inherits(ClientWritableObjectStream, Writable);
/** /**
* Class for representing a gRPC client streaming call as a Node stream on the * _read implementation for both types of streams that allow reading.
* client side. Extends from stream.Writable. * @this {ClientReadableObjectStream}
* @constructor * @param {number} size Ignored
* @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {object} options Stream options
*/ */
function ClientWritableObjectStream(stream, serialize, options) { function _read(size) {
options = _.extend(options, {objectMode: true}); this._stream.resume();
Writable.call(this, options);
this._stream = stream;
this._serialize = serialize;
forwardEvent(stream, this, 'status');
forwardEvent(stream, this, 'metadata');
this.on('finish', function() {
this._stream.end();
});
} }
/**
* See docs for _read
*/
ClientReadableObjectStream.prototype._read = _read;
util.inherits(ClientBidiObjectStream, Duplex); util.inherits(ClientWritableObjectStream, Writable);
/** /**
* Class for representing a gRPC bidi streaming call as a Node stream on the * Class for representing a gRPC client streaming call as a Node stream on the
* client side. Extends from stream.Duplex. * client side. Extends from stream.Writable.
* @constructor * @constructor
* @param {stream} stream Underlying binary Duplex stream for the call * @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/ */
function ClientBidiObjectStream(stream, serialize, deserialize, options) { function ClientWritableObjectStream(stream) {
options = _.extend(options, {objectMode: true}); var options = {objectMode: true};
Duplex.call(this, options); Writable.call(this, options);
this._stream = stream; this._stream = stream;
this._serialize = serialize;
var self = this;
forwardEvent(stream, this, 'status'); forwardEvent(stream, this, 'status');
forwardEvent(stream, this, 'metadata'); forwardEvent(stream, this, 'metadata');
this._stream.on('data', function forwardData(chunk) {
if (!self.push(deserialize(chunk))) {
self._stream.pause();
}
});
this._stream.pause();
this.on('finish', function() { this.on('finish', function() {
this._stream.end(); this._stream.end();
}); });
} }
/**
* _read implementation for both types of streams that allow reading.
* @this {ClientReadableObjectStream|ClientBidiObjectStream}
* @param {number} size Ignored
*/
function _read(size) {
this._stream.resume();
}
/**
* See docs for _read
*/
ClientReadableObjectStream.prototype._read = _read;
/**
* See docs for _read
*/
ClientBidiObjectStream.prototype._read = _read;
/** /**
* _write implementation for both types of streams that allow writing * _write implementation for both types of streams that allow writing
* @this {ClientWritableObjectStream|ClientBidiObjectStream} * @this {ClientWritableObjectStream}
* @param {*} chunk The value to write to the stream * @param {*} chunk The value to write to the stream
* @param {string} encoding Ignored * @param {string} encoding Ignored
* @param {function(Error)} callback Callback to call when finished writing * @param {function(Error)} callback Callback to call when finished writing
*/ */
function _write(chunk, encoding, callback) { function _write(chunk, encoding, callback) {
this._stream.write(this._serialize(chunk), encoding, callback); this._stream.write(chunk, encoding, callback);
} }
/** /**
* See docs for _write * See docs for _write
*/ */
ClientWritableObjectStream.prototype._write = _write; ClientWritableObjectStream.prototype._write = _write;
/** /**
* See docs for _write * Cancel the underlying call
*/ */
ClientBidiObjectStream.prototype._write = _write; function cancel() {
this._stream.cancel();
}
ClientReadableObjectStream.prototype.cancel = cancel;
ClientWritableObjectStream.prototype.cancel = cancel;
/** /**
* Get a function that can make unary requests to the specified method. * Get a function that can make unary requests to the specified method.
@ -196,19 +162,28 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeUnaryRequest(argument, callback, metadata, deadline) { function makeUnaryRequest(argument, callback, metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline); var stream = client.makeRequest(this.channel, method, serialize,
deserialize, metadata, deadline);
var emitter = new EventEmitter(); var emitter = new EventEmitter();
emitter.cancel = function cancel() {
stream.cancel();
};
forwardEvent(stream, emitter, 'status'); forwardEvent(stream, emitter, 'status');
forwardEvent(stream, emitter, 'metadata'); forwardEvent(stream, emitter, 'metadata');
stream.write(serialize(argument)); stream.write(argument);
stream.end(); stream.end();
stream.on('data', function forwardData(chunk) { stream.on('data', function forwardData(chunk) {
try { try {
callback(null, deserialize(chunk)); callback(null, chunk);
} catch (e) { } catch (e) {
callback(e); callback(e);
} }
}); });
stream.on('status', function forwardStatus(status) {
if (status.code !== client.status.OK) {
callback(status);
}
});
return emitter; return emitter;
} }
return makeUnaryRequest; return makeUnaryRequest;
@ -236,15 +211,21 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeClientStreamRequest(callback, metadata, deadline) { function makeClientStreamRequest(callback, metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline); var stream = client.makeRequest(this.channel, method, serialize,
var obj_stream = new ClientWritableObjectStream(stream, serialize, {}); deserialize, metadata, deadline);
var obj_stream = new ClientWritableObjectStream(stream);
stream.on('data', function forwardData(chunk) { stream.on('data', function forwardData(chunk) {
try { try {
callback(null, deserialize(chunk)); callback(null, chunk);
} catch (e) { } catch (e) {
callback(e); callback(e);
} }
}); });
stream.on('status', function forwardStatus(status) {
if (status.code !== client.status.OK) {
callback(status);
}
});
return obj_stream; return obj_stream;
} }
return makeClientStreamRequest; return makeClientStreamRequest;
@ -272,9 +253,10 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeServerStreamRequest(argument, metadata, deadline) { function makeServerStreamRequest(argument, metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline); var stream = client.makeRequest(this.channel, method, serialize,
var obj_stream = new ClientReadableObjectStream(stream, deserialize, {}); deserialize, metadata, deadline);
stream.write(serialize(argument)); var obj_stream = new ClientReadableObjectStream(stream);
stream.write(argument);
stream.end(); stream.end();
return obj_stream; return obj_stream;
} }
@ -301,12 +283,8 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
* @return {EventEmitter} An event emitter for stream related events * @return {EventEmitter} An event emitter for stream related events
*/ */
function makeBidiStreamRequest(metadata, deadline) { function makeBidiStreamRequest(metadata, deadline) {
var stream = client.makeRequest(this.channel, method, metadata, deadline); return client.makeRequest(this.channel, method, serialize,
var obj_stream = new ClientBidiObjectStream(stream, deserialize, metadata, deadline);
serialize,
deserialize,
{});
return obj_stream;
} }
return makeBidiStreamRequest; return makeBidiStreamRequest;
} }

@ -54,67 +54,20 @@ util.inherits(ServerReadableObjectStream, Readable);
* server side. Extends from stream.Readable. * server side. Extends from stream.Readable.
* @constructor * @constructor
* @param {stream} stream Underlying binary Duplex stream for the call * @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/ */
function ServerReadableObjectStream(stream, deserialize, options) { function ServerReadableObjectStream(stream) {
options = _.extend(options, {objectMode: true}); var options = {objectMode: true};
Readable.call(this, options); Readable.call(this, options);
this._stream = stream; this._stream = stream;
Object.defineProperty(this, 'cancelled', { Object.defineProperty(this, 'cancelled', {
get: function() { return stream.cancelled; } get: function() { return stream.cancelled; }
}); });
var self = this; var self = this;
this._stream.on('data', function forwardData(chunk) { this._stream.on('cancelled', function() {
if (!self.push(deserialize(chunk))) { self.emit('cancelled');
self._stream.pause();
}
});
this._stream.on('end', function forwardEnd() {
self.push(null);
}); });
this._stream.pause();
}
util.inherits(ServerWritableObjectStream, Writable);
/**
* Class for representing a gRPC server streaming call as a Node stream on the
* server side. Extends from stream.Writable.
* @constructor
* @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {object} options Stream options
*/
function ServerWritableObjectStream(stream, serialize, options) {
options = _.extend(options, {objectMode: true});
Writable.call(this, options);
this._stream = stream;
this._serialize = serialize;
this.on('finish', function() {
this._stream.end();
});
}
util.inherits(ServerBidiObjectStream, Duplex);
/**
* Class for representing a gRPC bidi streaming call as a Node stream on the
* server side. Extends from stream.Duplex.
* @constructor
* @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/
function ServerBidiObjectStream(stream, serialize, deserialize, options) {
options = _.extend(options, {objectMode: true});
Duplex.call(this, options);
this._stream = stream;
this._serialize = serialize;
var self = this;
this._stream.on('data', function forwardData(chunk) { this._stream.on('data', function forwardData(chunk) {
if (!self.push(deserialize(chunk))) { if (!self.push(chunk)) {
self._stream.pause(); self._stream.pause();
} }
}); });
@ -122,9 +75,6 @@ function ServerBidiObjectStream(stream, serialize, deserialize, options) {
self.push(null); self.push(null);
}); });
this._stream.pause(); this._stream.pause();
this.on('finish', function() {
this._stream.end();
});
} }
/** /**
@ -140,39 +90,49 @@ function _read(size) {
* See docs for _read * See docs for _read
*/ */
ServerReadableObjectStream.prototype._read = _read; ServerReadableObjectStream.prototype._read = _read;
util.inherits(ServerWritableObjectStream, Writable);
/** /**
* See docs for _read * Class for representing a gRPC server streaming call as a Node stream on the
* server side. Extends from stream.Writable.
* @constructor
* @param {stream} stream Underlying binary Duplex stream for the call
*/ */
ServerBidiObjectStream.prototype._read = _read; function ServerWritableObjectStream(stream) {
var options = {objectMode: true};
Writable.call(this, options);
this._stream = stream;
this._stream.on('cancelled', function() {
self.emit('cancelled');
});
this.on('finish', function() {
this._stream.end();
});
}
/** /**
* _write implementation for both types of streams that allow writing * _write implementation for both types of streams that allow writing
* @this {ServerWritableObjectStream|ServerBidiObjectStream} * @this {ServerWritableObjectStream}
* @param {*} chunk The value to write to the stream * @param {*} chunk The value to write to the stream
* @param {string} encoding Ignored * @param {string} encoding Ignored
* @param {function(Error)} callback Callback to call when finished writing * @param {function(Error)} callback Callback to call when finished writing
*/ */
function _write(chunk, encoding, callback) { function _write(chunk, encoding, callback) {
this._stream.write(this._serialize(chunk), encoding, callback); this._stream.write(chunk, encoding, callback);
} }
/** /**
* See docs for _write * See docs for _write
*/ */
ServerWritableObjectStream.prototype._write = _write; ServerWritableObjectStream.prototype._write = _write;
/**
* See docs for _write
*/
ServerBidiObjectStream.prototype._write = _write;
/** /**
* Creates a binary stream handler function from a unary handler function * Creates a binary stream handler function from a unary handler function
* @param {function(Object, function(Error, *))} handler Unary call handler * @param {function(Object, function(Error, *))} handler Unary call handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler * @return {function(stream)} Binary stream handler
*/ */
function makeUnaryHandler(handler, serialize, deserialize) { function makeUnaryHandler(handler) {
/** /**
* Handles a stream by reading a single data value, passing it to the handler, * Handles a stream by reading a single data value, passing it to the handler,
* and writing the response back to the stream. * and writing the response back to the stream.
@ -180,15 +140,18 @@ function makeUnaryHandler(handler, serialize, deserialize) {
*/ */
return function handleUnaryCall(stream) { return function handleUnaryCall(stream) {
stream.on('data', function handleUnaryData(value) { stream.on('data', function handleUnaryData(value) {
var call = {request: deserialize(value)}; var call = {request: value};
Object.defineProperty(call, 'cancelled', { Object.defineProperty(call, 'cancelled', {
get: function() { return stream.cancelled;} get: function() { return stream.cancelled;}
}); });
stream.on('cancelled', function() {
call.emit('cancelled');
});
handler(call, function sendUnaryData(err, value) { handler(call, function sendUnaryData(err, value) {
if (err) { if (err) {
stream.emit('error', err); stream.emit('error', err);
} else { } else {
stream.write(serialize(value)); stream.write(value);
stream.end(); stream.end();
} }
}); });
@ -201,23 +164,21 @@ function makeUnaryHandler(handler, serialize, deserialize) {
* function * function
* @param {function(Readable, function(Error, *))} handler Client stream call * @param {function(Readable, function(Error, *))} handler Client stream call
* handler * handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler * @return {function(stream)} Binary stream handler
*/ */
function makeClientStreamHandler(handler, serialize, deserialize) { function makeClientStreamHandler(handler) {
/** /**
* Handles a stream by passing a deserializing stream to the handler and * Handles a stream by passing a deserializing stream to the handler and
* writing the response back to the stream. * writing the response back to the stream.
* @param {stream} stream Binary data stream * @param {stream} stream Binary data stream
*/ */
return function handleClientStreamCall(stream) { return function handleClientStreamCall(stream) {
var object_stream = new ServerReadableObjectStream(stream, deserialize, {}); var object_stream = new ServerReadableObjectStream(stream);
handler(object_stream, function sendClientStreamData(err, value) { handler(object_stream, function sendClientStreamData(err, value) {
if (err) { if (err) {
stream.emit('error', err); stream.emit('error', err);
} else { } else {
stream.write(serialize(value)); stream.write(value);
stream.end(); stream.end();
} }
}); });
@ -228,11 +189,9 @@ function makeClientStreamHandler(handler, serialize, deserialize) {
* Creates a binary stream handler function from a server stream handler * Creates a binary stream handler function from a server stream handler
* function * function
* @param {function(Writable)} handler Server stream call handler * @param {function(Writable)} handler Server stream call handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler * @return {function(stream)} Binary stream handler
*/ */
function makeServerStreamHandler(handler, serialize, deserialize) { function makeServerStreamHandler(handler) {
/** /**
* Handles a stream by attaching it to a serializing stream, and passing it to * Handles a stream by attaching it to a serializing stream, and passing it to
* the handler. * the handler.
@ -240,10 +199,8 @@ function makeServerStreamHandler(handler, serialize, deserialize) {
*/ */
return function handleServerStreamCall(stream) { return function handleServerStreamCall(stream) {
stream.on('data', function handleClientData(value) { stream.on('data', function handleClientData(value) {
var object_stream = new ServerWritableObjectStream(stream, var object_stream = new ServerWritableObjectStream(stream);
serialize, object_stream.request = value;
{});
object_stream.request = deserialize(value);
handler(object_stream); handler(object_stream);
}); });
}; };
@ -252,23 +209,10 @@ function makeServerStreamHandler(handler, serialize, deserialize) {
/** /**
* Creates a binary stream handler function from a bidi stream handler function * Creates a binary stream handler function from a bidi stream handler function
* @param {function(Duplex)} handler Unary call handler * @param {function(Duplex)} handler Unary call handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler * @return {function(stream)} Binary stream handler
*/ */
function makeBidiStreamHandler(handler, serialize, deserialize) { function makeBidiStreamHandler(handler) {
/** return handler;
* Handles a stream by wrapping it in a serializing and deserializing object
* stream, and passing it to the handler.
* @param {stream} stream Binary data stream
*/
return function handleBidiStreamCall(stream) {
var object_stream = new ServerBidiObjectStream(stream,
serialize,
deserialize,
{});
handler(object_stream);
};
} }
/** /**
@ -341,10 +285,13 @@ function makeServerConstructor(services) {
common.fullyQualifiedName(method) + ' not provided.'); common.fullyQualifiedName(method) + ' not provided.');
} }
var binary_handler = handler_makers[method_type]( var binary_handler = handler_makers[method_type](
service_handlers[service_name][decapitalize(method.name)], service_handlers[service_name][decapitalize(method.name)]);
common.serializeCls(method.resolvedResponseType.build()), var serialize = common.serializeCls(
common.deserializeCls(method.resolvedRequestType.build())); method.resolvedResponseType.build());
server.register(prefix + capitalize(method.name), binary_handler); var deserialize = common.deserializeCls(
method.resolvedRequestType.build());
server.register(prefix + capitalize(method.name), binary_handler,
serialize, deserialize);
}); });
}, this); }, this);
} }

@ -34,8 +34,6 @@
var assert = require('assert'); var assert = require('assert');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc.node');
var channel = new grpc.Channel('localhost:7070');
/** /**
* Helper function to return an absolute deadline given a relative timeout in * Helper function to return an absolute deadline given a relative timeout in
* seconds. * seconds.
@ -49,6 +47,17 @@ function getDeadline(timeout_secs) {
} }
describe('call', function() { describe('call', function() {
var channel;
var server;
before(function() {
server = new grpc.Server();
var port = server.addHttp2Port('localhost:0');
server.start();
channel = new grpc.Channel('localhost:' + port);
});
after(function() {
server.shutdown();
});
describe('constructor', function() { describe('constructor', function() {
it('should reject anything less than 3 arguments', function() { it('should reject anything less than 3 arguments', function() {
assert.throws(function() { assert.throws(function() {

@ -35,9 +35,9 @@ var assert = require('assert');
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc.node');
var Server = require('../server'); var Server = require('../src/server');
var client = require('../client'); var client = require('../src/client');
var common = require('../common'); var common = require('../src/common');
var _ = require('highland'); var _ = require('highland');
var ca_path = path.join(__dirname, 'data/ca.pem'); var ca_path = path.join(__dirname, 'data/ca.pem');
@ -77,15 +77,32 @@ function errorHandler(stream) {
}; };
} }
/**
* Wait for a cancellation instead of responding
* @param {Stream} stream
*/
function cancelHandler(stream) {
// do nothing
}
describe('echo client', function() { describe('echo client', function() {
it('should receive echo responses', function(done) { var server;
var server = new Server(); var channel;
before(function() {
server = new Server();
var port_num = server.bind('0.0.0.0:0'); var port_num = server.bind('0.0.0.0:0');
server.register('echo', echoHandler); server.register('echo', echoHandler);
server.register('error', errorHandler);
server.register('cancellation', cancelHandler);
server.start(); server.start();
channel = new grpc.Channel('localhost:' + port_num);
});
after(function() {
server.shutdown();
});
it('should receive echo responses', function(done) {
var messages = ['echo1', 'echo2', 'echo3', 'echo4']; var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
var channel = new grpc.Channel('localhost:' + port_num);
var stream = client.makeRequest( var stream = client.makeRequest(
channel, channel,
'echo'); 'echo');
@ -98,17 +115,10 @@ describe('echo client', function() {
index += 1; index += 1;
}); });
stream.on('end', function() { stream.on('end', function() {
server.shutdown();
done(); done();
}); });
}); });
it('should get an error status that the server throws', function(done) { it('should get an error status that the server throws', function(done) {
var 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( var stream = client.makeRequest(
channel, channel,
'error', 'error',
@ -121,7 +131,19 @@ describe('echo client', function() {
stream.on('status', function(status) { stream.on('status', function(status) {
assert.equal(status.code, grpc.status.UNIMPLEMENTED); assert.equal(status.code, grpc.status.UNIMPLEMENTED);
assert.equal(status.details, 'error details'); assert.equal(status.details, 'error details');
server.shutdown(); done();
});
});
it('should be able to cancel a call', function(done) {
var stream = client.makeRequest(
channel,
'cancellation',
null,
getDeadline(1));
stream.cancel();
stream.on('status', function(status) {
assert.equal(status.code, grpc.status.CANCELLED);
done(); done();
}); });
}); });
@ -129,7 +151,9 @@ describe('echo client', function() {
/* TODO(mlumish): explore options for reducing duplication between this test /* TODO(mlumish): explore options for reducing duplication between this test
* and the insecure echo client test */ * and the insecure echo client test */
describe('secure echo client', function() { describe('secure echo client', function() {
it('should recieve echo responses', function(done) { var server;
var channel;
before(function(done) {
fs.readFile(ca_path, function(err, ca_data) { fs.readFile(ca_path, function(err, ca_data) {
assert.ifError(err); assert.ifError(err);
fs.readFile(key_path, function(err, key_data) { fs.readFile(key_path, function(err, key_data) {
@ -141,34 +165,40 @@ describe('secure echo client', function() {
key_data, key_data,
pem_data); pem_data);
var server = new Server({'credentials' : server_creds}); server = new Server({'credentials' : server_creds});
var port_num = server.bind('0.0.0.0:0', true); var port_num = server.bind('0.0.0.0:0', true);
server.register('echo', echoHandler); server.register('echo', echoHandler);
server.start(); server.start();
var messages = ['echo1', 'echo2', 'echo3', 'echo4']; channel = new grpc.Channel('localhost:' + port_num, {
var channel = new grpc.Channel('localhost:' + port_num, {
'grpc.ssl_target_name_override' : 'foo.test.google.com', 'grpc.ssl_target_name_override' : 'foo.test.google.com',
'credentials' : creds 'credentials' : creds
}); });
var stream = client.makeRequest( done();
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();
});
}); });
}); });
}); });
}); });
after(function() {
server.shutdown();
});
it('should recieve echo responses', function(done) {
var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
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();
});
});
}); });

@ -56,14 +56,21 @@ function multiDone(done, count) {
} }
describe('end-to-end', function() { describe('end-to-end', function() {
var server;
var channel;
before(function() {
server = new grpc.Server();
var port_num = server.addHttp2Port('0.0.0.0:0');
server.start();
channel = new grpc.Channel('localhost:' + port_num);
});
after(function() {
server.shutdown();
});
it('should start and end a request without error', function(complete) { it('should start and end a request without error', function(complete) {
var server = new grpc.Server();
var done = multiDone(function() { var done = multiDone(function() {
complete(); complete();
server.shutdown();
}, 2); }, 2);
var port_num = server.addHttp2Port('0.0.0.0:0');
var channel = new grpc.Channel('localhost:' + port_num);
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3); deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'xyz'; var status_text = 'xyz';
@ -81,7 +88,6 @@ describe('end-to-end', function() {
done(); done();
}, 0); }, 0);
server.start();
server.requestCall(function(event) { server.requestCall(function(event) {
assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW);
var server_call = event.call; var server_call = event.call;
@ -109,13 +115,10 @@ describe('end-to-end', function() {
it('should send and receive data without error', function(complete) { it('should send and receive data without error', function(complete) {
var req_text = 'client_request'; var req_text = 'client_request';
var reply_text = 'server_response'; var reply_text = 'server_response';
var server = new grpc.Server();
var done = multiDone(function() { var done = multiDone(function() {
complete(); complete();
server.shutdown(); server.shutdown();
}, 6); }, 6);
var port_num = server.addHttp2Port('0.0.0.0:0');
var channel = new grpc.Channel('localhost:' + port_num);
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3); deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'success'; var status_text = 'success';
@ -151,8 +154,6 @@ describe('end-to-end', function() {
assert.strictEqual(event.data.toString(), reply_text); assert.strictEqual(event.data.toString(), reply_text);
done(); done();
}); });
server.start();
server.requestCall(function(event) { server.requestCall(function(event) {
assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW);
var server_call = event.call; var server_call = event.call;

@ -48,6 +48,9 @@ describe('Interop tests', function() {
port = 'localhost:' + server_obj.port; port = 'localhost:' + server_obj.port;
done(); done();
}); });
after(function() {
server.shutdown();
});
// This depends on not using a binary stream // This depends on not using a binary stream
it('should pass empty_unary', function(done) { it('should pass empty_unary', function(done) {
interop_client.runTest(port, name_override, 'empty_unary', true, done); interop_client.runTest(port, name_override, 'empty_unary', true, done);
@ -65,7 +68,7 @@ describe('Interop tests', function() {
it('should pass ping_pong', function(done) { it('should pass ping_pong', function(done) {
interop_client.runTest(port, name_override, 'ping_pong', true, done); interop_client.runTest(port, name_override, 'ping_pong', true, done);
}); });
it.skip('should pass empty_stream', function(done) { it('should pass empty_stream', function(done) {
interop_client.runTest(port, name_override, 'empty_stream', true, done); interop_client.runTest(port, name_override, 'empty_stream', true, done);
}); });
}); });

@ -33,7 +33,7 @@
var assert = require('assert'); var assert = require('assert');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc.node');
var Server = require('../server'); var Server = require('../src/server');
/** /**
* This is used for testing functions with multiple asynchronous calls that * This is used for testing functions with multiple asynchronous calls that
@ -65,17 +65,22 @@ function echoHandler(stream) {
} }
describe('echo server', function() { describe('echo server', function() {
it('should echo inputs as responses', function(done) { var server;
done = multiDone(done, 4); var channel;
var server = new Server(); before(function() {
server = new Server();
var port_num = server.bind('[::]:0'); var port_num = server.bind('[::]:0');
server.register('echo', echoHandler); server.register('echo', echoHandler);
server.start(); server.start();
channel = new grpc.Channel('localhost:' + port_num);
});
it('should echo inputs as responses', function(done) {
done = multiDone(done, 4);
var req_text = 'echo test string'; var req_text = 'echo test string';
var status_text = 'OK'; var status_text = 'OK';
var channel = new grpc.Channel('localhost:' + port_num);
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3); deadline.setSeconds(deadline.getSeconds() + 3);
var call = new grpc.Call(channel, var call = new grpc.Call(channel,

@ -33,7 +33,9 @@
var assert = require('assert'); var assert = require('assert');
var surface_server = require('../surface_server.js'); var surface_server = require('../src/surface_server.js');
var surface_client = require('../src/surface_client.js');
var ProtoBuf = require('protobufjs'); var ProtoBuf = require('protobufjs');
@ -73,3 +75,54 @@ describe('Surface server constructor', function() {
}, /math.Math/); }, /math.Math/);
}); });
}); });
describe('Surface client', function() {
var client;
var server;
before(function() {
var Server = grpc.buildServer([mathService]);
server = new Server({
'math.Math': {
'div': function(stream) {},
'divMany': function(stream) {},
'fib': function(stream) {},
'sum': function(stream) {}
}
});
var port = server.bind('localhost:0');
var Client = surface_client.makeClientConstructor(mathService);
client = new Client('localhost:' + port);
});
after(function() {
server.shutdown();
});
it('Should correctly cancel a unary call', function(done) {
var call = client.div({'divisor': 0, 'dividend': 0}, function(err, resp) {
assert.strictEqual(err.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
it('Should correctly cancel a client stream call', function(done) {
var call = client.sum(function(err, resp) {
assert.strictEqual(err.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
it('Should correctly cancel a server stream call', function(done) {
var call = client.fib({'limit': 5});
call.on('status', function(status) {
assert.strictEqual(status.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
it('Should correctly cancel a bidi stream call', function(done) {
var call = client.divMany();
call.on('status', function(status) {
assert.strictEqual(status.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
});

@ -14,9 +14,10 @@ INSTALLING
---------- ----------
- Install the gRPC core library - Install the gRPC core library
TODO: describe this, once the core distribution mechanism is defined. TODO: describe this, once the core distribution mechanism is defined.
```
$ gem install grpc $ gem install grpc
```
Installing from source Installing from source
@ -24,37 +25,47 @@ Installing from source
- Build or Install the gRPC core - Build or Install the gRPC core
E.g, from the root of the grpc [git repo](https://github.com/google/grpc) E.g, from the root of the grpc [git repo](https://github.com/google/grpc)
```
$ cd ../.. $ cd ../..
$ make && sudo make install $ make && sudo make install
```
- Install Ruby 2.x. Consider doing this with [RVM](http://rvm.io), it's a nice way of controlling - Install Ruby 2.x. Consider doing this with [RVM](http://rvm.io), it's a nice way of controlling
the exact ruby version that's used. the exact ruby version that's used.
```
$ command curl -sSL https://rvm.io/mpapis.asc | gpg --import - $ command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
$ \curl -sSL https://get.rvm.io | bash -s stable --ruby $ \curl -sSL https://get.rvm.io | bash -s stable --ruby
$ $
$ # follow the instructions to ensure that your're using the latest stable version of Ruby $ # follow the instructions to ensure that your're using the latest stable version of Ruby
$ # and that the rvm command is installed $ # and that the rvm command is installed
```
- Install [bundler](http://bundler.io/) - Install [bundler](http://bundler.io/)
```
$ gem install bundler $ gem install bundler
```
- Finally, install grpc ruby locally. - Finally, install grpc ruby locally.
```
$ cd <install_dir> $ cd <install_dir>
$ bundle install $ bundle install
$ rake # compiles the extension, runs the unit tests, see rake -T for other options $ rake # compiles the extension, runs the unit tests, see rake -T for other options
```
CONTENTS CONTENTS
-------- --------
Directory structure is the layout for [ruby extensions](http://guides.rubygems.org/gems-with-extensions/) Directory structure is the layout for [ruby extensions](http://guides.rubygems.org/gems-with-extensions/)
* ext: the extension code - ext:
* lib: the entrypoint grpc ruby library to be used in a 'require' statement the gRPC ruby extension
* spec: tests - lib:
* bin: example gRPC clients and servers, e.g, the entrypoint grpc ruby library to be used in a 'require' statement
- spec:
Rspec unittest
- bin:
example gRPC clients and servers, e.g,
```ruby ```ruby
# client
stub = Math::Math::Stub.new('my.test.math.server.com:8080') stub = Math::Math::Stub.new('my.test.math.server.com:8080')
req = Math::DivArgs.new(dividend: 7, divisor: 3) req = Math::DivArgs.new(dividend: 7, divisor: 3)
logger.info("div(7/3): req=#{req.inspect}") logger.info("div(7/3): req=#{req.inspect}")

@ -54,6 +54,8 @@ require 'test/cpp/interop/test_services'
require 'test/cpp/interop/messages' require 'test/cpp/interop/messages'
require 'test/cpp/interop/empty' require 'test/cpp/interop/empty'
require 'signet/ssl_config'
# loads the certificates used to access the test server securely. # loads the certificates used to access the test server securely.
def load_test_certs def load_test_certs
this_dir = File.expand_path(File.dirname(__FILE__)) this_dir = File.expand_path(File.dirname(__FILE__))
@ -62,21 +64,49 @@ def load_test_certs
files.map { |f| File.open(File.join(data_dir, f)).read } files.map { |f| File.open(File.join(data_dir, f)).read }
end end
# loads the certificates used to access the test server securely.
def load_prod_cert
fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil?
p "loading prod certs from #{ENV['SSL_CERT_FILE']}"
File.open(ENV['SSL_CERT_FILE']).read
end
# creates a Credentials from the test certificates. # creates a Credentials from the test certificates.
def test_creds def test_creds
certs = load_test_certs certs = load_test_certs
GRPC::Core::Credentials.new(certs[0]) GRPC::Core::Credentials.new(certs[0])
end end
RX_CERT = /-----BEGIN CERTIFICATE-----\n.*?-----END CERTIFICATE-----\n/m
# creates a Credentials from the production certificates.
def prod_creds
cert_text = load_prod_cert
GRPC::Core::Credentials.new(cert_text)
end
# creates a test stub that accesses host:port securely. # creates a test stub that accesses host:port securely.
def create_stub(host, port) def create_stub(host, port, is_secure, host_override, use_test_ca)
address = "#{host}:#{port}" address = "#{host}:#{port}"
stub_opts = { if is_secure
:creds => test_creds, creds = nil
GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com' if use_test_ca
} creds = test_creds
logger.info("... connecting securely to #{address}") else
Grpc::Testing::TestService::Stub.new(address, **stub_opts) creds = prod_creds
end
stub_opts = {
:creds => creds,
GRPC::Core::Channel::SSL_TARGET => host_override
}
logger.info("... connecting securely to #{address}")
Grpc::Testing::TestService::Stub.new(address, **stub_opts)
else
logger.info("... connecting insecurely to #{address}")
Grpc::Testing::TestService::Stub.new(address)
end
end end
# produces a string of null chars (\0) of length l. # produces a string of null chars (\0) of length l.
@ -133,20 +163,12 @@ class NamedTests
@stub = stub @stub = stub
end end
# TESTING
# PASSED
# FAIL
# ruby server: fails protobuf-ruby can't pass an empty message
def empty_unary def empty_unary
resp = @stub.empty_call(Empty.new) resp = @stub.empty_call(Empty.new)
assert resp.is_a?(Empty), 'empty_unary: invalid response' assert resp.is_a?(Empty), 'empty_unary: invalid response'
p 'OK: empty_unary' p 'OK: empty_unary'
end end
# TESTING
# PASSED
# ruby server
# FAILED
def large_unary def large_unary
req_size, wanted_response_size = 271_828, 314_159 req_size, wanted_response_size = 271_828, 314_159
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size)) payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
@ -163,10 +185,6 @@ class NamedTests
p 'OK: large_unary' p 'OK: large_unary'
end end
# TESTING:
# PASSED
# ruby server
# FAILED
def client_streaming def client_streaming
msg_sizes = [27_182, 8, 1828, 45_904] msg_sizes = [27_182, 8, 1828, 45_904]
wanted_aggregate_size = 74_922 wanted_aggregate_size = 74_922
@ -180,10 +198,6 @@ class NamedTests
p 'OK: client_streaming' p 'OK: client_streaming'
end end
# TESTING:
# PASSED
# ruby server
# FAILED
def server_streaming def server_streaming
msg_sizes = [31_415, 9, 2653, 58_979] msg_sizes = [31_415, 9, 2653, 58_979]
response_spec = msg_sizes.map { |s| ResponseParameters.new(size: s) } response_spec = msg_sizes.map { |s| ResponseParameters.new(size: s) }
@ -200,10 +214,6 @@ class NamedTests
p 'OK: server_streaming' p 'OK: server_streaming'
end end
# TESTING:
# PASSED
# ruby server
# FAILED
def ping_pong def ping_pong
msg_sizes = [[27_182, 31_415], [8, 9], [1828, 2653], [45_904, 58_979]] msg_sizes = [[27_182, 31_415], [8, 9], [1828, 2653], [45_904, 58_979]]
ppp = PingPongPlayer.new(msg_sizes) ppp = PingPongPlayer.new(msg_sizes)
@ -211,12 +221,23 @@ class NamedTests
resps.each { |r| ppp.queue.push(r) } resps.each { |r| ppp.queue.push(r) }
p 'OK: ping_pong' p 'OK: ping_pong'
end end
def all
all_methods = NamedTests.instance_methods(false).map(&:to_s)
all_methods.each do |m|
next if m == 'all' || m.start_with?('assert')
p "TESTCASE: #{m}"
method(m).call
end
end
end end
# validates the the command line options, returning them as a Hash. # validates the the command line options, returning them as a Hash.
def parse_options def parse_options
options = { options = {
'secure' => false,
'server_host' => nil, 'server_host' => nil,
'server_host_override' => nil,
'server_port' => nil, 'server_port' => nil,
'test_case' => nil 'test_case' => nil
} }
@ -225,6 +246,10 @@ def parse_options
opts.on('--server_host SERVER_HOST', 'server hostname') do |v| opts.on('--server_host SERVER_HOST', 'server hostname') do |v|
options['server_host'] = v options['server_host'] = v
end end
opts.on('--server_host_override HOST_OVERRIDE',
'override host via a HTTP header') do |v|
options['server_host_override'] = v
end
opts.on('--server_port SERVER_PORT', 'server port') do |v| opts.on('--server_port SERVER_PORT', 'server port') do |v|
options['server_port'] = v options['server_port'] = v
end end
@ -235,19 +260,33 @@ def parse_options
" (#{test_case_list})") do |v| " (#{test_case_list})") do |v|
options['test_case'] = v options['test_case'] = v
end end
opts.on('-s', '--use_tls', 'require a secure connection?') do |v|
options['secure'] = v
end
opts.on('-t', '--use_test_ca',
'if secure, use the test certificate?') do |v|
options['use_test_ca'] = v
end
end.parse! end.parse!
_check_options(options)
end
def _check_options(opts)
%w(server_host server_port test_case).each do |arg| %w(server_host server_port test_case).each do |arg|
if options[arg].nil? if opts[arg].nil?
fail(OptionParser::MissingArgument, "please specify --#{arg}") fail(OptionParser::MissingArgument, "please specify --#{arg}")
end end
end end
options if opts['server_host_override'].nil?
opts['server_host_override'] = opts['server_host']
end
opts
end end
def main def main
opts = parse_options opts = parse_options
stub = create_stub(opts['server_host'], opts['server_port']) stub = create_stub(opts['server_host'], opts['server_port'], opts['secure'],
opts['server_host_override'], opts['use_test_ca'])
NamedTests.new(stub).method(opts['test_case']).call NamedTests.new(stub).method(opts['test_case']).call
end end

@ -154,13 +154,17 @@ end
# validates the the command line options, returning them as a Hash. # validates the the command line options, returning them as a Hash.
def parse_options def parse_options
options = { options = {
'port' => nil 'port' => nil,
'secure' => false
} }
OptionParser.new do |opts| OptionParser.new do |opts|
opts.banner = 'Usage: --port port' opts.banner = 'Usage: --port port'
opts.on('--port PORT', 'server port') do |v| opts.on('--port PORT', 'server port') do |v|
options['port'] = v options['port'] = v
end end
opts.on('-s', '--use_tls', 'require a secure connection?') do |v|
options['secure'] = v
end
end.parse! end.parse!
if options['port'].nil? if options['port'].nil?
@ -172,10 +176,15 @@ end
def main def main
opts = parse_options opts = parse_options
host = "0.0.0.0:#{opts['port']}" host = "0.0.0.0:#{opts['port']}"
s = GRPC::RpcServer.new(creds: test_server_creds) if opts['secure']
s.add_http2_port(host, true) s = GRPC::RpcServer.new(creds: test_server_creds)
logger.info("... running securely on #{host}") s.add_http2_port(host, true)
logger.info("... running securely on #{host}")
else
s = GRPC::RpcServer.new
s.add_http2_port(host)
logger.info("... running insecurely on #{host}")
end
s.handle(TestTarget) s.handle(TestTarget)
s.run s.run
end end

@ -84,7 +84,6 @@ static void grpc_rb_credentials_mark(void *p) {
} }
/* Allocates Credential instances. /* Allocates Credential instances.
Provides safe initial defaults for the instance fields. */ Provides safe initial defaults for the instance fields. */
static VALUE grpc_rb_credentials_alloc(VALUE cls) { static VALUE grpc_rb_credentials_alloc(VALUE cls) {
grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials); grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
@ -95,7 +94,6 @@ static VALUE grpc_rb_credentials_alloc(VALUE cls) {
} }
/* Clones Credentials instances. /* Clones Credentials instances.
Gives Credentials a consistent implementation of Ruby's object copy/dup Gives Credentials a consistent implementation of Ruby's object copy/dup
protocol. */ protocol. */
static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) { static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) {
@ -124,7 +122,6 @@ static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) {
/* /*
call-seq: call-seq:
creds = Credentials.default() creds = Credentials.default()
Creates the default credential instances. */ Creates the default credential instances. */
static VALUE grpc_rb_default_credentials_create(VALUE cls) { static VALUE grpc_rb_default_credentials_create(VALUE cls) {
grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials); grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
@ -143,7 +140,6 @@ static VALUE grpc_rb_default_credentials_create(VALUE cls) {
/* /*
call-seq: call-seq:
creds = Credentials.compute_engine() creds = Credentials.compute_engine()
Creates the default credential instances. */ Creates the default credential instances. */
static VALUE grpc_rb_compute_engine_credentials_create(VALUE cls) { static VALUE grpc_rb_compute_engine_credentials_create(VALUE cls) {
grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials); grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
@ -164,7 +160,6 @@ static VALUE grpc_rb_compute_engine_credentials_create(VALUE cls) {
creds1 = ... creds1 = ...
creds2 = ... creds2 = ...
creds3 = creds1.add(creds2) creds3 = creds1.add(creds2)
Creates the default credential instances. */ Creates the default credential instances. */
static VALUE grpc_rb_composite_credentials_create(VALUE self, VALUE other) { static VALUE grpc_rb_composite_credentials_create(VALUE self, VALUE other) {
grpc_rb_credentials *self_wrapper = NULL; grpc_rb_credentials *self_wrapper = NULL;
@ -202,11 +197,9 @@ static ID id_pem_cert_chain;
... ...
creds2 = Credentials.new(pem_root_certs, pem_private_key, creds2 = Credentials.new(pem_root_certs, pem_private_key,
pem_cert_chain) pem_cert_chain)
pem_root_certs: (required) PEM encoding of the server root certificate pem_root_certs: (required) PEM encoding of the server root certificate
pem_private_key: (optional) PEM encoding of the client's private key pem_private_key: (optional) PEM encoding of the client's private key
pem_cert_chain: (optional) PEM encoding of the client's cert chain pem_cert_chain: (optional) PEM encoding of the client's cert chain
Initializes Credential instances. */ Initializes Credential instances. */
static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) { static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
VALUE pem_root_certs = Qnil; VALUE pem_root_certs = Qnil;
@ -214,6 +207,8 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
VALUE pem_cert_chain = Qnil; VALUE pem_cert_chain = Qnil;
grpc_rb_credentials *wrapper = NULL; grpc_rb_credentials *wrapper = NULL;
grpc_credentials *creds = NULL; grpc_credentials *creds = NULL;
grpc_ssl_pem_key_cert_pair key_cert_pair;
MEMZERO(&key_cert_pair, grpc_ssl_pem_key_cert_pair, 1);
/* TODO: Remove mandatory arg when we support default roots. */ /* TODO: Remove mandatory arg when we support default roots. */
/* "12" == 1 mandatory arg, 2 (credentials) is optional */ /* "12" == 1 mandatory arg, 2 (credentials) is optional */
rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key, rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key,
@ -228,10 +223,10 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
if (pem_private_key == Qnil && pem_cert_chain == Qnil) { if (pem_private_key == Qnil && pem_cert_chain == Qnil) {
creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL); creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL);
} else { } else {
grpc_ssl_pem_key_cert_pair key_cert_pair = {RSTRING_PTR(pem_private_key), key_cert_pair.private_key = RSTRING_PTR(pem_private_key);
RSTRING_PTR(pem_cert_chain)}; key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain);
creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), creds = grpc_ssl_credentials_create(
&key_cert_pair); RSTRING_PTR(pem_root_certs), &key_cert_pair);
} }
if (creds == NULL) { if (creds == NULL) {
rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");

@ -223,7 +223,7 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
VALUE port = Qnil; VALUE port = Qnil;
VALUE is_secure = Qnil; VALUE is_secure = Qnil;
grpc_rb_server *s = NULL; grpc_rb_server *s = NULL;
int added_ok = 0; int recvd_port = 0;
/* "11" == 1 mandatory args, 1 (is_secure) is optional */ /* "11" == 1 mandatory args, 1 (is_secure) is optional */
rb_scan_args(argc, argv, "11", &port, &is_secure); rb_scan_args(argc, argv, "11", &port, &is_secure);
@ -233,22 +233,22 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
return Qnil; return Qnil;
} else if (is_secure == Qnil || TYPE(is_secure) != T_TRUE) { } else if (is_secure == Qnil || TYPE(is_secure) != T_TRUE) {
added_ok = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port)); recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
if (added_ok == 0) { if (recvd_port == 0) {
rb_raise(rb_eRuntimeError, rb_raise(rb_eRuntimeError,
"could not add port %s to server, not sure why", "could not add port %s to server, not sure why",
StringValueCStr(port)); StringValueCStr(port));
} }
} else if (TYPE(is_secure) != T_FALSE) { } else if (TYPE(is_secure) != T_FALSE) {
added_ok = recvd_port =
grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port)); grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port));
if (added_ok == 0) { if (recvd_port == 0) {
rb_raise(rb_eRuntimeError, rb_raise(rb_eRuntimeError,
"could not add secure port %s to server, not sure why", "could not add secure port %s to server, not sure why",
StringValueCStr(port)); StringValueCStr(port));
} }
} }
return Qnil; return INT2NUM(recvd_port);
} }
void Init_google_rpc_server() { void Init_google_rpc_server() {

@ -22,6 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency 'xray' s.add_dependency 'xray'
s.add_dependency 'logging', '~> 1.8' s.add_dependency 'logging', '~> 1.8'
s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1' s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
s.add_dependency 'signet', '~> 0.5.1'
s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests
s.add_development_dependency 'bundler', '~> 1.7' s.add_development_dependency 'bundler', '~> 1.7'

@ -67,7 +67,7 @@ module Google
fail(ArgumentError, 'not a CompletionQueue') fail(ArgumentError, 'not a CompletionQueue')
end end
call.add_metadata(kw) if kw.length > 0 call.add_metadata(kw) if kw.length > 0
invoke_accepted, client_metadata_read = Object.new, Object.new client_metadata_read = Object.new
finished_tag = Object.new finished_tag = Object.new
call.invoke(q, client_metadata_read, finished_tag) call.invoke(q, client_metadata_read, finished_tag)
[finished_tag, client_metadata_read] [finished_tag, client_metadata_read]

@ -28,7 +28,6 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc' require 'grpc'
require 'port_picker'
include GRPC::Core::StatusCodes include GRPC::Core::StatusCodes
@ -71,16 +70,8 @@ describe GRPC::Core::Call do
before(:each) do before(:each) do
@tag = Object.new @tag = Object.new
@client_queue = GRPC::Core::CompletionQueue.new @client_queue = GRPC::Core::CompletionQueue.new
@server_queue = GRPC::Core::CompletionQueue.new fake_host = 'localhost:10101'
port = find_unused_tcp_port @ch = GRPC::Core::Channel.new(fake_host, nil)
host = "localhost:#{port}"
@server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port(host)
@ch = GRPC::Core::Channel.new(host, nil)
end
after(:each) do
@server.close
end end
describe '#start_read' do describe '#start_read' do
@ -122,19 +113,6 @@ describe GRPC::Core::Call do
end end
end end
describe '#start_write' do
it 'should cause the WRITE_ACCEPTED event' do
call = make_test_call
call.invoke(@client_queue, @tag, @tag)
expect(call.start_write(GRPC::Core::ByteBuffer.new('test_start_write'),
@tag)).to be_nil
ev = @client_queue.next(deadline)
expect(ev.call).to be_a(GRPC::Core::Call)
expect(ev.type).to be(GRPC::Core::CompletionType::WRITE_ACCEPTED)
expect(ev.tag).to be(@tag)
end
end
describe '#status' do describe '#status' do
it 'can save the status and read it back' do it 'can save the status and read it back' do
call = make_test_call call = make_test_call

@ -28,7 +28,8 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc' require 'grpc'
require 'port_picker'
FAKE_HOST='localhost:0'
def load_test_certs def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata') test_root = File.join(File.dirname(__FILE__), 'testdata')
@ -114,8 +115,7 @@ describe GRPC::Core::Channel do
describe '#create_call' do describe '#create_call' do
it 'creates a call OK' do it 'creates a call OK' do
port = find_unused_tcp_port host = FAKE_HOST
host = "localhost:#{port}"
ch = GRPC::Core::Channel.new(host, nil) ch = GRPC::Core::Channel.new(host, nil)
deadline = Time.now + 5 deadline = Time.now + 5
@ -127,8 +127,7 @@ describe GRPC::Core::Channel do
end end
it 'raises an error if called on a closed channel' do it 'raises an error if called on a closed channel' do
port = find_unused_tcp_port host = FAKE_HOST
host = "localhost:#{port}"
ch = GRPC::Core::Channel.new(host, nil) ch = GRPC::Core::Channel.new(host, nil)
ch.close ch.close
@ -142,16 +141,14 @@ describe GRPC::Core::Channel do
describe '#destroy' do describe '#destroy' do
it 'destroys a channel ok' do it 'destroys a channel ok' do
port = find_unused_tcp_port host = FAKE_HOST
host = "localhost:#{port}"
ch = GRPC::Core::Channel.new(host, nil) ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.destroy } blk = proc { ch.destroy }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'can be called more than once without error' do it 'can be called more than once without error' do
port = find_unused_tcp_port host = FAKE_HOST
host = "localhost:#{port}"
ch = GRPC::Core::Channel.new(host, nil) ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.destroy } blk = proc { ch.destroy }
blk.call blk.call
@ -167,16 +164,14 @@ describe GRPC::Core::Channel do
describe '#close' do describe '#close' do
it 'closes a channel ok' do it 'closes a channel ok' do
port = find_unused_tcp_port host = FAKE_HOST
host = "localhost:#{port}"
ch = GRPC::Core::Channel.new(host, nil) ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.close } blk = proc { ch.close }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'can be called more than once without error' do it 'can be called more than once without error' do
port = find_unused_tcp_port host = FAKE_HOST
host = "localhost:#{port}"
ch = GRPC::Core::Channel.new(host, nil) ch = GRPC::Core::Channel.new(host, nil)
blk = proc { ch.close } blk = proc { ch.close }
blk.call blk.call

@ -28,7 +28,6 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc' require 'grpc'
require 'port_picker'
require 'spec_helper' require 'spec_helper'
include GRPC::Core::CompletionType include GRPC::Core::CompletionType
@ -44,12 +43,13 @@ shared_context 'setup: tags' do
before(:example) do before(:example) do
@server_finished_tag = Object.new @server_finished_tag = Object.new
@client_finished_tag = Object.new @client_finished_tag = Object.new
@client_metadata_tag = Object.new
@server_tag = Object.new @server_tag = Object.new
@tag = Object.new @tag = Object.new
end end
def deadline def deadline
Time.now + 0.05 Time.now + 2
end end
def expect_next_event_on(queue, type, tag) def expect_next_event_on(queue, type, tag)
@ -63,27 +63,30 @@ shared_context 'setup: tags' do
ev ev
end end
def server_receives_and_responds_with(reply_text) def server_allows_client_to_proceed
reply = ByteBuffer.new(reply_text)
@server.request_call(@server_tag) @server.request_call(@server_tag)
ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE) ev = @server_queue.pluck(@server_tag, deadline)
expect(ev).not_to be_nil expect(ev).not_to be_nil
expect(ev.type).to be(SERVER_RPC_NEW) expect(ev.type).to be(SERVER_RPC_NEW)
ev.call.server_accept(@server_queue, @server_finished_tag) server_call = ev.call
ev.call.server_end_initial_metadata server_call.server_accept(@server_queue, @server_finished_tag)
ev.call.start_read(@server_tag) server_call.server_end_initial_metadata
server_call
end
def server_responds_with(server_call, reply_text)
reply = ByteBuffer.new(reply_text)
server_call.start_read(@server_tag)
ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE) ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)
expect(ev.type).to be(READ) expect(ev.type).to be(READ)
ev.call.start_write(reply, @server_tag) server_call.start_write(reply, @server_tag)
ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE) ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)
expect(ev).not_to be_nil expect(ev).not_to be_nil
expect(ev.type).to be(WRITE_ACCEPTED) expect(ev.type).to be(WRITE_ACCEPTED)
ev.call
end end
def client_sends(call, sent = 'a message') def client_sends(call, sent = 'a message')
req = ByteBuffer.new(sent) req = ByteBuffer.new(sent)
call.invoke(@client_queue, @tag, @client_finished_tag)
call.start_write(req, @tag) call.start_write(req, @tag)
ev = @client_queue.pluck(@tag, TimeConsts::INFINITE_FUTURE) ev = @client_queue.pluck(@tag, TimeConsts::INFINITE_FUTURE)
expect(ev).not_to be_nil expect(ev).not_to be_nil
@ -102,16 +105,20 @@ shared_examples 'basic GRPC message delivery is OK' do
it 'servers receive requests from clients and start responding' do it 'servers receive requests from clients and start responding' do
reply = ByteBuffer.new('the server payload') reply = ByteBuffer.new('the server payload')
call = new_client_call call = new_client_call
msg = client_sends(call) call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
# check the server rpc new was received # check the server rpc new was received
@server.request_call(@server_tag) # @server.request_call(@server_tag)
ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag) # ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)
# accept the call # accept the call
server_call = ev.call # server_call = ev.call
server_call.server_accept(@server_queue, @server_finished_tag) # server_call.server_accept(@server_queue, @server_finished_tag)
server_call.server_end_initial_metadata # server_call.server_end_initial_metadata
server_call = server_allows_client_to_proceed
# client sends a message
msg = client_sends(call)
# confirm the server can read the inbound message # confirm the server can read the inbound message
server_call.start_read(@server_tag) server_call.start_read(@server_tag)
@ -125,18 +132,19 @@ shared_examples 'basic GRPC message delivery is OK' do
it 'responses written by servers are received by the client' do it 'responses written by servers are received by the client' do
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
server_call = server_allows_client_to_proceed
client_sends(call) client_sends(call)
server_receives_and_responds_with('server_response') server_responds_with(server_call, 'server_response')
call.start_read(@tag) call.start_read(@tag)
expect_next_event_on(@client_queue, CLIENT_METADATA_READ, @tag)
ev = expect_next_event_on(@client_queue, READ, @tag) ev = expect_next_event_on(@client_queue, READ, @tag)
expect(ev.result.to_s).to eq('server_response') expect(ev.result.to_s).to eq('server_response')
end end
it 'servers can ignore a client write and send a status' do it 'servers can ignore a client write and send a status' do
call = new_client_call call = new_client_call
client_sends(call) call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
# check the server rpc new was received # check the server rpc new was received
@server.request_call(@server_tag) @server.request_call(@server_tag)
@ -150,9 +158,13 @@ shared_examples 'basic GRPC message delivery is OK' do
server_call.start_write_status(StatusCodes::NOT_FOUND, 'not found', server_call.start_write_status(StatusCodes::NOT_FOUND, 'not found',
@server_tag) @server_tag)
# Client sends some data
client_sends(call)
# client gets an empty response for the read, preceeded by some metadata. # client gets an empty response for the read, preceeded by some metadata.
call.start_read(@tag) call.start_read(@tag)
expect_next_event_on(@client_queue, CLIENT_METADATA_READ, @tag) expect_next_event_on(@client_queue, CLIENT_METADATA_READ,
@client_metadata_tag)
ev = expect_next_event_on(@client_queue, READ, @tag) ev = expect_next_event_on(@client_queue, READ, @tag)
expect(ev.tag).to be(@tag) expect(ev.tag).to be(@tag)
expect(ev.result.to_s).to eq('') expect(ev.result.to_s).to eq('')
@ -166,13 +178,14 @@ shared_examples 'basic GRPC message delivery is OK' do
it 'completes calls by sending status to client and server' do it 'completes calls by sending status to client and server' do
call = new_client_call call = new_client_call
call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
server_call = server_allows_client_to_proceed
client_sends(call) client_sends(call)
server_call = server_receives_and_responds_with('server_response') server_responds_with(server_call, 'server_response')
server_call.start_write_status(10_101, 'status code is 10101', @server_tag) server_call.start_write_status(10_101, 'status code is 10101', @server_tag)
# first the client says writes are done # first the client says writes are done
call.start_read(@tag) call.start_read(@tag)
expect_next_event_on(@client_queue, CLIENT_METADATA_READ, @tag)
expect_next_event_on(@client_queue, READ, @tag) expect_next_event_on(@client_queue, READ, @tag)
call.writes_done(@tag) call.writes_done(@tag)
@ -215,22 +228,13 @@ shared_examples 'GRPC metadata delivery works OK' do
end end
end end
it 'sends an empty hash when no metadata is added' do
call = new_client_call
client_sends(call)
# Server gets a response
@server.request_call(@server_tag)
expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)
end
it 'sends all the metadata pairs when keys and values are valid' do it 'sends all the metadata pairs when keys and values are valid' do
@valid_metadata.each do |md| @valid_metadata.each do |md|
call = new_client_call call = new_client_call
call.add_metadata(md) call.add_metadata(md)
# Client begins a call OK # Client begins a call OK
call.invoke(@client_queue, @tag, @client_finished_tag) call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
# ... server has all metadata available even though the client did not # ... server has all metadata available even though the client did not
# send a write # send a write
@ -262,7 +266,7 @@ shared_examples 'GRPC metadata delivery works OK' do
it 'raises an exception if a metadata key is invalid' do it 'raises an exception if a metadata key is invalid' do
@bad_keys.each do |md| @bad_keys.each do |md|
call = new_client_call call = new_client_call
client_sends(call) call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
# server gets the invocation # server gets the invocation
@server.request_call(@server_tag) @server.request_call(@server_tag)
@ -273,7 +277,7 @@ shared_examples 'GRPC metadata delivery works OK' do
it 'sends a hash that contains the status when no metadata is added' do it 'sends a hash that contains the status when no metadata is added' do
call = new_client_call call = new_client_call
client_sends(call) call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
# server gets the invocation # server gets the invocation
@server.request_call(@server_tag) @server.request_call(@server_tag)
@ -284,21 +288,17 @@ shared_examples 'GRPC metadata delivery works OK' do
server_call.server_accept(@server_queue, @server_finished_tag) server_call.server_accept(@server_queue, @server_finished_tag)
server_call.server_end_initial_metadata server_call.server_end_initial_metadata
# ... these server sends some data, allowing the metadata read
server_call.start_write(ByteBuffer.new('reply with metadata'),
@server_tag)
expect_next_event_on(@server_queue, WRITE_ACCEPTED, @server_tag)
# there is the HTTP status metadata, though there should not be any # there is the HTTP status metadata, though there should not be any
# TODO: update this with the bug number to be resolved # TODO: update this with the bug number to be resolved
ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, @tag) ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ,
@client_metadata_tag)
expect(ev.result).to eq(':status' => '200') expect(ev.result).to eq(':status' => '200')
end end
it 'sends all the pairs and status:200 when keys and values are valid' do it 'sends all the pairs and status:200 when keys and values are valid' do
@valid_metadata.each do |md| @valid_metadata.each do |md|
call = new_client_call call = new_client_call
client_sends(call) call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)
# server gets the invocation # server gets the invocation
@server.request_call(@server_tag) @server.request_call(@server_tag)
@ -311,7 +311,8 @@ shared_examples 'GRPC metadata delivery works OK' do
server_call.server_end_initial_metadata server_call.server_end_initial_metadata
# Now the client can read the metadata # Now the client can read the metadata
ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, @tag) ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ,
@client_metadata_tag)
replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }] replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
replace_symbols[':status'] = '200' replace_symbols[':status'] = '200'
expect(ev.result).to eq(replace_symbols) expect(ev.result).to eq(replace_symbols)
@ -322,17 +323,17 @@ end
describe 'the http client/server' do describe 'the http client/server' do
before(:example) do before(:example) do
port = find_unused_tcp_port server_host = '0.0.0.0:0'
host = "localhost:#{port}"
@client_queue = GRPC::Core::CompletionQueue.new @client_queue = GRPC::Core::CompletionQueue.new
@server_queue = GRPC::Core::CompletionQueue.new @server_queue = GRPC::Core::CompletionQueue.new
@server = GRPC::Core::Server.new(@server_queue, nil) @server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port(host) server_port = @server.add_http2_port(server_host)
@server.start @server.start
@ch = Channel.new(host, nil) @ch = Channel.new("0.0.0.0:#{server_port}", nil)
end end
after(:example) do after(:example) do
@ch.close
@server.close @server.close
end end
@ -346,16 +347,15 @@ end
describe 'the secure http client/server' do describe 'the secure http client/server' do
before(:example) do before(:example) do
certs = load_test_certs certs = load_test_certs
port = find_unused_tcp_port server_host = 'localhost:0'
host = "localhost:#{port}"
@client_queue = GRPC::Core::CompletionQueue.new @client_queue = GRPC::Core::CompletionQueue.new
@server_queue = GRPC::Core::CompletionQueue.new @server_queue = GRPC::Core::CompletionQueue.new
server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2]) server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
@server = GRPC::Core::Server.new(@server_queue, nil, server_creds) @server = GRPC::Core::Server.new(@server_queue, nil, server_creds)
@server.add_http2_port(host, true) server_port = @server.add_http2_port(server_host, true)
@server.start @server.start
args = { Channel::SSL_TARGET => 'foo.test.google.com' } args = { Channel::SSL_TARGET => 'foo.test.google.com' }
@ch = Channel.new(host, args, @ch = Channel.new("0.0.0.0:#{server_port}", args,
GRPC::Core::Credentials.new(certs[0], nil, nil)) GRPC::Core::Credentials.new(certs[0], nil, nil))
end end

@ -28,7 +28,6 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc' require 'grpc'
require_relative '../port_picker'
include GRPC::Core::StatusCodes include GRPC::Core::StatusCodes
@ -45,12 +44,11 @@ describe GRPC::ActiveCall do
@client_queue = GRPC::Core::CompletionQueue.new @client_queue = GRPC::Core::CompletionQueue.new
@server_queue = GRPC::Core::CompletionQueue.new @server_queue = GRPC::Core::CompletionQueue.new
port = find_unused_tcp_port host = '0.0.0.0:0'
host = "localhost:#{port}"
@server = GRPC::Core::Server.new(@server_queue, nil) @server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port(host) server_port = @server.add_http2_port(host)
@server.start @server.start
@ch = GRPC::Core::Channel.new(host, nil) @ch = GRPC::Core::Channel.new("localhost:#{server_port}", nil)
end end
after(:each) do after(:each) do
@ -206,7 +204,7 @@ describe GRPC::ActiveCall do
it 'get a nil msg before a status when an OK status is sent' do it 'get a nil msg before a status when an OK status is sent' do
call = make_test_call call = make_test_call
done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue,
deadline) deadline)
client_call = ActiveCall.new(call, @client_queue, @pass_through, client_call = ActiveCall.new(call, @client_queue, @pass_through,
@pass_through, deadline, @pass_through, deadline,
finished_tag: done_tag, finished_tag: done_tag,

@ -29,9 +29,9 @@
require 'grpc' require 'grpc'
require 'xray/thread_dump_signal_handler' require 'xray/thread_dump_signal_handler'
require_relative '../port_picker'
NOOP = proc { |x| x } NOOP = proc { |x| x }
FAKE_HOST = 'localhost:0'
def wakey_thread(&blk) def wakey_thread(&blk)
awake_mutex, awake_cond = Mutex.new, ConditionVariable.new awake_mutex, awake_cond = Mutex.new, ConditionVariable.new
@ -67,7 +67,7 @@ describe 'ClientStub' do
describe '#new' do describe '#new' do
it 'can be created from a host and args' do it 'can be created from a host and args' do
host = new_test_host host = FAKE_HOST
opts = { a_channel_arg: 'an_arg' } opts = { a_channel_arg: 'an_arg' }
blk = proc do blk = proc do
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(host, @cq, **opts)
@ -76,7 +76,7 @@ describe 'ClientStub' do
end end
it 'can be created with a default deadline' do it 'can be created with a default deadline' do
host = new_test_host host = FAKE_HOST
opts = { a_channel_arg: 'an_arg', deadline: 5 } opts = { a_channel_arg: 'an_arg', deadline: 5 }
blk = proc do blk = proc do
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(host, @cq, **opts)
@ -85,7 +85,7 @@ describe 'ClientStub' do
end end
it 'can be created with an channel override' do it 'can be created with an channel override' do
host = new_test_host host = FAKE_HOST
opts = { a_channel_arg: 'an_arg', channel_override: @ch } opts = { a_channel_arg: 'an_arg', channel_override: @ch }
blk = proc do blk = proc do
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(host, @cq, **opts)
@ -94,7 +94,7 @@ describe 'ClientStub' do
end end
it 'cannot be created with a bad channel override' do it 'cannot be created with a bad channel override' do
host = new_test_host host = FAKE_HOST
blk = proc do blk = proc do
opts = { a_channel_arg: 'an_arg', channel_override: Object.new } opts = { a_channel_arg: 'an_arg', channel_override: Object.new }
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(host, @cq, **opts)
@ -103,7 +103,7 @@ describe 'ClientStub' do
end end
it 'cannot be created with bad credentials' do it 'cannot be created with bad credentials' do
host = new_test_host host = FAKE_HOST
blk = proc do blk = proc do
opts = { a_channel_arg: 'an_arg', creds: Object.new } opts = { a_channel_arg: 'an_arg', creds: Object.new }
GRPC::ClientStub.new(host, @cq, **opts) GRPC::ClientStub.new(host, @cq, **opts)
@ -113,7 +113,7 @@ describe 'ClientStub' do
it 'can be created with test test credentials' do it 'can be created with test test credentials' do
certs = load_test_certs certs = load_test_certs
host = new_test_host host = FAKE_HOST
blk = proc do blk = proc do
opts = { opts = {
GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com', GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com',
@ -133,16 +133,17 @@ describe 'ClientStub' do
shared_examples 'request response' do shared_examples 'request response' do
it 'should send a request to/receive a reply from a server' do it 'should send a request to/receive a reply from a server' do
host = new_test_host server_port = create_test_server
th = run_request_response(host, @sent_msg, @resp, @pass) th = run_request_response(@sent_msg, @resp, @pass)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new("localhost:#{server_port}", @cq)
expect(get_response(stub)).to eq(@resp) expect(get_response(stub)).to eq(@resp)
th.join th.join
end end
it 'should send metadata to the server ok' do it 'should send metadata to the server ok' do
host = new_test_host server_port = create_test_server
th = run_request_response(host, @sent_msg, @resp, @pass, host = "localhost:#{server_port}"
th = run_request_response(@sent_msg, @resp, @pass,
k1: 'v1', k2: 'v2') k1: 'v1', k2: 'v2')
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
expect(get_response(stub)).to eq(@resp) expect(get_response(stub)).to eq(@resp)
@ -150,8 +151,9 @@ describe 'ClientStub' do
end end
it 'should update the sent metadata with a provided metadata updater' do it 'should update the sent metadata with a provided metadata updater' do
host = new_test_host server_port = create_test_server
th = run_request_response(host, @sent_msg, @resp, @pass, host = "localhost:#{server_port}"
th = run_request_response(@sent_msg, @resp, @pass,
k1: 'updated-v1', k2: 'v2') k1: 'updated-v1', k2: 'v2')
update_md = proc do |md| update_md = proc do |md|
md[:k1] = 'updated-v1' md[:k1] = 'updated-v1'
@ -163,8 +165,9 @@ describe 'ClientStub' do
end end
it 'should send a request when configured using an override channel' do it 'should send a request when configured using an override channel' do
alt_host = new_test_host server_port = create_test_server
th = run_request_response(alt_host, @sent_msg, @resp, @pass) alt_host = "localhost:#{server_port}"
th = run_request_response(@sent_msg, @resp, @pass)
ch = GRPC::Core::Channel.new(alt_host, nil) ch = GRPC::Core::Channel.new(alt_host, nil)
stub = GRPC::ClientStub.new('ignored-host', @cq, channel_override: ch) stub = GRPC::ClientStub.new('ignored-host', @cq, channel_override: ch)
expect(get_response(stub)).to eq(@resp) expect(get_response(stub)).to eq(@resp)
@ -172,8 +175,9 @@ describe 'ClientStub' do
end end
it 'should raise an error if the status is not OK' do it 'should raise an error if the status is not OK' do
host = new_test_host server_port = create_test_server
th = run_request_response(host, @sent_msg, @resp, @fail) host = "localhost:#{server_port}"
th = run_request_response(@sent_msg, @resp, @fail)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
blk = proc { get_response(stub) } blk = proc { get_response(stub) }
expect(&blk).to raise_error(GRPC::BadStatus) expect(&blk).to raise_error(GRPC::BadStatus)
@ -210,16 +214,18 @@ describe 'ClientStub' do
end end
it 'should send requests to/receive a reply from a server' do it 'should send requests to/receive a reply from a server' do
host = new_test_host server_port = create_test_server
th = run_client_streamer(host, @sent_msgs, @resp, @pass) host = "localhost:#{server_port}"
th = run_client_streamer(@sent_msgs, @resp, @pass)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
expect(get_response(stub)).to eq(@resp) expect(get_response(stub)).to eq(@resp)
th.join th.join
end end
it 'should send metadata to the server ok' do it 'should send metadata to the server ok' do
host = new_test_host server_port = create_test_server
th = run_client_streamer(host, @sent_msgs, @resp, @pass, host = "localhost:#{server_port}"
th = run_client_streamer(@sent_msgs, @resp, @pass,
k1: 'v1', k2: 'v2') k1: 'v1', k2: 'v2')
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
expect(get_response(stub)).to eq(@resp) expect(get_response(stub)).to eq(@resp)
@ -227,8 +233,9 @@ describe 'ClientStub' do
end end
it 'should update the sent metadata with a provided metadata updater' do it 'should update the sent metadata with a provided metadata updater' do
host = new_test_host server_port = create_test_server
th = run_client_streamer(host, @sent_msgs, @resp, @pass, host = "localhost:#{server_port}"
th = run_client_streamer(@sent_msgs, @resp, @pass,
k1: 'updated-v1', k2: 'v2') k1: 'updated-v1', k2: 'v2')
update_md = proc do |md| update_md = proc do |md|
md[:k1] = 'updated-v1' md[:k1] = 'updated-v1'
@ -240,8 +247,9 @@ describe 'ClientStub' do
end end
it 'should raise an error if the status is not ok' do it 'should raise an error if the status is not ok' do
host = new_test_host server_port = create_test_server
th = run_client_streamer(host, @sent_msgs, @resp, @fail) host = "localhost:#{server_port}"
th = run_client_streamer(@sent_msgs, @resp, @fail)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
blk = proc { get_response(stub) } blk = proc { get_response(stub) }
expect(&blk).to raise_error(GRPC::BadStatus) expect(&blk).to raise_error(GRPC::BadStatus)
@ -278,16 +286,18 @@ describe 'ClientStub' do
end end
it 'should send a request to/receive replies from a server' do it 'should send a request to/receive replies from a server' do
host = new_test_host server_port = create_test_server
th = run_server_streamer(host, @sent_msg, @replys, @pass) host = "localhost:#{server_port}"
th = run_server_streamer(@sent_msg, @replys, @pass)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
expect(get_responses(stub).collect { |r| r }).to eq(@replys) expect(get_responses(stub).collect { |r| r }).to eq(@replys)
th.join th.join
end end
it 'should raise an error if the status is not ok' do it 'should raise an error if the status is not ok' do
host = new_test_host server_port = create_test_server
th = run_server_streamer(host, @sent_msg, @replys, @fail) host = "localhost:#{server_port}"
th = run_server_streamer(@sent_msg, @replys, @fail)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
e = get_responses(stub) e = get_responses(stub)
expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus) expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus)
@ -295,8 +305,9 @@ describe 'ClientStub' do
end end
it 'should send metadata to the server ok' do it 'should send metadata to the server ok' do
host = new_test_host server_port = create_test_server
th = run_server_streamer(host, @sent_msg, @replys, @fail, host = "localhost:#{server_port}"
th = run_server_streamer(@sent_msg, @replys, @fail,
k1: 'v1', k2: 'v2') k1: 'v1', k2: 'v2')
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
e = get_responses(stub) e = get_responses(stub)
@ -305,8 +316,9 @@ describe 'ClientStub' do
end end
it 'should update the sent metadata with a provided metadata updater' do it 'should update the sent metadata with a provided metadata updater' do
host = new_test_host server_port = create_test_server
th = run_server_streamer(host, @sent_msg, @replys, @pass, host = "localhost:#{server_port}"
th = run_server_streamer(@sent_msg, @replys, @pass,
k1: 'updated-v1', k2: 'v2') k1: 'updated-v1', k2: 'v2')
update_md = proc do |md| update_md = proc do |md|
md[:k1] = 'updated-v1' md[:k1] = 'updated-v1'
@ -352,8 +364,9 @@ describe 'ClientStub' do
end end
it 'supports sending all the requests first', bidi: true do it 'supports sending all the requests first', bidi: true do
host = new_test_host server_port = create_test_server
th = run_bidi_streamer_handle_inputs_first(host, @sent_msgs, @replys, host = "localhost:#{server_port}"
th = run_bidi_streamer_handle_inputs_first(@sent_msgs, @replys,
@pass) @pass)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
e = get_responses(stub) e = get_responses(stub)
@ -362,8 +375,9 @@ describe 'ClientStub' do
end end
it 'supports client-initiated ping pong', bidi: true do it 'supports client-initiated ping pong', bidi: true do
host = new_test_host server_port = create_test_server
th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, true) host = "localhost:#{server_port}"
th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, true)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
e = get_responses(stub) e = get_responses(stub)
expect(e.collect { |r| r }).to eq(@sent_msgs) expect(e.collect { |r| r }).to eq(@sent_msgs)
@ -377,8 +391,9 @@ describe 'ClientStub' do
# they receive a message from the client. Without receiving all the # they receive a message from the client. Without receiving all the
# metadata, the server does not accept the call, so this test hangs. # metadata, the server does not accept the call, so this test hangs.
xit 'supports a server-initiated ping pong', bidi: true do xit 'supports a server-initiated ping pong', bidi: true do
host = new_test_host server_port = create_test_server
th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, false) host = "localhost:#{server_port}"
th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, false)
stub = GRPC::ClientStub.new(host, @cq) stub = GRPC::ClientStub.new(host, @cq)
e = get_responses(stub) e = get_responses(stub)
expect(e.collect { |r| r }).to eq(@sent_msgs) expect(e.collect { |r| r }).to eq(@sent_msgs)
@ -410,10 +425,10 @@ describe 'ClientStub' do
end end
end end
def run_server_streamer(hostname, expected_input, replys, status, **kw) def run_server_streamer(expected_input, replys, status, **kw)
wanted_metadata = kw.clone wanted_metadata = kw.clone
wakey_thread do |mtx, cnd| wakey_thread do |mtx, cnd|
c = expect_server_to_be_invoked(hostname, mtx, cnd) c = expect_server_to_be_invoked(mtx, cnd)
wanted_metadata.each do |k, v| wanted_metadata.each do |k, v|
expect(c.metadata[k.to_s]).to eq(v) expect(c.metadata[k.to_s]).to eq(v)
end end
@ -423,20 +438,19 @@ describe 'ClientStub' do
end end
end end
def run_bidi_streamer_handle_inputs_first(hostname, expected_inputs, replys, def run_bidi_streamer_handle_inputs_first(expected_inputs, replys,
status) status)
wakey_thread do |mtx, cnd| wakey_thread do |mtx, cnd|
c = expect_server_to_be_invoked(hostname, mtx, cnd) c = expect_server_to_be_invoked(mtx, cnd)
expected_inputs.each { |i| expect(c.remote_read).to eq(i) } expected_inputs.each { |i| expect(c.remote_read).to eq(i) }
replys.each { |r| c.remote_send(r) } replys.each { |r| c.remote_send(r) }
c.send_status(status, status == @pass ? 'OK' : 'NOK', true) c.send_status(status, status == @pass ? 'OK' : 'NOK', true)
end end
end end
def run_bidi_streamer_echo_ping_pong(hostname, expected_inputs, status, def run_bidi_streamer_echo_ping_pong(expected_inputs, status, client_starts)
client_starts)
wakey_thread do |mtx, cnd| wakey_thread do |mtx, cnd|
c = expect_server_to_be_invoked(hostname, mtx, cnd) c = expect_server_to_be_invoked(mtx, cnd)
expected_inputs.each do |i| expected_inputs.each do |i|
if client_starts if client_starts
expect(c.remote_read).to eq(i) expect(c.remote_read).to eq(i)
@ -450,10 +464,10 @@ describe 'ClientStub' do
end end
end end
def run_client_streamer(hostname, expected_inputs, resp, status, **kw) def run_client_streamer(expected_inputs, resp, status, **kw)
wanted_metadata = kw.clone wanted_metadata = kw.clone
wakey_thread do |mtx, cnd| wakey_thread do |mtx, cnd|
c = expect_server_to_be_invoked(hostname, mtx, cnd) c = expect_server_to_be_invoked(mtx, cnd)
expected_inputs.each { |i| expect(c.remote_read).to eq(i) } expected_inputs.each { |i| expect(c.remote_read).to eq(i) }
wanted_metadata.each do |k, v| wanted_metadata.each do |k, v|
expect(c.metadata[k.to_s]).to eq(v) expect(c.metadata[k.to_s]).to eq(v)
@ -463,10 +477,10 @@ describe 'ClientStub' do
end end
end end
def run_request_response(hostname, expected_input, resp, status, **kw) def run_request_response(expected_input, resp, status, **kw)
wanted_metadata = kw.clone wanted_metadata = kw.clone
wakey_thread do |mtx, cnd| wakey_thread do |mtx, cnd|
c = expect_server_to_be_invoked(hostname, mtx, cnd) c = expect_server_to_be_invoked(mtx, cnd)
expect(c.remote_read).to eq(expected_input) expect(c.remote_read).to eq(expected_input)
wanted_metadata.each do |k, v| wanted_metadata.each do |k, v|
expect(c.metadata[k.to_s]).to eq(v) expect(c.metadata[k.to_s]).to eq(v)
@ -476,32 +490,30 @@ describe 'ClientStub' do
end end
end end
def start_test_server(hostname, awake_mutex, awake_cond) def create_test_server
server_queue = GRPC::Core::CompletionQueue.new @server_queue = GRPC::Core::CompletionQueue.new
@server = GRPC::Core::Server.new(server_queue, nil) @server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port(hostname) @server.add_http2_port('0.0.0.0:0')
end
def start_test_server(awake_mutex, awake_cond)
@server.start @server.start
@server_tag = Object.new @server_tag = Object.new
@server.request_call(@server_tag) @server.request_call(@server_tag)
awake_mutex.synchronize { awake_cond.signal } awake_mutex.synchronize { awake_cond.signal }
server_queue
end end
def expect_server_to_be_invoked(hostname, awake_mutex, awake_cond) def expect_server_to_be_invoked(awake_mutex, awake_cond)
server_queue = start_test_server(hostname, awake_mutex, awake_cond) start_test_server(awake_mutex, awake_cond)
ev = server_queue.pluck(@server_tag, INFINITE_FUTURE) ev = @server_queue.pluck(@server_tag, INFINITE_FUTURE)
fail OutOfTime if ev.nil? fail OutOfTime if ev.nil?
server_call = ev.call server_call = ev.call
server_call.metadata = ev.result.metadata server_call.metadata = ev.result.metadata
finished_tag = Object.new finished_tag = Object.new
server_call.server_accept(server_queue, finished_tag) server_call.server_accept(@server_queue, finished_tag)
server_call.server_end_initial_metadata server_call.server_end_initial_metadata
GRPC::ActiveCall.new(server_call, server_queue, NOOP, NOOP, INFINITE_FUTURE, GRPC::ActiveCall.new(server_call, @server_queue, NOOP, NOOP,
INFINITE_FUTURE,
finished_tag: finished_tag) finished_tag: finished_tag)
end end
def new_test_host
port = find_unused_tcp_port
"localhost:#{port}"
end
end end

@ -29,7 +29,6 @@
require 'grpc' require 'grpc'
require 'xray/thread_dump_signal_handler' require 'xray/thread_dump_signal_handler'
require_relative '../port_picker'
def load_test_certs def load_test_certs
test_root = File.join(File.dirname(File.dirname(__FILE__)), 'testdata') test_root = File.join(File.dirname(File.dirname(__FILE__)), 'testdata')
@ -104,10 +103,10 @@ describe GRPC::RpcServer do
@noop = proc { |x| x } @noop = proc { |x| x }
@server_queue = GRPC::Core::CompletionQueue.new @server_queue = GRPC::Core::CompletionQueue.new
port = find_unused_tcp_port server_host = '0.0.0.0:0'
@host = "localhost:#{port}"
@server = GRPC::Core::Server.new(@server_queue, nil) @server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port(@host) server_port = @server.add_http2_port(server_host)
@host = "localhost:#{server_port}"
@ch = GRPC::Core::Channel.new(@host, nil) @ch = GRPC::Core::Channel.new(@host, nil)
end end

@ -1,45 +0,0 @@
# 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.
require 'socket'
# @param [Fixnum] the minimum port number to accept
# @param [Fixnum] the maximum port number to accept
# @return [Fixnum ]a free tcp port
def find_unused_tcp_port(min = 32_768, max = 60_000)
# Allow the system to assign a port, by specifying 0.
# Loop until a port is assigned in the required range
loop do
socket = Socket.new(:INET, :STREAM, 0)
socket.bind(Addrinfo.tcp('127.0.0.1', 0))
p = socket.local_address.ip_port
socket.close
return p if p > min && p < max
end
end

@ -28,7 +28,6 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc' require 'grpc'
require 'port_picker'
def load_test_certs def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata') test_root = File.join(File.dirname(__FILE__), 'testdata')
@ -205,10 +204,8 @@ describe Server do
end end
def start_a_server def start_a_server
port = find_unused_tcp_port
host = "localhost:#{port}"
s = Server.new(@cq, nil) s = Server.new(@cq, nil)
s.add_http2_port(host) s.add_http2_port('0.0.0.0:0')
s.start s.start
s s
end end

@ -32,6 +32,7 @@
*/ */
#include "src/core/channel/metadata_buffer.h" #include "src/core/channel/metadata_buffer.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
@ -42,12 +43,12 @@
/* construct a buffer with some prefix followed by an integer converted to /* construct a buffer with some prefix followed by an integer converted to
a string */ a string */
static gpr_slice construct_buffer(size_t prefix_length, size_t index) { static gpr_slice construct_buffer(size_t prefix_length, size_t index) {
gpr_slice buffer = gpr_slice_malloc(prefix_length + 32); gpr_slice buffer = gpr_slice_malloc(prefix_length + GPR_LTOA_MIN_BUFSIZE);
memset(GPR_SLICE_START_PTR(buffer), 'a', prefix_length); memset(GPR_SLICE_START_PTR(buffer), 'a', prefix_length);
GPR_SLICE_SET_LENGTH( GPR_SLICE_SET_LENGTH(
buffer, prefix_length + buffer,
sprintf((char *)GPR_SLICE_START_PTR(buffer) + prefix_length, prefix_length +
"%d", (int)index)); gpr_ltoa(index, (char *)GPR_SLICE_START_PTR(buffer) + prefix_length));
return buffer; return buffer;
} }

@ -32,6 +32,8 @@
*/ */
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/grpc_http.h>
#include <grpc/grpc_security.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@ -42,10 +44,12 @@
#include "src/core/support/string.h" #include "src/core/support/string.h"
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/cmdline.h>
#include <grpc/support/host_port.h> #include <grpc/support/host_port.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include "test/core/util/port.h" #include "test/core/util/port.h"
#include "test/core/end2end/data/ssl_test_data.h"
static grpc_completion_queue *cq; static grpc_completion_queue *cq;
static grpc_server *server; static grpc_server *server;
@ -83,29 +87,74 @@ static void sigint_handler(int x) { got_sigint = 1; }
int main(int argc, char **argv) { int main(int argc, char **argv) {
grpc_event *ev; grpc_event *ev;
char *addr;
call_state *s; call_state *s;
char *addr_buf = NULL;
gpr_cmdline *cl;
int shutdown_started = 0; int shutdown_started = 0;
int shutdown_finished = 0; int shutdown_finished = 0;
grpc_test_init(argc, argv); int secure = 0;
char *addr = NULL;
char *fake_argv[1];
#define MAX_ARGS 4
grpc_arg arge[MAX_ARGS];
grpc_arg *e;
grpc_channel_args args = {0, NULL};
grpc_http_server_page home_page = {"/", "text/html",
"<head>\n"
"<title>Echo Server</title>\n"
"</head>\n"
"<body>\n"
"Welcome to the world of the future!\n"
"</body>\n"};
GPR_ASSERT(argc >= 1);
fake_argv[0] = argv[0];
grpc_test_init(1, fake_argv);
grpc_init(); grpc_init();
srand(clock()); srand(clock());
memset(arge, 0, sizeof(arge));
if (argc == 2) { args.args = arge;
addr = gpr_strdup(argv[1]);
} else { cl = gpr_cmdline_create("echo server");
gpr_join_host_port(&addr, "::", grpc_pick_unused_port_or_die()); gpr_cmdline_add_string(cl, "bind", "Bind host:port", &addr);
gpr_cmdline_add_flag(cl, "secure", "Run with security?", &secure);
gpr_cmdline_parse(cl, argc, argv);
gpr_cmdline_destroy(cl);
e = &arge[args.num_args++];
e->type = GRPC_ARG_POINTER;
e->key = GRPC_ARG_SERVE_OVER_HTTP;
e->value.pointer.p = &home_page;
if (addr == NULL) {
gpr_join_host_port(&addr_buf, "::", grpc_pick_unused_port_or_die());
addr = addr_buf;
} }
gpr_log(GPR_INFO, "creating server on: %s", addr); gpr_log(GPR_INFO, "creating server on: %s", addr);
cq = grpc_completion_queue_create(); cq = grpc_completion_queue_create();
server = grpc_server_create(cq, NULL); if (secure) {
GPR_ASSERT(grpc_server_add_http2_port(server, addr)); grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key,
gpr_free(addr); test_server1_cert};
grpc_server_credentials *ssl_creds =
grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1);
server = grpc_secure_server_create(ssl_creds, cq, &args);
GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr));
grpc_server_credentials_release(ssl_creds);
} else {
server = grpc_server_create(cq, &args);
GPR_ASSERT(grpc_server_add_http2_port(server, addr));
}
grpc_server_start(server); grpc_server_start(server);
gpr_free(addr_buf);
addr = addr_buf = NULL;
request_call(); request_call();
signal(SIGINT, sigint_handler); signal(SIGINT, sigint_handler);

@ -231,100 +231,91 @@ static void verify_matches(expectation *e, grpc_event *ev) {
} }
} }
static char *metadata_expectation_string(metadata *md) { static void metadata_expectation(gpr_strvec *buf, metadata *md) {
size_t len;
size_t i; size_t i;
char *out; char *tmp;
char *p;
if (!md) {
if (!md) return gpr_strdup("nil"); gpr_strvec_add(buf, gpr_strdup("nil"));
} else {
for (len = 0, i = 0; i < md->count; i++) { for (i = 0; i < md->count; i++) {
len += strlen(md->keys[i]); gpr_asprintf(&tmp, "%c%s:%s", i ? ',' : '{', md->keys[i], md->values[i]);
len += strlen(md->values[i]); gpr_strvec_add(buf, tmp);
} }
len += 3 + md->count; gpr_strvec_add(buf, gpr_strdup("}"));
p = out = gpr_malloc(len);
*p++ = '{';
for (i = 0; i < md->count; i++) {
if (i) *p++ = ',';
p += sprintf(p, "%s:%s", md->keys[i], md->values[i]);
} }
*p++ = '}';
*p++ = 0;
return out;
} }
static size_t expectation_to_string(char *out, expectation *e) { static void expectation_to_strvec(gpr_strvec *buf, expectation *e) {
gpr_timespec timeout; gpr_timespec timeout;
char *str = NULL; char *tmp;
size_t len;
switch (e->type) { switch (e->type) {
case GRPC_FINISH_ACCEPTED: case GRPC_FINISH_ACCEPTED:
return sprintf(out, "GRPC_FINISH_ACCEPTED result=%d", gpr_asprintf(&tmp, "GRPC_FINISH_ACCEPTED result=%d",
e->data.finish_accepted); e->data.finish_accepted);
gpr_strvec_add(buf, tmp);
break;
case GRPC_WRITE_ACCEPTED: case GRPC_WRITE_ACCEPTED:
return sprintf(out, "GRPC_WRITE_ACCEPTED result=%d", gpr_asprintf(&tmp, "GRPC_WRITE_ACCEPTED result=%d",
e->data.write_accepted); e->data.write_accepted);
gpr_strvec_add(buf, tmp);
break;
case GRPC_INVOKE_ACCEPTED: case GRPC_INVOKE_ACCEPTED:
return sprintf(out, "GRPC_INVOKE_ACCEPTED"); gpr_strvec_add(buf, gpr_strdup("GRPC_INVOKE_ACCEPTED"));
break;
case GRPC_SERVER_RPC_NEW: case GRPC_SERVER_RPC_NEW:
timeout = gpr_time_sub(e->data.server_rpc_new.deadline, gpr_now()); timeout = gpr_time_sub(e->data.server_rpc_new.deadline, gpr_now());
return sprintf(out, "GRPC_SERVER_RPC_NEW method=%s host=%s timeout=%fsec", gpr_asprintf(&tmp, "GRPC_SERVER_RPC_NEW method=%s host=%s timeout=%fsec",
e->data.server_rpc_new.method, e->data.server_rpc_new.host, e->data.server_rpc_new.method, e->data.server_rpc_new.host,
timeout.tv_sec + 1e-9 * timeout.tv_nsec); timeout.tv_sec + 1e-9 * timeout.tv_nsec);
gpr_strvec_add(buf, tmp);
break;
case GRPC_CLIENT_METADATA_READ: case GRPC_CLIENT_METADATA_READ:
str = metadata_expectation_string(e->data.client_metadata_read); gpr_strvec_add(buf, gpr_strdup("GRPC_CLIENT_METADATA_READ "));
len = sprintf(out, "GRPC_CLIENT_METADATA_READ %s", str); metadata_expectation(buf, e->data.client_metadata_read);
gpr_free(str); break;
return len;
case GRPC_FINISHED: case GRPC_FINISHED:
str = metadata_expectation_string(e->data.finished.metadata); gpr_asprintf(&tmp, "GRPC_FINISHED status=%d details=%s ",
len = sprintf(out, "GRPC_FINISHED status=%d details=%s %s", e->data.finished.status, e->data.finished.details);
e->data.finished.status, e->data.finished.details, str); gpr_strvec_add(buf, tmp);
gpr_free(str); metadata_expectation(buf, e->data.finished.metadata);
return len; break;
case GRPC_READ: case GRPC_READ:
if (e->data.read) { gpr_strvec_add(buf, gpr_strdup("GRPC_READ data="));
str = gpr_strvec_add(buf, gpr_hexdump((char *)GPR_SLICE_START_PTR(*e->data.read),
gpr_hexdump((char *)GPR_SLICE_START_PTR(*e->data.read), GPR_SLICE_LENGTH(*e->data.read), GPR_HEXDUMP_PLAINTEXT));
GPR_SLICE_LENGTH(*e->data.read), GPR_HEXDUMP_PLAINTEXT); break;
}
len = sprintf(out, "GRPC_READ data=%s", str);
gpr_free(str);
return len;
case GRPC_SERVER_SHUTDOWN: case GRPC_SERVER_SHUTDOWN:
return sprintf(out, "GRPC_SERVER_SHUTDOWN"); gpr_strvec_add(buf, gpr_strdup("GRPC_SERVER_SHUTDOWN"));
break;
case GRPC_COMPLETION_DO_NOT_USE: case GRPC_COMPLETION_DO_NOT_USE:
case GRPC_QUEUE_SHUTDOWN: case GRPC_QUEUE_SHUTDOWN:
gpr_log(GPR_ERROR, "not implemented"); gpr_log(GPR_ERROR, "not implemented");
abort(); abort();
break; break;
} }
return 0;
} }
static char *expectations_to_string(cq_verifier *v) { static void expectations_to_strvec(gpr_strvec *buf, cq_verifier *v) {
/* allocate a large buffer: we're about to crash anyway */
char *buffer = gpr_malloc(32 * 1024 * 1024);
char *p = buffer;
expectation *e; expectation *e;
for (e = v->expect.next; e != &v->expect; e = e->next) { for (e = v->expect.next; e != &v->expect; e = e->next) {
p += expectation_to_string(p, e); expectation_to_strvec(buf, e);
*p++ = '\n'; gpr_strvec_add(buf, gpr_strdup("\n"));
} }
*p = 0;
return buffer;
} }
static void fail_no_event_received(cq_verifier *v) { static void fail_no_event_received(cq_verifier *v) {
char *expectations = expectations_to_string(v); gpr_strvec buf;
gpr_log(GPR_ERROR, "no event received, but expected:\n%s", expectations); char *msg;
gpr_free(expectations); gpr_strvec_init(&buf);
gpr_strvec_add(&buf, gpr_strdup("no event received, but expected:\n"));
expectations_to_strvec(&buf, v);
msg = gpr_strvec_flatten(&buf, NULL);
gpr_log(GPR_ERROR, "%s", msg);
gpr_strvec_destroy(&buf);
gpr_free(msg);
abort(); abort();
} }
@ -333,9 +324,10 @@ void cq_verify(cq_verifier *v) {
gpr_time_add(gpr_now(), gpr_time_from_micros(10 * GPR_US_PER_SEC)); gpr_time_add(gpr_now(), gpr_time_from_micros(10 * GPR_US_PER_SEC));
grpc_event *ev; grpc_event *ev;
expectation *e; expectation *e;
char *s;
gpr_strvec have_tags;
char have_tags[512] = {0}; gpr_strvec_init(&have_tags);
char *phave = have_tags;
while (v->expect.next != &v->expect) { while (v->expect.next != &v->expect) {
ev = grpc_completion_queue_next(v->cq, deadline); ev = grpc_completion_queue_next(v->cq, deadline);
@ -344,7 +336,8 @@ void cq_verify(cq_verifier *v) {
} }
for (e = v->expect.next; e != &v->expect; e = e->next) { for (e = v->expect.next; e != &v->expect; e = e->next) {
phave += sprintf(phave, " %p", e->tag); gpr_asprintf(&s, " %p", e->tag);
gpr_strvec_add(&have_tags, s);
if (e->tag == ev->tag) { if (e->tag == ev->tag) {
verify_matches(e, ev); verify_matches(e, ev);
e->next->prev = e->prev; e->next->prev = e->prev;
@ -354,15 +347,20 @@ void cq_verify(cq_verifier *v) {
} }
} }
if (e == &v->expect) { if (e == &v->expect) {
char *s = grpc_event_string(ev); s = grpc_event_string(ev);
gpr_log(GPR_ERROR, "event not found: %s", s); gpr_log(GPR_ERROR, "event not found: %s", s);
gpr_log(GPR_ERROR, "have tags:%s", have_tags);
gpr_free(s); gpr_free(s);
s = gpr_strvec_flatten(&have_tags, NULL);
gpr_log(GPR_ERROR, "have tags:%s", s);
gpr_free(s);
gpr_strvec_destroy(&have_tags);
abort(); abort();
} }
grpc_event_finish(ev); grpc_event_finish(ev);
} }
gpr_strvec_destroy(&have_tags);
} }
void cq_verify_empty(cq_verifier *v) { void cq_verify_empty(cq_verifier *v) {

@ -37,6 +37,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "src/core/support/string.h"
#include <grpc/byte_buffer.h> #include <grpc/byte_buffer.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
@ -145,7 +146,7 @@ static void test_body(grpc_end2end_test_fixture f) {
static void test_invoke_request_with_census( static void test_invoke_request_with_census(
grpc_end2end_test_config config, const char *name, grpc_end2end_test_config config, const char *name,
void (*body)(grpc_end2end_test_fixture f)) { void (*body)(grpc_end2end_test_fixture f)) {
char fullname[64]; char *fullname;
grpc_end2end_test_fixture f; grpc_end2end_test_fixture f;
grpc_arg client_arg, server_arg; grpc_arg client_arg, server_arg;
grpc_channel_args client_args, server_args; grpc_channel_args client_args, server_args;
@ -163,11 +164,12 @@ static void test_invoke_request_with_census(
server_args.num_args = 1; server_args.num_args = 1;
server_args.args = &server_arg; server_args.args = &server_arg;
sprintf(fullname, "%s/%s", __FUNCTION__, name); gpr_asprintf(&fullname, "%s/%s", __FUNCTION__, name);
f = begin_test(config, fullname, &client_args, &server_args); f = begin_test(config, fullname, &client_args, &server_args);
body(f); body(f);
end_test(&f); end_test(&f);
config.tear_down_data(&f); config.tear_down_data(&f);
gpr_free(fullname);
} }
void grpc_end2end_tests(grpc_end2end_test_config config) { void grpc_end2end_tests(grpc_end2end_test_config config) {

@ -37,6 +37,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "src/core/support/string.h"
#include <grpc/byte_buffer.h> #include <grpc/byte_buffer.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
@ -198,15 +199,16 @@ static void simple_request_body2(grpc_end2end_test_fixture f) {
static void test_invoke_simple_request( static void test_invoke_simple_request(
grpc_end2end_test_config config, const char *name, grpc_end2end_test_config config, const char *name,
void (*body)(grpc_end2end_test_fixture f)) { void (*body)(grpc_end2end_test_fixture f)) {
char fullname[64]; char *fullname;
grpc_end2end_test_fixture f; grpc_end2end_test_fixture f;
sprintf(fullname, "%s/%s", __FUNCTION__, name); gpr_asprintf(&fullname, "%s/%s", __FUNCTION__, name);
f = begin_test(config, fullname, NULL, NULL); f = begin_test(config, fullname, NULL, NULL);
body(f); body(f);
end_test(&f); end_test(&f);
config.tear_down_data(&f); config.tear_down_data(&f);
gpr_free(fullname);
} }
static void test_invoke_10_simple_requests(grpc_end2end_test_config config) { static void test_invoke_10_simple_requests(grpc_end2end_test_config config) {

@ -498,10 +498,8 @@ static void validate_service_account_http_request(
char *expected_body = NULL; char *expected_body = NULL;
GPR_ASSERT(body != NULL); GPR_ASSERT(body != NULL);
GPR_ASSERT(body_size != 0); GPR_ASSERT(body_size != 0);
expected_body = gpr_malloc(strlen(expected_service_account_http_body_prefix) + gpr_asprintf(&expected_body, "%s%s",
strlen(test_signed_jwt) + 1); expected_service_account_http_body_prefix, test_signed_jwt);
sprintf(expected_body, "%s%s", expected_service_account_http_body_prefix,
test_signed_jwt);
GPR_ASSERT(strlen(expected_body) == body_size); GPR_ASSERT(strlen(expected_body) == body_size);
GPR_ASSERT(!memcmp(expected_body, body, body_size)); GPR_ASSERT(!memcmp(expected_body, body, body_size));
gpr_free(expected_body); gpr_free(expected_body);

@ -38,6 +38,7 @@
#include "src/core/statistics/hash_table.h" #include "src/core/statistics/hash_table.h"
#include "src/core/support/murmur_hash.h" #include "src/core/support/murmur_hash.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
@ -187,15 +188,15 @@ static void test_insertion_and_deletion_with_high_collision_rate(void) {
census_ht_option opt = {CENSUS_HT_POINTER, 13, &force_collision, census_ht_option opt = {CENSUS_HT_POINTER, 13, &force_collision,
&cmp_str_keys, NULL, NULL}; &cmp_str_keys, NULL, NULL};
census_ht* ht = census_ht_create(&opt); census_ht* ht = census_ht_create(&opt);
char key_str[1000][10]; char key_str[1000][GPR_LTOA_MIN_BUFSIZE];
gpr_uint64 val = 0; gpr_uint64 val = 0;
int i = 0; int i = 0;
for (i = 0; i < 1000; i++) { for (i = 0; i < 1000; i++) {
census_ht_key key; census_ht_key key;
key.ptr = key_str[i]; key.ptr = key_str[i];
sprintf(key_str[i], "%d", i); gpr_ltoa(i, key_str[i]);
census_ht_insert(ht, key, (void*)(&val)); census_ht_insert(ht, key, (void*)(&val));
printf("%d\n", i); gpr_log(GPR_INFO, "%d\n", i);
GPR_ASSERT(census_ht_get_size(ht) == (i + 1)); GPR_ASSERT(census_ht_get_size(ht) == (i + 1));
} }
for (i = 0; i < 1000; i++) { for (i = 0; i < 1000; i++) {

@ -36,6 +36,7 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "src/core/support/string.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
@ -131,8 +132,8 @@ static void test_static_lookup(void) {
static void test_many_additions(void) { static void test_many_additions(void) {
grpc_chttp2_hptbl tbl; grpc_chttp2_hptbl tbl;
int i; int i;
char key[32]; char *key;
char value[32]; char *value;
grpc_mdctx *mdctx; grpc_mdctx *mdctx;
LOG_TEST(); LOG_TEST();
@ -141,14 +142,18 @@ static void test_many_additions(void) {
grpc_chttp2_hptbl_init(&tbl, mdctx); grpc_chttp2_hptbl_init(&tbl, mdctx);
for (i = 0; i < 1000000; i++) { for (i = 0; i < 1000000; i++) {
sprintf(key, "K:%d", i); gpr_asprintf(&key, "K:%d", i);
sprintf(value, "VALUE:%d", i); gpr_asprintf(&value, "VALUE:%d", i);
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, key, value)); grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, key, value));
assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value); assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
gpr_free(key);
gpr_free(value);
if (i) { if (i) {
sprintf(key, "K:%d", i - 1); gpr_asprintf(&key, "K:%d", i - 1);
sprintf(value, "VALUE:%d", i - 1); gpr_asprintf(&value, "VALUE:%d", i - 1);
assert_index(&tbl, 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value); assert_index(&tbl, 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
gpr_free(key);
gpr_free(value);
} }
} }
@ -226,7 +231,7 @@ static void test_find(void) {
/* overflow the string buffer, check find still works */ /* overflow the string buffer, check find still works */
for (i = 0; i < 10000; i++) { for (i = 0; i < 10000; i++) {
sprintf(buffer, "%d", i); gpr_ltoa(i, buffer);
grpc_chttp2_hptbl_add(&tbl, grpc_chttp2_hptbl_add(&tbl,
grpc_mdelem_from_strings(mdctx, "test", buffer)); grpc_mdelem_from_strings(mdctx, "test", buffer));
} }
@ -245,7 +250,7 @@ static void test_find(void) {
for (i = 0; i < tbl.num_ents; i++) { for (i = 0; i < tbl.num_ents; i++) {
int expect = 9999 - i; int expect = 9999 - i;
sprintf(buffer, "%d", expect); gpr_ltoa(expect, buffer);
r = find_simple(&tbl, "test", buffer); r = find_simple(&tbl, "test", buffer);
GPR_ASSERT(r.index == i + 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY); GPR_ASSERT(r.index == i + 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);

@ -186,7 +186,7 @@ static void encode_int_to_str(int i, char *p) {
static void test_decode_table_overflow(void) { static void test_decode_table_overflow(void) {
int i; int i;
char key[3], value[3]; char key[3], value[3];
char expect[128]; char *expect;
for (i = 0; i < 114; i++) { for (i = 0; i < 114; i++) {
if (i > 0) { if (i > 0) {
@ -197,18 +197,21 @@ static void test_decode_table_overflow(void) {
encode_int_to_str(i + 1, value); encode_int_to_str(i + 1, value);
if (i + 61 >= 127) { if (i + 61 >= 127) {
sprintf(expect, "000009 0104 deadbeef ff%02x 40 02%02x%02x 02%02x%02x", gpr_asprintf(&expect,
i + 61 - 127, key[0], key[1], value[0], value[1]); "000009 0104 deadbeef ff%02x 40 02%02x%02x 02%02x%02x",
i + 61 - 127, key[0], key[1], value[0], value[1]);
} else if (i > 0) { } else if (i > 0) {
sprintf(expect, "000008 0104 deadbeef %02x 40 02%02x%02x 02%02x%02x", gpr_asprintf(&expect,
0x80 + 61 + i, key[0], key[1], value[0], value[1]); "000008 0104 deadbeef %02x 40 02%02x%02x 02%02x%02x",
0x80 + 61 + i, key[0], key[1], value[0], value[1]);
} else { } else {
sprintf(expect, "000007 0104 deadbeef 40 02%02x%02x 02%02x%02x", key[0], gpr_asprintf(&expect, "000007 0104 deadbeef 40 02%02x%02x 02%02x%02x",
key[1], value[0], value[1]); key[0], key[1], value[0], value[1]);
} }
add_sopb_header(key, value); add_sopb_header(key, value);
verify_sopb(0, 0, 0, expect); verify_sopb(0, 0, 0, expect);
gpr_free(expect);
} }
/* if the above passes, then we must have just knocked this pair out of the /* if the above passes, then we must have just knocked this pair out of the

@ -36,6 +36,8 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
@ -93,16 +95,23 @@ void decode_suite(char ext, gpr_timespec (*answer)(long x)) {
1234567, 12345678, 123456789, 98765432, 9876543, 987654, 1234567, 12345678, 123456789, 98765432, 9876543, 987654,
98765, 9876, 987, 98, 9}; 98765, 9876, 987, 98, 9};
int i; int i;
char input[32]; char *input;
for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) { for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) {
sprintf(input, "%ld%c", test_vals[i], ext); gpr_asprintf(&input, "%ld%c", test_vals[i], ext);
assert_decodes_as(input, answer(test_vals[i])); assert_decodes_as(input, answer(test_vals[i]));
sprintf(input, " %ld%c", test_vals[i], ext); gpr_free(input);
gpr_asprintf(&input, " %ld%c", test_vals[i], ext);
assert_decodes_as(input, answer(test_vals[i])); assert_decodes_as(input, answer(test_vals[i]));
sprintf(input, "%ld %c", test_vals[i], ext); gpr_free(input);
gpr_asprintf(&input, "%ld %c", test_vals[i], ext);
assert_decodes_as(input, answer(test_vals[i])); assert_decodes_as(input, answer(test_vals[i]));
sprintf(input, "%ld %c ", test_vals[i], ext); gpr_free(input);
gpr_asprintf(&input, "%ld %c ", test_vals[i], ext);
assert_decodes_as(input, answer(test_vals[i])); assert_decodes_as(input, answer(test_vals[i]));
gpr_free(input);
} }
} }

@ -35,6 +35,7 @@
#include <stdio.h> #include <stdio.h>
#include "src/core/support/string.h"
#include "src/core/transport/chttp2/bin_encoder.h" #include "src/core/transport/chttp2/bin_encoder.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
@ -99,7 +100,7 @@ static void test_create_metadata(void) {
static void test_create_many_ephemeral_metadata(void) { static void test_create_many_ephemeral_metadata(void) {
grpc_mdctx *ctx; grpc_mdctx *ctx;
char buffer[256]; char buffer[GPR_LTOA_MIN_BUFSIZE];
long i; long i;
size_t mdtab_capacity_before; size_t mdtab_capacity_before;
@ -109,7 +110,7 @@ static void test_create_many_ephemeral_metadata(void) {
mdtab_capacity_before = grpc_mdctx_get_mdtab_capacity_test_only(ctx); mdtab_capacity_before = grpc_mdctx_get_mdtab_capacity_test_only(ctx);
/* add, and immediately delete a bunch of different elements */ /* add, and immediately delete a bunch of different elements */
for (i = 0; i < MANY; i++) { for (i = 0; i < MANY; i++) {
sprintf(buffer, "%ld", i); gpr_ltoa(i, buffer);
grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", buffer)); grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", buffer));
} }
/* capacity should not grow */ /* capacity should not grow */
@ -120,7 +121,7 @@ static void test_create_many_ephemeral_metadata(void) {
static void test_create_many_persistant_metadata(void) { static void test_create_many_persistant_metadata(void) {
grpc_mdctx *ctx; grpc_mdctx *ctx;
char buffer[256]; char buffer[GPR_LTOA_MIN_BUFSIZE];
long i; long i;
grpc_mdelem **created = gpr_malloc(sizeof(grpc_mdelem *) * MANY); grpc_mdelem **created = gpr_malloc(sizeof(grpc_mdelem *) * MANY);
grpc_mdelem *md; grpc_mdelem *md;
@ -130,12 +131,12 @@ static void test_create_many_persistant_metadata(void) {
ctx = grpc_mdctx_create(); ctx = grpc_mdctx_create();
/* add phase */ /* add phase */
for (i = 0; i < MANY; i++) { for (i = 0; i < MANY; i++) {
sprintf(buffer, "%ld", i); gpr_ltoa(i, buffer);
created[i] = grpc_mdelem_from_strings(ctx, "a", buffer); created[i] = grpc_mdelem_from_strings(ctx, "a", buffer);
} }
/* verify phase */ /* verify phase */
for (i = 0; i < MANY; i++) { for (i = 0; i < MANY; i++) {
sprintf(buffer, "%ld", i); gpr_ltoa(i, buffer);
md = grpc_mdelem_from_strings(ctx, "a", buffer); md = grpc_mdelem_from_strings(ctx, "a", buffer);
GPR_ASSERT(md == created[i]); GPR_ASSERT(md == created[i]);
grpc_mdelem_unref(md); grpc_mdelem_unref(md);
@ -176,7 +177,7 @@ static void test_spin_creating_the_same_thing(void) {
static void test_things_stick_around(void) { static void test_things_stick_around(void) {
grpc_mdctx *ctx; grpc_mdctx *ctx;
int i, j; int i, j;
char buffer[64]; char *buffer;
int nstrs = 10000; int nstrs = 10000;
grpc_mdstr **strs = gpr_malloc(sizeof(grpc_mdstr *) * nstrs); grpc_mdstr **strs = gpr_malloc(sizeof(grpc_mdstr *) * nstrs);
int *shuf = gpr_malloc(sizeof(int) * nstrs); int *shuf = gpr_malloc(sizeof(int) * nstrs);
@ -187,9 +188,10 @@ static void test_things_stick_around(void) {
ctx = grpc_mdctx_create(); ctx = grpc_mdctx_create();
for (i = 0; i < nstrs; i++) { for (i = 0; i < nstrs; i++) {
sprintf(buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", i); gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", i);
strs[i] = grpc_mdstr_from_string(ctx, buffer); strs[i] = grpc_mdstr_from_string(ctx, buffer);
shuf[i] = i; shuf[i] = i;
gpr_free(buffer);
} }
for (i = 0; i < nstrs; i++) { for (i = 0; i < nstrs; i++) {
@ -208,10 +210,11 @@ static void test_things_stick_around(void) {
for (i = 0; i < nstrs; i++) { for (i = 0; i < nstrs; i++) {
grpc_mdstr_unref(strs[shuf[i]]); grpc_mdstr_unref(strs[shuf[i]]);
for (j = i + 1; j < nstrs; j++) { for (j = i + 1; j < nstrs; j++) {
sprintf(buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]); gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]);
test = grpc_mdstr_from_string(ctx, buffer); test = grpc_mdstr_from_string(ctx, buffer);
GPR_ASSERT(test == strs[shuf[j]]); GPR_ASSERT(test == strs[shuf[j]]);
grpc_mdstr_unref(test); grpc_mdstr_unref(test);
gpr_free(buffer);
} }
} }

@ -1,9 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/config.sh
cd $(dirname $0)/../..
for dir in src test include
do
find $dir -name '*.c' -or -name '*.cc' -or -name '*.h' | xargs $CLANG_FORMAT -i
done

@ -1,11 +0,0 @@
CLANG_FORMAT=clang-format-3.5
set -ex
if not hash $CLANG_FORMAT 2>/dev/null; then
echo "$CLANG_FORMAT is needed but not installed"
echo "perhaps try:"
echo " sudo apt-get install $CLANG_FORMAT"
exit 1
fi

@ -1,9 +1,6 @@
# Dockerfile for the gRPC Java dev image # Dockerfile for the gRPC Java dev image
FROM grpc/java_base FROM grpc/java_base
# Start the daemon that allows access to private git-on-borg repos
RUN /var/local/git/gcompute-tools/git-cookie-authdaemon
RUN cd /var/local/git/grpc-java/lib/okhttp && \ RUN cd /var/local/git/grpc-java/lib/okhttp && \
mvn -pl okhttp -am install mvn -pl okhttp -am install
RUN cd /var/local/git/grpc-java/lib/netty && \ RUN cd /var/local/git/grpc-java/lib/netty && \
@ -13,4 +10,4 @@ RUN cd /var/local/git/grpc-java && \
mvn install mvn install
# Specify the default command such that the interop server runs on its known testing port # Specify the default command such that the interop server runs on its known testing port
CMD ["/var/local/git/grpc-java/run-test-server.sh", "--transport=HTTP2_NETTY_TLS", "--grpc_version=2", "--port=8030"] CMD ["/var/local/git/grpc-java/run-test-server.sh", "--use_tls=true", "--port=8030"]

@ -20,14 +20,24 @@ ENV M2_HOME /var/local/apache-maven-3.2.1
ENV PATH $PATH:$JAVA_HOME/bin:$M2_HOME/bin ENV PATH $PATH:$JAVA_HOME/bin:$M2_HOME/bin
ENV LD_LIBRARY_PATH /usr/local/lib ENV LD_LIBRARY_PATH /usr/local/lib
# Start the daemon that allows access to the protected git-on-borg repos # Install a GitHub SSH service credential that gives access to the GitHub repo while it's private
RUN /var/local/git/gcompute-tools/git-cookie-authdaemon # TODO: remove this once the repo is public
ADD .ssh .ssh
RUN chmod 600 .ssh/github.rsa
RUN mkdir -p $HOME/.ssh && echo 'Host github.com' > $HOME/.ssh/config
RUN echo " IdentityFile /.ssh/github.rsa" >> $HOME/.ssh/config
RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config
RUN git clone --recursive https://team.googlesource.com/one-platform-grpc-team/grpc-java /var/local/git/grpc-java # Get the protobuf source from GitHub and install it
RUN git clone --recursive --branch v2.6.1 git@github.com:google/protobuf.git /var/local/git/protobuf
RUN cd /var/local/git/protobuf && \
./autogen.sh && \
./configure --prefix=/usr && \
make -j12 && make check && make install && make clean
RUN cd /var/local/git/grpc-java/lib/okhttp && \ RUN cd /var/local/git/grpc-java/lib/okhttp && \
mvn -pl okhttp -am validate mvn -pl okhttp -am validate
RUN cd /var/local/git/grpc-java/lib/netty && \ RUN cd /var/local/git/grpc-java/lib/netty && \
mvn -pl codec-http2 -am validate mvn -pl codec-http2 -am validate
RUN cd /var/local/git/grpc-java && \ RUN cd /var/local/git/grpc-java && \
mvn validate mvn validate

@ -6,6 +6,9 @@ RUN cd /var/local/git/grpc \
&& git pull --recurse-submodules \ && git pull --recurse-submodules \
&& git submodule update --init --recursive && git submodule update --init --recursive
# TODO: remove this, once make install is fixed
RUN touch /var/local/git/grpc/include/grpc/support/string.h
# Build the C core. # Build the C core.
RUN make install_c -C /var/local/git/grpc RUN make install_c -C /var/local/git/grpc
@ -18,5 +21,8 @@ RUN /bin/bash -l -c 'cd /var/local/git/grpc/src/ruby && bundle && rake compile:g
# - however, the interop server and client run OK, so this bug can be investigated # - however, the interop server and client run OK, so this bug can be investigated
# RUN /bin/bash -l -c 'cd /var/local/git/grpc/src/ruby && bundle && rake' # RUN /bin/bash -l -c 'cd /var/local/git/grpc/src/ruby && bundle && rake'
# Add a cacerts directory containing the Google root pem file, allowing the ruby client to access the production test instance
ADD cacerts cacerts
# Specify the default command such that the interop server runs on its known testing port # Specify the default command such that the interop server runs on its known testing port
CMD ["/bin/bash", "-l", "-c", "ruby /var/local/git/grpc/src/ruby/bin/interop/interop_server.rb --port 8060"] CMD ["/bin/bash", "-l", "-c", "ruby /var/local/git/grpc/src/ruby/bin/interop/interop_server.rb --use_tls --port 8060"]

@ -0,0 +1,24 @@
#!/bin/bash
main() {
source grpc_docker.sh
test_cases=(large_unary empty_unary client_streaming server_streaming)
clients=(cxx java go ruby)
for test_case in "${test_cases[@]}"
do
for client in "${clients[@]}"
do
if grpc_cloud_prod_test $test_case grpc-docker-testclients $client
then
echo "$test_case $client $server passed" >> /tmp/cloud_prod_result.txt
else
echo "$test_case $client $server failed" >> /tmp/cloud_prod_result.txt
fi
done
done
gsutil cp /tmp/cloud_prod_result.txt gs://stoked-keyword-656-output/cloud_prod_result.txt
rm /tmp/cloud_prod_result.txt
}
set -x
main "$@"

@ -393,6 +393,53 @@ grpc_interop_test_args() {
} }
} }
# checks the positional args and assigns them to variables visible in the caller
#
# these are the positional args passed to grpc_cloud_prod_test after option flags
# are removed
#
# three args are expected, in order
# - test_case
# - host <the gce docker instance on which to run the test>
# - client to run
grpc_cloud_prod_test_args() {
[[ -n $1 ]] && { # test_case
test_case=$1
shift
} || {
echo "$FUNCNAME: missing arg: test_case" 1>&2
return 1
}
[[ -n $1 ]] && { # host
host=$1
shift
} || {
echo "$FUNCNAME: missing arg: host" 1>&2
return 1
}
[[ -n $1 ]] && { # client_type
case $1 in
cxx|go|java|nodejs|php|python|ruby)
grpc_gen_test_cmd="grpc_cloud_prod_gen_$1_cmd"
declare -F $grpc_gen_test_cmd >> /dev/null || {
echo "-f: test_func for $1 => $grpc_gen_test_cmd is not defined" 1>&2
return 2
}
shift
;;
*)
echo "bad client_type: $1" 1>&2
return 1
;;
esac
} || {
echo "$FUNCNAME: missing arg: client_type" 1>&2
return 1
}
}
_grpc_sync_scripts_args() { _grpc_sync_scripts_args() {
grpc_gce_script_root='tools/gce_setup' grpc_gce_script_root='tools/gce_setup'
@ -622,6 +669,52 @@ grpc_interop_test() {
gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
} }
# Runs a test command on a docker instance.
#
# call-seq:
# grpc_cloud_prod_test <test_name> <host> <client_type>
#
# requirements:
# host is a GCE instance running docker with access to the gRPC docker images
# test_name is one of the named gRPC tests [http://go/grpc_interop_tests]
# client_type is one of [cxx,go,java,php,python,ruby]
#
# it assumes:
# that each grpc-imp has a docker image named grpc/<imp>, e.g, grpc/java
# a test is run using $ docker run 'path/to/interop_test_bin --flags'
# the required images are available on <host>
#
# each client_type should have an associated bash func:
# grpc_cloud_prod_gen_<client_type>_cmd
# the func provides the dockerized commmand for running client_type's test.
# If no such func is available, tests for that client type cannot be run.
grpc_cloud_prod_test() {
_grpc_ensure_gcloud_ssh || return 1;
# declare vars local so that they don't pollute the shell environment
# where they this func is used.
local grpc_zone grpc_project dry_run # set by _grpc_set_project_and_zone
# grpc_cloud_prod_test_args
local test_case host grpc_gen_test_cmd
# set the project zone and check that all necessary args are provided
_grpc_set_project_and_zone -f grpc_cloud_prod_test_args "$@" || return 1
gce_has_instance $grpc_project $host || return 1;
local test_case_flag=" --test_case=$test_case"
cmd=$($grpc_gen_test_cmd $test_case_flag)
[[ -n $cmd ]] || return 1
local project_opt="--project $grpc_project"
local zone_opt="--zone $grpc_zone"
local ssh_cmd="bash -l -c \"$cmd\""
echo "will run:"
echo " $ssh_cmd"
echo "on $host"
[[ $dry_run == 1 ]] && return 0 # don't run the command on a dry run
gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
}
# constructs the full dockerized ruby interop test cmd. # constructs the full dockerized ruby interop test cmd.
# #
# call-seq: # call-seq:
@ -630,10 +723,27 @@ grpc_interop_test() {
grpc_interop_gen_ruby_cmd() { grpc_interop_gen_ruby_cmd() {
local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c" local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c"
local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb" local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
local the_cmd="$cmd_prefix 'ruby $test_script $@'" local the_cmd="$cmd_prefix 'ruby $test_script --use_test_ca --use_tls $@'"
echo $the_cmd
}
# constructs the full dockerized java interop test cmd.
#
# call-seq:
# flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_gen_ruby_cmd() {
local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c"
local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
local test_script+=" --use_tls"
local gfe_flags=" --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $@'"
echo $the_cmd echo $the_cmd
} }
# constructs the full dockerized Go interop test cmd. # constructs the full dockerized Go interop test cmd.
# #
# call-seq: # call-seq:
@ -655,11 +765,25 @@ grpc_interop_gen_go_cmd() {
grpc_interop_gen_java_cmd() { grpc_interop_gen_java_cmd() {
local cmd_prefix="sudo docker run grpc/java"; local cmd_prefix="sudo docker run grpc/java";
local test_script="/var/local/git/grpc-java/run-test-client.sh"; local test_script="/var/local/git/grpc-java/run-test-client.sh";
local test_script+=" --transport=NETTY_TLS --grpc_version=2" local test_script+=" --server_host_override=foo.test.google.fr --use_test_ca=true --use_tls=true"
local the_cmd="$cmd_prefix $test_script $@"; local the_cmd="$cmd_prefix $test_script $@";
echo $the_cmd echo $the_cmd
} }
# constructs the full dockerized java interop test cmd.
#
# call-seq:
# flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_gen_java_cmd() {
local cmd_prefix="sudo docker run grpc/java";
local test_script="/var/local/git/grpc-java/run-test-client.sh";
local test_script+=" --use_tls=true"
local gfe_flags=" --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
local the_cmd="$cmd_prefix $test_script $gfe_flags $@";
echo $the_cmd
}
# constructs the full dockerized php interop test cmd. # constructs the full dockerized php interop test cmd.
# #
# TODO(mlumish): update this to use the script once that's on git-on-borg # TODO(mlumish): update this to use the script once that's on git-on-borg
@ -683,10 +807,24 @@ grpc_interop_gen_php_cmd() {
# flags= .... # generic flags to include the command # flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags) # cmd=$($grpc_gen_test_cmd $flags)
grpc_interop_gen_cxx_cmd() { grpc_interop_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx"; local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl"; local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl";
local the_cmd="$cmd_prefix $test_script $@"; local the_cmd="$cmd_prefix $test_script $@";
echo $the_cmd echo $the_cmd
} }
# constructs the full dockerized cpp interop test cmd.
#
#
# call-seq:
# flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl";
local gfe_flags=" --use_prod_roots --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
local the_cmd="$cmd_prefix $test_script $gfe_flags $@";
echo $the_cmd
}
# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs # TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs

@ -0,0 +1,28 @@
#!/bin/bash
main() {
source grpc_docker.sh
test_cases=(large_unary empty_unary ping_pong client_streaming server_streaming)
clients=(cxx java go ruby)
servers=(cxx java go ruby)
for test_case in "${test_cases[@]}"
do
for client in "${clients[@]}"
do
for server in "${servers[@]}"
do
if grpc_interop_test $test_case grpc-docker-testclients $client grpc-docker-server $server
then
echo "$test_case $client $server passed" >> /tmp/interop_result.txt
else
echo "$test_case $client $server failed" >> /tmp/interop_result.txt
fi
done
done
done
gsutil cp /tmp/interop_result.txt gs://stoked-keyword-656-output/interop_result.txt
rm /tmp/interop_result.txt
}
set -x
main "$@"

@ -405,14 +405,18 @@ grpc_dockerfile_install() {
# For specific base images, sync the ssh key into the .ssh dir in the dockerfile context # For specific base images, sync the ssh key into the .ssh dir in the dockerfile context
[[ $image_label == "grpc/base" ]] && { [[ $image_label == "grpc/base" ]] && {
grpc_docker_sync_github_key $dockerfile_dir/.ssh 'base_ssh_key'|| return 1; grpc_docker_sync_github_key $dockerfile_dir/.ssh 'base_ssh_key' || return 1;
} }
[[ $image_label == "grpc/go" ]] && { [[ $image_label == "grpc/go" ]] && {
grpc_docker_sync_github_key $dockerfile_dir/.ssh 'go_ssh_key'|| return 1; grpc_docker_sync_github_key $dockerfile_dir/.ssh 'go_ssh_key' || return 1;
} }
[[ $image_label == "grpc/java_base" ]] && { [[ $image_label == "grpc/java_base" ]] && {
grpc_docker_sync_github_key $dockerfile_dir/.ssh 'java_base_ssh_key'|| return 1; grpc_docker_sync_github_key $dockerfile_dir/.ssh 'java_base_ssh_key' || return 1;
} }
[[ $image_label == "grpc/ruby" ]] && {
grpc_docker_sync_roots_pem $dockerfile_dir/cacerts || return 1;
}
# TODO(temiola): maybe make cache/no-cache a func option? # TODO(temiola): maybe make cache/no-cache a func option?
sudo docker build $cache_opt -t $image_label $dockerfile_dir || { sudo docker build $cache_opt -t $image_label $dockerfile_dir || {
@ -471,3 +475,31 @@ grpc_docker_sync_github_key() {
} }
gsutil cp $src $gcs_key_path $local_key_path gsutil cp $src $gcs_key_path $local_key_path
} }
# grpc_docker_sync_roots_pem.
#
# Copies the root pems from GCS to the target dir
#
# call-seq:
# grpc_docker_sync_roots_pem <target_dir>
grpc_docker_sync_roots_pem() {
local target_dir=$1
[[ -n $target_dir ]] || { echo "$FUNCNAME: missing arg: target_dir" >&2; return 1; }
# determine the admin root; the parent of the dockerfile root,
local gs_dockerfile_root=$(load_metadata "attributes/gs_dockerfile_root")
[[ -n $gs_dockerfile_root ]] || {
echo "$FUNCNAME: missing metadata: gs_dockerfile_root" >&2
return 1
}
local gcs_admin_root=$(dirname $gs_dockerfile_root)
# cp the file from gsutil to a known local area
local gcs_certs_path=$gcs_admin_root/cacerts/roots.pem
local local_certs_path=$target_dir/roots.pem
mkdir -p $target_dir || {
echo "$FUNCNAME: could not create dir: $target_dir" 1>&2
return 1
}
gsutil cp $src $gcs_certs_path $local_certs_path
}

@ -2,19 +2,18 @@
set -ex set -ex
CONFIG=${CONFIG:-opt}
# change to grpc repo root # change to grpc repo root
cd $(dirname $0)/../.. cd $(dirname $0)/../..
# tells npm install to look for files in that directory # tells npm install to look for files in that directory
export GRPC_ROOT=`pwd` export GRPC_ROOT=`pwd`
# tells npm install the subdirectory with library files # tells npm install the subdirectory with library files
export GRPC_LIB_SUBDIR=libs/opt export GRPC_LIB_SUBDIR=libs/$CONFIG
# tells npm install not to use default locations # tells npm install not to use default locations
export GRPC_NO_INSTALL=yes export GRPC_NO_INSTALL=yes
# build the c libraries
make -j static_c
cd src/node cd src/node
npm install npm install

@ -2,14 +2,13 @@
set -ex set -ex
CONFIG=${CONFIG:-opt}
# change to grpc repo root # change to grpc repo root
cd $(dirname $0)/../.. cd $(dirname $0)/../..
root=`pwd` root=`pwd`
export GRPC_LIB_SUBDIR=libs/opt export GRPC_LIB_SUBDIR=libs/$CONFIG
# make the libraries
make -j static_c
# build php # build php
cd src/php cd src/php
@ -18,4 +17,3 @@ cd ext/grpc
phpize phpize
./configure --enable-grpc=$root ./configure --enable-grpc=$root
make make

@ -7,7 +7,7 @@ out=`realpath ${1:-coverage}`
root=`realpath $(dirname $0)/../..` root=`realpath $(dirname $0)/../..`
tmp=`mktemp` tmp=`mktemp`
cd $root cd $root
tools/run_tests/run_tests.py -c gcov tools/run_tests/run_tests.py -c gcov -l c c++
lcov --capture --directory . --output-file $tmp lcov --capture --directory . --output-file $tmp
genhtml $tmp --output-directory $out genhtml $tmp --output-directory $out
rm $tmp rm $tmp

@ -0,0 +1,10 @@
#!/bin/bash
set -ex
# change to grpc repo root
cd $(dirname $0)/../..
root=`pwd`
$root/src/node/node_modules/mocha/bin/mocha $root/src/node/test

@ -46,8 +46,8 @@ class CLanguage(object):
self.make_target = make_target self.make_target = make_target
with open('tools/run_tests/tests.json') as f: with open('tools/run_tests/tests.json') as f:
js = json.load(f) js = json.load(f)
self.binaries = [tgt['name'] self.binaries = [tgt['name']
for tgt in js for tgt in js
if tgt['language'] == test_lang] if tgt['language'] == test_lang]
def test_binaries(self, config): def test_binaries(self, config):
@ -59,6 +59,19 @@ class CLanguage(object):
def build_steps(self): def build_steps(self):
return [] return []
class NodeLanguage(object):
def __init__(self):
self.allow_hashing = False
def test_binaries(self, config):
return ['tools/run_tests/run_node.sh']
def make_targets(self):
return ['static_c']
def build_steps(self):
return [['tools/run_tests/build_node.sh']]
class PhpLanguage(object): class PhpLanguage(object):
@ -69,7 +82,7 @@ class PhpLanguage(object):
return ['src/php/bin/run_tests.sh'] return ['src/php/bin/run_tests.sh']
def make_targets(self): def make_targets(self):
return [] return ['static_c']
def build_steps(self): def build_steps(self):
return [['tools/run_tests/build_php.sh']] return [['tools/run_tests/build_php.sh']]
@ -107,6 +120,7 @@ _DEFAULT = ['dbg', 'opt']
_LANGUAGES = { _LANGUAGES = {
'c++': CLanguage('cxx', 'c++'), 'c++': CLanguage('cxx', 'c++'),
'c': CLanguage('c', 'c'), 'c': CLanguage('c', 'c'),
'node': NodeLanguage(),
'php': PhpLanguage(), 'php': PhpLanguage(),
'python': PythonLanguage(), 'python': PythonLanguage(),
} }
@ -190,8 +204,8 @@ class TestCache(object):
def _build_and_run(check_cancelled, newline_on_success, cache): def _build_and_run(check_cancelled, newline_on_success, cache):
"""Do one pass of building & running tests.""" """Do one pass of building & running tests."""
# build latest, sharing cpu between the various makes # build latest sequentially
if not jobset.run(build_steps): if not jobset.run(build_steps, maxjobs=1):
return 1 return 1
# run all the tests # run all the tests

@ -86,7 +86,6 @@
<ClInclude Include="..\..\include\grpc\support\port_platform.h" /> <ClInclude Include="..\..\include\grpc\support\port_platform.h" />
<ClInclude Include="..\..\include\grpc\support\slice.h" /> <ClInclude Include="..\..\include\grpc\support\slice.h" />
<ClInclude Include="..\..\include\grpc\support\slice_buffer.h" /> <ClInclude Include="..\..\include\grpc\support\slice_buffer.h" />
<ClInclude Include="..\..\include\grpc\support\string.h" />
<ClInclude Include="..\..\include\grpc\support\sync.h" /> <ClInclude Include="..\..\include\grpc\support\sync.h" />
<ClInclude Include="..\..\include\grpc\support\sync_generic.h" /> <ClInclude Include="..\..\include\grpc\support\sync_generic.h" />
<ClInclude Include="..\..\include\grpc\support\sync_posix.h" /> <ClInclude Include="..\..\include\grpc\support\sync_posix.h" />
@ -102,6 +101,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\src\core\support\cpu.h" /> <ClInclude Include="..\..\src\core\support\cpu.h" />
<ClInclude Include="..\..\src\core\support\murmur_hash.h" /> <ClInclude Include="..\..\src\core\support\murmur_hash.h" />
<ClInclude Include="..\..\src\core\support\string.h" />
<ClInclude Include="..\..\src\core\support\thd_internal.h" /> <ClInclude Include="..\..\src\core\support\thd_internal.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -120,9 +120,6 @@
<ClInclude Include="..\..\include\grpc\support\slice_buffer.h"> <ClInclude Include="..\..\include\grpc\support\slice_buffer.h">
<Filter>include\grpc\support</Filter> <Filter>include\grpc\support</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\include\grpc\support\string.h">
<Filter>include\grpc\support</Filter>
</ClInclude>
<ClInclude Include="..\..\include\grpc\support\sync.h"> <ClInclude Include="..\..\include\grpc\support\sync.h">
<Filter>include\grpc\support</Filter> <Filter>include\grpc\support</Filter>
</ClInclude> </ClInclude>
@ -164,6 +161,9 @@
<ClInclude Include="..\..\src\core\support\murmur_hash.h"> <ClInclude Include="..\..\src\core\support\murmur_hash.h">
<Filter>src\core\support</Filter> <Filter>src\core\support</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\core\support\string.h">
<Filter>src\core\support</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\support\thd_internal.h"> <ClInclude Include="..\..\src\core\support\thd_internal.h">
<Filter>src\core\support</Filter> <Filter>src\core\support</Filter>
</ClInclude> </ClInclude>

Loading…
Cancel
Save