merge with head

pull/4089/head
yang-g 9 years ago
commit 3ff9727a37
  1. 439
      Makefile
  2. 34
      build.yaml
  3. 5
      include/grpc++/security/credentials.h
  4. 6
      include/grpc/grpc.h
  5. 141
      reports/interop_html_report.template
  6. 6
      src/core/client_config/subchannel.h
  7. 3
      src/core/iomgr/pollset_windows.c
  8. 19
      src/core/iomgr/tcp_server.h
  9. 149
      src/core/iomgr/tcp_server_posix.c
  10. 121
      src/core/iomgr/tcp_server_windows.c
  11. 48
      src/core/security/credentials.c
  12. 3
      src/core/security/credentials.h
  13. 3
      src/core/security/google_default_credentials.c
  14. 6
      src/core/security/security_connector.c
  15. 4
      src/core/security/server_secure_chttp2.c
  16. 1
      src/core/surface/byte_buffer_reader.c
  17. 3
      src/core/surface/call.c
  18. 4
      src/core/surface/server_chttp2.c
  19. 113
      src/core/transport/chttp2/hpack_encoder.c
  20. 24
      src/core/transport/chttp2/hpack_encoder.h
  21. 98
      src/core/transport/chttp2/hpack_parser.c
  22. 115
      src/core/transport/chttp2/hpack_table.c
  23. 32
      src/core/transport/chttp2/hpack_table.h
  24. 6
      src/core/transport/chttp2/internal.h
  25. 7
      src/core/transport/chttp2/parsing.c
  26. 9
      src/core/transport/chttp2/writing.c
  27. 38
      src/core/transport/chttp2_transport.c
  28. 4
      src/cpp/client/insecure_credentials.cc
  29. 1
      src/cpp/client/secure_credentials.h
  30. 2
      src/csharp/Grpc.Core/Profiling/IProfiler.cs
  31. 3
      src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs
  32. 13
      src/csharp/Grpc.Core/Profiling/Profilers.cs
  33. 3
      src/csharp/Grpc.IntegrationTesting.QpsWorker/.gitignore
  34. 60
      src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
  35. 46
      src/csharp/Grpc.IntegrationTesting.QpsWorker/Program.cs
  36. 11
      src/csharp/Grpc.IntegrationTesting.QpsWorker/Properties/AssemblyInfo.cs
  37. 76
      src/csharp/Grpc.IntegrationTesting/BenchmarkServiceImpl.cs
  38. 153
      src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
  39. 2362
      src/csharp/Grpc.IntegrationTesting/Control.cs
  40. 67
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  41. 153
      src/csharp/Grpc.IntegrationTesting/Histogram.cs
  42. 104
      src/csharp/Grpc.IntegrationTesting/HistogramTest.cs
  43. 67
      src/csharp/Grpc.IntegrationTesting/IClientRunner.cs
  44. 72
      src/csharp/Grpc.IntegrationTesting/IServerRunner.cs
  45. 580
      src/csharp/Grpc.IntegrationTesting/Payloads.cs
  46. 108
      src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
  47. 117
      src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs
  48. 124
      src/csharp/Grpc.IntegrationTesting/ServerRunners.cs
  49. 44
      src/csharp/Grpc.IntegrationTesting/Services.cs
  50. 198
      src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
  51. 5
      src/csharp/Grpc.IntegrationTesting/Settings.StyleCop
  52. 744
      src/csharp/Grpc.IntegrationTesting/Stats.cs
  53. 78
      src/csharp/Grpc.IntegrationTesting/WallClockStopwatch.cs
  54. 96
      src/csharp/Grpc.IntegrationTesting/WorkerServiceImpl.cs
  55. 96
      src/csharp/Grpc.sln
  56. 6
      src/csharp/generate_proto_csharp.sh
  57. 4
      test/core/client_config/lb_policies_test.c
  58. 6
      test/core/end2end/fixtures/proxy.c
  59. 14
      test/core/end2end/gen_build_yaml.py
  60. 3
      test/core/end2end/tests/cancel_with_status.c
  61. 446
      test/core/end2end/tests/hpack_size.c
  62. 3
      test/core/end2end/tests/negative_deadline.c
  63. 8
      test/core/network_benchmarks/low_level_ping_pong.c
  64. 10
      test/core/security/credentials_test.c
  65. 4
      test/core/surface/byte_buffer_reader_test.c
  66. 6
      test/core/transport/chttp2/hpack_parser_test.c
  67. 27
      test/core/transport/chttp2/hpack_table_test.c
  68. 5
      test/core/util/test_tcp_server.c
  69. 103
      test/cpp/interop/metrics_client.cc
  70. 44
      test/cpp/interop/stress_interop_client.cc
  71. 13
      test/cpp/interop/stress_interop_client.h
  72. 74
      test/cpp/interop/stress_test.cc
  73. 1
      test/cpp/qps/async_streaming_ping_pong_test.cc
  74. 1
      test/cpp/qps/async_unary_ping_pong_test.cc
  75. 46
      test/cpp/qps/client.h
  76. 2
      test/cpp/qps/driver.cc
  77. 8
      test/cpp/qps/histogram.h
  78. 6
      test/cpp/qps/qps_driver.cc
  79. 1
      test/cpp/qps/qps_openloop_test.cc
  80. 1
      test/cpp/qps/qps_test.cc
  81. 1
      test/cpp/qps/qps_test_with_poll.cc
  82. 1
      test/cpp/qps/secure_sync_unary_ping_pong_test.cc
  83. 2
      test/cpp/qps/server_async.cc
  84. 2
      test/cpp/qps/server_sync.cc
  85. 1
      test/cpp/qps/sync_streaming_ping_pong_test.cc
  86. 1
      test/cpp/qps/sync_unary_ping_pong_test.cc
  87. 119
      test/cpp/util/metrics_server.cc
  88. 100
      test/cpp/util/metrics_server.h
  89. 24
      test/proto/benchmarks/control.proto
  90. 14
      test/proto/benchmarks/services.proto
  91. 17
      test/proto/benchmarks/stats.proto
  92. 56
      test/proto/metrics.proto
  93. 216
      tools/gke/kubernetes_api.py
  94. 72
      tools/http2_interop/goaway.go
  95. 52
      tools/http2_interop/http2interop.go
  96. 55
      tools/http2_interop/http2interop_test.go
  97. 3
      tools/run_tests/dockerjob.py
  98. 168
      tools/run_tests/report_utils.py
  99. 9
      tools/run_tests/run_interop_tests.py
  100. 6
      tools/run_tests/run_tests.py
  101. Some files were not shown because too many files have changed in this diff Show More

File diff suppressed because one or more lines are too long

@ -1914,6 +1914,20 @@ targets:
- mac - mac
- linux - linux
- posix - posix
- name: metrics_client
build: test
run: false
language: c++
headers:
- test/cpp/util/metrics_server.h
src:
- test/proto/metrics.proto
- test/cpp/interop/metrics_client.cc
deps:
- grpc++
- grpc
- gpr
- grpc++_test_config
- name: mock_test - name: mock_test
build: test build: test
language: c++ language: c++
@ -2155,13 +2169,16 @@ targets:
- test/cpp/interop/client_helper.h - test/cpp/interop/client_helper.h
- test/cpp/interop/interop_client.h - test/cpp/interop/interop_client.h
- test/cpp/interop/stress_interop_client.h - test/cpp/interop/stress_interop_client.h
- test/cpp/util/metrics_server.h
src: src:
- test/proto/empty.proto - test/proto/empty.proto
- test/proto/messages.proto - test/proto/messages.proto
- test/proto/metrics.proto
- test/proto/test.proto - test/proto/test.proto
- test/cpp/interop/interop_client.cc - test/cpp/interop/interop_client.cc
- test/cpp/interop/stress_interop_client.cc - test/cpp/interop/stress_interop_client.cc
- test/cpp/interop/stress_test.cc - test/cpp/interop/stress_test.cc
- test/cpp/util/metrics_server.cc
deps: deps:
- grpc++_test_util - grpc++_test_util
- grpc_test_util - grpc_test_util
@ -2256,6 +2273,23 @@ node_modules:
- deps: - deps:
- grpc - grpc
- gpr - gpr
headers:
- src/node/ext/byte_buffer.h
- src/node/ext/call.h
- src/node/ext/call_credentials.h
- src/node/ext/channel.h
- src/node/ext/channel_credentials.h
- src/node/ext/completion_queue_async_worker.h
- src/node/ext/server.h
- src/node/ext/server_credentials.h
- src/node/ext/timeval.h
js:
- src/node/index.js
- src/node/src/client.js
- src/node/src/common.js
- src/node/src/credentials.js
- src/node/src/metadata.js
- src/node/src/server.js
name: grpc_node name: grpc_node
src: src:
- src/node/ext/byte_buffer.cc - src/node/ext/byte_buffer.cc

@ -186,9 +186,8 @@ std::shared_ptr<CallCredentials> GoogleIAMCredentials(
/// Combines a channel credentials and a call credentials into a composite /// Combines a channel credentials and a call credentials into a composite
/// channel credentials. /// channel credentials.
std::shared_ptr<ChannelCredentials> CompositeChannelCredentials( std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
const std::shared_ptr<ChannelCredentials>& channel_creds, const std::shared_ptr<ChannelCredentials>& channel_creds,
const std::shared_ptr<CallCredentials>& call_creds); const std::shared_ptr<CallCredentials>& call_creds);
/// Combines two call credentials objects into a composite call credentials. /// Combines two call credentials objects into a composite call credentials.
std::shared_ptr<CallCredentials> CompositeCallCredentials( std::shared_ptr<CallCredentials> CompositeCallCredentials(

@ -127,6 +127,12 @@ typedef struct {
/** Initial sequence number for http2 transports */ /** Initial sequence number for http2 transports */
#define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \ #define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \
"grpc.http2.initial_sequence_number" "grpc.http2.initial_sequence_number"
/** How much memory to use for hpack decoding */
#define GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER \
"grpc.http2.hpack_table_size.decoder"
/** How much memory to use for hpack encoding */
#define GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER \
"grpc.http2.hpack_table_size.encoder"
/** Default authority to pass if none specified on call construction */ /** Default authority to pass if none specified on call construction */
#define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority" #define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority"
/** Primary user agent: goes at the start of the user-agent metadata /** Primary user agent: goes at the start of the user-agent metadata

@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en">
<head><title>Interop Test Result</title></head>
<body>
<%def name="fill_one_test_result(shortname, resultset)">
% if shortname in resultset:
## Because interop tests does not have runs_per_test flag, each test is
## run once. So there should only be one element for each result.
<% result = resultset[shortname][0] %>
% if result.state == 'PASSED':
<td bgcolor="green">PASS</td>
% else:
<%
tooltip = ''
if result.returncode > 0 or result.message:
if result.returncode > 0:
tooltip = 'returncode: %d ' % result.returncode
if result.message:
tooltip = '%smessage: %s' % (tooltip, result.message)
%>
% if result.state == 'FAILED':
<td bgcolor="red">
% if tooltip:
<a href="#" data-toggle="tooltip" data-placement="auto" title="${tooltip | h}">FAIL</a></td>
% else:
FAIL</td>
% endif
% elif result.state == 'TIMEOUT':
<td bgcolor="yellow">
% if tooltip:
<a href="#" data-toggle="tooltip" data-placement="auto" title="${tooltip | h}">TIMEOUT</a></td>
% else:
TIMEOUT</td>
% endif
% endif
% endif
% else:
<td bgcolor="magenta">Not implemented</td>
% endif
</%def>
% if num_failures > 1:
<p><h2><font color="red">${num_failures} tests failed!</font></h2></p>
% elif num_failures:
<p><h2><font color="red">${num_failures} test failed!</font></h2></p>
% else:
<p><h2><font color="green">All tests passed!</font></h2></p>
% endif
% if cloud_to_prod:
## Each column header is the client language.
<h2>Cloud to Prod</h2>
<table style="width:100%" border="1">
<tr bgcolor="#00BFFF">
<th>Client languages &#9658;<br/>Test Cases &#9660;</th>
% for client_lang in client_langs:
<th>${client_lang}</th>
% endfor
</tr>
% for test_case in test_cases + auth_test_cases:
<tr><td><b>${test_case}</b></td>
% for client_lang in client_langs:
<%
if test_case in auth_test_cases:
shortname = 'cloud_to_prod_auth:%s:%s' % (client_lang, test_case)
else:
shortname = 'cloud_to_prod:%s:%s' % (client_lang, test_case)
%>
${fill_one_test_result(shortname, resultset)}
% endfor
</tr>
% endfor
</table>
% endif
% if http2_interop:
## Each column header is the server language.
<h2>HTTP/2 Interop</h2>
<table style="width:100%" border="1">
<tr bgcolor="#00BFFF">
<th>Servers &#9658;<br/>Test Cases &#9660;</th>
% for server_lang in server_langs:
<th>${server_lang}</th>
% endfor
% if cloud_to_prod:
<th>prod</th>
% endif
</tr>
% for test_case in http2_cases:
<tr><td><b>${test_case}</b></td>
## Fill up the cells with test result.
% for server_lang in server_langs:
<%
shortname = 'cloud_to_cloud:http2:%s_server:%s' % (
server_lang, test_case)
%>
${fill_one_test_result(shortname, resultset)}
% endfor
% if cloud_to_prod:
<% shortname = 'cloud_to_prod:http2:%s' % test_case %>
${fill_one_test_result(shortname, resultset)}
% endif
</tr>
% endfor
</table>
% endif
% if server_langs:
% for test_case in test_cases:
## Each column header is the client language.
<h2>${test_case}</h2>
<table style="width:100%" border="1">
<tr bgcolor="#00BFFF">
<th>Client languages &#9658;<br/>Server languages &#9660;</th>
% for client_lang in client_langs:
<th>${client_lang}</th>
% endfor
</tr>
## Each row head is the server language.
% for server_lang in server_langs:
<tr>
<td><b>${server_lang}</b></td>
% for client_lang in client_langs:
<%
shortname = 'cloud_to_cloud:%s:%s_server:%s' % (
client_lang, server_lang, test_case)
%>
${fill_one_test_result(shortname, resultset)}
% endfor
</tr>
% endfor
</table>
% endfor
% endif
<script>
$(document).ready(function(){$('[data-toggle="tooltip"]').tooltip();});
</script>
</body>
</html>

@ -77,10 +77,10 @@ void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx,
/** construct a subchannel call (possibly asynchronously). /** construct a subchannel call (possibly asynchronously).
* *
* If the returned status is 1, the call will return immediately and \a target * If the returned status is 1, the call will return immediately and \a target
* will point to a connected \a subchannel_call instance. Note that \a notify * will point to a connected \a subchannel_call instance. Note that \a notify
* will \em not be invoked in this case. * will \em not be invoked in this case.
* Otherwise, if the returned status is 0, the subchannel call will be created * Otherwise, if the returned status is 0, the subchannel call will be created
* asynchronously, invoking the \a notify callback upon completion. */ * asynchronously, invoking the \a notify callback upon completion. */
int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx, int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx,
grpc_subchannel *subchannel, grpc_subchannel *subchannel,

@ -126,7 +126,8 @@ void grpc_pollset_destroy(grpc_pollset *pollset) {}
void grpc_pollset_reset(grpc_pollset *pollset) { void grpc_pollset_reset(grpc_pollset *pollset) {
GPR_ASSERT(pollset->shutting_down); GPR_ASSERT(pollset->shutting_down);
GPR_ASSERT(!has_workers(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET)); GPR_ASSERT(
!has_workers(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET));
pollset->shutting_down = 0; pollset->shutting_down = 0;
pollset->is_iocp_worker = 0; pollset->is_iocp_worker = 0;
pollset->kicked_without_pollers = 0; pollset->kicked_without_pollers = 0;

@ -39,6 +39,9 @@
/* Forward decl of grpc_tcp_server */ /* Forward decl of grpc_tcp_server */
typedef struct grpc_tcp_server grpc_tcp_server; typedef struct grpc_tcp_server grpc_tcp_server;
/* Forward decl of grpc_tcp_listener */
typedef struct grpc_tcp_listener grpc_tcp_listener;
/* Called for newly connected TCP connections. */ /* Called for newly connected TCP connections. */
typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg, typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg,
grpc_endpoint *ep); grpc_endpoint *ep);
@ -51,19 +54,17 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server,
grpc_pollset **pollsets, size_t pollset_count, grpc_pollset **pollsets, size_t pollset_count,
grpc_tcp_server_cb on_accept_cb, void *cb_arg); grpc_tcp_server_cb on_accept_cb, void *cb_arg);
/* Add a port to the server, returning port number on success, or negative /* Add a port to the server, returning the newly created listener on success,
on failure. or a null pointer on failure.
The :: and 0.0.0.0 wildcard addresses are treated identically, accepting The :: and 0.0.0.0 wildcard addresses are treated identically, accepting
both IPv4 and IPv6 connections, but :: is the preferred style. This usually both IPv4 and IPv6 connections, but :: is the preferred style. This usually
creates one socket, but possibly two on systems which support IPv6, creates one socket, but possibly two on systems which support IPv6,
but not dualstack sockets. but not dualstack sockets. */
For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */
/* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle /* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle
all of the multiple socket port matching logic in one place */ all of the multiple socket port matching logic in one place */
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s,
size_t addr_len); const void *addr, size_t addr_len);
/* Returns the file descriptor of the Nth listening socket on this server, /* Returns the file descriptor of the Nth listening socket on this server,
or -1 if the index is out of bounds. or -1 if the index is out of bounds.
@ -75,4 +76,8 @@ int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index);
void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server,
grpc_closure *closure); grpc_closure *closure);
int grpc_tcp_listener_get_port(grpc_tcp_listener *listener);
void grpc_tcp_listener_ref(grpc_tcp_listener *listener);
void grpc_tcp_listener_unref(grpc_tcp_listener *listener);
#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H */ #endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H */

@ -67,14 +67,13 @@
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#define INIT_PORT_CAP 2
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 #define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
static gpr_once s_init_max_accept_queue_size; static gpr_once s_init_max_accept_queue_size;
static int s_max_accept_queue_size; static int s_max_accept_queue_size;
/* one listening port */ /* one listening port */
typedef struct { struct grpc_tcp_listener {
int fd; int fd;
grpc_fd *emfd; grpc_fd *emfd;
grpc_tcp_server *server; grpc_tcp_server *server;
@ -84,9 +83,18 @@ typedef struct {
struct sockaddr_un un; struct sockaddr_un un;
} addr; } addr;
size_t addr_len; size_t addr_len;
int port;
grpc_closure read_closure; grpc_closure read_closure;
grpc_closure destroyed_closure; grpc_closure destroyed_closure;
} server_port; gpr_refcount refs;
struct grpc_tcp_listener *next;
/* When we add a listener, more than one can be created, mainly because of
IPv6. A sibling will still be in the normal list, but will be flagged
as such. Any action, such as ref or unref, will affect all of the
siblings in the list. */
struct grpc_tcp_listener *sibling;
int is_sibling;
};
static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) { static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) {
struct stat st; struct stat st;
@ -112,10 +120,9 @@ struct grpc_tcp_server {
/* is this server shutting down? (boolean) */ /* is this server shutting down? (boolean) */
int shutdown; int shutdown;
/* all listening ports */ /* linked list of server ports */
server_port *ports; grpc_tcp_listener *head;
size_t nports; unsigned nports;
size_t port_capacity;
/* shutdown callback */ /* shutdown callback */
grpc_closure *shutdown_complete; grpc_closure *shutdown_complete;
@ -134,9 +141,8 @@ grpc_tcp_server *grpc_tcp_server_create(void) {
s->shutdown = 0; s->shutdown = 0;
s->on_accept_cb = NULL; s->on_accept_cb = NULL;
s->on_accept_cb_arg = NULL; s->on_accept_cb_arg = NULL;
s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); s->head = NULL;
s->nports = 0; s->nports = 0;
s->port_capacity = INIT_PORT_CAP;
return s; return s;
} }
@ -145,7 +151,12 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
gpr_mu_destroy(&s->mu); gpr_mu_destroy(&s->mu);
gpr_free(s->ports); while (s->head) {
grpc_tcp_listener *sp = s->head;
s->head = sp->next;
grpc_tcp_listener_unref(sp);
}
gpr_free(s); gpr_free(s);
} }
@ -166,8 +177,6 @@ static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, int success) {
events will be received on them - at this point it's safe to destroy events will be received on them - at this point it's safe to destroy
things */ things */
static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
size_t i;
/* delete ALL the things */ /* delete ALL the things */
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
@ -176,9 +185,9 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
return; return;
} }
if (s->nports) { if (s->head) {
for (i = 0; i < s->nports; i++) { grpc_tcp_listener *sp;
server_port *sp = &s->ports[i]; for (sp = s->head; sp; sp = sp->next) {
if (sp->addr.sockaddr.sa_family == AF_UNIX) { if (sp->addr.sockaddr.sa_family == AF_UNIX) {
unlink_if_unix_domain_socket(&sp->addr.un); unlink_if_unix_domain_socket(&sp->addr.un);
} }
@ -196,7 +205,6 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
grpc_closure *closure) { grpc_closure *closure) {
size_t i;
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->shutdown); GPR_ASSERT(!s->shutdown);
@ -206,8 +214,9 @@ void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
/* shutdown all fd's */ /* shutdown all fd's */
if (s->active_ports) { if (s->active_ports) {
for (i = 0; i < s->nports; i++) { grpc_tcp_listener *sp;
grpc_fd_shutdown(exec_ctx, s->ports[i].emfd); for (sp = s->head; sp; sp = sp->next) {
grpc_fd_shutdown(exec_ctx, sp->emfd);
} }
gpr_mu_unlock(&s->mu); gpr_mu_unlock(&s->mu);
} else { } else {
@ -298,7 +307,7 @@ error:
/* event manager callback when reads are ready */ /* event manager callback when reads are ready */
static void on_read(grpc_exec_ctx *exec_ctx, void *arg, int success) { static void on_read(grpc_exec_ctx *exec_ctx, void *arg, int success) {
server_port *sp = arg; grpc_tcp_listener *sp = arg;
grpc_fd *fdobj; grpc_fd *fdobj;
size_t i; size_t i;
@ -364,9 +373,10 @@ error:
} }
} }
static int add_socket_to_server(grpc_tcp_server *s, int fd, static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd,
const struct sockaddr *addr, size_t addr_len) { const struct sockaddr *addr,
server_port *sp; size_t addr_len) {
grpc_tcp_listener *sp = NULL;
int port; int port;
char *addr_str; char *addr_str;
char *name; char *name;
@ -376,32 +386,34 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd,
grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1); grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1);
gpr_asprintf(&name, "tcp-server-listener:%s", addr_str); gpr_asprintf(&name, "tcp-server-listener:%s", addr_str);
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
s->nports++;
GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
/* append it to the list under a lock */ sp = gpr_malloc(sizeof(grpc_tcp_listener));
if (s->nports == s->port_capacity) { sp->next = s->head;
s->port_capacity *= 2; s->head = sp;
s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity);
}
sp = &s->ports[s->nports++];
sp->server = s; sp->server = s;
sp->fd = fd; sp->fd = fd;
sp->emfd = grpc_fd_create(fd, name); sp->emfd = grpc_fd_create(fd, name);
memcpy(sp->addr.untyped, addr, addr_len); memcpy(sp->addr.untyped, addr, addr_len);
sp->addr_len = addr_len; sp->addr_len = addr_len;
sp->port = port;
sp->is_sibling = 0;
sp->sibling = NULL;
gpr_ref_init(&sp->refs, 1);
GPR_ASSERT(sp->emfd); GPR_ASSERT(sp->emfd);
gpr_mu_unlock(&s->mu); gpr_mu_unlock(&s->mu);
gpr_free(addr_str); gpr_free(addr_str);
gpr_free(name); gpr_free(name);
} }
return port; return sp;
} }
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s,
size_t addr_len) { const void *addr, size_t addr_len) {
int allocated_port1 = -1; int allocated_port = -1;
int allocated_port2 = -1; grpc_tcp_listener *sp;
unsigned i; grpc_tcp_listener *sp2 = NULL;
int fd; int fd;
grpc_dualstack_mode dsmode; grpc_dualstack_mode dsmode;
struct sockaddr_in6 addr6_v4mapped; struct sockaddr_in6 addr6_v4mapped;
@ -420,9 +432,9 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
/* Check if this is a wildcard port, and if so, try to keep the port the same /* Check if this is a wildcard port, and if so, try to keep the port the same
as some previously created listener. */ as some previously created listener. */
if (grpc_sockaddr_get_port(addr) == 0) { if (grpc_sockaddr_get_port(addr) == 0) {
for (i = 0; i < s->nports; i++) { for (sp = s->head; sp; sp = sp->next) {
sockname_len = sizeof(sockname_temp); sockname_len = sizeof(sockname_temp);
if (0 == getsockname(s->ports[i].fd, (struct sockaddr *)&sockname_temp, if (0 == getsockname(sp->fd, (struct sockaddr *)&sockname_temp,
&sockname_len)) { &sockname_len)) {
port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
if (port > 0) { if (port > 0) {
@ -436,6 +448,8 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
} }
} }
sp = NULL;
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
addr = (const struct sockaddr *)&addr6_v4mapped; addr = (const struct sockaddr *)&addr6_v4mapped;
addr_len = sizeof(addr6_v4mapped); addr_len = sizeof(addr6_v4mapped);
@ -449,14 +463,16 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
addr = (struct sockaddr *)&wild6; addr = (struct sockaddr *)&wild6;
addr_len = sizeof(wild6); addr_len = sizeof(wild6);
fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode); fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
allocated_port1 = add_socket_to_server(s, fd, addr, addr_len); sp = add_socket_to_server(s, fd, addr, addr_len);
allocated_port = sp->port;
if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
goto done; goto done;
} }
/* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
if (port == 0 && allocated_port1 > 0) { if (port == 0 && allocated_port > 0) {
grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port1); grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port);
sp2 = sp;
} }
addr = (struct sockaddr *)&wild4; addr = (struct sockaddr *)&wild4;
addr_len = sizeof(wild4); addr_len = sizeof(wild4);
@ -471,22 +487,32 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
addr = (struct sockaddr *)&addr4_copy; addr = (struct sockaddr *)&addr4_copy;
addr_len = sizeof(addr4_copy); addr_len = sizeof(addr4_copy);
} }
allocated_port2 = add_socket_to_server(s, fd, addr, addr_len); sp = add_socket_to_server(s, fd, addr, addr_len);
sp->sibling = sp2;
if (sp2) sp2->is_sibling = 1;
done: done:
gpr_free(allocated_addr); gpr_free(allocated_addr);
return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; return sp;
} }
int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned port_index) { int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned port_index) {
return (port_index < s->nports) ? s->ports[port_index].fd : -1; grpc_tcp_listener *sp;
for (sp = s->head; sp && port_index != 0; sp = sp->next, port_index--)
;
if (port_index == 0 && sp) {
return sp->fd;
} else {
return -1;
}
} }
void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
grpc_pollset **pollsets, size_t pollset_count, grpc_pollset **pollsets, size_t pollset_count,
grpc_tcp_server_cb on_accept_cb, grpc_tcp_server_cb on_accept_cb,
void *on_accept_cb_arg) { void *on_accept_cb_arg) {
size_t i, j; size_t i;
grpc_tcp_listener *sp;
GPR_ASSERT(on_accept_cb); GPR_ASSERT(on_accept_cb);
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->on_accept_cb); GPR_ASSERT(!s->on_accept_cb);
@ -495,17 +521,40 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
s->on_accept_cb_arg = on_accept_cb_arg; s->on_accept_cb_arg = on_accept_cb_arg;
s->pollsets = pollsets; s->pollsets = pollsets;
s->pollset_count = pollset_count; s->pollset_count = pollset_count;
for (i = 0; i < s->nports; i++) { for (sp = s->head; sp; sp = sp->next) {
for (j = 0; j < pollset_count; j++) { for (i = 0; i < pollset_count; i++) {
grpc_pollset_add_fd(exec_ctx, pollsets[j], s->ports[i].emfd); grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
} }
s->ports[i].read_closure.cb = on_read; sp->read_closure.cb = on_read;
s->ports[i].read_closure.cb_arg = &s->ports[i]; sp->read_closure.cb_arg = sp;
grpc_fd_notify_on_read(exec_ctx, s->ports[i].emfd, grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
&s->ports[i].read_closure);
s->active_ports++; s->active_ports++;
} }
gpr_mu_unlock(&s->mu); gpr_mu_unlock(&s->mu);
} }
int grpc_tcp_listener_get_port(grpc_tcp_listener *listener) {
grpc_tcp_listener *sp = listener;
return sp->port;
}
void grpc_tcp_listener_ref(grpc_tcp_listener *listener) {
grpc_tcp_listener *sp = listener;
gpr_ref(&sp->refs);
}
void grpc_tcp_listener_unref(grpc_tcp_listener *listener) {
grpc_tcp_listener *sp = listener;
if (sp->is_sibling) return;
if (gpr_unref(&sp->refs)) {
grpc_tcp_listener *sibling = sp->sibling;
while (sibling) {
sp = sibling;
sibling = sp->sibling;
gpr_free(sp);
}
gpr_free(listener);
}
}
#endif #endif

@ -35,7 +35,8 @@
#ifdef GPR_WINSOCK_SOCKET #ifdef GPR_WINSOCK_SOCKET
#define _GNU_SOURCE #include <io.h>
#include "src/core/iomgr/sockaddr_utils.h" #include "src/core/iomgr/sockaddr_utils.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
@ -51,25 +52,29 @@
#include "src/core/iomgr/tcp_server.h" #include "src/core/iomgr/tcp_server.h"
#include "src/core/iomgr/tcp_windows.h" #include "src/core/iomgr/tcp_windows.h"
#define INIT_PORT_CAP 2
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 #define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
/* one listening port */ /* one listening port */
typedef struct server_port { struct grpc_tcp_listener {
/* This seemingly magic number comes from AcceptEx's documentation. each /* This seemingly magic number comes from AcceptEx's documentation. each
address buffer needs to have at least 16 more bytes at their end. */ address buffer needs to have at least 16 more bytes at their end. */
gpr_uint8 addresses[(sizeof(struct sockaddr_in6) + 16) * 2]; gpr_uint8 addresses[(sizeof(struct sockaddr_in6) + 16) * 2];
/* This will hold the socket for the next accept. */ /* This will hold the socket for the next accept. */
SOCKET new_socket; SOCKET new_socket;
/* The listener winsocked. */ /* The listener winsocket. */
grpc_winsocket *socket; grpc_winsocket *socket;
/* The actual TCP port number. */
int port;
grpc_tcp_server *server; grpc_tcp_server *server;
/* The cached AcceptEx for that port. */ /* The cached AcceptEx for that port. */
LPFN_ACCEPTEX AcceptEx; LPFN_ACCEPTEX AcceptEx;
int shutting_down; int shutting_down;
/* closure for socket notification of accept being ready */ /* closure for socket notification of accept being ready */
grpc_closure on_accept; grpc_closure on_accept;
} server_port; gpr_refcount refs;
/* linked list */
struct grpc_tcp_listener *next;
};
/* the overall server */ /* the overall server */
struct grpc_tcp_server { struct grpc_tcp_server {
@ -82,10 +87,8 @@ struct grpc_tcp_server {
/* active port count: how many ports are actually still listening */ /* active port count: how many ports are actually still listening */
int active_ports; int active_ports;
/* all listening ports */ /* linked list of server ports */
server_port *ports; grpc_tcp_listener *head;
size_t nports;
size_t port_capacity;
/* shutdown callback */ /* shutdown callback */
grpc_closure *shutdown_complete; grpc_closure *shutdown_complete;
@ -99,9 +102,7 @@ grpc_tcp_server *grpc_tcp_server_create(void) {
s->active_ports = 0; s->active_ports = 0;
s->on_accept_cb = NULL; s->on_accept_cb = NULL;
s->on_accept_cb_arg = NULL; s->on_accept_cb_arg = NULL;
s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); s->head = NULL;
s->nports = 0;
s->port_capacity = INIT_PORT_CAP;
s->shutdown_complete = NULL; s->shutdown_complete = NULL;
return s; return s;
} }
@ -109,26 +110,26 @@ grpc_tcp_server *grpc_tcp_server_create(void) {
static void dont_care_about_shutdown_completion(void *arg) {} static void dont_care_about_shutdown_completion(void *arg) {}
static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
size_t i;
grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1); grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1);
/* Now that the accepts have been aborted, we can destroy the sockets. /* Now that the accepts have been aborted, we can destroy the sockets.
The IOCP won't get notified on these, so we can flag them as already The IOCP won't get notified on these, so we can flag them as already
closed by the system. */ closed by the system. */
for (i = 0; i < s->nports; i++) { while (s->head) {
server_port *sp = &s->ports[i]; grpc_tcp_listener *sp = s->head;
s->head = sp->next;
sp->next = NULL;
grpc_winsocket_destroy(sp->socket); grpc_winsocket_destroy(sp->socket);
grpc_tcp_listener_unref(sp);
} }
gpr_free(s->ports);
gpr_free(s); gpr_free(s);
} }
/* Public function. Stops and destroys a grpc_tcp_server. */ /* Public function. Stops and destroys a grpc_tcp_server. */
void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
grpc_closure *shutdown_complete) { grpc_closure *shutdown_complete) {
size_t i;
int immediately_done = 0; int immediately_done = 0;
grpc_tcp_listener *sp;
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
s->shutdown_complete = shutdown_complete; s->shutdown_complete = shutdown_complete;
@ -138,8 +139,7 @@ void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
if (s->active_ports == 0) { if (s->active_ports == 0) {
immediately_done = 1; immediately_done = 1;
} }
for (i = 0; i < s->nports; i++) { for (sp = s->head; sp; sp = sp->next) {
server_port *sp = &s->ports[i];
sp->shutting_down = 1; sp->shutting_down = 1;
grpc_winsocket_shutdown(sp->socket); grpc_winsocket_shutdown(sp->socket);
} }
@ -199,7 +199,7 @@ error:
} }
static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx, static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx,
server_port *sp) { grpc_tcp_listener *sp) {
int notify = 0; int notify = 0;
sp->shutting_down = 0; sp->shutting_down = 0;
gpr_mu_lock(&sp->server->mu); gpr_mu_lock(&sp->server->mu);
@ -216,7 +216,7 @@ static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx,
/* In order to do an async accept, we need to create a socket first which /* In order to do an async accept, we need to create a socket first which
will be the one assigned to the new incoming connection. */ will be the one assigned to the new incoming connection. */
static void start_accept(grpc_exec_ctx *exec_ctx, server_port *port) { static void start_accept(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *port) {
SOCKET sock = INVALID_SOCKET; SOCKET sock = INVALID_SOCKET;
char *message; char *message;
char *utf8_message; char *utf8_message;
@ -276,7 +276,7 @@ failure:
/* Event manager callback when reads are ready. */ /* Event manager callback when reads are ready. */
static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, int from_iocp) { static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, int from_iocp) {
server_port *sp = arg; grpc_tcp_listener *sp = arg;
SOCKET sock = sp->new_socket; SOCKET sock = sp->new_socket;
grpc_winsocket_callback_info *info = &sp->socket->read_info; grpc_winsocket_callback_info *info = &sp->socket->read_info;
grpc_endpoint *ep = NULL; grpc_endpoint *ep = NULL;
@ -351,16 +351,17 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, int from_iocp) {
start_accept(exec_ctx, sp); start_accept(exec_ctx, sp);
} }
static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
const struct sockaddr *addr, size_t addr_len) { const struct sockaddr *addr,
server_port *sp; size_t addr_len) {
grpc_tcp_listener *sp = NULL;
int port; int port;
int status; int status;
GUID guid = WSAID_ACCEPTEX; GUID guid = WSAID_ACCEPTEX;
DWORD ioctl_num_bytes; DWORD ioctl_num_bytes;
LPFN_ACCEPTEX AcceptEx; LPFN_ACCEPTEX AcceptEx;
if (sock == INVALID_SOCKET) return -1; if (sock == INVALID_SOCKET) return NULL;
/* We need to grab the AcceptEx pointer for that port, as it may be /* We need to grab the AcceptEx pointer for that port, as it may be
interface-dependent. We'll cache it to avoid doing that again. */ interface-dependent. We'll cache it to avoid doing that again. */
@ -373,37 +374,34 @@ static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
gpr_free(utf8_message); gpr_free(utf8_message);
closesocket(sock); closesocket(sock);
return -1; return NULL;
} }
port = prepare_socket(sock, addr, addr_len); port = prepare_socket(sock, addr, addr_len);
if (port >= 0) { if (port >= 0) {
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
/* append it to the list under a lock */ sp = gpr_malloc(sizeof(grpc_tcp_listener));
if (s->nports == s->port_capacity) { sp->next = s->head;
/* too many ports, and we need to store their address in a closure */ s->head = sp;
/* TODO(ctiller): make server_port a linked list */
abort();
}
sp = &s->ports[s->nports++];
sp->server = s; sp->server = s;
sp->socket = grpc_winsocket_create(sock, "listener"); sp->socket = grpc_winsocket_create(sock, "listener");
sp->shutting_down = 0; sp->shutting_down = 0;
sp->AcceptEx = AcceptEx; sp->AcceptEx = AcceptEx;
sp->new_socket = INVALID_SOCKET; sp->new_socket = INVALID_SOCKET;
sp->port = port;
gpr_ref_init(&sp->refs, 1);
grpc_closure_init(&sp->on_accept, on_accept, sp); grpc_closure_init(&sp->on_accept, on_accept, sp);
GPR_ASSERT(sp->socket); GPR_ASSERT(sp->socket);
gpr_mu_unlock(&s->mu); gpr_mu_unlock(&s->mu);
} }
return port; return sp;
} }
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s,
size_t addr_len) { const void *addr, size_t addr_len) {
int allocated_port = -1; grpc_tcp_listener *sp;
unsigned i;
SOCKET sock; SOCKET sock;
struct sockaddr_in6 addr6_v4mapped; struct sockaddr_in6 addr6_v4mapped;
struct sockaddr_in6 wildcard; struct sockaddr_in6 wildcard;
@ -415,9 +413,9 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
/* Check if this is a wildcard port, and if so, try to keep the port the same /* Check if this is a wildcard port, and if so, try to keep the port the same
as some previously created listener. */ as some previously created listener. */
if (grpc_sockaddr_get_port(addr) == 0) { if (grpc_sockaddr_get_port(addr) == 0) {
for (i = 0; i < s->nports; i++) { for (sp = s->head; sp; sp = sp->next) {
sockname_len = sizeof(sockname_temp); sockname_len = sizeof(sockname_temp);
if (0 == getsockname(s->ports[i].socket->socket, if (0 == getsockname(sp->socket->socket,
(struct sockaddr *)&sockname_temp, &sockname_len)) { (struct sockaddr *)&sockname_temp, &sockname_len)) {
port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
if (port > 0) { if (port > 0) {
@ -452,33 +450,56 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
gpr_free(utf8_message); gpr_free(utf8_message);
} }
allocated_port = add_socket_to_server(s, sock, addr, addr_len); sp = add_socket_to_server(s, sock, addr, addr_len);
gpr_free(allocated_addr); gpr_free(allocated_addr);
return allocated_port; return sp;
} }
SOCKET int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned port_index) {
grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) { grpc_tcp_listener *sp;
return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET; for (sp = s->head; sp && port_index != 0; sp = sp->next, port_index--)
;
if (port_index == 0 && sp) {
return _open_osfhandle(sp->socket->socket, 0);
} else {
return -1;
}
} }
void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
grpc_pollset **pollset, size_t pollset_count, grpc_pollset **pollset, size_t pollset_count,
grpc_tcp_server_cb on_accept_cb, grpc_tcp_server_cb on_accept_cb,
void *on_accept_cb_arg) { void *on_accept_cb_arg) {
size_t i; grpc_tcp_listener *sp;
GPR_ASSERT(on_accept_cb); GPR_ASSERT(on_accept_cb);
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->on_accept_cb); GPR_ASSERT(!s->on_accept_cb);
GPR_ASSERT(s->active_ports == 0); GPR_ASSERT(s->active_ports == 0);
s->on_accept_cb = on_accept_cb; s->on_accept_cb = on_accept_cb;
s->on_accept_cb_arg = on_accept_cb_arg; s->on_accept_cb_arg = on_accept_cb_arg;
for (i = 0; i < s->nports; i++) { for (sp = s->head; sp; sp = sp->next) {
start_accept(exec_ctx, s->ports + i); start_accept(exec_ctx, sp);
s->active_ports++; s->active_ports++;
} }
gpr_mu_unlock(&s->mu); gpr_mu_unlock(&s->mu);
} }
int grpc_tcp_listener_get_port(grpc_tcp_listener *listener) {
grpc_tcp_listener *sp = listener;
return sp->port;
}
void grpc_tcp_listener_ref(grpc_tcp_listener *listener) {
grpc_tcp_listener *sp = listener;
gpr_ref(&sp->refs);
}
void grpc_tcp_listener_unref(grpc_tcp_listener *listener) {
grpc_tcp_listener *sp = listener;
if (gpr_unref(&sp->refs)) {
gpr_free(listener);
}
}
#endif /* GPR_WINSOCK_SOCKET */ #endif /* GPR_WINSOCK_SOCKET */

@ -116,9 +116,12 @@ void grpc_call_credentials_release(grpc_call_credentials *creds) {
grpc_call_credentials_unref(creds); grpc_call_credentials_unref(creds);
} }
void grpc_call_credentials_get_request_metadata( void grpc_call_credentials_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset, grpc_call_credentials *creds,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_pollset *pollset,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data) {
if (creds == NULL || creds->vtable->get_request_metadata == NULL) { if (creds == NULL || creds->vtable->get_request_metadata == NULL) {
if (cb != NULL) { if (cb != NULL) {
cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK); cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
@ -423,9 +426,12 @@ static void jwt_destruct(grpc_call_credentials *creds) {
gpr_mu_destroy(&c->cache_mu); gpr_mu_destroy(&c->cache_mu);
} }
static void jwt_get_request_metadata( static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset, grpc_call_credentials *creds,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_pollset *pollset,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data) {
grpc_service_account_jwt_access_credentials *c = grpc_service_account_jwt_access_credentials *c =
(grpc_service_account_jwt_access_credentials *)creds; (grpc_service_account_jwt_access_credentials *)creds;
gpr_timespec refresh_threshold = gpr_time_from_seconds( gpr_timespec refresh_threshold = gpr_time_from_seconds(
@ -798,9 +804,12 @@ static void on_simulated_token_fetch_done(void *user_data) {
grpc_exec_ctx_finish(&exec_ctx); grpc_exec_ctx_finish(&exec_ctx);
} }
static void md_only_test_get_request_metadata( static void md_only_test_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset, grpc_call_credentials *creds,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_pollset *pollset,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data) {
grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
if (c->is_async) { if (c->is_async) {
@ -837,9 +846,12 @@ static void access_token_destruct(grpc_call_credentials *creds) {
grpc_credentials_md_store_unref(c->access_token_md); grpc_credentials_md_store_unref(c->access_token_md);
} }
static void access_token_get_request_metadata( static void access_token_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset, grpc_call_credentials *creds,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_pollset *pollset,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data) {
grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK); cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
} }
@ -978,9 +990,12 @@ static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
composite_call_md_context_destroy(ctx); composite_call_md_context_destroy(ctx);
} }
static void composite_call_get_request_metadata( static void composite_call_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset, grpc_call_credentials *creds,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_pollset *pollset,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data) {
grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
grpc_composite_call_credentials_metadata_context *ctx; grpc_composite_call_credentials_metadata_context *ctx;
@ -1097,7 +1112,7 @@ static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx,
} }
static grpc_call_credentials_vtable iam_vtable = {iam_destruct, static grpc_call_credentials_vtable iam_vtable = {iam_destruct,
iam_get_request_metadata}; iam_get_request_metadata};
grpc_call_credentials *grpc_google_iam_credentials_create( grpc_call_credentials *grpc_google_iam_credentials_create(
const char *token, const char *authority_selector, void *reserved) { const char *token, const char *authority_selector, void *reserved) {
@ -1265,4 +1280,3 @@ grpc_channel_credentials *grpc_composite_channel_credentials_create(
c->call_creds = grpc_call_credentials_ref(call_creds); c->call_creds = grpc_call_credentials_ref(call_creds);
return &c->base; return &c->base;
} }

@ -187,7 +187,8 @@ typedef struct {
size_t num_creds; size_t num_creds;
} grpc_call_credentials_array; } grpc_call_credentials_array;
const grpc_call_credentials_array *grpc_composite_call_credentials_get_credentials( const grpc_call_credentials_array *
grpc_composite_call_credentials_get_credentials(
grpc_call_credentials *composite_creds); grpc_call_credentials *composite_creds);
/* Returns creds if creds is of the specified type or the inner creds of the /* Returns creds if creds is of the specified type or the inner creds of the

@ -214,7 +214,8 @@ grpc_channel_credentials *grpc_google_default_credentials_create(void) {
end: end:
if (result == NULL) { if (result == NULL) {
if (call_creds != NULL) { if (call_creds != NULL) {
/* Blend with default ssl credentials and add a global reference so that it /* Blend with default ssl credentials and add a global reference so that
it
can be cached and re-served. */ can be cached and re-served. */
grpc_channel_credentials *ssl_creds = grpc_channel_credentials *ssl_creds =
grpc_ssl_credentials_create(NULL, NULL, NULL); grpc_ssl_credentials_create(NULL, NULL, NULL);

@ -571,9 +571,9 @@ size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs) {
} }
grpc_security_status grpc_ssl_channel_security_connector_create( grpc_security_status grpc_ssl_channel_security_connector_create(
grpc_call_credentials *request_metadata_creds, const grpc_ssl_config *config, grpc_call_credentials *request_metadata_creds,
const char *target_name, const char *overridden_target_name, const grpc_ssl_config *config, const char *target_name,
grpc_channel_security_connector **sc) { const char *overridden_target_name, grpc_channel_security_connector **sc) {
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const unsigned char **alpn_protocol_strings = const unsigned char **alpn_protocol_strings =
gpr_malloc(sizeof(const char *) * num_alpn_protocols); gpr_malloc(sizeof(const char *) * num_alpn_protocols);

@ -249,9 +249,11 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
} }
for (i = 0; i < resolved->naddrs; i++) { for (i = 0; i < resolved->naddrs; i++) {
port_temp = grpc_tcp_server_add_port( grpc_tcp_listener *listener;
listener = grpc_tcp_server_add_port(
tcp, (struct sockaddr *)&resolved->addrs[i].addr, tcp, (struct sockaddr *)&resolved->addrs[i].addr,
resolved->addrs[i].len); resolved->addrs[i].len);
port_temp = grpc_tcp_listener_get_port(listener);
if (port_temp >= 0) { if (port_temp >= 0) {
if (port_num == -1) { if (port_num == -1) {
port_num = port_temp; port_num = port_temp;

@ -121,4 +121,3 @@ gpr_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader) {
} }
return out_slice; return out_slice;
} }

@ -922,7 +922,8 @@ static batch_control *allocate_batch_control(grpc_call *call) {
size_t i; size_t i;
for (i = 0; i < MAX_CONCURRENT_BATCHES; i++) { for (i = 0; i < MAX_CONCURRENT_BATCHES; i++) {
if ((call->used_batches & (1 << i)) == 0) { if ((call->used_batches & (1 << i)) == 0) {
call->used_batches |= (gpr_uint8)(1 << i); call->used_batches =
(gpr_uint8)(call->used_batches | (gpr_uint8)(1 << i));
return &call->active_batches[i]; return &call->active_batches[i];
} }
} }

@ -107,9 +107,11 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) {
} }
for (i = 0; i < resolved->naddrs; i++) { for (i = 0; i < resolved->naddrs; i++) {
port_temp = grpc_tcp_server_add_port( grpc_tcp_listener *listener;
listener = grpc_tcp_server_add_port(
tcp, (struct sockaddr *)&resolved->addrs[i].addr, tcp, (struct sockaddr *)&resolved->addrs[i].addr,
resolved->addrs[i].len); resolved->addrs[i].len);
port_temp = grpc_tcp_listener_get_port(listener);
if (port_temp >= 0) { if (port_temp >= 0) {
if (port_num == -1) { if (port_num == -1) {
port_num = port_temp; port_num = port_temp;

@ -36,8 +36,10 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <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 "src/core/transport/chttp2/bin_encoder.h" #include "src/core/transport/chttp2/bin_encoder.h"
#include "src/core/transport/chttp2/hpack_table.h" #include "src/core/transport/chttp2/hpack_table.h"
#include "src/core/transport/chttp2/timeout_encoding.h" #include "src/core/transport/chttp2/timeout_encoding.h"
@ -154,6 +156,18 @@ static gpr_uint8 *add_tiny_header_data(framer_state *st, size_t len) {
return gpr_slice_buffer_tiny_add(st->output, len); return gpr_slice_buffer_tiny_add(st->output, len);
} }
static void evict_entry(grpc_chttp2_hpack_compressor *c) {
c->tail_remote_index++;
GPR_ASSERT(c->tail_remote_index > 0);
GPR_ASSERT(c->table_size >=
c->table_elem_size[c->tail_remote_index % c->cap_table_elems]);
GPR_ASSERT(c->table_elems > 0);
c->table_size = (gpr_uint16)(
c->table_size -
c->table_elem_size[c->tail_remote_index % c->cap_table_elems]);
c->table_elems--;
}
/* add an element to the decoder table */ /* add an element to the decoder table */
static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) { static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
gpr_uint32 key_hash = elem->key->hash; gpr_uint32 key_hash = elem->key->hash;
@ -164,26 +178,21 @@ static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
GPR_ASSERT(elem_size < 65536); GPR_ASSERT(elem_size < 65536);
if (elem_size > c->max_table_size) {
while (c->table_size > 0) {
evict_entry(c);
}
return;
}
/* Reserve space for this element in the remote table: if this overflows /* Reserve space for this element in the remote table: if this overflows
the current table, drop elements until it fits, matching the decompressor the current table, drop elements until it fits, matching the decompressor
algorithm */ algorithm */
/* TODO(ctiller): constant */ while (c->table_size + elem_size > c->max_table_size) {
while (c->table_size + elem_size > 4096) { evict_entry(c);
c->tail_remote_index++;
GPR_ASSERT(c->tail_remote_index > 0);
GPR_ASSERT(c->table_size >=
c->table_elem_size[c->tail_remote_index %
GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]);
GPR_ASSERT(c->table_elems > 0);
c->table_size =
(gpr_uint16)(c->table_size -
c->table_elem_size[c->tail_remote_index %
GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]);
c->table_elems--;
} }
GPR_ASSERT(c->table_elems < GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS); GPR_ASSERT(c->table_elems < c->max_table_size);
c->table_elem_size[new_index % GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS] = c->table_elem_size[new_index % c->cap_table_elems] = (gpr_uint16)elem_size;
(gpr_uint16)elem_size;
c->table_size = (gpr_uint16)(c->table_size + elem_size); c->table_size = (gpr_uint16)(c->table_size + elem_size);
c->table_elems++; c->table_elems++;
@ -329,6 +338,14 @@ static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c,
add_header_data(st, gpr_slice_ref(value_slice)); add_header_data(st, gpr_slice_ref(value_slice));
} }
static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor *c,
framer_state *st) {
gpr_uint32 len = GRPC_CHTTP2_VARINT_LENGTH(c->max_table_size, 3);
GRPC_CHTTP2_WRITE_VARINT(c->max_table_size, 3, 0x20,
add_tiny_header_data(st, len), len);
c->advertise_table_size_change = 0;
}
static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c, static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c,
gpr_uint32 elem_index) { gpr_uint32 elem_index) {
return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index +
@ -447,11 +464,23 @@ gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) {
return slice; return slice;
} }
static gpr_uint32 elems_for_bytes(gpr_uint32 bytes) {
return (bytes + 31) / 32;
}
void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
grpc_mdctx *ctx) { grpc_mdctx *ctx) {
memset(c, 0, sizeof(*c)); memset(c, 0, sizeof(*c));
c->mdctx = ctx; c->mdctx = ctx;
c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout"); c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout");
c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE;
c->cap_table_elems = elems_for_bytes(c->max_table_size);
c->max_table_elems = c->cap_table_elems;
c->max_usable_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE;
c->table_elem_size =
gpr_malloc(sizeof(*c->table_elem_size) * c->cap_table_elems);
memset(c->table_elem_size, 0,
sizeof(*c->table_elem_size) * c->cap_table_elems);
} }
void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) { void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
@ -461,6 +490,55 @@ void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]); if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]);
} }
GRPC_MDSTR_UNREF(c->timeout_key_str); GRPC_MDSTR_UNREF(c->timeout_key_str);
gpr_free(c->table_elem_size);
}
void grpc_chttp2_hpack_compressor_set_max_usable_size(
grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size) {
c->max_usable_size = max_table_size;
grpc_chttp2_hpack_compressor_set_max_table_size(
c, GPR_MIN(c->max_table_size, max_table_size));
}
static void rebuild_elems(grpc_chttp2_hpack_compressor *c, gpr_uint32 new_cap) {
gpr_uint16 *table_elem_size = gpr_malloc(sizeof(*table_elem_size) * new_cap);
gpr_uint32 i;
memset(table_elem_size, 0, sizeof(*table_elem_size) * new_cap);
GPR_ASSERT(c->table_elems <= new_cap);
for (i = 0; i < c->table_elems; i++) {
gpr_uint32 ofs = c->tail_remote_index + i + 1;
table_elem_size[ofs % new_cap] =
c->table_elem_size[ofs % c->cap_table_elems];
}
c->cap_table_elems = new_cap;
gpr_free(c->table_elem_size);
c->table_elem_size = table_elem_size;
}
void grpc_chttp2_hpack_compressor_set_max_table_size(
grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size) {
max_table_size = GPR_MIN(max_table_size, c->max_usable_size);
if (max_table_size == c->max_table_size) {
return;
}
while (c->table_size > 0 && c->table_size > max_table_size) {
evict_entry(c);
}
c->max_table_size = max_table_size;
c->max_table_elems = elems_for_bytes(max_table_size);
if (c->max_table_elems > c->cap_table_elems) {
rebuild_elems(c, GPR_MAX(c->max_table_elems, 2 * c->cap_table_elems));
} else if (c->max_table_elems < c->cap_table_elems / 3) {
gpr_uint32 new_cap = GPR_MAX(c->max_table_elems, 16);
if (new_cap != c->cap_table_elems) {
rebuild_elems(c, new_cap);
}
}
c->advertise_table_size_change = 1;
gpr_log(GPR_DEBUG, "set max table size from encoder to %d", max_table_size);
} }
void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
@ -483,6 +561,9 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got
updated). After this loop, we'll do a batch unref of elements. */ updated). After this loop, we'll do a batch unref of elements. */
begin_frame(&st); begin_frame(&st);
if (c->advertise_table_size_change != 0) {
emit_advertise_table_size_change(c, &st);
}
grpc_metadata_batch_assert_ok(metadata); grpc_metadata_batch_assert_ok(metadata);
for (l = metadata->list.head; l; l = l->next) { for (l = metadata->list.head; l; l = l->next) {
hpack_enc(c, l->md, &st); hpack_enc(c, l->md, &st);

@ -43,14 +43,26 @@
#define GRPC_CHTTP2_HPACKC_NUM_FILTERS 256 #define GRPC_CHTTP2_HPACKC_NUM_FILTERS 256
#define GRPC_CHTTP2_HPACKC_NUM_VALUES 256 #define GRPC_CHTTP2_HPACKC_NUM_VALUES 256
#define GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS (4096 / 32) /* initial table size, per spec */
#define GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE 4096
/* maximum table size we'll actually use */
#define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024)
typedef struct { typedef struct {
gpr_uint32 filter_elems_sum; gpr_uint32 filter_elems_sum;
gpr_uint32 max_table_size;
gpr_uint32 max_table_elems;
gpr_uint32 cap_table_elems;
/** if non-zero, advertise to the decoder that we'll start using a table
of this size */
gpr_uint8 advertise_table_size_change;
/** maximum number of bytes we'll use for the decode table (to guard against
peers ooming us by setting decode table size high) */
gpr_uint32 max_usable_size;
/* one before the lowest usable table index */ /* one before the lowest usable table index */
gpr_uint32 tail_remote_index; gpr_uint32 tail_remote_index;
gpr_uint16 table_size; gpr_uint32 table_size;
gpr_uint16 table_elems; gpr_uint32 table_elems;
/* filter tables for elems: this tables provides an approximate /* filter tables for elems: this tables provides an approximate
popularity count for particular hashes, and are used to determine whether popularity count for particular hashes, and are used to determine whether
@ -71,12 +83,16 @@ typedef struct {
gpr_uint32 indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; gpr_uint32 indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES];
gpr_uint32 indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; gpr_uint32 indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES];
gpr_uint16 table_elem_size[GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]; gpr_uint16 *table_elem_size;
} grpc_chttp2_hpack_compressor; } grpc_chttp2_hpack_compressor;
void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
grpc_mdctx *mdctx); grpc_mdctx *mdctx);
void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c); void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c);
void grpc_chttp2_hpack_compressor_set_max_table_size(
grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size);
void grpc_chttp2_hpack_compressor_set_max_usable_size(
grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size);
void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, gpr_uint32 id, void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, gpr_uint32 id,
grpc_metadata_batch *metadata, int is_eof, grpc_metadata_batch *metadata, int is_eof,

@ -74,6 +74,8 @@ static int parse_begin(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end); const gpr_uint8 *end);
static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end); const gpr_uint8 *end);
static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end);
static int parse_string_prefix(grpc_chttp2_hpack_parser *p, static int parse_string_prefix(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end); const gpr_uint8 *cur, const gpr_uint8 *end);
@ -156,7 +158,7 @@ static const grpc_chttp2_hpack_parser_state first_byte_action[] = {
parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx, parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx,
parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx, parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx,
parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size, parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size,
parse_max_tbl_size_x, parse_error}; parse_max_tbl_size_x, parse_illegal_op};
/* indexes the first byte to a parse state function - generated by /* indexes the first byte to a parse state function - generated by
gen_hpack_tables.c */ gen_hpack_tables.c */
@ -169,7 +171,7 @@ static const gpr_uint8 first_byte_lut[256] = {
LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X,
ILLEGAL, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
@ -622,13 +624,15 @@ static const gpr_uint8 inverse_base64[256] = {
}; };
/* emission helpers */ /* emission helpers */
static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md, static int on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
int add_to_table) { int add_to_table) {
if (add_to_table) { if (add_to_table) {
GRPC_MDELEM_REF(md); if (!grpc_chttp2_hptbl_add(&p->table, md)) {
grpc_chttp2_hptbl_add(&p->table, md); return 0;
}
} }
p->on_header(p->on_header_user_data, md); p->on_header(p->on_header_user_data, md);
return 1;
} }
static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p, static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p,
@ -714,9 +718,12 @@ static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
static int finish_indexed_field(grpc_chttp2_hpack_parser *p, static int finish_indexed_field(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
if (md == NULL) {
gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index);
return 0;
}
GRPC_MDELEM_REF(md); GRPC_MDELEM_REF(md);
on_hdr(p, md, 0); return on_hdr(p, md, 0) && parse_begin(p, cur, end);
return parse_begin(p, cur, end);
} }
/* parse an indexed field with index < 127 */ /* parse an indexed field with index < 127 */
@ -742,21 +749,21 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p, static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key), GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)), take_string(p, &p->value)),
1); 1) &&
return parse_begin(p, cur, end); parse_begin(p, cur, end);
} }
/* finish a literal header with incremental indexing with no index */ /* finish a literal header with incremental indexing with no index */
static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key), take_string(p, &p->key),
take_string(p, &p->value)), take_string(p, &p->value)),
1); 1) &&
return parse_begin(p, cur, end); parse_begin(p, cur, end);
} }
/* parse a literal header with incremental indexing; index < 63 */ /* parse a literal header with incremental indexing; index < 63 */
@ -795,21 +802,21 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p, static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key), GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)), take_string(p, &p->value)),
0); 0) &&
return parse_begin(p, cur, end); parse_begin(p, cur, end);
} }
/* finish a literal header without incremental indexing with index = 0 */ /* finish a literal header without incremental indexing with index = 0 */
static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key), take_string(p, &p->key),
take_string(p, &p->value)), take_string(p, &p->value)),
0); 0) &&
return parse_begin(p, cur, end); parse_begin(p, cur, end);
} }
/* parse a literal header without incremental indexing; index < 15 */ /* parse a literal header without incremental indexing; index < 15 */
@ -848,21 +855,21 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p, static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key), GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)), take_string(p, &p->value)),
0); 0) &&
return parse_begin(p, cur, end); parse_begin(p, cur, end);
} }
/* finish a literal header that is never indexed with an extra value */ /* finish a literal header that is never indexed with an extra value */
static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key), take_string(p, &p->key),
take_string(p, &p->value)), take_string(p, &p->value)),
0); 0) &&
return parse_begin(p, cur, end); parse_begin(p, cur, end);
} }
/* parse a literal header that is never indexed; index < 15 */ /* parse a literal header that is never indexed; index < 15 */
@ -901,14 +908,14 @@ static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p, static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index); gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
abort(); /* not implemented */ return grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index) &&
return parse_begin(p, cur, end); parse_begin(p, cur, end);
} }
/* parse a max table size change, max size < 15 */ /* parse a max table size change, max size < 15 */
static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end) { const gpr_uint8 *end) {
p->index = (*cur) & 0xf; p->index = (*cur) & 0x1f;
return finish_max_tbl_size(p, cur + 1, end); return finish_max_tbl_size(p, cur + 1, end);
} }
@ -918,7 +925,7 @@ static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = { static const grpc_chttp2_hpack_parser_state and_then[] = {
finish_max_tbl_size}; finish_max_tbl_size};
p->next_state = and_then; p->next_state = and_then;
p->index = 0xf; p->index = 0x1f;
p->parsing.value = &p->index; p->parsing.value = &p->index;
return parse_value0(p, cur + 1, end); return parse_value0(p, cur + 1, end);
} }
@ -930,6 +937,13 @@ static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
return 0; return 0;
} }
static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end) {
GPR_ASSERT(cur != end);
gpr_log(GPR_DEBUG, "Illegal hpack op code %d", *cur);
return parse_error(p, cur, end);
}
/* parse the 1st byte of a varint into p->parsing.value /* parse the 1st byte of a varint into p->parsing.value
no overflow is possible */ no overflow is possible */
static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,

@ -36,7 +36,9 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include "src/core/support/murmur_hash.h" #include "src/core/support/murmur_hash.h"
static struct { static struct {
@ -169,12 +171,22 @@ static struct {
{"www-authenticate", ""}, {"www-authenticate", ""},
}; };
static gpr_uint32 entries_for_bytes(gpr_uint32 bytes) {
return (bytes + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) /
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
}
void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) { void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) {
size_t i; size_t i;
memset(tbl, 0, sizeof(*tbl)); memset(tbl, 0, sizeof(*tbl));
tbl->mdctx = mdctx; tbl->mdctx = mdctx;
tbl->max_bytes = GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE; tbl->current_table_bytes = tbl->max_bytes =
GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
tbl->max_entries = tbl->cap_entries =
entries_for_bytes(tbl->current_table_bytes);
tbl->ents = gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries);
memset(tbl->ents, 0, sizeof(*tbl->ents) * tbl->cap_entries);
for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
tbl->static_ents[i - 1] = grpc_mdelem_from_strings( tbl->static_ents[i - 1] = grpc_mdelem_from_strings(
mdctx, static_table[i].key, static_table[i].value); mdctx, static_table[i].key, static_table[i].value);
@ -187,9 +199,9 @@ void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) {
GRPC_MDELEM_UNREF(tbl->static_ents[i]); GRPC_MDELEM_UNREF(tbl->static_ents[i]);
} }
for (i = 0; i < tbl->num_ents; i++) { for (i = 0; i < tbl->num_ents; i++) {
GRPC_MDELEM_UNREF( GRPC_MDELEM_UNREF(tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]);
tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]);
} }
gpr_free(tbl->ents);
} }
grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
@ -201,8 +213,8 @@ grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
/* Otherwise, find the value in the list of valid entries */ /* Otherwise, find the value in the list of valid entries */
tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1);
if (tbl_index < tbl->num_ents) { if (tbl_index < tbl->num_ents) {
gpr_uint32 offset = (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % gpr_uint32 offset =
GRPC_CHTTP2_MAX_TABLE_COUNT; (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries;
return tbl->ents[offset]; return tbl->ents[offset];
} }
/* Invalid entry: return error */ /* Invalid entry: return error */
@ -216,21 +228,81 @@ static void evict1(grpc_chttp2_hptbl *tbl) {
GPR_SLICE_LENGTH(first_ent->value->slice) + GPR_SLICE_LENGTH(first_ent->value->slice) +
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
GPR_ASSERT(elem_bytes <= tbl->mem_used); GPR_ASSERT(elem_bytes <= tbl->mem_used);
tbl->mem_used = (gpr_uint16)(tbl->mem_used - elem_bytes); tbl->mem_used -= (gpr_uint32)elem_bytes;
tbl->first_ent = tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries);
(gpr_uint16)((tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT);
tbl->num_ents--; tbl->num_ents--;
GRPC_MDELEM_UNREF(first_ent); GRPC_MDELEM_UNREF(first_ent);
} }
void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { static void rebuild_ents(grpc_chttp2_hptbl *tbl, gpr_uint32 new_cap) {
grpc_mdelem **ents = gpr_malloc(sizeof(*ents) * new_cap);
gpr_uint32 i;
for (i = 0; i < tbl->num_ents; i++) {
ents[i] = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
}
gpr_free(tbl->ents);
tbl->ents = ents;
tbl->cap_entries = new_cap;
tbl->first_ent = 0;
}
void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
gpr_uint32 max_bytes) {
if (tbl->max_bytes == max_bytes) {
return;
}
gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes);
while (tbl->mem_used > max_bytes) {
evict1(tbl);
}
tbl->max_bytes = max_bytes;
}
int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
gpr_uint32 bytes) {
if (tbl->current_table_bytes == bytes) {
return 1;
}
if (bytes > tbl->max_bytes) {
gpr_log(GPR_ERROR,
"Attempt to make hpack table %d bytes when max is %d bytes", bytes,
tbl->max_bytes);
return 0;
}
gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes);
while (tbl->mem_used > bytes) {
evict1(tbl);
}
tbl->current_table_bytes = bytes;
tbl->max_entries = entries_for_bytes(bytes);
if (tbl->max_entries > tbl->cap_entries) {
rebuild_ents(tbl, GPR_MAX(tbl->max_entries, 2 * tbl->cap_entries));
} else if (tbl->max_entries < tbl->cap_entries / 3) {
gpr_uint32 new_cap = GPR_MAX(tbl->max_entries, 16u);
if (new_cap != tbl->cap_entries) {
rebuild_ents(tbl, new_cap);
}
}
return 1;
}
int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
/* determine how many bytes of buffer this entry represents */ /* determine how many bytes of buffer this entry represents */
size_t elem_bytes = GPR_SLICE_LENGTH(md->key->slice) + size_t elem_bytes = GPR_SLICE_LENGTH(md->key->slice) +
GPR_SLICE_LENGTH(md->value->slice) + GPR_SLICE_LENGTH(md->value->slice) +
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
if (tbl->current_table_bytes > tbl->max_bytes) {
gpr_log(GPR_ERROR,
"HPACK max table size reduced to %d but not reflected by hpack "
"stream (still at %d)",
tbl->max_bytes, tbl->current_table_bytes);
return 0;
}
/* we can't add elements bigger than the max table size */ /* we can't add elements bigger than the max table size */
if (elem_bytes > tbl->max_bytes) { if (elem_bytes > tbl->current_table_bytes) {
/* HPACK draft 10 section 4.4 states: /* HPACK draft 10 section 4.4 states:
* If the size of the new entry is less than or equal to the maximum * If the size of the new entry is less than or equal to the maximum
* size, that entry is added to the table. It is not an error to * size, that entry is added to the table. It is not an error to
@ -243,44 +315,43 @@ void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
while (tbl->num_ents) { while (tbl->num_ents) {
evict1(tbl); evict1(tbl);
} }
return; return 1;
} }
/* evict entries to ensure no overflow */ /* evict entries to ensure no overflow */
while (elem_bytes > (size_t)tbl->max_bytes - tbl->mem_used) { while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) {
evict1(tbl); evict1(tbl);
} }
/* copy the finalized entry in */ /* copy the finalized entry in */
tbl->ents[tbl->last_ent] = md; tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] =
GRPC_MDELEM_REF(md);
/* update accounting values */ /* update accounting values */
tbl->last_ent =
(gpr_uint16)((tbl->last_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT);
tbl->num_ents++; tbl->num_ents++;
tbl->mem_used = (gpr_uint16)(tbl->mem_used + elem_bytes); tbl->mem_used += (gpr_uint32)elem_bytes;
return 1;
} }
grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
grpc_chttp2_hptbl_find_result r = {0, 0}; grpc_chttp2_hptbl_find_result r = {0, 0};
gpr_uint16 i; gpr_uint32 i;
/* See if the string is in the static table */ /* See if the string is in the static table */
for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
grpc_mdelem *ent = tbl->static_ents[i]; grpc_mdelem *ent = tbl->static_ents[i];
if (md->key != ent->key) continue; if (md->key != ent->key) continue;
r.index = (gpr_uint16)(i + 1); r.index = i + 1u;
r.has_value = md->value == ent->value; r.has_value = md->value == ent->value;
if (r.has_value) return r; if (r.has_value) return r;
} }
/* Scan the dynamic table */ /* Scan the dynamic table */
for (i = 0; i < tbl->num_ents; i++) { for (i = 0; i < tbl->num_ents; i++) {
gpr_uint16 idx = gpr_uint32 idx =
(gpr_uint16)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY); (gpr_uint32)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY);
grpc_mdelem *ent = grpc_mdelem *ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT];
if (md->key != ent->key) continue; if (md->key != ent->key) continue;
r.index = idx; r.index = idx;
r.has_value = md->value == ent->value; r.has_value = md->value == ent->value;

@ -49,47 +49,59 @@
#define GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE #define GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE
/* Per entry overhead bytes as per the spec */ /* Per entry overhead bytes as per the spec */
#define GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD 32 #define GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD 32
#if 0
/* Maximum number of entries we could possibly fit in the table, given defined /* Maximum number of entries we could possibly fit in the table, given defined
overheads */ overheads */
#define GRPC_CHTTP2_MAX_TABLE_COUNT \ #define GRPC_CHTTP2_MAX_TABLE_COUNT \
((GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / \ ((GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / \
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD) GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD)
#endif
/* hpack decoder table */ /* hpack decoder table */
typedef struct { typedef struct {
grpc_mdctx *mdctx; grpc_mdctx *mdctx;
/* the first used entry in ents */ /* the first used entry in ents */
gpr_uint16 first_ent; gpr_uint32 first_ent;
/* the last used entry in ents */
gpr_uint16 last_ent;
/* how many entries are in the table */ /* how many entries are in the table */
gpr_uint16 num_ents; gpr_uint32 num_ents;
/* the amount of memory used by the table, according to the hpack algorithm */ /* the amount of memory used by the table, according to the hpack algorithm */
gpr_uint16 mem_used; gpr_uint32 mem_used;
/* the max memory allowed to be used by the table, according to the hpack /* the max memory allowed to be used by the table, according to the hpack
algorithm */ algorithm */
gpr_uint16 max_bytes; gpr_uint32 max_bytes;
/* the currently agreed size of the table, according to the hpack algorithm */
gpr_uint32 current_table_bytes;
/* Maximum number of entries we could possibly fit in the table, given defined
overheads */
gpr_uint32 max_entries;
/* Number of entries allocated in ents */
gpr_uint32 cap_entries;
/* a circular buffer of headers - this is stored in the opposite order to /* a circular buffer of headers - this is stored in the opposite order to
what hpack specifies, in order to simplify table management a little... what hpack specifies, in order to simplify table management a little...
meaning lookups need to SUBTRACT from the end position */ meaning lookups need to SUBTRACT from the end position */
grpc_mdelem *ents[GRPC_CHTTP2_MAX_TABLE_COUNT]; grpc_mdelem **ents;
grpc_mdelem *static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY]; grpc_mdelem *static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY];
} grpc_chttp2_hptbl; } grpc_chttp2_hptbl;
/* initialize a hpack table */ /* initialize a hpack table */
void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx); void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx);
void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl); void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl);
void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
gpr_uint32 max_bytes);
int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
gpr_uint32 bytes);
/* lookup a table entry based on its hpack index */ /* lookup a table entry based on its hpack index */
grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
gpr_uint32 index); gpr_uint32 index);
/* add a table entry to the index */ /* add a table entry to the index */
void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md); int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl,
grpc_mdelem *md) GRPC_MUST_USE_RESULT;
/* Find a key/value pair in the table... returns the index in the table of the /* Find a key/value pair in the table... returns the index in the table of the
most similar entry, or 0 if the value was not found */ most similar entry, or 0 if the value was not found */
typedef struct { typedef struct {
gpr_uint16 index; gpr_uint32 index;
gpr_uint8 has_value; int has_value;
} grpc_chttp2_hptbl_find_result; } grpc_chttp2_hptbl_find_result;
grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
const grpc_chttp2_hptbl *tbl, grpc_mdelem *md); const grpc_chttp2_hptbl *tbl, grpc_mdelem *md);

@ -227,6 +227,9 @@ struct grpc_chttp2_transport_parsing {
/** was a goaway frame received? */ /** was a goaway frame received? */
gpr_uint8 goaway_received; gpr_uint8 goaway_received;
/** the last sent max_table_size setting */
gpr_uint32 last_sent_max_table_size;
/** initial window change */ /** initial window change */
gpr_int64 initial_window_update; gpr_int64 initial_window_update;
@ -483,7 +486,8 @@ struct grpc_chttp2_stream {
/** Someone is unlocking the transport mutex: check to see if writes /** Someone is unlocking the transport mutex: check to see if writes
are required, and schedule them if so */ are required, and schedule them if so */
int grpc_chttp2_unlocking_check_writes(grpc_chttp2_transport_global *global, int grpc_chttp2_unlocking_check_writes(grpc_chttp2_transport_global *global,
grpc_chttp2_transport_writing *writing); grpc_chttp2_transport_writing *writing,
int is_parsing);
void grpc_chttp2_perform_writes( void grpc_chttp2_perform_writes(
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing, grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing,
grpc_endpoint *endpoint); grpc_endpoint *endpoint);

@ -78,6 +78,9 @@ void grpc_chttp2_prepare_to_read(
GPR_TIMER_BEGIN("grpc_chttp2_prepare_to_read", 0); GPR_TIMER_BEGIN("grpc_chttp2_prepare_to_read", 0);
transport_parsing->next_stream_id = transport_global->next_stream_id; transport_parsing->next_stream_id = transport_global->next_stream_id;
transport_parsing->last_sent_max_table_size =
transport_global->settings[GRPC_SENT_SETTINGS]
[GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE];
/* update the parsing view of incoming window */ /* update the parsing view of incoming window */
while (grpc_chttp2_list_pop_unannounced_incoming_window_available( while (grpc_chttp2_list_pop_unannounced_incoming_window_available(
@ -127,6 +130,7 @@ void grpc_chttp2_publish_reads(
transport_global->settings[GRPC_SENT_SETTINGS], transport_global->settings[GRPC_SENT_SETTINGS],
GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32)); GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32));
transport_parsing->settings_ack_received = 0; transport_parsing->settings_ack_received = 0;
transport_global->sent_local_settings = 0;
} }
/* move goaway to the global state if we received one (it will be /* move goaway to the global state if we received one (it will be
@ -819,6 +823,9 @@ static int init_settings_frame_parser(
} }
if (transport_parsing->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) { if (transport_parsing->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
transport_parsing->settings_ack_received = 1; transport_parsing->settings_ack_received = 1;
grpc_chttp2_hptbl_set_max_bytes(
&transport_parsing->hpack_parser.table,
transport_parsing->last_sent_max_table_size);
} }
transport_parsing->parser = grpc_chttp2_settings_parser_parse; transport_parsing->parser = grpc_chttp2_settings_parser_parse;
transport_parsing->parser_data = &transport_parsing->simple.settings; transport_parsing->parser_data = &transport_parsing->simple.settings;

@ -45,7 +45,7 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
int grpc_chttp2_unlocking_check_writes( int grpc_chttp2_unlocking_check_writes(
grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_global *transport_global,
grpc_chttp2_transport_writing *transport_writing) { grpc_chttp2_transport_writing *transport_writing, int is_parsing) {
grpc_chttp2_stream_global *stream_global; grpc_chttp2_stream_global *stream_global;
grpc_chttp2_stream_writing *stream_writing; grpc_chttp2_stream_writing *stream_writing;
@ -55,8 +55,13 @@ int grpc_chttp2_unlocking_check_writes(
gpr_slice_buffer_swap(&transport_global->qbuf, &transport_writing->outbuf); gpr_slice_buffer_swap(&transport_global->qbuf, &transport_writing->outbuf);
GPR_ASSERT(transport_global->qbuf.count == 0); GPR_ASSERT(transport_global->qbuf.count == 0);
grpc_chttp2_hpack_compressor_set_max_table_size(
&transport_writing->hpack_compressor,
transport_global->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
if (transport_global->dirtied_local_settings && if (transport_global->dirtied_local_settings &&
!transport_global->sent_local_settings) { !transport_global->sent_local_settings && !is_parsing) {
gpr_slice_buffer_add( gpr_slice_buffer_add(
&transport_writing->outbuf, &transport_writing->outbuf,
grpc_chttp2_settings_create( grpc_chttp2_settings_create(

@ -338,6 +338,31 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
t->global.next_stream_id = t->global.next_stream_id =
(gpr_uint32)channel_args->args[i].value.integer; (gpr_uint32)channel_args->args[i].value.integer;
} }
} else if (0 == strcmp(channel_args->args[i].key,
GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER)) {
if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
gpr_log(GPR_ERROR, "%s: must be an integer",
GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
} else if (channel_args->args[i].value.integer < 0) {
gpr_log(GPR_DEBUG, "%s: must be non-negative",
GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
} else {
push_setting(t, GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE,
(gpr_uint32)channel_args->args[i].value.integer);
}
} else if (0 == strcmp(channel_args->args[i].key,
GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) {
if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
gpr_log(GPR_ERROR, "%s: must be an integer",
GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
} else if (channel_args->args[i].value.integer < 0) {
gpr_log(GPR_DEBUG, "%s: must be non-negative",
GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
} else {
grpc_chttp2_hpack_compressor_set_max_usable_size(
&t->writing.hpack_compressor,
(gpr_uint32)channel_args->args[i].value.integer);
}
} }
} }
} }
@ -563,7 +588,8 @@ static void lock(grpc_chttp2_transport *t) { gpr_mu_lock(&t->mu); }
static void unlock(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { static void unlock(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) {
GPR_TIMER_BEGIN("unlock", 0); GPR_TIMER_BEGIN("unlock", 0);
if (!t->writing_active && !t->closed && if (!t->writing_active && !t->closed &&
grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) { grpc_chttp2_unlocking_check_writes(&t->global, &t->writing,
t->parsing_active)) {
t->writing_active = 1; t->writing_active = 1;
REF_TRANSPORT(t, "writing"); REF_TRANSPORT(t, "writing");
grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, 1); grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, 1);
@ -735,6 +761,8 @@ static int contains_non_ok_status(
return 0; return 0;
} }
static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, int success) {}
static void perform_stream_op_locked( static void perform_stream_op_locked(
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) { grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) {
@ -743,6 +771,9 @@ static void perform_stream_op_locked(
GPR_TIMER_BEGIN("perform_stream_op_locked", 0); GPR_TIMER_BEGIN("perform_stream_op_locked", 0);
on_complete = op->on_complete; on_complete = op->on_complete;
if (on_complete == NULL) {
on_complete = grpc_closure_create(do_nothing, NULL);
}
/* use final_data as a barrier until enqueue time; the inital counter is /* use final_data as a barrier until enqueue time; the inital counter is
dropped at the end of this function */ dropped at the end of this function */
on_complete->final_data = 2; on_complete->final_data = 2;
@ -806,7 +837,7 @@ static void perform_stream_op_locked(
} }
if (stream_global->write_closed) { if (stream_global->write_closed) {
grpc_chttp2_complete_closure_step( grpc_chttp2_complete_closure_step(
exec_ctx, &stream_global->send_trailing_metadata_finished, exec_ctx, &stream_global->send_trailing_metadata_finished,
grpc_metadata_batch_is_empty(op->send_trailing_metadata)); grpc_metadata_batch_is_empty(op->send_trailing_metadata));
} else if (stream_global->id != 0) { } else if (stream_global->id != 0) {
/* TODO(ctiller): check if there's flow control for any outstanding /* TODO(ctiller): check if there's flow control for any outstanding
@ -1033,7 +1064,8 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx,
to the upper layers - drop what we've got, and then publish to the upper layers - drop what we've got, and then publish
what we want - which is safe because we haven't told anyone what we want - which is safe because we haven't told anyone
about the metadata yet */ about the metadata yet */
if (!stream_global->published_trailing_metadata || stream_global->recv_trailing_metadata_finished != NULL) { if (!stream_global->published_trailing_metadata ||
stream_global->recv_trailing_metadata_finished != NULL) {
grpc_mdctx *mdctx = grpc_mdctx *mdctx =
TRANSPORT_FROM_GLOBAL(transport_global)->metadata_context; TRANSPORT_FROM_GLOBAL(transport_global)->metadata_context;
char status_string[GPR_LTOA_MIN_BUFSIZE]; char status_string[GPR_LTOA_MIN_BUFSIZE];

@ -54,7 +54,9 @@ class InsecureChannelCredentialsImpl GRPC_FINAL : public ChannelCredentials {
grpc_insecure_channel_create(target.c_str(), &channel_args, nullptr)); grpc_insecure_channel_create(target.c_str(), &channel_args, nullptr));
} }
SecureChannelCredentials* AsSecureCredentials() GRPC_OVERRIDE { return nullptr; } SecureChannelCredentials* AsSecureCredentials() GRPC_OVERRIDE {
return nullptr;
}
}; };
} // namespace } // namespace

@ -76,7 +76,6 @@ class SecureCallCredentials GRPC_FINAL : public CallCredentials {
grpc_call_credentials* const c_creds_; grpc_call_credentials* const c_creds_;
}; };
class MetadataCredentialsPluginWrapper GRPC_FINAL { class MetadataCredentialsPluginWrapper GRPC_FINAL {
public: public:
static void Destroy(void* wrapper); static void Destroy(void* wrapper);

@ -41,7 +41,9 @@ namespace Grpc.Core.Profiling
internal interface IProfiler internal interface IProfiler
{ {
void Begin(string tag); void Begin(string tag);
void End(string tag); void End(string tag);
void Mark(string tag); void Mark(string tag);
} }
} }

@ -40,7 +40,8 @@ namespace Grpc.Core.Profiling
{ {
internal struct ProfilerEntry internal struct ProfilerEntry
{ {
public enum Type { public enum Type
{
BEGIN, BEGIN,
END, END,
MARK MARK

@ -40,12 +40,12 @@ namespace Grpc.Core.Profiling
{ {
internal static class Profilers internal static class Profilers
{ {
static readonly NopProfiler defaultProfiler = new NopProfiler(); static readonly NopProfiler DefaultProfiler = new NopProfiler();
static readonly ThreadLocal<IProfiler> profilers = new ThreadLocal<IProfiler>(); static readonly ThreadLocal<IProfiler> profilers = new ThreadLocal<IProfiler>();
public static IProfiler ForCurrentThread() public static IProfiler ForCurrentThread()
{ {
return profilers.Value ?? defaultProfiler; return profilers.Value ?? DefaultProfiler;
} }
public static void SetForCurrentThread(IProfiler profiler) public static void SetForCurrentThread(IProfiler profiler)
@ -89,15 +89,18 @@ namespace Grpc.Core.Profiling
this.entries = new ProfilerEntry[capacity]; this.entries = new ProfilerEntry[capacity];
} }
public void Begin(string tag) { public void Begin(string tag)
{
AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.BEGIN, tag)); AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.BEGIN, tag));
} }
public void End(string tag) { public void End(string tag)
{
AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.END, tag)); AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.END, tag));
} }
public void Mark(string tag) { public void Mark(string tag)
{
AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.MARK, tag)); AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.MARK, tag));
} }

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Grpc.IntegrationTesting.QpsWorker</RootNamespace>
<AssemblyName>Grpc.IntegrationTesting.QpsWorker</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\ReleaseSigned</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj">
<Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
<Name>Grpc.Core</Name>
</ProjectReference>
<ProjectReference Include="..\Grpc.IntegrationTesting\Grpc.IntegrationTesting.csproj">
<Project>{C61154BA-DD4A-4838-8420-0162A28925E0}</Project>
<Name>Grpc.IntegrationTesting</Name>
</ProjectReference>
</ItemGroup>
</Project>

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

@ -0,0 +1,11 @@
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Grpc.IntegrationTesting.QpsWorker")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

@ -0,0 +1,76 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
namespace Grpc.Testing
{
/// <summary>
/// Implementation of BenchmarkService server
/// </summary>
public class BenchmarkServiceImpl : BenchmarkService.IBenchmarkService
{
private readonly int responseSize;
public BenchmarkServiceImpl(int responseSize)
{
this.responseSize = responseSize;
}
public Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
{
var response = new SimpleResponse { Payload = CreateZerosPayload(responseSize) };
return Task.FromResult(response);
}
public async Task StreamingCall(IAsyncStreamReader<SimpleRequest> requestStream, IServerStreamWriter<SimpleResponse> responseStream, ServerCallContext context)
{
await requestStream.ForEachAsync(async request =>
{
var response = new SimpleResponse { Payload = CreateZerosPayload(responseSize) };
await responseStream.WriteAsync(response);
});
}
private static Payload CreateZerosPayload(int size)
{
return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
}
}
}

@ -0,0 +1,153 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
using NUnit.Framework;
using Grpc.Testing;
namespace Grpc.IntegrationTesting
{
/// <summary>
/// Helper methods to start client runners for performance testing.
/// </summary>
public static class ClientRunners
{
/// <summary>
/// Creates a started client runner.
/// </summary>
public static IClientRunner CreateStarted(ClientConfig config)
{
string target = config.ServerTargets.Single();
Grpc.Core.Utils.Preconditions.CheckArgument(config.LoadParams.LoadCase == LoadParams.LoadOneofCase.ClosedLoop);
var credentials = config.SecurityParams != null ? TestCredentials.CreateSslCredentials() : ChannelCredentials.Insecure;
var channel = new Channel(target, credentials);
switch (config.RpcType)
{
case RpcType.UNARY:
return new SyncUnaryClientRunner(channel,
config.PayloadConfig.SimpleParams.ReqSize,
config.HistogramParams);
case RpcType.STREAMING:
default:
throw new ArgumentException("Unsupported RpcType.");
}
}
}
/// <summary>
/// Client that starts synchronous unary calls in a closed loop.
/// </summary>
public class SyncUnaryClientRunner : IClientRunner
{
const double SecondsToNanos = 1e9;
readonly Channel channel;
readonly int payloadSize;
readonly Histogram histogram;
readonly BenchmarkService.IBenchmarkServiceClient client;
readonly Task runnerTask;
readonly CancellationTokenSource stoppedCts;
readonly WallClockStopwatch wallClockStopwatch = new WallClockStopwatch();
public SyncUnaryClientRunner(Channel channel, int payloadSize, HistogramParams histogramParams)
{
this.channel = Grpc.Core.Utils.Preconditions.CheckNotNull(channel);
this.payloadSize = payloadSize;
this.histogram = new Histogram(histogramParams.Resolution, histogramParams.MaxPossible);
this.stoppedCts = new CancellationTokenSource();
this.client = BenchmarkService.NewClient(channel);
this.runnerTask = Task.Factory.StartNew(Run, TaskCreationOptions.LongRunning);
}
public ClientStats GetStats(bool reset)
{
var histogramData = histogram.GetSnapshot(reset);
var secondsElapsed = wallClockStopwatch.GetElapsedSnapshot(reset).TotalSeconds;
// TODO: populate user time and system time
return new ClientStats
{
Latencies = histogramData,
TimeElapsed = secondsElapsed,
TimeUser = 0,
TimeSystem = 0
};
}
public async Task StopAsync()
{
stoppedCts.Cancel();
await runnerTask;
await channel.ShutdownAsync();
}
private void Run()
{
var request = new SimpleRequest
{
Payload = CreateZerosPayload(payloadSize)
};
var stopwatch = new Stopwatch();
while (!stoppedCts.Token.IsCancellationRequested)
{
stopwatch.Restart();
client.UnaryCall(request);
stopwatch.Stop();
// spec requires data point in nanoseconds.
histogram.AddObservation(stopwatch.Elapsed.TotalSeconds * SecondsToNanos);
}
}
private static Payload CreateZerosPayload(int size)
{
return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
}
}
}

File diff suppressed because it is too large Load Diff

@ -38,55 +38,46 @@
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="CommandLine"> <Reference Include="CommandLine">
<HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath> <HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="nunit.framework">
<SpecificVersion>False</SpecificVersion> <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Interactive.Async">
<HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="BouncyCastle.Crypto">
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth">
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath> <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Google.Apis.Auth.PlatformServices">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Google.Apis.Core">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL"> <Reference Include="Google.Protobuf">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath> <HintPath>..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks.Extensions">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Interactive.Async">
<HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs"> <Compile Include="..\Grpc.Core\Version.cs">
@ -104,6 +95,22 @@
<Compile Include="TestGrpc.cs" /> <Compile Include="TestGrpc.cs" />
<Compile Include="SslCredentialsTest.cs" /> <Compile Include="SslCredentialsTest.cs" />
<Compile Include="Test.cs" /> <Compile Include="Test.cs" />
<Compile Include="IClientRunner.cs" />
<Compile Include="ClientRunners.cs" />
<Compile Include="IServerRunner.cs" />
<Compile Include="ServerRunners.cs" />
<Compile Include="RunnerClientServerTest.cs" />
<Compile Include="Control.cs" />
<Compile Include="Payloads.cs" />
<Compile Include="Services.cs" />
<Compile Include="ServicesGrpc.cs" />
<Compile Include="Stats.cs" />
<Compile Include="BenchmarkServiceImpl.cs" />
<Compile Include="Histogram.cs" />
<Compile Include="HistogramTest.cs" />
<Compile Include="WorkerServiceImpl.cs" />
<Compile Include="QpsWorker.cs" />
<Compile Include="WallClockStopwatch.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>

@ -0,0 +1,153 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
using NUnit.Framework;
using Grpc.Testing;
namespace Grpc.IntegrationTesting
{
/// <summary>
/// Basic implementation of histogram based on grpc/support/histogram.h.
/// </summary>
public class Histogram
{
readonly object myLock = new object();
readonly double multiplier;
readonly double oneOnLogMultiplier;
readonly double maxPossible;
readonly uint[] buckets;
int count;
double sum;
double sumOfSquares;
double min;
double max;
public Histogram(double resolution, double maxPossible)
{
Grpc.Core.Utils.Preconditions.CheckArgument(resolution > 0);
Grpc.Core.Utils.Preconditions.CheckArgument(maxPossible > 0);
this.maxPossible = maxPossible;
this.multiplier = 1.0 + resolution;
this.oneOnLogMultiplier = 1.0 / Math.Log(1.0 + resolution);
this.buckets = new uint[FindBucket(maxPossible) + 1];
ResetUnsafe();
}
public void AddObservation(double value)
{
lock (myLock)
{
AddObservationUnsafe(value);
}
}
/// <summary>
/// Gets snapshot of stats and reset
/// </summary>
public HistogramData GetSnapshot(bool reset = false)
{
lock (myLock)
{
return GetSnapshotUnsafe(reset);
}
}
/// <summary>
/// Finds bucket index to which given observation should go.
/// </summary>
private int FindBucket(double value)
{
value = Math.Max(value, 1.0);
value = Math.Min(value, this.maxPossible);
return (int)(Math.Log(value) * oneOnLogMultiplier);
}
private void AddObservationUnsafe(double value)
{
this.count++;
this.sum += value;
this.sumOfSquares += value * value;
this.min = Math.Min(this.min, value);
this.max = Math.Max(this.max, value);
this.buckets[FindBucket(value)]++;
}
private HistogramData GetSnapshotUnsafe(bool reset)
{
var data = new HistogramData
{
Count = count,
Sum = sum,
SumOfSquares = sumOfSquares,
MinSeen = min,
MaxSeen = max,
Bucket = { buckets }
};
if (reset)
{
ResetUnsafe();
}
return data;
}
private void ResetUnsafe()
{
this.count = 0;
this.sum = 0;
this.sumOfSquares = 0;
this.min = double.PositiveInfinity;
this.max = double.NegativeInfinity;
for (int i = 0; i < this.buckets.Length; i++)
{
this.buckets[i] = 0;
}
}
}
}

@ -0,0 +1,104 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Utils;
using Grpc.Testing;
using NUnit.Framework;
namespace Grpc.IntegrationTesting
{
public class HistogramTest
{
[Test]
public void Simple()
{
var hist = new Histogram(0.01, 60e9);
hist.AddObservation(10000);
hist.AddObservation(10000);
hist.AddObservation(11000);
hist.AddObservation(11000);
var data = hist.GetSnapshot();
Assert.AreEqual(4, data.Count);
Assert.AreEqual(42000.0, data.Sum, 1e-6);
Assert.AreEqual(10000, data.MinSeen);
Assert.AreEqual(11000, data.MaxSeen);
Assert.AreEqual(2.0*10000*10000 + 2.0*11000*11000, data.SumOfSquares, 1e-6);
// 1.01^925 < 10000 < 1.01^926
Assert.AreEqual(2, data.Bucket[925]);
Assert.AreEqual(2, data.Bucket[935]);
}
[Test]
public void ExtremeObservations()
{
var hist = new Histogram(0.01, 60e9);
hist.AddObservation(-0.5); // should be in the first bucket
hist.AddObservation(1e12); // should be in the last bucket
var data = hist.GetSnapshot();
Assert.AreEqual(1, data.Bucket[0]);
Assert.AreEqual(1, data.Bucket[data.Bucket.Count - 1]);
}
[Test]
public void Reset()
{
var hist = new Histogram(0.01, 60e9);
hist.AddObservation(10000);
hist.AddObservation(11000);
var data = hist.GetSnapshot(true); // snapshot contains data before reset
Assert.AreEqual(2, data.Count);
Assert.AreEqual(10000, data.MinSeen);
Assert.AreEqual(11000, data.MaxSeen);
data = hist.GetSnapshot(); // snapshot contains state after reset
Assert.AreEqual(0, data.Count);
Assert.AreEqual(double.PositiveInfinity, data.MinSeen);
Assert.AreEqual(double.NegativeInfinity, data.MaxSeen);
Assert.AreEqual(0, data.Sum);
Assert.AreEqual(0, data.SumOfSquares);
CollectionAssert.AreEqual(new uint[data.Bucket.Count], data.Bucket);
}
}
}

@ -0,0 +1,67 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
using NUnit.Framework;
using Grpc.Testing;
namespace Grpc.IntegrationTesting
{
/// <summary>
/// Abstract client runner.
/// </summary>
public interface IClientRunner
{
/// <summary>
/// Gets stats snapshot.
/// </summary>
/// <returns>The stats.</returns>
ClientStats GetStats(bool reset);
/// <summary>
/// Asynchronously stops the client.
/// </summary>
/// <returns>Task that finishes when client has come to a full stop.</returns>
Task StopAsync();
}
}

@ -0,0 +1,72 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
using NUnit.Framework;
using Grpc.Testing;
namespace Grpc.IntegrationTesting
{
/// <summary>
/// Abstract server runner.
/// </summary>
public interface IServerRunner
{
/// <summary>
/// Port on which the server is listening.
/// </summary>
int BoundPort { get; }
/// <summary>
/// Gets server stats.
/// </summary>
/// <returns>The stats.</returns>
ServerStats GetStats(bool reset);
/// <summary>
/// Asynchronously stops the server.
/// </summary>
/// <returns>Task that finishes when server has shutdown.</returns>
Task StopAsync();
}
}

@ -0,0 +1,580 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: test/proto/benchmarks/payloads.proto
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Grpc.Testing {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial class Payloads {
#region Descriptor
public static pbr::FileDescriptor Descriptor {
get { return descriptor; }
}
private static pbr::FileDescriptor descriptor;
static Payloads() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CiR0ZXN0L3Byb3RvL2JlbmNobWFya3MvcGF5bG9hZHMucHJvdG8SDGdycGMu",
"dGVzdGluZyI3ChBCeXRlQnVmZmVyUGFyYW1zEhAKCHJlcV9zaXplGAEgASgF",
"EhEKCXJlc3Bfc2l6ZRgCIAEoBSI4ChFTaW1wbGVQcm90b1BhcmFtcxIQCghy",
"ZXFfc2l6ZRgBIAEoBRIRCglyZXNwX3NpemUYAiABKAUiFAoSQ29tcGxleFBy",
"b3RvUGFyYW1zIsoBCg1QYXlsb2FkQ29uZmlnEjgKDmJ5dGVidWZfcGFyYW1z",
"GAEgASgLMh4uZ3JwYy50ZXN0aW5nLkJ5dGVCdWZmZXJQYXJhbXNIABI4Cg1z",
"aW1wbGVfcGFyYW1zGAIgASgLMh8uZ3JwYy50ZXN0aW5nLlNpbXBsZVByb3Rv",
"UGFyYW1zSAASOgoOY29tcGxleF9wYXJhbXMYAyABKAsyIC5ncnBjLnRlc3Rp",
"bmcuQ29tcGxleFByb3RvUGFyYW1zSABCCQoHcGF5bG9hZGIGcHJvdG8z"));
descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ByteBufferParams), new[]{ "ReqSize", "RespSize" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleProtoParams), new[]{ "ReqSize", "RespSize" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ComplexProtoParams), null, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.PayloadConfig), new[]{ "BytebufParams", "SimpleParams", "ComplexParams" }, new[]{ "Payload" }, null, null)
}));
}
#endregion
}
#region Messages
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class ByteBufferParams : pb::IMessage<ByteBufferParams> {
private static readonly pb::MessageParser<ByteBufferParams> _parser = new pb::MessageParser<ByteBufferParams>(() => new ByteBufferParams());
public static pb::MessageParser<ByteBufferParams> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Payloads.Descriptor.MessageTypes[0]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public ByteBufferParams() {
OnConstruction();
}
partial void OnConstruction();
public ByteBufferParams(ByteBufferParams other) : this() {
reqSize_ = other.reqSize_;
respSize_ = other.respSize_;
}
public ByteBufferParams Clone() {
return new ByteBufferParams(this);
}
public const int ReqSizeFieldNumber = 1;
private int reqSize_;
public int ReqSize {
get { return reqSize_; }
set {
reqSize_ = value;
}
}
public const int RespSizeFieldNumber = 2;
private int respSize_;
public int RespSize {
get { return respSize_; }
set {
respSize_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as ByteBufferParams);
}
public bool Equals(ByteBufferParams other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (ReqSize != other.ReqSize) return false;
if (RespSize != other.RespSize) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (ReqSize != 0) hash ^= ReqSize.GetHashCode();
if (RespSize != 0) hash ^= RespSize.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (ReqSize != 0) {
output.WriteRawTag(8);
output.WriteInt32(ReqSize);
}
if (RespSize != 0) {
output.WriteRawTag(16);
output.WriteInt32(RespSize);
}
}
public int CalculateSize() {
int size = 0;
if (ReqSize != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(ReqSize);
}
if (RespSize != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(RespSize);
}
return size;
}
public void MergeFrom(ByteBufferParams other) {
if (other == null) {
return;
}
if (other.ReqSize != 0) {
ReqSize = other.ReqSize;
}
if (other.RespSize != 0) {
RespSize = other.RespSize;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 8: {
ReqSize = input.ReadInt32();
break;
}
case 16: {
RespSize = input.ReadInt32();
break;
}
}
}
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class SimpleProtoParams : pb::IMessage<SimpleProtoParams> {
private static readonly pb::MessageParser<SimpleProtoParams> _parser = new pb::MessageParser<SimpleProtoParams>(() => new SimpleProtoParams());
public static pb::MessageParser<SimpleProtoParams> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Payloads.Descriptor.MessageTypes[1]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public SimpleProtoParams() {
OnConstruction();
}
partial void OnConstruction();
public SimpleProtoParams(SimpleProtoParams other) : this() {
reqSize_ = other.reqSize_;
respSize_ = other.respSize_;
}
public SimpleProtoParams Clone() {
return new SimpleProtoParams(this);
}
public const int ReqSizeFieldNumber = 1;
private int reqSize_;
public int ReqSize {
get { return reqSize_; }
set {
reqSize_ = value;
}
}
public const int RespSizeFieldNumber = 2;
private int respSize_;
public int RespSize {
get { return respSize_; }
set {
respSize_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as SimpleProtoParams);
}
public bool Equals(SimpleProtoParams other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (ReqSize != other.ReqSize) return false;
if (RespSize != other.RespSize) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (ReqSize != 0) hash ^= ReqSize.GetHashCode();
if (RespSize != 0) hash ^= RespSize.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (ReqSize != 0) {
output.WriteRawTag(8);
output.WriteInt32(ReqSize);
}
if (RespSize != 0) {
output.WriteRawTag(16);
output.WriteInt32(RespSize);
}
}
public int CalculateSize() {
int size = 0;
if (ReqSize != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(ReqSize);
}
if (RespSize != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(RespSize);
}
return size;
}
public void MergeFrom(SimpleProtoParams other) {
if (other == null) {
return;
}
if (other.ReqSize != 0) {
ReqSize = other.ReqSize;
}
if (other.RespSize != 0) {
RespSize = other.RespSize;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 8: {
ReqSize = input.ReadInt32();
break;
}
case 16: {
RespSize = input.ReadInt32();
break;
}
}
}
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class ComplexProtoParams : pb::IMessage<ComplexProtoParams> {
private static readonly pb::MessageParser<ComplexProtoParams> _parser = new pb::MessageParser<ComplexProtoParams>(() => new ComplexProtoParams());
public static pb::MessageParser<ComplexProtoParams> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Payloads.Descriptor.MessageTypes[2]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public ComplexProtoParams() {
OnConstruction();
}
partial void OnConstruction();
public ComplexProtoParams(ComplexProtoParams other) : this() {
}
public ComplexProtoParams Clone() {
return new ComplexProtoParams(this);
}
public override bool Equals(object other) {
return Equals(other as ComplexProtoParams);
}
public bool Equals(ComplexProtoParams other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
return true;
}
public override int GetHashCode() {
int hash = 1;
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
}
public int CalculateSize() {
int size = 0;
return size;
}
public void MergeFrom(ComplexProtoParams other) {
if (other == null) {
return;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
}
}
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class PayloadConfig : pb::IMessage<PayloadConfig> {
private static readonly pb::MessageParser<PayloadConfig> _parser = new pb::MessageParser<PayloadConfig>(() => new PayloadConfig());
public static pb::MessageParser<PayloadConfig> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Payloads.Descriptor.MessageTypes[3]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public PayloadConfig() {
OnConstruction();
}
partial void OnConstruction();
public PayloadConfig(PayloadConfig other) : this() {
switch (other.PayloadCase) {
case PayloadOneofCase.BytebufParams:
BytebufParams = other.BytebufParams.Clone();
break;
case PayloadOneofCase.SimpleParams:
SimpleParams = other.SimpleParams.Clone();
break;
case PayloadOneofCase.ComplexParams:
ComplexParams = other.ComplexParams.Clone();
break;
}
}
public PayloadConfig Clone() {
return new PayloadConfig(this);
}
public const int BytebufParamsFieldNumber = 1;
public global::Grpc.Testing.ByteBufferParams BytebufParams {
get { return payloadCase_ == PayloadOneofCase.BytebufParams ? (global::Grpc.Testing.ByteBufferParams) payload_ : null; }
set {
payload_ = value;
payloadCase_ = value == null ? PayloadOneofCase.None : PayloadOneofCase.BytebufParams;
}
}
public const int SimpleParamsFieldNumber = 2;
public global::Grpc.Testing.SimpleProtoParams SimpleParams {
get { return payloadCase_ == PayloadOneofCase.SimpleParams ? (global::Grpc.Testing.SimpleProtoParams) payload_ : null; }
set {
payload_ = value;
payloadCase_ = value == null ? PayloadOneofCase.None : PayloadOneofCase.SimpleParams;
}
}
public const int ComplexParamsFieldNumber = 3;
public global::Grpc.Testing.ComplexProtoParams ComplexParams {
get { return payloadCase_ == PayloadOneofCase.ComplexParams ? (global::Grpc.Testing.ComplexProtoParams) payload_ : null; }
set {
payload_ = value;
payloadCase_ = value == null ? PayloadOneofCase.None : PayloadOneofCase.ComplexParams;
}
}
private object payload_;
public enum PayloadOneofCase {
None = 0,
BytebufParams = 1,
SimpleParams = 2,
ComplexParams = 3,
}
private PayloadOneofCase payloadCase_ = PayloadOneofCase.None;
public PayloadOneofCase PayloadCase {
get { return payloadCase_; }
}
public void ClearPayload() {
payloadCase_ = PayloadOneofCase.None;
payload_ = null;
}
public override bool Equals(object other) {
return Equals(other as PayloadConfig);
}
public bool Equals(PayloadConfig other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (!object.Equals(BytebufParams, other.BytebufParams)) return false;
if (!object.Equals(SimpleParams, other.SimpleParams)) return false;
if (!object.Equals(ComplexParams, other.ComplexParams)) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (payloadCase_ == PayloadOneofCase.BytebufParams) hash ^= BytebufParams.GetHashCode();
if (payloadCase_ == PayloadOneofCase.SimpleParams) hash ^= SimpleParams.GetHashCode();
if (payloadCase_ == PayloadOneofCase.ComplexParams) hash ^= ComplexParams.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (payloadCase_ == PayloadOneofCase.BytebufParams) {
output.WriteRawTag(10);
output.WriteMessage(BytebufParams);
}
if (payloadCase_ == PayloadOneofCase.SimpleParams) {
output.WriteRawTag(18);
output.WriteMessage(SimpleParams);
}
if (payloadCase_ == PayloadOneofCase.ComplexParams) {
output.WriteRawTag(26);
output.WriteMessage(ComplexParams);
}
}
public int CalculateSize() {
int size = 0;
if (payloadCase_ == PayloadOneofCase.BytebufParams) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(BytebufParams);
}
if (payloadCase_ == PayloadOneofCase.SimpleParams) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(SimpleParams);
}
if (payloadCase_ == PayloadOneofCase.ComplexParams) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(ComplexParams);
}
return size;
}
public void MergeFrom(PayloadConfig other) {
if (other == null) {
return;
}
switch (other.PayloadCase) {
case PayloadOneofCase.BytebufParams:
BytebufParams = other.BytebufParams;
break;
case PayloadOneofCase.SimpleParams:
SimpleParams = other.SimpleParams;
break;
case PayloadOneofCase.ComplexParams:
ComplexParams = other.ComplexParams;
break;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 10: {
global::Grpc.Testing.ByteBufferParams subBuilder = new global::Grpc.Testing.ByteBufferParams();
if (payloadCase_ == PayloadOneofCase.BytebufParams) {
subBuilder.MergeFrom(BytebufParams);
}
input.ReadMessage(subBuilder);
BytebufParams = subBuilder;
break;
}
case 18: {
global::Grpc.Testing.SimpleProtoParams subBuilder = new global::Grpc.Testing.SimpleProtoParams();
if (payloadCase_ == PayloadOneofCase.SimpleParams) {
subBuilder.MergeFrom(SimpleParams);
}
input.ReadMessage(subBuilder);
SimpleParams = subBuilder;
break;
}
case 26: {
global::Grpc.Testing.ComplexProtoParams subBuilder = new global::Grpc.Testing.ComplexProtoParams();
if (payloadCase_ == PayloadOneofCase.ComplexParams) {
subBuilder.MergeFrom(ComplexParams);
}
input.ReadMessage(subBuilder);
ComplexParams = subBuilder;
break;
}
}
}
}
}
#endregion
}
#endregion Designer generated code

@ -0,0 +1,108 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CommandLine;
using CommandLine.Text;
using Grpc.Core;
using Grpc.Core.Utils;
using Grpc.Testing;
using NUnit.Framework;
namespace Grpc.IntegrationTesting
{
public class QpsWorker
{
private class ServerOptions
{
[Option("driver_port", DefaultValue = 0)]
public int DriverPort { get; set; }
[HelpOption]
public string GetUsage()
{
var help = new HelpText
{
Heading = "gRPC C# performance testing worker",
AddDashesToOption = true
};
help.AddPreOptionsLine("Usage:");
help.AddOptions(this);
return help;
}
}
ServerOptions options;
private QpsWorker(ServerOptions options)
{
this.options = options;
}
public static void Run(string[] args)
{
var options = new ServerOptions();
if (!Parser.Default.ParseArguments(args, options))
{
Environment.Exit(1);
}
var workerServer = new QpsWorker(options);
workerServer.Run();
}
private void Run()
{
string host = "0.0.0.0";
int port = options.DriverPort;
var server = new Server
{
Services = { WorkerService.BindService(new WorkerServiceImpl()) },
Ports = { new ServerPort(host, options.DriverPort, ServerCredentials.Insecure )}
};
int boundPort = server.Ports.Single().BoundPort;
Console.WriteLine("Running qps worker server on " + string.Format("{0}:{1}", host, boundPort));
server.Start();
server.ShutdownTask.Wait();
}
}
}

@ -0,0 +1,117 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Utils;
using Grpc.Testing;
using NUnit.Framework;
namespace Grpc.IntegrationTesting
{
/// <summary>
/// Runs performance tests in-process.
/// </summary>
public class RunnerClientServerTest
{
const string Host = "localhost";
IServerRunner serverRunner;
[TestFixtureSetUp]
public void Init()
{
var serverConfig = new ServerConfig
{
ServerType = ServerType.ASYNC_SERVER,
Host = Host,
PayloadConfig = new PayloadConfig
{
SimpleParams = new SimpleProtoParams
{
RespSize = 100
}
}
};
serverRunner = ServerRunners.CreateStarted(serverConfig);
}
[TestFixtureTearDown]
public void Cleanup()
{
serverRunner.StopAsync().Wait();
}
// Test attribute commented out to prevent running as part of the default test suite.
//[Test]
//[Category("Performance")]
public async Task ClientServerRunner()
{
var config = new ClientConfig
{
ServerTargets = { string.Format("{0}:{1}", Host, serverRunner.BoundPort) },
RpcType = RpcType.UNARY,
LoadParams = new LoadParams { ClosedLoop = new ClosedLoopParams() },
PayloadConfig = new PayloadConfig
{
SimpleParams = new SimpleProtoParams
{
ReqSize = 100
}
},
HistogramParams = new HistogramParams
{
Resolution = 0.01,
MaxPossible = 60e9
}
};
var runner = ClientRunners.CreateStarted(config);
System.Console.WriteLine("Warming up");
await Task.Delay(3000);
runner.GetStats(true); // throw away warm-up data
System.Console.WriteLine("Benchmarking");
await Task.Delay(3000);
var stats = runner.GetStats(true);
await runner.StopAsync();
System.Console.WriteLine(stats);
System.Console.WriteLine("avg micros/call " + (long) (stats.Latencies.Sum / stats.Latencies.Count / 1000.0));
}
}
}

@ -0,0 +1,124 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
using NUnit.Framework;
using Grpc.Testing;
namespace Grpc.IntegrationTesting
{
/// <summary>
/// Helper methods to start server runners for performance testing.
/// </summary>
public static class ServerRunners
{
/// <summary>
/// Creates a started server runner.
/// </summary>
public static IServerRunner CreateStarted(ServerConfig config)
{
Grpc.Core.Utils.Preconditions.CheckArgument(config.ServerType == ServerType.ASYNC_SERVER);
var credentials = config.SecurityParams != null ? TestCredentials.CreateSslServerCredentials() : ServerCredentials.Insecure;
// TODO: qps_driver needs to setup payload properly...
int responseSize = config.PayloadConfig != null ? config.PayloadConfig.SimpleParams.RespSize : 0;
var server = new Server
{
Services = { BenchmarkService.BindService(new BenchmarkServiceImpl(responseSize)) },
Ports = { new ServerPort(config.Host, config.Port, credentials) }
};
server.Start();
return new ServerRunnerImpl(server);
}
}
/// <summary>
/// Server runner.
/// </summary>
public class ServerRunnerImpl : IServerRunner
{
readonly Server server;
readonly WallClockStopwatch wallClockStopwatch = new WallClockStopwatch();
public ServerRunnerImpl(Server server)
{
this.server = Grpc.Core.Utils.Preconditions.CheckNotNull(server);
}
public int BoundPort
{
get
{
return server.Ports.Single().BoundPort;
}
}
/// <summary>
/// Gets server stats.
/// </summary>
/// <returns>The stats.</returns>
public ServerStats GetStats(bool reset)
{
var secondsElapsed = wallClockStopwatch.GetElapsedSnapshot(reset).TotalSeconds;
// TODO: populate user time and system time
return new ServerStats
{
TimeElapsed = secondsElapsed,
TimeUser = 0,
TimeSystem = 0
};
}
/// <summary>
/// Asynchronously stops the server.
/// </summary>
/// <returns>Task that finishes when server has shutdown.</returns>
public Task StopAsync()
{
return server.ShutdownAsync();
}
}
}

@ -0,0 +1,44 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: test/proto/benchmarks/services.proto
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Grpc.Testing {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial class Services {
#region Descriptor
public static pbr::FileDescriptor Descriptor {
get { return descriptor; }
}
private static pbr::FileDescriptor descriptor;
static Services() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CiR0ZXN0L3Byb3RvL2JlbmNobWFya3Mvc2VydmljZXMucHJvdG8SDGdycGMu",
"dGVzdGluZxoZdGVzdC9wcm90by9tZXNzYWdlcy5wcm90bxojdGVzdC9wcm90",
"by9iZW5jaG1hcmtzL2NvbnRyb2wucHJvdG8yqgEKEEJlbmNobWFya1NlcnZp",
"Y2USRgoJVW5hcnlDYWxsEhsuZ3JwYy50ZXN0aW5nLlNpbXBsZVJlcXVlc3Qa",
"HC5ncnBjLnRlc3RpbmcuU2ltcGxlUmVzcG9uc2USTgoNU3RyZWFtaW5nQ2Fs",
"bBIbLmdycGMudGVzdGluZy5TaW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0aW5n",
"LlNpbXBsZVJlc3BvbnNlKAEwATKdAQoNV29ya2VyU2VydmljZRJFCglSdW5T",
"ZXJ2ZXISGC5ncnBjLnRlc3RpbmcuU2VydmVyQXJncxoaLmdycGMudGVzdGlu",
"Zy5TZXJ2ZXJTdGF0dXMoATABEkUKCVJ1bkNsaWVudBIYLmdycGMudGVzdGlu",
"Zy5DbGllbnRBcmdzGhouZ3JwYy50ZXN0aW5nLkNsaWVudFN0YXR1cygBMAFi",
"BnByb3RvMw=="));
descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbr::FileDescriptor[] { global::Grpc.Testing.Messages.Descriptor, global::Grpc.Testing.Control.Descriptor, },
new pbr::GeneratedCodeInfo(null, null));
}
#endregion
}
}
#endregion Designer generated code

@ -0,0 +1,198 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: test/proto/benchmarks/services.proto
#region Designer generated code
using System;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
namespace Grpc.Testing {
public static class BenchmarkService
{
static readonly string __ServiceName = "grpc.testing.BenchmarkService";
static readonly Marshaller<global::Grpc.Testing.SimpleRequest> __Marshaller_SimpleRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.SimpleRequest.Parser.ParseFrom);
static readonly Marshaller<global::Grpc.Testing.SimpleResponse> __Marshaller_SimpleResponse = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.SimpleResponse.Parser.ParseFrom);
static readonly Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_UnaryCall = new Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
MethodType.Unary,
__ServiceName,
"UnaryCall",
__Marshaller_SimpleRequest,
__Marshaller_SimpleResponse);
static readonly Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_StreamingCall = new Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>(
MethodType.DuplexStreaming,
__ServiceName,
"StreamingCall",
__Marshaller_SimpleRequest,
__Marshaller_SimpleResponse);
// service descriptor
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
get { return global::Grpc.Testing.Services.Descriptor.Services[0]; }
}
// client interface
public interface IBenchmarkServiceClient
{
global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options);
AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options);
AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(CallOptions options);
}
// server-side interface
public interface IBenchmarkService
{
Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context);
Task StreamingCall(IAsyncStreamReader<global::Grpc.Testing.SimpleRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.SimpleResponse> responseStream, ServerCallContext context);
}
// client stub
public class BenchmarkServiceClient : ClientBase, IBenchmarkServiceClient
{
public BenchmarkServiceClient(Channel channel) : base(channel)
{
}
public global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken));
return Calls.BlockingUnaryCall(call, request);
}
public global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options)
{
var call = CreateCall(__Method_UnaryCall, options);
return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken));
return Calls.AsyncUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options)
{
var call = CreateCall(__Method_UnaryCall, options);
return Calls.AsyncUnaryCall(call, request);
}
public AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_StreamingCall, new CallOptions(headers, deadline, cancellationToken));
return Calls.AsyncDuplexStreamingCall(call);
}
public AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(CallOptions options)
{
var call = CreateCall(__Method_StreamingCall, options);
return Calls.AsyncDuplexStreamingCall(call);
}
}
// creates service definition that can be registered with a server
public static ServerServiceDefinition BindService(IBenchmarkService serviceImpl)
{
return ServerServiceDefinition.CreateBuilder(__ServiceName)
.AddMethod(__Method_UnaryCall, serviceImpl.UnaryCall)
.AddMethod(__Method_StreamingCall, serviceImpl.StreamingCall).Build();
}
// creates a new client
public static BenchmarkServiceClient NewClient(Channel channel)
{
return new BenchmarkServiceClient(channel);
}
}
public static class WorkerService
{
static readonly string __ServiceName = "grpc.testing.WorkerService";
static readonly Marshaller<global::Grpc.Testing.ServerArgs> __Marshaller_ServerArgs = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ServerArgs.Parser.ParseFrom);
static readonly Marshaller<global::Grpc.Testing.ServerStatus> __Marshaller_ServerStatus = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ServerStatus.Parser.ParseFrom);
static readonly Marshaller<global::Grpc.Testing.ClientArgs> __Marshaller_ClientArgs = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ClientArgs.Parser.ParseFrom);
static readonly Marshaller<global::Grpc.Testing.ClientStatus> __Marshaller_ClientStatus = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ClientStatus.Parser.ParseFrom);
static readonly Method<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> __Method_RunServer = new Method<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus>(
MethodType.DuplexStreaming,
__ServiceName,
"RunServer",
__Marshaller_ServerArgs,
__Marshaller_ServerStatus);
static readonly Method<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> __Method_RunClient = new Method<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus>(
MethodType.DuplexStreaming,
__ServiceName,
"RunClient",
__Marshaller_ClientArgs,
__Marshaller_ClientStatus);
// service descriptor
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
get { return global::Grpc.Testing.Services.Descriptor.Services[1]; }
}
// client interface
public interface IWorkerServiceClient
{
AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(CallOptions options);
AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(CallOptions options);
}
// server-side interface
public interface IWorkerService
{
Task RunServer(IAsyncStreamReader<global::Grpc.Testing.ServerArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ServerStatus> responseStream, ServerCallContext context);
Task RunClient(IAsyncStreamReader<global::Grpc.Testing.ClientArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ClientStatus> responseStream, ServerCallContext context);
}
// client stub
public class WorkerServiceClient : ClientBase, IWorkerServiceClient
{
public WorkerServiceClient(Channel channel) : base(channel)
{
}
public AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_RunServer, new CallOptions(headers, deadline, cancellationToken));
return Calls.AsyncDuplexStreamingCall(call);
}
public AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(CallOptions options)
{
var call = CreateCall(__Method_RunServer, options);
return Calls.AsyncDuplexStreamingCall(call);
}
public AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_RunClient, new CallOptions(headers, deadline, cancellationToken));
return Calls.AsyncDuplexStreamingCall(call);
}
public AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(CallOptions options)
{
var call = CreateCall(__Method_RunClient, options);
return Calls.AsyncDuplexStreamingCall(call);
}
}
// creates service definition that can be registered with a server
public static ServerServiceDefinition BindService(IWorkerService serviceImpl)
{
return ServerServiceDefinition.CreateBuilder(__ServiceName)
.AddMethod(__Method_RunServer, serviceImpl.RunServer)
.AddMethod(__Method_RunClient, serviceImpl.RunClient).Build();
}
// creates a new client
public static WorkerServiceClient NewClient(Channel channel)
{
return new WorkerServiceClient(channel);
}
}
}
#endregion

@ -1,7 +1,10 @@
<StyleCopSettings Version="105"> <StyleCopSettings Version="105">
<SourceFileList> <SourceFileList>
<SourceFile>Messages.cs</SourceFile>
<SourceFile>Empty.cs</SourceFile> <SourceFile>Empty.cs</SourceFile>
<SourceFile>Control.cs</SourceFile>
<SourceFile>Messages.cs</SourceFile>
<SourceFile>Payloads.cs</SourceFile>
<SourceFile>Stats.cs</SourceFile>
<Settings> <Settings>
<GlobalSettings> <GlobalSettings>
<BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty> <BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty>

@ -0,0 +1,744 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: test/proto/benchmarks/stats.proto
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Grpc.Testing {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial class Stats {
#region Descriptor
public static pbr::FileDescriptor Descriptor {
get { return descriptor; }
}
private static pbr::FileDescriptor descriptor;
static Stats() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CiF0ZXN0L3Byb3RvL2JlbmNobWFya3Mvc3RhdHMucHJvdG8SDGdycGMudGVz",
"dGluZyJLCgtTZXJ2ZXJTdGF0cxIUCgx0aW1lX2VsYXBzZWQYASABKAESEQoJ",
"dGltZV91c2VyGAIgASgBEhMKC3RpbWVfc3lzdGVtGAMgASgBIjsKD0hpc3Rv",
"Z3JhbVBhcmFtcxISCgpyZXNvbHV0aW9uGAEgASgBEhQKDG1heF9wb3NzaWJs",
"ZRgCIAEoASJ3Cg1IaXN0b2dyYW1EYXRhEg4KBmJ1Y2tldBgBIAMoDRIQCght",
"aW5fc2VlbhgCIAEoARIQCghtYXhfc2VlbhgDIAEoARILCgNzdW0YBCABKAES",
"FgoOc3VtX29mX3NxdWFyZXMYBSABKAESDQoFY291bnQYBiABKAEiewoLQ2xp",
"ZW50U3RhdHMSLgoJbGF0ZW5jaWVzGAEgASgLMhsuZ3JwYy50ZXN0aW5nLkhp",
"c3RvZ3JhbURhdGESFAoMdGltZV9lbGFwc2VkGAIgASgBEhEKCXRpbWVfdXNl",
"chgDIAEoARITCgt0aW1lX3N5c3RlbRgEIAEoAWIGcHJvdG8z"));
descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ServerStats), new[]{ "TimeElapsed", "TimeUser", "TimeSystem" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.HistogramParams), new[]{ "Resolution", "MaxPossible" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.HistogramData), new[]{ "Bucket", "MinSeen", "MaxSeen", "Sum", "SumOfSquares", "Count" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ClientStats), new[]{ "Latencies", "TimeElapsed", "TimeUser", "TimeSystem" }, null, null, null)
}));
}
#endregion
}
#region Messages
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class ServerStats : pb::IMessage<ServerStats> {
private static readonly pb::MessageParser<ServerStats> _parser = new pb::MessageParser<ServerStats>(() => new ServerStats());
public static pb::MessageParser<ServerStats> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Stats.Descriptor.MessageTypes[0]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public ServerStats() {
OnConstruction();
}
partial void OnConstruction();
public ServerStats(ServerStats other) : this() {
timeElapsed_ = other.timeElapsed_;
timeUser_ = other.timeUser_;
timeSystem_ = other.timeSystem_;
}
public ServerStats Clone() {
return new ServerStats(this);
}
public const int TimeElapsedFieldNumber = 1;
private double timeElapsed_;
public double TimeElapsed {
get { return timeElapsed_; }
set {
timeElapsed_ = value;
}
}
public const int TimeUserFieldNumber = 2;
private double timeUser_;
public double TimeUser {
get { return timeUser_; }
set {
timeUser_ = value;
}
}
public const int TimeSystemFieldNumber = 3;
private double timeSystem_;
public double TimeSystem {
get { return timeSystem_; }
set {
timeSystem_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as ServerStats);
}
public bool Equals(ServerStats other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (TimeElapsed != other.TimeElapsed) return false;
if (TimeUser != other.TimeUser) return false;
if (TimeSystem != other.TimeSystem) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (TimeElapsed != 0D) hash ^= TimeElapsed.GetHashCode();
if (TimeUser != 0D) hash ^= TimeUser.GetHashCode();
if (TimeSystem != 0D) hash ^= TimeSystem.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (TimeElapsed != 0D) {
output.WriteRawTag(9);
output.WriteDouble(TimeElapsed);
}
if (TimeUser != 0D) {
output.WriteRawTag(17);
output.WriteDouble(TimeUser);
}
if (TimeSystem != 0D) {
output.WriteRawTag(25);
output.WriteDouble(TimeSystem);
}
}
public int CalculateSize() {
int size = 0;
if (TimeElapsed != 0D) {
size += 1 + 8;
}
if (TimeUser != 0D) {
size += 1 + 8;
}
if (TimeSystem != 0D) {
size += 1 + 8;
}
return size;
}
public void MergeFrom(ServerStats other) {
if (other == null) {
return;
}
if (other.TimeElapsed != 0D) {
TimeElapsed = other.TimeElapsed;
}
if (other.TimeUser != 0D) {
TimeUser = other.TimeUser;
}
if (other.TimeSystem != 0D) {
TimeSystem = other.TimeSystem;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 9: {
TimeElapsed = input.ReadDouble();
break;
}
case 17: {
TimeUser = input.ReadDouble();
break;
}
case 25: {
TimeSystem = input.ReadDouble();
break;
}
}
}
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class HistogramParams : pb::IMessage<HistogramParams> {
private static readonly pb::MessageParser<HistogramParams> _parser = new pb::MessageParser<HistogramParams>(() => new HistogramParams());
public static pb::MessageParser<HistogramParams> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Stats.Descriptor.MessageTypes[1]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public HistogramParams() {
OnConstruction();
}
partial void OnConstruction();
public HistogramParams(HistogramParams other) : this() {
resolution_ = other.resolution_;
maxPossible_ = other.maxPossible_;
}
public HistogramParams Clone() {
return new HistogramParams(this);
}
public const int ResolutionFieldNumber = 1;
private double resolution_;
public double Resolution {
get { return resolution_; }
set {
resolution_ = value;
}
}
public const int MaxPossibleFieldNumber = 2;
private double maxPossible_;
public double MaxPossible {
get { return maxPossible_; }
set {
maxPossible_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as HistogramParams);
}
public bool Equals(HistogramParams other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (Resolution != other.Resolution) return false;
if (MaxPossible != other.MaxPossible) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (Resolution != 0D) hash ^= Resolution.GetHashCode();
if (MaxPossible != 0D) hash ^= MaxPossible.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (Resolution != 0D) {
output.WriteRawTag(9);
output.WriteDouble(Resolution);
}
if (MaxPossible != 0D) {
output.WriteRawTag(17);
output.WriteDouble(MaxPossible);
}
}
public int CalculateSize() {
int size = 0;
if (Resolution != 0D) {
size += 1 + 8;
}
if (MaxPossible != 0D) {
size += 1 + 8;
}
return size;
}
public void MergeFrom(HistogramParams other) {
if (other == null) {
return;
}
if (other.Resolution != 0D) {
Resolution = other.Resolution;
}
if (other.MaxPossible != 0D) {
MaxPossible = other.MaxPossible;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 9: {
Resolution = input.ReadDouble();
break;
}
case 17: {
MaxPossible = input.ReadDouble();
break;
}
}
}
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class HistogramData : pb::IMessage<HistogramData> {
private static readonly pb::MessageParser<HistogramData> _parser = new pb::MessageParser<HistogramData>(() => new HistogramData());
public static pb::MessageParser<HistogramData> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Stats.Descriptor.MessageTypes[2]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public HistogramData() {
OnConstruction();
}
partial void OnConstruction();
public HistogramData(HistogramData other) : this() {
bucket_ = other.bucket_.Clone();
minSeen_ = other.minSeen_;
maxSeen_ = other.maxSeen_;
sum_ = other.sum_;
sumOfSquares_ = other.sumOfSquares_;
count_ = other.count_;
}
public HistogramData Clone() {
return new HistogramData(this);
}
public const int BucketFieldNumber = 1;
private static readonly pb::FieldCodec<uint> _repeated_bucket_codec
= pb::FieldCodec.ForUInt32(10);
private readonly pbc::RepeatedField<uint> bucket_ = new pbc::RepeatedField<uint>();
public pbc::RepeatedField<uint> Bucket {
get { return bucket_; }
}
public const int MinSeenFieldNumber = 2;
private double minSeen_;
public double MinSeen {
get { return minSeen_; }
set {
minSeen_ = value;
}
}
public const int MaxSeenFieldNumber = 3;
private double maxSeen_;
public double MaxSeen {
get { return maxSeen_; }
set {
maxSeen_ = value;
}
}
public const int SumFieldNumber = 4;
private double sum_;
public double Sum {
get { return sum_; }
set {
sum_ = value;
}
}
public const int SumOfSquaresFieldNumber = 5;
private double sumOfSquares_;
public double SumOfSquares {
get { return sumOfSquares_; }
set {
sumOfSquares_ = value;
}
}
public const int CountFieldNumber = 6;
private double count_;
public double Count {
get { return count_; }
set {
count_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as HistogramData);
}
public bool Equals(HistogramData other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if(!bucket_.Equals(other.bucket_)) return false;
if (MinSeen != other.MinSeen) return false;
if (MaxSeen != other.MaxSeen) return false;
if (Sum != other.Sum) return false;
if (SumOfSquares != other.SumOfSquares) return false;
if (Count != other.Count) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
hash ^= bucket_.GetHashCode();
if (MinSeen != 0D) hash ^= MinSeen.GetHashCode();
if (MaxSeen != 0D) hash ^= MaxSeen.GetHashCode();
if (Sum != 0D) hash ^= Sum.GetHashCode();
if (SumOfSquares != 0D) hash ^= SumOfSquares.GetHashCode();
if (Count != 0D) hash ^= Count.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
bucket_.WriteTo(output, _repeated_bucket_codec);
if (MinSeen != 0D) {
output.WriteRawTag(17);
output.WriteDouble(MinSeen);
}
if (MaxSeen != 0D) {
output.WriteRawTag(25);
output.WriteDouble(MaxSeen);
}
if (Sum != 0D) {
output.WriteRawTag(33);
output.WriteDouble(Sum);
}
if (SumOfSquares != 0D) {
output.WriteRawTag(41);
output.WriteDouble(SumOfSquares);
}
if (Count != 0D) {
output.WriteRawTag(49);
output.WriteDouble(Count);
}
}
public int CalculateSize() {
int size = 0;
size += bucket_.CalculateSize(_repeated_bucket_codec);
if (MinSeen != 0D) {
size += 1 + 8;
}
if (MaxSeen != 0D) {
size += 1 + 8;
}
if (Sum != 0D) {
size += 1 + 8;
}
if (SumOfSquares != 0D) {
size += 1 + 8;
}
if (Count != 0D) {
size += 1 + 8;
}
return size;
}
public void MergeFrom(HistogramData other) {
if (other == null) {
return;
}
bucket_.Add(other.bucket_);
if (other.MinSeen != 0D) {
MinSeen = other.MinSeen;
}
if (other.MaxSeen != 0D) {
MaxSeen = other.MaxSeen;
}
if (other.Sum != 0D) {
Sum = other.Sum;
}
if (other.SumOfSquares != 0D) {
SumOfSquares = other.SumOfSquares;
}
if (other.Count != 0D) {
Count = other.Count;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 10:
case 8: {
bucket_.AddEntriesFrom(input, _repeated_bucket_codec);
break;
}
case 17: {
MinSeen = input.ReadDouble();
break;
}
case 25: {
MaxSeen = input.ReadDouble();
break;
}
case 33: {
Sum = input.ReadDouble();
break;
}
case 41: {
SumOfSquares = input.ReadDouble();
break;
}
case 49: {
Count = input.ReadDouble();
break;
}
}
}
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class ClientStats : pb::IMessage<ClientStats> {
private static readonly pb::MessageParser<ClientStats> _parser = new pb::MessageParser<ClientStats>(() => new ClientStats());
public static pb::MessageParser<ClientStats> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Stats.Descriptor.MessageTypes[3]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public ClientStats() {
OnConstruction();
}
partial void OnConstruction();
public ClientStats(ClientStats other) : this() {
Latencies = other.latencies_ != null ? other.Latencies.Clone() : null;
timeElapsed_ = other.timeElapsed_;
timeUser_ = other.timeUser_;
timeSystem_ = other.timeSystem_;
}
public ClientStats Clone() {
return new ClientStats(this);
}
public const int LatenciesFieldNumber = 1;
private global::Grpc.Testing.HistogramData latencies_;
public global::Grpc.Testing.HistogramData Latencies {
get { return latencies_; }
set {
latencies_ = value;
}
}
public const int TimeElapsedFieldNumber = 2;
private double timeElapsed_;
public double TimeElapsed {
get { return timeElapsed_; }
set {
timeElapsed_ = value;
}
}
public const int TimeUserFieldNumber = 3;
private double timeUser_;
public double TimeUser {
get { return timeUser_; }
set {
timeUser_ = value;
}
}
public const int TimeSystemFieldNumber = 4;
private double timeSystem_;
public double TimeSystem {
get { return timeSystem_; }
set {
timeSystem_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as ClientStats);
}
public bool Equals(ClientStats other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (!object.Equals(Latencies, other.Latencies)) return false;
if (TimeElapsed != other.TimeElapsed) return false;
if (TimeUser != other.TimeUser) return false;
if (TimeSystem != other.TimeSystem) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (latencies_ != null) hash ^= Latencies.GetHashCode();
if (TimeElapsed != 0D) hash ^= TimeElapsed.GetHashCode();
if (TimeUser != 0D) hash ^= TimeUser.GetHashCode();
if (TimeSystem != 0D) hash ^= TimeSystem.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (latencies_ != null) {
output.WriteRawTag(10);
output.WriteMessage(Latencies);
}
if (TimeElapsed != 0D) {
output.WriteRawTag(17);
output.WriteDouble(TimeElapsed);
}
if (TimeUser != 0D) {
output.WriteRawTag(25);
output.WriteDouble(TimeUser);
}
if (TimeSystem != 0D) {
output.WriteRawTag(33);
output.WriteDouble(TimeSystem);
}
}
public int CalculateSize() {
int size = 0;
if (latencies_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(Latencies);
}
if (TimeElapsed != 0D) {
size += 1 + 8;
}
if (TimeUser != 0D) {
size += 1 + 8;
}
if (TimeSystem != 0D) {
size += 1 + 8;
}
return size;
}
public void MergeFrom(ClientStats other) {
if (other == null) {
return;
}
if (other.latencies_ != null) {
if (latencies_ == null) {
latencies_ = new global::Grpc.Testing.HistogramData();
}
Latencies.MergeFrom(other.Latencies);
}
if (other.TimeElapsed != 0D) {
TimeElapsed = other.TimeElapsed;
}
if (other.TimeUser != 0D) {
TimeUser = other.TimeUser;
}
if (other.TimeSystem != 0D) {
TimeSystem = other.TimeSystem;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 10: {
if (latencies_ == null) {
latencies_ = new global::Grpc.Testing.HistogramData();
}
input.ReadMessage(latencies_);
break;
}
case 17: {
TimeElapsed = input.ReadDouble();
break;
}
case 25: {
TimeUser = input.ReadDouble();
break;
}
case 33: {
TimeSystem = input.ReadDouble();
break;
}
}
}
}
}
#endregion
}
#endregion Designer generated code

@ -0,0 +1,78 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
using NUnit.Framework;
using Grpc.Testing;
namespace Grpc.IntegrationTesting
{
/// <summary>
/// Snapshottable wall clock stopwatch.
/// </summary>
public class WallClockStopwatch
{
long startTicks;
public WallClockStopwatch()
{
this.startTicks = DateTime.UtcNow.Ticks;
}
public TimeSpan GetElapsedSnapshot(bool reset)
{
var utcNow = DateTime.UtcNow;
long oldStartTicks;
if (reset)
{
oldStartTicks = Interlocked.Exchange(ref this.startTicks, utcNow.Ticks);
}
else
{
oldStartTicks = this.startTicks;
}
return utcNow - new DateTime(oldStartTicks, DateTimeKind.Utc);
}
}
}

@ -0,0 +1,96 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
using Grpc.Core.Utils;
using Grpc.IntegrationTesting;
namespace Grpc.Testing
{
/// <summary>
/// Implementation of WorkerService server
/// </summary>
public class WorkerServiceImpl : WorkerService.IWorkerService
{
public async Task RunServer(IAsyncStreamReader<ServerArgs> requestStream, IServerStreamWriter<ServerStatus> responseStream, ServerCallContext context)
{
Grpc.Core.Utils.Preconditions.CheckState(await requestStream.MoveNext());
var serverConfig = requestStream.Current.Setup;
var runner = ServerRunners.CreateStarted(serverConfig);
await responseStream.WriteAsync(new ServerStatus
{
Stats = runner.GetStats(false),
Port = runner.BoundPort,
Cores = 0, // TODO: set number of cores
});
while (await requestStream.MoveNext())
{
var reset = requestStream.Current.Mark.Reset;
await responseStream.WriteAsync(new ServerStatus
{
Stats = runner.GetStats(reset)
});
}
await runner.StopAsync();
}
public async Task RunClient(IAsyncStreamReader<ClientArgs> requestStream, IServerStreamWriter<ClientStatus> responseStream, ServerCallContext context)
{
Grpc.Core.Utils.Preconditions.CheckState(await requestStream.MoveNext());
var clientConfig = requestStream.Current.Setup;
var runner = ClientRunners.CreateStarted(clientConfig);
await responseStream.WriteAsync(new ClientStatus
{
Stats = runner.GetStats(false)
});
while (await requestStream.MoveNext())
{
var reset = requestStream.Current.Mark.Reset;
await responseStream.WriteAsync(new ClientStatus
{
Stats = runner.GetStats(reset)
});
}
await runner.StopAsync();
}
}
}

@ -1,6 +1,6 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013 # Visual Studio 2012
VisualStudioVersion = 12.0.31101.0 VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Examples", "Grpc.Examples\Grpc.Examples.csproj", "{7DC1433E-3225-42C7-B7EA-546D56E27A4B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Examples", "Grpc.Examples\Grpc.Examples.csproj", "{7DC1433E-3225-42C7-B7EA-546D56E27A4B}"
@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck", "Grpc.He
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck.Tests", "Grpc.HealthCheck.Tests\Grpc.HealthCheck.Tests.csproj", "{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck.Tests", "Grpc.HealthCheck.Tests\Grpc.HealthCheck.Tests.csproj", "{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.QpsWorker", "Grpc.IntegrationTesting.QpsWorker\Grpc.IntegrationTesting.QpsWorker.csproj", "{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -39,72 +41,78 @@ Global
ReleaseSigned|Any CPU = ReleaseSigned|Any CPU ReleaseSigned|Any CPU = ReleaseSigned|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.Build.0 = Release|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.Build.0 = Release|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.Build.0 = Release|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.Build.0 = Release|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.Build.0 = Release|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.Build.0 = Release|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.Build.0 = Release|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.Build.0 = Release|Any CPU {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.Build.0 = Release|Any CPU
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.Build.0 = Release|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.Build.0 = Release|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.Build.0 = Release|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.Build.0 = Release|Any CPU {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.Build.0 = Release|Any CPU
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.Build.0 = Release|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.Build.0 = Release|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.Build.0 = Release|Any CPU {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.Build.0 = Release|Any CPU
{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.Build.0 = Release|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Release|Any CPU.Build.0 = Release|Any CPU
{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.Build.0 = Release|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.Build.0 = Release|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.Build.0 = Release|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.Build.0 = Debug|Any CPU {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.ActiveCfg = Release|Any CPU {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -112,6 +120,8 @@ Global
{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection

@ -35,14 +35,14 @@ cd $(dirname $0)
PROTOC=../../bins/opt/protobuf/protoc PROTOC=../../bins/opt/protobuf/protoc
PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin
EXAMPLES_DIR=Grpc.Examples EXAMPLES_DIR=Grpc.Examples
INTEROP_DIR=Grpc.IntegrationTesting TESTING_DIR=Grpc.IntegrationTesting
HEALTHCHECK_DIR=Grpc.HealthCheck HEALTHCHECK_DIR=Grpc.HealthCheck
$PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \ $PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \
-I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto -I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$INTEROP_DIR --grpc_out=$INTEROP_DIR \ $PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \
-I ../.. ../../test/proto/*.proto -I ../.. ../../test/proto/*.proto ../../test/proto/benchmarks/*.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \ $PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \
-I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto -I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto

@ -269,8 +269,8 @@ int *perform_request(servers_fixture *f, grpc_channel *client,
memset(s_valid, 0, f->num_servers * sizeof(int)); memset(s_valid, 0, f->num_servers * sizeof(int));
c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq, c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq,
"/foo", "foo.test.google.fr", gpr_inf_future(GPR_CLOCK_REALTIME), "/foo", "foo.test.google.fr",
NULL); gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
GPR_ASSERT(c); GPR_ASSERT(c);
completed_client = 0; completed_client = 0;

@ -146,7 +146,6 @@ void grpc_end2end_proxy_destroy(grpc_end2end_proxy *proxy) {
} }
static void unrefpc(proxy_call *pc, const char *reason) { static void unrefpc(proxy_call *pc, const char *reason) {
gpr_log(GPR_DEBUG, "PROXY UNREF %s", reason);
if (gpr_unref(&pc->refs)) { if (gpr_unref(&pc->refs)) {
grpc_call_destroy(pc->c2p); grpc_call_destroy(pc->c2p);
grpc_call_destroy(pc->p2s); grpc_call_destroy(pc->p2s);
@ -158,10 +157,7 @@ static void unrefpc(proxy_call *pc, const char *reason) {
} }
} }
static void refpc(proxy_call *pc, const char *reason) { static void refpc(proxy_call *pc, const char *reason) { gpr_ref(&pc->refs); }
gpr_log(GPR_DEBUG, "PROXY REF %s", reason);
gpr_ref(&pc->refs);
}
static void on_c2p_sent_initial_metadata(void *arg, int success) { static void on_c2p_sent_initial_metadata(void *arg, int success) {
proxy_call *pc = arg; proxy_call *pc = arg;

@ -37,8 +37,8 @@ import collections
import hashlib import hashlib
FixtureOptions = collections.namedtuple('FixtureOptions', 'fullstack includes_proxy dns_resolver secure platforms ci_mac') FixtureOptions = collections.namedtuple('FixtureOptions', 'fullstack includes_proxy dns_resolver secure platforms ci_mac tracing')
default_unsecure_fixture_options = FixtureOptions(True, False, True, False, ['windows', 'linux', 'mac', 'posix'], True) default_unsecure_fixture_options = FixtureOptions(True, False, True, False, ['windows', 'linux', 'mac', 'posix'], True, False)
socketpair_unsecure_fixture_options = default_unsecure_fixture_options._replace(fullstack=False, dns_resolver=False) socketpair_unsecure_fixture_options = default_unsecure_fixture_options._replace(fullstack=False, dns_resolver=False)
default_secure_fixture_options = default_unsecure_fixture_options._replace(secure=True) default_secure_fixture_options = default_unsecure_fixture_options._replace(secure=True)
uds_fixture_options = default_unsecure_fixture_options._replace(dns_resolver=False, platforms=['linux', 'mac', 'posix']) uds_fixture_options = default_unsecure_fixture_options._replace(dns_resolver=False, platforms=['linux', 'mac', 'posix'])
@ -54,7 +54,7 @@ END2END_FIXTURES = {
'h2_proxy': default_unsecure_fixture_options._replace(includes_proxy=True, ci_mac=False), 'h2_proxy': default_unsecure_fixture_options._replace(includes_proxy=True, ci_mac=False),
'h2_sockpair_1byte': socketpair_unsecure_fixture_options._replace(ci_mac=False), 'h2_sockpair_1byte': socketpair_unsecure_fixture_options._replace(ci_mac=False),
'h2_sockpair': socketpair_unsecure_fixture_options._replace(ci_mac=False), 'h2_sockpair': socketpair_unsecure_fixture_options._replace(ci_mac=False),
'h2_sockpair+trace': socketpair_unsecure_fixture_options, 'h2_sockpair+trace': socketpair_unsecure_fixture_options._replace(tracing=True),
'h2_ssl': default_secure_fixture_options, 'h2_ssl': default_secure_fixture_options,
'h2_ssl+poll': default_secure_fixture_options._replace(platforms=['linux']), 'h2_ssl+poll': default_secure_fixture_options._replace(platforms=['linux']),
'h2_ssl_proxy': default_secure_fixture_options._replace(includes_proxy=True, ci_mac=False), 'h2_ssl_proxy': default_secure_fixture_options._replace(includes_proxy=True, ci_mac=False),
@ -63,8 +63,8 @@ END2END_FIXTURES = {
'h2_uds': uds_fixture_options, 'h2_uds': uds_fixture_options,
} }
TestOptions = collections.namedtuple('TestOptions', 'needs_fullstack needs_dns proxyable flaky secure') TestOptions = collections.namedtuple('TestOptions', 'needs_fullstack needs_dns proxyable flaky secure traceable')
default_test_options = TestOptions(False, False, True, False, False) default_test_options = TestOptions(False, False, True, False, False, True)
connectivity_test_options = default_test_options._replace(needs_fullstack=True) connectivity_test_options = default_test_options._replace(needs_fullstack=True)
# maps test names to options # maps test names to options
@ -85,6 +85,7 @@ END2END_TESTS = {
'disappearing_server': connectivity_test_options, 'disappearing_server': connectivity_test_options,
'empty_batch': default_test_options, 'empty_batch': default_test_options,
'graceful_server_shutdown': default_test_options, 'graceful_server_shutdown': default_test_options,
'hpack_size': default_test_options._replace(proxyable=False, traceable=False),
'high_initial_seqno': default_test_options, 'high_initial_seqno': default_test_options,
'invoke_large_request': default_test_options, 'invoke_large_request': default_test_options,
'large_metadata': default_test_options, 'large_metadata': default_test_options,
@ -117,6 +118,9 @@ def compatible(f, t):
if not END2END_TESTS[t].proxyable: if not END2END_TESTS[t].proxyable:
if END2END_FIXTURES[f].includes_proxy: if END2END_FIXTURES[f].includes_proxy:
return False return False
if not END2END_TESTS[t].traceable:
if END2END_FIXTURES[f].tracing:
return False
return True return True

@ -166,7 +166,8 @@ static void simple_request_body(grpc_end2end_test_fixture f, size_t num_ops) {
cq_verifier_destroy(cqv); cq_verifier_destroy(cqv);
} }
static void test_invoke_simple_request(grpc_end2end_test_config config, size_t num_ops) { static void test_invoke_simple_request(grpc_end2end_test_config config,
size_t num_ops) {
grpc_end2end_test_fixture f; grpc_end2end_test_fixture f;
f = begin_test(config, "test_invoke_simple_request", NULL, NULL); f = begin_test(config, "test_invoke_simple_request", NULL, NULL);

@ -0,0 +1,446 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "test/core/end2end/end2end_tests.h"
#include <stdio.h>
#include <string.h>
#include <grpc/byte_buffer.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/time.h>
#include <grpc/support/useful.h>
#include "src/core/support/string.h"
#include "test/core/end2end/cq_verifier.h"
static void *tag(gpr_intptr t) { return (void *)t; }
const char *hobbits[][2] = {{"Adaldrida", "Brandybuck"},
{"Adamanta", "Took"},
{"Adalgrim", "Took"},
{"Adelard", "Took"},
{"Amaranth", "Brandybuck"},
{"Andwise", "Roper"},
{"Angelica", "Baggins"},
{"Asphodel", "Burrows"},
{"Balbo", "Baggins"},
{"Bandobras", "Took"},
{"Belba", "Bolger"},
{"Bell", "Gamgee"},
{"Belladonna", "Baggins"},
{"Berylla", "Baggins"},
{"Bilbo", "Baggins"},
{"Bilbo", "Gardner"},
{"Bill", "Butcher"},
{"Bingo", "Baggins"},
{"Bodo", "Proudfoot"},
{"Bowman", "Cotton"},
{"Bungo", "Baggins"},
{"Camellia", "Sackville"},
{"Carl", "Cotton"},
{"Celandine", "Brandybuck"},
{"Chica", "Baggins"},
{"Daddy", "Twofoot"},
{"Daisy", "Boffin"},
{"Diamond", "Took"},
{"Dinodas", "Brandybuck"},
{"Doderic", "Brandybuck"},
{"Dodinas", "Brandybuck"},
{"Donnamira", "Boffin"},
{"Dora", "Baggins"},
{"Drogo", "Baggins"},
{"Dudo", "Baggins"},
{"Eglantine", "Took"},
{"Elanor", "Fairbairn"},
{"Elfstan", "Fairbairn"},
{"Esmeralda", "Brandybuck"},
{"Estella", "Brandybuck"},
{"Everard", "Took"},
{"Falco", "Chubb-Baggins"},
{"Faramir", "Took"},
{"Farmer", "Maggot"},
{"Fastolph", "Bolger"},
{"Ferdibrand", "Took"},
{"Ferdinand", "Took"},
{"Ferumbras", "Took"},
{"Ferumbras", "Took"},
{"Filibert", "Bolger"},
{"Firiel", "Fairbairn"},
{"Flambard", "Took"},
{"Folco", "Boffin"},
{"Fortinbras", "Took"},
{"Fortinbras", "Took"},
{"Fosco", "Baggins"},
{"Fredegar", "Bolger"},
{"Frodo", "Baggins"},
{"Frodo", "Gardner"},
{"Gerontius", "Took"},
{"Gilly", "Baggins"},
{"Goldilocks", "Took"},
{"Gorbadoc", "Brandybuck"},
{"Gorbulas", "Brandybuck"},
{"Gorhendad", "Brandybuck"},
{"Gormadoc", "Brandybuck"},
{"Griffo", "Boffin"},
{"Halfast", "Gamgee"},
{"Halfred", "Gamgee"},
{"Halfred", "Greenhand"},
{"Hanna", "Brandybuck"},
{"Hamfast", "Gamgee"},
{"Hamfast", "Gardner"},
{"Hamson", "Gamgee"},
{"Harding", "Gardner"},
{"Hilda", "Brandybuck"},
{"Hildibrand", "Took"},
{"Hildifons", "Took"},
{"Hildigard", "Took"},
{"Hildigrim", "Took"},
{"Hob", "Gammidge"},
{"Hob", "Hayward"},
{"Hobson", "Gamgee"},
{"Holfast", "Gardner"},
{"Holman", "Cotton"},
{"Holman", "Greenhand"},
{"Hugo", "Boffin"},
{"Hugo", "Bracegirdle"},
{"Ilberic", "Brandybuck"},
{"Isembard", "Took"},
{"Isembold", "Took"},
{"Isengar", "Took"},
{"Isengrim", "Took"},
{"Isengrim", "Took"},
{"Isumbras", "Took"},
{"Isumbras", "Took"},
{"Jolly", "Cotton"},
{"Lalia", "Took"},
{"Largo", "Baggins"},
{"Laura", "Baggins"},
{"Lily", "Goodbody"},
{"Lily", "Cotton"},
{"Linda", "Proudfoot"},
{"Lobelia", "Sackville-Baggins"},
{"Longo", "Baggins"},
{"Lotho", "Sackville-Baggins"},
{"Madoc", "Brandybuck"},
{"Malva", "Brandybuck"},
{"Marigold", "Cotton"},
{"Marmadas", "Brandybuck"},
{"Marmadoc", "Brandybuck"},
{"Marroc", "Brandybuck"},
{"May", "Gamgee"},
{"Melilot", "Brandybuck"},
{"Menegilda", "Brandybuck"},
{"Mentha", "Brandybuck"},
{"Meriadoc", "Brandybuck"},
{"Merimac", "Brandybuck"},
{"Merimas", "Brandybuck"},
{"Merry", "Gardner"},
{"Milo", "Burrows"},
{"Mimosa", "Baggins"},
{"Minto", "Burrows"},
{"Mirabella", "Brandybuck"},
{"Moro", "Burrows"},
{"Mosco", "Burrows"},
{"Mungo", "Baggins"},
{"Myrtle", "Burrows"},
{"Odo", "Proudfoot"},
{"Odovacar", "Bolger"},
{"Olo", "Proudfoot"},
{"Orgulas", "Brandybuck"},
{"Otho", "Sackville-Baggins"},
{"Paladin", "Took"},
{"Pansy", "Bolger"},
{"Pearl", "Took"},
{"Peony", "Burrows"},
{"Peregrin", "Took"},
{"Pervinca", "Took"},
{"Pimpernel", "Took"},
{"Pippin", "Gardner"},
{"Polo", "Baggins"},
{"Ponto", "Baggins"},
{"Porto", "Baggins"},
{"Posco", "Baggins"},
{"Poppy", "Bolger"},
{"Primrose", "Gardner"},
{"Primula", "Baggins"},
{"Prisca", "Bolger"},
{"Reginard", "Took"},
{"Robin", "Smallburrow"},
{"Robin", "Gardner"},
{"Rorimac", "Brandybuck"},
{"Rosa", "Took"},
{"Rosamunda", "Bolger"},
{"Rose", "Gardner"},
{"Ruby", "Baggins"},
{"Ruby", "Gardner"},
{"Rudigar", "Bolger"},
{"Rufus", "Burrows"},
{"Sadoc", "Brandybuck"},
{"Salvia", "Bolger"},
{"Samwise", "Gamgee"},
{"Sancho", "Proudfoot"},
{"Saradas", "Brandybuck"},
{"Saradoc", "Brandybuck"},
{"Seredic", "Brandybuck"},
{"Sigismond", "Took"},
{"Smeagol", "Gollum"},
{"Tanta", "Baggins"},
{"Ted", "Sandyman"},
{"Tobold", "Hornblower"},
{"Togo", "Goodbody"},
{"Tolman", "Cotton"},
{"Tolman", "Gardner"},
{"Widow", "Rumble"},
{"Wilcome", "Cotton"},
{"Wilcome", "Cotton"},
{"Wilibald", "Bolger"},
{"Will", "Whitfoot"},
{"Wiseman", "Gamwich"}};
const char *dragons[] = {"Ancalagon", "Glaurung", "Scatha",
"Smaug the Magnificent"};
static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
const char *test_name,
grpc_channel_args *client_args,
grpc_channel_args *server_args) {
grpc_end2end_test_fixture f;
gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
f = config.create_fixture(client_args, server_args);
config.init_client(&f, client_args);
config.init_server(&f, server_args);
return f;
}
static gpr_timespec n_seconds_time(int n) {
return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
}
static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
static void drain_cq(grpc_completion_queue *cq) {
grpc_event ev;
do {
ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
} while (ev.type != GRPC_QUEUE_SHUTDOWN);
}
static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return;
grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(
f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
.type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server);
f->server = NULL;
}
static void shutdown_client(grpc_end2end_test_fixture *f) {
if (!f->client) return;
grpc_channel_destroy(f->client);
f->client = NULL;
}
static void end_test(grpc_end2end_test_fixture *f) {
shutdown_server(f);
shutdown_client(f);
grpc_completion_queue_shutdown(f->cq);
drain_cq(f->cq);
grpc_completion_queue_destroy(f->cq);
}
static void simple_request_body(grpc_end2end_test_fixture f, size_t index) {
grpc_call *c;
grpc_call *s;
gpr_timespec deadline = five_seconds_time();
cq_verifier *cqv = cq_verifier_create(f.cq);
grpc_op ops[6];
grpc_op *op;
grpc_metadata_array initial_metadata_recv;
grpc_metadata_array trailing_metadata_recv;
grpc_metadata_array request_metadata_recv;
grpc_call_details call_details;
grpc_status_code status;
grpc_call_error error;
grpc_metadata extra_metadata[3];
char *details = NULL;
size_t details_capacity = 0;
int was_cancelled = 2;
memset(extra_metadata, 0, sizeof(extra_metadata));
extra_metadata[0].key = "hobbit-first-name";
extra_metadata[0].value = hobbits[index % GPR_ARRAY_SIZE(hobbits)][0];
extra_metadata[0].value_length = strlen(extra_metadata[0].value);
extra_metadata[1].key = "hobbit-second-name";
extra_metadata[1].value = hobbits[index % GPR_ARRAY_SIZE(hobbits)][1];
extra_metadata[1].value_length = strlen(extra_metadata[1].value);
extra_metadata[2].key = "dragon";
extra_metadata[2].value = dragons[index % GPR_ARRAY_SIZE(dragons)];
extra_metadata[2].value_length = strlen(extra_metadata[2].value);
c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
"/foo", "foo.test.google.fr:1234", deadline,
NULL);
GPR_ASSERT(c);
grpc_metadata_array_init(&initial_metadata_recv);
grpc_metadata_array_init(&trailing_metadata_recv);
grpc_metadata_array_init(&request_metadata_recv);
grpc_call_details_init(&call_details);
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = GPR_ARRAY_SIZE(extra_metadata);
op->data.send_initial_metadata.metadata = extra_metadata;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata = &initial_metadata_recv;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
op->data.recv_status_on_client.status = &status;
op->data.recv_status_on_client.status_details = &details;
op->data.recv_status_on_client.status_details_capacity = &details_capacity;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
error =
grpc_server_request_call(f.server, &s, &call_details,
&request_metadata_recv, f.cq, f.cq, tag(101));
GPR_ASSERT(GRPC_CALL_OK == error);
cq_expect_completion(cqv, tag(101), 1);
cq_verify(cqv);
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
op->data.send_status_from_server.status_details = "xyz";
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
op->data.recv_close_on_server.cancelled = &was_cancelled;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
cq_expect_completion(cqv, tag(102), 1);
cq_expect_completion(cqv, tag(1), 1);
cq_verify(cqv);
GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
GPR_ASSERT(0 == strcmp(details, "xyz"));
GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr:1234"));
GPR_ASSERT(was_cancelled == 1);
gpr_free(details);
grpc_metadata_array_destroy(&initial_metadata_recv);
grpc_metadata_array_destroy(&trailing_metadata_recv);
grpc_metadata_array_destroy(&request_metadata_recv);
grpc_call_details_destroy(&call_details);
grpc_call_destroy(c);
grpc_call_destroy(s);
cq_verifier_destroy(cqv);
}
static void test_size(grpc_end2end_test_config config, int encode_size,
int decode_size) {
size_t i;
grpc_end2end_test_fixture f;
grpc_arg server_arg;
grpc_channel_args server_args;
grpc_arg client_arg;
grpc_channel_args client_args;
char *name;
server_arg.type = GRPC_ARG_INTEGER;
server_arg.key = GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER;
server_arg.value.integer = decode_size;
server_args.num_args = 1;
server_args.args = &server_arg;
client_arg.type = GRPC_ARG_INTEGER;
client_arg.key = GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER;
client_arg.value.integer = encode_size;
client_args.num_args = 1;
client_args.args = &client_arg;
gpr_asprintf(&name, "test_size:e=%d:d=%d", encode_size, decode_size);
f = begin_test(config, name, encode_size != 4096 ? &client_args : NULL,
decode_size != 4096 ? &server_args : NULL);
for (i = 0; i < 4 * GPR_ARRAY_SIZE(hobbits); i++) {
simple_request_body(f, i);
}
end_test(&f);
config.tear_down_data(&f);
gpr_free(name);
}
void grpc_end2end_tests(grpc_end2end_test_config config) {
static const int interesting_sizes[] = {4096, 0, 100,
1000, 32768, 4 * 1024 * 1024};
size_t i, j;
for (i = 0; i < GPR_ARRAY_SIZE(interesting_sizes); i++) {
for (j = 0; j < GPR_ARRAY_SIZE(interesting_sizes); j++) {
test_size(config, interesting_sizes[i], interesting_sizes[j]);
}
}
}

@ -163,7 +163,8 @@ static void simple_request_body(grpc_end2end_test_fixture f, size_t num_ops) {
cq_verifier_destroy(cqv); cq_verifier_destroy(cqv);
} }
static void test_invoke_simple_request(grpc_end2end_test_config config, size_t num_ops) { static void test_invoke_simple_request(grpc_end2end_test_config config,
size_t num_ops) {
grpc_end2end_test_fixture f; grpc_end2end_test_fixture f;
f = begin_test(config, "test_invoke_simple_request", NULL, NULL); f = begin_test(config, "test_invoke_simple_request", NULL, NULL);

@ -139,7 +139,7 @@ static int poll_read_bytes(int fd, char *buf, size_t read_size, int spin) {
gpr_log(GPR_ERROR, "Read failed: %s", strerror(errno)); gpr_log(GPR_ERROR, "Read failed: %s", strerror(errno));
return -1; return -1;
} }
bytes_read += (size_t) err2; bytes_read += (size_t)err2;
} while (bytes_read < read_size); } while (bytes_read < read_size);
return 0; return 0;
} }
@ -174,11 +174,11 @@ static int epoll_read_bytes(struct thread_args *args, char *buf, int spin) {
GPR_ASSERT(ev.data.fd == args->fds.read_fd); GPR_ASSERT(ev.data.fd == args->fds.read_fd);
do { do {
do { do {
err2 = read(args->fds.read_fd, buf + bytes_read, err2 =
read_size - bytes_read); read(args->fds.read_fd, buf + bytes_read, read_size - bytes_read);
} while (err2 < 0 && errno == EINTR); } while (err2 < 0 && errno == EINTR);
if (errno == EAGAIN) break; if (errno == EAGAIN) break;
bytes_read += (size_t) err2; bytes_read += (size_t)err2;
/* TODO(klempner): This should really be doing an extra call after we are /* TODO(klempner): This should really be doing an extra call after we are
done to ensure we see an EAGAIN */ done to ensure we see an EAGAIN */
} while (bytes_read < read_size); } while (bytes_read < read_size);

@ -353,8 +353,8 @@ static void test_google_iam_creds(void) {
test_google_iam_authorization_token, test_google_iam_authority_selector, test_google_iam_authorization_token, test_google_iam_authority_selector,
NULL); NULL);
grpc_call_credentials_get_request_metadata(&exec_ctx, creds, NULL, grpc_call_credentials_get_request_metadata(&exec_ctx, creds, NULL,
test_service_url, test_service_url,
check_google_iam_metadata, creds); check_google_iam_metadata, creds);
grpc_exec_ctx_finish(&exec_ctx); grpc_exec_ctx_finish(&exec_ctx);
} }
@ -436,7 +436,8 @@ static void test_oauth2_google_iam_composite_creds(void) {
test_google_iam_authorization_token, test_google_iam_authority_selector, test_google_iam_authorization_token, test_google_iam_authority_selector,
NULL); NULL);
grpc_call_credentials *composite_creds = grpc_call_credentials *composite_creds =
grpc_composite_call_credentials_create(oauth2_creds, google_iam_creds, NULL); grpc_composite_call_credentials_create(oauth2_creds, google_iam_creds,
NULL);
grpc_call_credentials_unref(oauth2_creds); grpc_call_credentials_unref(oauth2_creds);
grpc_call_credentials_unref(google_iam_creds); grpc_call_credentials_unref(google_iam_creds);
GPR_ASSERT( GPR_ASSERT(
@ -481,7 +482,8 @@ static void test_channel_oauth2_google_iam_composite_creds(void) {
grpc_call_credentials *oauth2_creds = grpc_call_credentials *oauth2_creds =
grpc_access_token_credentials_create("blah", NULL); grpc_access_token_credentials_create("blah", NULL);
grpc_channel_credentials *channel_oauth2_creds = grpc_channel_credentials *channel_oauth2_creds =
grpc_composite_channel_credentials_create(channel_creds, oauth2_creds, NULL); grpc_composite_channel_credentials_create(channel_creds, oauth2_creds,
NULL);
grpc_call_credentials *google_iam_creds = grpc_google_iam_credentials_create( grpc_call_credentials *google_iam_creds = grpc_google_iam_credentials_create(
test_google_iam_authorization_token, test_google_iam_authority_selector, test_google_iam_authorization_token, test_google_iam_authority_selector,
NULL); NULL);

@ -185,8 +185,8 @@ static void test_byte_buffer_from_reader(void) {
} }
static void test_readall(void) { static void test_readall(void) {
char* lotsa_as[512]; char *lotsa_as[512];
char* lotsa_bs[1024]; char *lotsa_bs[1024];
gpr_slice slices[2]; gpr_slice slices[2];
grpc_byte_buffer *buffer; grpc_byte_buffer *buffer;
grpc_byte_buffer_reader reader; grpc_byte_buffer_reader reader;

@ -151,7 +151,8 @@ static void test_vectors(grpc_slice_split_mode mode) {
grpc_chttp2_hpack_parser_destroy(&parser); grpc_chttp2_hpack_parser_destroy(&parser);
grpc_chttp2_hpack_parser_init(&parser, mdctx); grpc_chttp2_hpack_parser_init(&parser, mdctx);
parser.table.max_bytes = 256; grpc_chttp2_hptbl_set_max_bytes(&parser.table, 256);
grpc_chttp2_hptbl_set_current_table_size(&parser.table, 256);
/* D.5.1 */ /* D.5.1 */
test_vector(&parser, mode, test_vector(&parser, mode,
"4803 3330 3258 0770 7269 7661 7465 611d" "4803 3330 3258 0770 7269 7661 7465 611d"
@ -184,7 +185,8 @@ static void test_vectors(grpc_slice_split_mode mode) {
grpc_chttp2_hpack_parser_destroy(&parser); grpc_chttp2_hpack_parser_destroy(&parser);
grpc_chttp2_hpack_parser_init(&parser, mdctx); grpc_chttp2_hpack_parser_init(&parser, mdctx);
parser.table.max_bytes = 256; grpc_chttp2_hptbl_set_max_bytes(&parser.table, 256);
grpc_chttp2_hptbl_set_current_table_size(&parser.table, 256);
/* D.6.1 */ /* D.6.1 */
test_vector(&parser, mode, test_vector(&parser, mode,
"4882 6402 5885 aec3 771a 4b61 96d0 7abe" "4882 6402 5885 aec3 771a 4b61 96d0 7abe"

@ -143,9 +143,12 @@ 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++) {
grpc_mdelem *elem;
gpr_asprintf(&key, "K:%d", i); gpr_asprintf(&key, "K:%d", i);
gpr_asprintf(&value, "VALUE:%d", i); gpr_asprintf(&value, "VALUE:%d", i);
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, key, value)); elem = grpc_mdelem_from_strings(mdctx, key, value);
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
GRPC_MDELEM_UNREF(elem);
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(key);
gpr_free(value); gpr_free(value);
@ -173,18 +176,25 @@ static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
static void test_find(void) { static void test_find(void) {
grpc_chttp2_hptbl tbl; grpc_chttp2_hptbl tbl;
int i; gpr_uint32 i;
char buffer[32]; char buffer[32];
grpc_mdctx *mdctx; grpc_mdctx *mdctx;
grpc_mdelem *elem;
grpc_chttp2_hptbl_find_result r; grpc_chttp2_hptbl_find_result r;
LOG_TEST("test_find"); LOG_TEST("test_find");
mdctx = grpc_mdctx_create(); mdctx = grpc_mdctx_create();
grpc_chttp2_hptbl_init(&tbl, mdctx); grpc_chttp2_hptbl_init(&tbl, mdctx);
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "xyz")); elem = grpc_mdelem_from_strings(mdctx, "abc", "xyz");
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "123")); GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "x", "1")); GRPC_MDELEM_UNREF(elem);
elem = grpc_mdelem_from_strings(mdctx, "abc", "123");
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
GRPC_MDELEM_UNREF(elem);
elem = grpc_mdelem_from_strings(mdctx, "x", "1");
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
GRPC_MDELEM_UNREF(elem);
r = find_simple(&tbl, "abc", "123"); r = find_simple(&tbl, "abc", "123");
GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY); GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
@ -233,8 +243,9 @@ 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++) {
gpr_ltoa(i, buffer); gpr_ltoa(i, buffer);
grpc_chttp2_hptbl_add(&tbl, elem = grpc_mdelem_from_strings(mdctx, "test", buffer);
grpc_mdelem_from_strings(mdctx, "test", buffer)); GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
GRPC_MDELEM_UNREF(elem);
} }
r = find_simple(&tbl, "abc", "123"); r = find_simple(&tbl, "abc", "123");
@ -250,7 +261,7 @@ static void test_find(void) {
GPR_ASSERT(r.has_value == 1); GPR_ASSERT(r.has_value == 1);
for (i = 0; i < tbl.num_ents; i++) { for (i = 0; i < tbl.num_ents; i++) {
int expect = 9999 - i; gpr_uint32 expect = 9999 - i;
gpr_ltoa(expect, buffer); gpr_ltoa(expect, buffer);
r = find_simple(&tbl, "test", buffer); r = find_simple(&tbl, "test", buffer);

@ -57,6 +57,7 @@ void test_tcp_server_init(test_tcp_server *server,
void test_tcp_server_start(test_tcp_server *server, int port) { void test_tcp_server_start(test_tcp_server *server, int port) {
struct sockaddr_in addr; struct sockaddr_in addr;
grpc_tcp_listener *listener;
int port_added; int port_added;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
@ -65,8 +66,8 @@ void test_tcp_server_start(test_tcp_server *server, int port) {
memset(&addr.sin_addr, 0, sizeof(addr.sin_addr)); memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
server->tcp_server = grpc_tcp_server_create(); server->tcp_server = grpc_tcp_server_create();
port_added = listener = grpc_tcp_server_add_port(server->tcp_server, &addr, sizeof(addr));
grpc_tcp_server_add_port(server->tcp_server, &addr, sizeof(addr)); port_added = grpc_tcp_listener_get_port(listener);
GPR_ASSERT(port_added == port); GPR_ASSERT(port_added == port);
grpc_tcp_server_start(&exec_ctx, server->tcp_server, server->pollsets, 1, grpc_tcp_server_start(&exec_ctx, server->tcp_server, server->pollsets, 1,

@ -0,0 +1,103 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*is % allowed in string
*/
#include <memory>
#include <string>
#include <gflags/gflags.h>
#include <grpc++/grpc++.h>
#include "test/cpp/util/metrics_server.h"
#include "test/cpp/util/test_config.h"
#include "test/proto/metrics.grpc.pb.h"
#include "test/proto/metrics.pb.h"
DEFINE_string(metrics_server_address, "",
"The metrics server addresses in the fomrat <hostname>:<port>");
using grpc::testing::EmptyMessage;
using grpc::testing::GaugeResponse;
using grpc::testing::MetricsService;
using grpc::testing::MetricsServiceImpl;
void PrintMetrics(grpc::string& server_address) {
gpr_log(GPR_INFO, "creating a channel to %s", server_address.c_str());
std::shared_ptr<grpc::Channel> channel(
grpc::CreateChannel(server_address, grpc::InsecureChannelCredentials()));
std::unique_ptr<MetricsService::Stub> stub(MetricsService::NewStub(channel));
grpc::ClientContext context;
EmptyMessage message;
std::unique_ptr<grpc::ClientReader<GaugeResponse>> reader(
stub->GetAllGauges(&context, message));
GaugeResponse gauge_response;
long overall_qps = 0;
int idx = 0;
while (reader->Read(&gauge_response)) {
if (gauge_response.value_case() == GaugeResponse::kLongValue) {
gpr_log(GPR_INFO, "Gauge: %d (%s: %ld)", ++idx,
gauge_response.name().c_str(), gauge_response.long_value());
overall_qps += gauge_response.long_value();
} else {
gpr_log(GPR_INFO, "Gauge %s is not a long value",
gauge_response.name().c_str());
}
}
gpr_log(GPR_INFO, "OVERALL: %ld", overall_qps);
const grpc::Status status = reader->Finish();
if (!status.ok()) {
gpr_log(GPR_ERROR, "Error in getting metrics from the client");
}
}
int main(int argc, char** argv) {
grpc::testing::InitTest(&argc, &argv, true);
// Make sure server_addresses flag is not empty
if (FLAGS_metrics_server_address.empty()) {
gpr_log(
GPR_ERROR,
"Cannot connect to the Metrics server. Please pass the address of the"
"metrics server to connect to via the 'metrics_server_address' flag");
return 1;
}
PrintMetrics(FLAGS_metrics_server_address);
return 0;
}

@ -40,6 +40,7 @@
#include <grpc++/create_channel.h> #include <grpc++/create_channel.h>
#include "test/cpp/interop/interop_client.h" #include "test/cpp/interop/interop_client.h"
#include "test/cpp/util/metrics_server.h"
namespace grpc { namespace grpc {
namespace testing { namespace testing {
@ -81,21 +82,19 @@ TestCaseType WeightedRandomTestSelector::GetNextTest() const {
StressTestInteropClient::StressTestInteropClient( StressTestInteropClient::StressTestInteropClient(
int test_id, const grpc::string& server_address, int test_id, const grpc::string& server_address,
std::shared_ptr<Channel> channel,
const WeightedRandomTestSelector& test_selector, long test_duration_secs, const WeightedRandomTestSelector& test_selector, long test_duration_secs,
long sleep_duration_ms) long sleep_duration_ms, long metrics_collection_interval_secs)
: test_id_(test_id), : test_id_(test_id),
server_address_(server_address), server_address_(server_address),
channel_(channel),
interop_client_(new InteropClient(channel, false)),
test_selector_(test_selector), test_selector_(test_selector),
test_duration_secs_(test_duration_secs), test_duration_secs_(test_duration_secs),
sleep_duration_ms_(sleep_duration_ms) { sleep_duration_ms_(sleep_duration_ms),
// TODO(sreek): This will change once we add support for other tests metrics_collection_interval_secs_(metrics_collection_interval_secs) {}
// that won't work with InsecureChannelCredentials()
std::shared_ptr<Channel> channel(
CreateChannel(server_address, InsecureChannelCredentials()));
interop_client_.reset(new InteropClient(channel, false));
}
void StressTestInteropClient::MainLoop() { void StressTestInteropClient::MainLoop(std::shared_ptr<Gauge> qps_gauge) {
gpr_log(GPR_INFO, "Running test %d. ServerAddr: %s", test_id_, gpr_log(GPR_INFO, "Running test %d. ServerAddr: %s", test_id_,
server_address_.c_str()); server_address_.c_str());
@ -104,21 +103,38 @@ void StressTestInteropClient::MainLoop() {
gpr_time_from_seconds(test_duration_secs_, GPR_TIMESPAN)); gpr_time_from_seconds(test_duration_secs_, GPR_TIMESPAN));
gpr_timespec current_time = gpr_now(GPR_CLOCK_REALTIME); gpr_timespec current_time = gpr_now(GPR_CLOCK_REALTIME);
gpr_timespec next_stat_collection_time = current_time;
gpr_timespec collection_interval =
gpr_time_from_seconds(metrics_collection_interval_secs_, GPR_TIMESPAN);
long num_calls_per_interval = 0;
while (test_duration_secs_ < 0 || while (test_duration_secs_ < 0 ||
gpr_time_cmp(current_time, test_end_time) < 0) { gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), test_end_time) < 0) {
// Select the test case to execute based on the weights and execute it // Select the test case to execute based on the weights and execute it
TestCaseType test_case = test_selector_.GetNextTest(); TestCaseType test_case = test_selector_.GetNextTest();
gpr_log(GPR_INFO, "%d - Executing the test case %d", test_id_, test_case); gpr_log(GPR_INFO, "%d - Executing the test case %d", test_id_, test_case);
RunTest(test_case); RunTest(test_case);
num_calls_per_interval++;
// See if its time to collect stats yet
current_time = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(next_stat_collection_time, current_time) < 0) {
qps_gauge->Set(num_calls_per_interval /
metrics_collection_interval_secs_);
num_calls_per_interval = 0;
next_stat_collection_time =
gpr_time_add(current_time, collection_interval);
}
// Sleep between successive calls if needed // Sleep between successive calls if needed
if (sleep_duration_ms_ > 0) { if (sleep_duration_ms_ > 0) {
gpr_timespec sleep_time = gpr_time_add( gpr_timespec sleep_time =
current_time, gpr_time_from_millis(sleep_duration_ms_, GPR_TIMESPAN)); gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_millis(sleep_duration_ms_, GPR_TIMESPAN));
gpr_sleep_until(sleep_time); gpr_sleep_until(sleep_time);
} }
current_time = gpr_now(GPR_CLOCK_REALTIME);
} }
} }

@ -41,6 +41,7 @@
#include <grpc++/create_channel.h> #include <grpc++/create_channel.h>
#include "test/cpp/interop/interop_client.h" #include "test/cpp/interop/interop_client.h"
#include "test/cpp/util/metrics_server.h"
namespace grpc { namespace grpc {
namespace testing { namespace testing {
@ -84,20 +85,26 @@ class WeightedRandomTestSelector {
class StressTestInteropClient { class StressTestInteropClient {
public: public:
StressTestInteropClient(int test_id, const grpc::string& server_address, StressTestInteropClient(int test_id, const grpc::string& server_address,
std::shared_ptr<Channel> channel,
const WeightedRandomTestSelector& test_selector, const WeightedRandomTestSelector& test_selector,
long test_duration_secs, long sleep_duration_ms); long test_duration_secs, long sleep_duration_ms,
long metrics_collection_interval_secs);
void MainLoop(); // The main function. Use this as the thread entry point. // The main function. Use this as the thread entry point.
// qps_gauge is the Gauge to record the requests per second metric
void MainLoop(std::shared_ptr<Gauge> qps_gauge);
private: private:
void RunTest(TestCaseType test_case); void RunTest(TestCaseType test_case);
int test_id_; int test_id_;
std::unique_ptr<InteropClient> interop_client_;
const grpc::string& server_address_; const grpc::string& server_address_;
std::shared_ptr<Channel> channel_;
std::unique_ptr<InteropClient> interop_client_;
const WeightedRandomTestSelector& test_selector_; const WeightedRandomTestSelector& test_selector_;
long test_duration_secs_; long test_duration_secs_;
long sleep_duration_ms_; long sleep_duration_ms_;
long metrics_collection_interval_secs_;
}; };
} // namespace testing } // namespace testing

@ -45,7 +45,15 @@
#include "test/cpp/interop/interop_client.h" #include "test/cpp/interop/interop_client.h"
#include "test/cpp/interop/stress_interop_client.h" #include "test/cpp/interop/stress_interop_client.h"
#include "test/cpp/util/metrics_server.h"
#include "test/cpp/util/test_config.h" #include "test/cpp/util/test_config.h"
#include "test/proto/metrics.grpc.pb.h"
#include "test/proto/metrics.pb.h"
DEFINE_int32(metrics_port, 8081, "The metrics server port.");
DEFINE_int32(metrics_collection_interval_secs, 5,
"How often (in seconds) should metrics be recorded.");
DEFINE_int32(sleep_duration_ms, 0, DEFINE_int32(sleep_duration_ms, 0,
"The duration (in millisec) between two" "The duration (in millisec) between two"
@ -62,6 +70,11 @@ DEFINE_string(server_addresses, "localhost:8080",
" \"<name_1>:<port_1>,<name_2>:<port_1>...<name_N>:<port_N>\"\n" " \"<name_1>:<port_1>,<name_2>:<port_1>...<name_N>:<port_N>\"\n"
" Note: <name> can be servername or IP address."); " Note: <name> can be servername or IP address.");
DEFINE_int32(num_stubs_per_channel, 1,
"Number of stubs per each channels to server. This number also "
"indicates the max number of parallel RPC calls on each channel "
"at any given time.");
// TODO(sreek): Add more test cases here in future // TODO(sreek): Add more test cases here in future
DEFINE_string(test_cases, "", DEFINE_string(test_cases, "",
"List of test cases to call along with the" "List of test cases to call along with the"
@ -79,15 +92,13 @@ DEFINE_string(test_cases, "",
" 'large_unary', 10% of the time and 'empty_stream' the remaining" " 'large_unary', 10% of the time and 'empty_stream' the remaining"
" 70% of the time"); " 70% of the time");
using std::make_pair;
using std::pair;
using std::vector;
using grpc::testing::kTestCaseList; using grpc::testing::kTestCaseList;
using grpc::testing::MetricsService;
using grpc::testing::MetricsServiceImpl;
using grpc::testing::StressTestInteropClient; using grpc::testing::StressTestInteropClient;
using grpc::testing::TestCaseType; using grpc::testing::TestCaseType;
using grpc::testing::WeightedRandomTestSelector;
using grpc::testing::UNKNOWN_TEST; using grpc::testing::UNKNOWN_TEST;
using grpc::testing::WeightedRandomTestSelector;
TestCaseType GetTestTypeFromName(const grpc::string& test_name) { TestCaseType GetTestTypeFromName(const grpc::string& test_name) {
TestCaseType test_case = UNKNOWN_TEST; TestCaseType test_case = UNKNOWN_TEST;
@ -104,7 +115,7 @@ TestCaseType GetTestTypeFromName(const grpc::string& test_name) {
// Converts a string of comma delimited tokens to a vector of tokens // Converts a string of comma delimited tokens to a vector of tokens
bool ParseCommaDelimitedString(const grpc::string& comma_delimited_str, bool ParseCommaDelimitedString(const grpc::string& comma_delimited_str,
vector<grpc::string>& tokens) { std::vector<grpc::string>& tokens) {
size_t bpos = 0; size_t bpos = 0;
size_t epos = grpc::string::npos; size_t epos = grpc::string::npos;
@ -122,10 +133,10 @@ bool ParseCommaDelimitedString(const grpc::string& comma_delimited_str,
// - Whether parsing was successful (return value) // - Whether parsing was successful (return value)
// - Vector of (test_type_enum, weight) pairs returned via 'tests' parameter // - Vector of (test_type_enum, weight) pairs returned via 'tests' parameter
bool ParseTestCasesString(const grpc::string& test_cases, bool ParseTestCasesString(const grpc::string& test_cases,
vector<pair<TestCaseType, int>>& tests) { std::vector<std::pair<TestCaseType, int>>& tests) {
bool is_success = true; bool is_success = true;
vector<grpc::string> tokens; std::vector<grpc::string> tokens;
ParseCommaDelimitedString(test_cases, tokens); ParseCommaDelimitedString(test_cases, tokens);
for (auto it = tokens.begin(); it != tokens.end(); it++) { for (auto it = tokens.begin(); it != tokens.end(); it++) {
@ -153,8 +164,8 @@ bool ParseTestCasesString(const grpc::string& test_cases,
} }
// For debugging purposes // For debugging purposes
void LogParameterInfo(const vector<grpc::string>& addresses, void LogParameterInfo(const std::vector<grpc::string>& addresses,
const vector<pair<TestCaseType, int>>& tests) { const std::vector<std::pair<TestCaseType, int>>& tests) {
gpr_log(GPR_INFO, "server_addresses: %s", FLAGS_server_addresses.c_str()); gpr_log(GPR_INFO, "server_addresses: %s", FLAGS_server_addresses.c_str());
gpr_log(GPR_INFO, "test_cases : %s", FLAGS_test_cases.c_str()); gpr_log(GPR_INFO, "test_cases : %s", FLAGS_test_cases.c_str());
gpr_log(GPR_INFO, "sleep_duration_ms: %d", FLAGS_sleep_duration_ms); gpr_log(GPR_INFO, "sleep_duration_ms: %d", FLAGS_sleep_duration_ms);
@ -180,7 +191,7 @@ int main(int argc, char** argv) {
srand(time(NULL)); srand(time(NULL));
// Parse the server addresses // Parse the server addresses
vector<grpc::string> server_addresses; std::vector<grpc::string> server_addresses;
ParseCommaDelimitedString(FLAGS_server_addresses, server_addresses); ParseCommaDelimitedString(FLAGS_server_addresses, server_addresses);
// Parse test cases and weights // Parse test cases and weights
@ -189,7 +200,7 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
vector<pair<TestCaseType, int>> tests; std::vector<std::pair<TestCaseType, int>> tests;
if (!ParseTestCasesString(FLAGS_test_cases, tests)) { if (!ParseTestCasesString(FLAGS_test_cases, tests)) {
gpr_log(GPR_ERROR, "Error in parsing test cases string %s ", gpr_log(GPR_ERROR, "Error in parsing test cases string %s ",
FLAGS_test_cases.c_str()); FLAGS_test_cases.c_str());
@ -199,23 +210,48 @@ int main(int argc, char** argv) {
LogParameterInfo(server_addresses, tests); LogParameterInfo(server_addresses, tests);
WeightedRandomTestSelector test_selector(tests); WeightedRandomTestSelector test_selector(tests);
MetricsServiceImpl metrics_service;
gpr_log(GPR_INFO, "Starting test(s).."); gpr_log(GPR_INFO, "Starting test(s)..");
vector<grpc::thread> test_threads; std::vector<grpc::thread> test_threads;
int thread_idx = 0; int thread_idx = 0;
for (auto it = server_addresses.begin(); it != server_addresses.end(); it++) { for (auto it = server_addresses.begin(); it != server_addresses.end(); it++) {
StressTestInteropClient* client = new StressTestInteropClient( // TODO(sreek): This will change once we add support for other tests
++thread_idx, *it, test_selector, FLAGS_test_duration_secs, // that won't work with InsecureChannelCredentials()
FLAGS_sleep_duration_ms); std::shared_ptr<grpc::Channel> channel(
grpc::CreateChannel(*it, grpc::InsecureChannelCredentials()));
test_threads.emplace_back(
grpc::thread(&StressTestInteropClient::MainLoop, client)); // Make multiple stubs (as defined by num_stubs_per_channel flag) to use the
// same channel. This is to test calling multiple RPC calls in parallel on
// each channel.
for (int i = 0; i < FLAGS_num_stubs_per_channel; i++) {
StressTestInteropClient* client = new StressTestInteropClient(
++thread_idx, *it, channel, test_selector, FLAGS_test_duration_secs,
FLAGS_sleep_duration_ms, FLAGS_metrics_collection_interval_secs);
bool is_already_created;
grpc::string metricName =
"/stress_test/qps/thread/" + std::to_string(thread_idx);
test_threads.emplace_back(grpc::thread(
&StressTestInteropClient::MainLoop, client,
metrics_service.CreateGauge(metricName, &is_already_created)));
// The Gauge should not have been already created
GPR_ASSERT(!is_already_created);
}
} }
// Start metrics server before waiting for the stress test threads
std::unique_ptr<grpc::Server> metrics_server =
metrics_service.StartServer(FLAGS_metrics_port);
// Wait for the stress test threads to complete
for (auto it = test_threads.begin(); it != test_threads.end(); it++) { for (auto it = test_threads.begin(); it != test_threads.end(); it++) {
it->join(); it->join();
} }
metrics_server->Wait();
return 0; return 0;
} }

@ -58,6 +58,7 @@ static void RunAsyncStreamingPingPong() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER); server_config.set_server_type(ASYNC_SERVER);
server_config.set_host("localhost");
server_config.set_async_server_threads(1); server_config.set_async_server_threads(1);
const auto result = const auto result =

@ -58,6 +58,7 @@ static void RunAsyncUnaryPingPong() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER); server_config.set_server_type(ASYNC_SERVER);
server_config.set_host("localhost");
server_config.set_async_server_threads(1); server_config.set_async_server_threads(1);
const auto result = const auto result =

@ -181,29 +181,29 @@ class Client {
std::unique_ptr<RandomDist> random_dist; std::unique_ptr<RandomDist> random_dist;
switch (load.load_case()) { switch (load.load_case()) {
case LoadParams::kClosedLoop: case LoadParams::kClosedLoop:
// Closed-loop doesn't use random dist at all // Closed-loop doesn't use random dist at all
break; break;
case LoadParams::kPoisson: case LoadParams::kPoisson:
random_dist.reset( random_dist.reset(
new ExpDist(load.poisson().offered_load() / num_threads)); new ExpDist(load.poisson().offered_load() / num_threads));
break; break;
case LoadParams::kUniform: case LoadParams::kUniform:
random_dist.reset( random_dist.reset(
new UniformDist(load.uniform().interarrival_lo() * num_threads, new UniformDist(load.uniform().interarrival_lo() * num_threads,
load.uniform().interarrival_hi() * num_threads)); load.uniform().interarrival_hi() * num_threads));
break; break;
case LoadParams::kDeterm: case LoadParams::kDeterm:
random_dist.reset( random_dist.reset(
new DetDist(num_threads / load.determ().offered_load())); new DetDist(num_threads / load.determ().offered_load()));
break; break;
case LoadParams::kPareto: case LoadParams::kPareto:
random_dist.reset( random_dist.reset(
new ParetoDist(load.pareto().interarrival_base() * num_threads, new ParetoDist(load.pareto().interarrival_base() * num_threads,
load.pareto().alpha())); load.pareto().alpha()));
break; break;
default: default:
GPR_ASSERT(false); GPR_ASSERT(false);
} }
// Set closed_loop_ based on whether or not random_dist is set // Set closed_loop_ based on whether or not random_dist is set

@ -110,7 +110,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
list<ClientContext> contexts; list<ClientContext> contexts;
// To be added to the result, containing the final configuration used for // To be added to the result, containing the final configuration used for
// client and config (incluiding host, etc.) // client and config (including host, etc.)
ClientConfig result_client_config; ClientConfig result_client_config;
ServerConfig result_server_config; ServerConfig result_server_config;

@ -42,7 +42,10 @@ namespace testing {
class Histogram { class Histogram {
public: public:
Histogram() : impl_(gpr_histogram_create(0.01, 60e9)) {} // TODO: look into making histogram params not hardcoded for C++
Histogram()
: impl_(gpr_histogram_create(default_resolution(),
default_max_possible())) {}
~Histogram() { ~Histogram() {
if (impl_) gpr_histogram_destroy(impl_); if (impl_) gpr_histogram_destroy(impl_);
} }
@ -73,6 +76,9 @@ class Histogram {
p.sum_of_squares(), p.count()); p.sum_of_squares(), p.count());
} }
static double default_resolution() { return 0.01; }
static double default_max_possible() { return 60e9; }
private: private:
Histogram(const Histogram&); Histogram(const Histogram&);
Histogram& operator=(const Histogram&); Histogram& operator=(const Histogram&);

@ -137,8 +137,14 @@ static void QpsDriver() {
// No further load parameters to set up for closed loop // No further load parameters to set up for closed loop
} }
client_config.mutable_histogram_params()->set_resolution(
Histogram::default_resolution());
client_config.mutable_histogram_params()->set_max_possible(
Histogram::default_max_possible());
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(server_type); server_config.set_server_type(server_type);
server_config.set_host("localhost");
server_config.set_async_server_threads(FLAGS_async_server_threads); server_config.set_async_server_threads(FLAGS_async_server_threads);
if (FLAGS_secure_test) { if (FLAGS_secure_test) {

@ -59,6 +59,7 @@ static void RunQPS() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER); server_config.set_server_type(ASYNC_SERVER);
server_config.set_host("localhost");
server_config.set_async_server_threads(4); server_config.set_async_server_threads(4);
const auto result = const auto result =

@ -58,6 +58,7 @@ static void RunQPS() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER); server_config.set_server_type(ASYNC_SERVER);
server_config.set_host("localhost");
server_config.set_async_server_threads(8); server_config.set_async_server_threads(8);
const auto result = const auto result =

@ -62,6 +62,7 @@ static void RunQPS() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER); server_config.set_server_type(ASYNC_SERVER);
server_config.set_host("localhost");
server_config.set_async_server_threads(4); server_config.set_async_server_threads(4);
const auto result = const auto result =

@ -57,6 +57,7 @@ static void RunSynchronousUnaryPingPong() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(SYNC_SERVER); server_config.set_server_type(SYNC_SERVER);
server_config.set_host("localhost");
// Set up security params // Set up security params
SecurityParams security; SecurityParams security;

@ -60,7 +60,7 @@ class AsyncQpsServerTest : public Server {
explicit AsyncQpsServerTest(const ServerConfig &config) : Server(config) { explicit AsyncQpsServerTest(const ServerConfig &config) : Server(config) {
char *server_address = NULL; char *server_address = NULL;
gpr_join_host_port(&server_address, "::", port()); gpr_join_host_port(&server_address, config.host().c_str(), port());
ServerBuilder builder; ServerBuilder builder;
builder.AddListeningPort(server_address, builder.AddListeningPort(server_address,

@ -89,7 +89,7 @@ class SynchronousServer GRPC_FINAL : public grpc::testing::Server {
char* server_address = NULL; char* server_address = NULL;
gpr_join_host_port(&server_address, "::", port()); gpr_join_host_port(&server_address, config.host().c_str(), port());
builder.AddListeningPort(server_address, builder.AddListeningPort(server_address,
Server::CreateServerCredentials(config)); Server::CreateServerCredentials(config));
gpr_free(server_address); gpr_free(server_address);

@ -57,6 +57,7 @@ static void RunSynchronousStreamingPingPong() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(SYNC_SERVER); server_config.set_server_type(SYNC_SERVER);
server_config.set_host("localhost");
const auto result = const auto result =
RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2); RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);

@ -57,6 +57,7 @@ static void RunSynchronousUnaryPingPong() {
ServerConfig server_config; ServerConfig server_config;
server_config.set_server_type(SYNC_SERVER); server_config.set_server_type(SYNC_SERVER);
server_config.set_host("localhost");
const auto result = const auto result =
RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2); RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);

@ -0,0 +1,119 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*is % allowed in string
*/
#include "test/cpp/util/metrics_server.h"
#include <grpc++/server_builder.h>
#include "test/proto/metrics.grpc.pb.h"
#include "test/proto/metrics.pb.h"
namespace grpc {
namespace testing {
Gauge::Gauge(long initial_val) : val_(initial_val) {}
void Gauge::Set(long new_val) {
std::lock_guard<std::mutex> lock(val_mu_);
val_ = new_val;
}
long Gauge::Get() {
std::lock_guard<std::mutex> lock(val_mu_);
return val_;
}
grpc::Status MetricsServiceImpl::GetAllGauges(
ServerContext* context, const EmptyMessage* request,
ServerWriter<GaugeResponse>* writer) {
gpr_log(GPR_INFO, "GetAllGauges called");
std::lock_guard<std::mutex> lock(mu_);
for (auto it = gauges_.begin(); it != gauges_.end(); it++) {
GaugeResponse resp;
resp.set_name(it->first); // Gauge name
resp.set_long_value(it->second->Get()); // Gauge value
writer->Write(resp);
}
return Status::OK;
}
grpc::Status MetricsServiceImpl::GetGauge(ServerContext* context,
const GaugeRequest* request,
GaugeResponse* response) {
std::lock_guard<std::mutex> lock(mu_);
const auto it = gauges_.find(request->name());
if (it != gauges_.end()) {
response->set_name(it->first);
response->set_long_value(it->second->Get());
}
return Status::OK;
}
std::shared_ptr<Gauge> MetricsServiceImpl::CreateGauge(const grpc::string& name,
bool* already_present) {
std::lock_guard<std::mutex> lock(mu_);
std::shared_ptr<Gauge> gauge(new Gauge(0));
const auto p = gauges_.emplace(name, gauge);
// p.first is an iterator pointing to <name, shared_ptr<Gauge>> pair. p.second
// is a boolean which is set to 'true' if the Gauge is inserted in the guages_
// map and 'false' if it is already present in the map
*already_present = !p.second;
return p.first->second;
}
// Starts the metrics server and returns the grpc::Server instance. Call Wait()
// on the returned server instance.
std::unique_ptr<grpc::Server> MetricsServiceImpl::StartServer(int port) {
gpr_log(GPR_INFO, "Building metrics server..");
const grpc::string address = "0.0.0.0:" + std::to_string(port);
ServerBuilder builder;
builder.AddListeningPort(address, grpc::InsecureServerCredentials());
builder.RegisterService(this);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
gpr_log(GPR_INFO, "Metrics server %s started. Ready to receive requests..",
address.c_str());
return server;
}
} // namespace testing
} // namespace grpc

@ -0,0 +1,100 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*is % allowed in string
*/
#ifndef GRPC_TEST_CPP_METRICS_SERVER_H
#define GRPC_TEST_CPP_METRICS_SERVER_H
#include <map>
#include <mutex>
#include "test/proto/metrics.grpc.pb.h"
#include "test/proto/metrics.pb.h"
/*
* This implements a Metrics server defined in test/proto/metrics.proto. Any
* test service can use this to export Metrics (TODO (sreek): Only Gauges for
* now).
*
* Example:
* MetricsServiceImpl metricsImpl;
* ..
* // Create Gauge(s). Note: Gauges can be created even after calling
* // 'StartServer'.
* Gauge gauge1 = metricsImpl.CreateGauge("foo",is_present);
* // gauge1 can now be used anywhere in the program to set values.
* ...
* // Create the metrics server
* std::unique_ptr<grpc::Server> server = metricsImpl.StartServer(port);
* server->Wait(); // Note: This is blocking.
*/
namespace grpc {
namespace testing {
// TODO(sreek): Add support for other types of Gauges like Double, String in
// future
class Gauge {
public:
Gauge(long initial_val);
void Set(long new_val);
long Get();
private:
long val_;
std::mutex val_mu_;
};
class MetricsServiceImpl GRPC_FINAL : public MetricsService::Service {
public:
grpc::Status GetAllGauges(ServerContext* context, const EmptyMessage* request,
ServerWriter<GaugeResponse>* writer) GRPC_OVERRIDE;
grpc::Status GetGauge(ServerContext* context, const GaugeRequest* request,
GaugeResponse* response) GRPC_OVERRIDE;
// Create a Gauge with name 'name'. is_present is set to true if the Gauge
// is already present in the map.
// NOTE: CreateGauge can be called anytime (i.e before or after calling
// StartServer).
std::shared_ptr<Gauge> CreateGauge(const grpc::string& name,
bool* already_present);
std::unique_ptr<grpc::Server> StartServer(int port);
private:
std::map<string, std::shared_ptr<Gauge>> gauges_;
std::mutex mu_;
};
} // namespace testing
} // namespace grpc
#endif // GRPC_TEST_CPP_METRICS_SERVER_H

@ -49,7 +49,10 @@ enum RpcType {
STREAMING = 1; STREAMING = 1;
} }
// Parameters of poisson process distribution, which is a good representation
// of activity coming in from independent identical stationary sources.
message PoissonParams { message PoissonParams {
// The rate of arrivals (a.k.a. lambda parameter of the exp distribution).
double offered_load = 1; double offered_load = 1;
} }
@ -67,6 +70,8 @@ message ParetoParams {
double alpha = 2; double alpha = 2;
} }
// Once an RPC finishes, immediately start a new one.
// No configuration parameters needed.
message ClosedLoopParams { message ClosedLoopParams {
} }
@ -87,16 +92,23 @@ message SecurityParams {
} }
message ClientConfig { message ClientConfig {
// List of targets to connect to. At least one target needs to be specified.
repeated string server_targets = 1; repeated string server_targets = 1;
ClientType client_type = 2; ClientType client_type = 2;
SecurityParams security_params = 3; SecurityParams security_params = 3;
// How many concurrent RPCs to start for each channel.
// For synchronous client, use a separate thread for each outstanding RPC.
int32 outstanding_rpcs_per_channel = 4; int32 outstanding_rpcs_per_channel = 4;
// Number of independent client channels to create.
// i-th channel will connect to server_target[i % server_targets.size()]
int32 client_channels = 5; int32 client_channels = 5;
// only for async client: // Only for async client. Number of threads to use to start/manage RPCs.
int32 async_client_threads = 7; int32 async_client_threads = 7;
RpcType rpc_type = 8; RpcType rpc_type = 8;
// The requested load for the entire client (aggregated over all the threads).
LoadParams load_params = 10; LoadParams load_params = 10;
PayloadConfig payload_config = 11; PayloadConfig payload_config = 11;
HistogramParams histogram_params = 12;
} }
message ClientStatus { message ClientStatus {
@ -105,6 +117,7 @@ message ClientStatus {
// Request current stats // Request current stats
message Mark { message Mark {
// if true, the stats will be reset after taking their snapshot.
bool reset = 1; bool reset = 1;
} }
@ -118,10 +131,13 @@ message ClientArgs {
message ServerConfig { message ServerConfig {
ServerType server_type = 1; ServerType server_type = 1;
SecurityParams security_params = 2; SecurityParams security_params = 2;
// Host on which to listen.
string host = 3;
// Port on which to listen. Zero means pick unused port.
int32 port = 4; int32 port = 4;
// only for async server // Only for async server. Number of threads used to serve the requests.
int32 async_server_threads = 7; int32 async_server_threads = 7;
// restrict core usage // restrict core usage, currently unused
int32 core_limit = 8; int32 core_limit = 8;
PayloadConfig payload_config = 9; PayloadConfig payload_config = 9;
} }
@ -135,6 +151,8 @@ message ServerArgs {
message ServerStatus { message ServerStatus {
ServerStats stats = 1; ServerStats stats = 1;
// the port bound by the server
int32 port = 2; int32 port = 2;
// Number of cores on the server. See gpr_cpu_num_cores.
int32 cores = 3; int32 cores = 3;
} }

@ -47,9 +47,19 @@ service BenchmarkService {
} }
service WorkerService { service WorkerService {
// Start server with specified workload // Start server with specified workload.
// First request sent specifies the ServerConfig followed by ServerStatus
// response. After that, a "Mark" can be sent anytime to request the latest
// stats. Closing the stream will initiate shutdown of the test server
// and once the shutdown has finished, the OK status is sent to terminate
// this RPC.
rpc RunServer(stream ServerArgs) returns (stream ServerStatus); rpc RunServer(stream ServerArgs) returns (stream ServerStatus);
// Start client with specified workload // Start client with specified workload.
// First request sent specifies the ClientConfig followed by ClientStatus
// response. After that, a "Mark" can be sent anytime to request the latest
// stats. Closing the stream will initiate shutdown of the test client
// and once the shutdown has finished, the OK status is sent to terminate
// this RPC.
rpc RunClient(stream ClientArgs) returns (stream ClientStatus); rpc RunClient(stream ClientArgs) returns (stream ClientStatus);
} }

@ -32,16 +32,24 @@ syntax = "proto3";
package grpc.testing; package grpc.testing;
message ServerStats { message ServerStats {
// wall clock time // wall clock time change in seconds since last reset
double time_elapsed = 1; double time_elapsed = 1;
// user time used by the server process and threads // change in user time (in seconds) used by the server since last reset
double time_user = 2; double time_user = 2;
// server time used by the server process and all threads // change in server time (in seconds) used by the server process and all
// threads since last reset
double time_system = 3; double time_system = 3;
} }
// Histogram params based on grpc/support/histogram.c
message HistogramParams {
double resolution = 1; // first bucket is [0, 1 + resolution)
double max_possible = 2; // use enough buckets to allow this value
}
// Histogram data based on grpc/support/histogram.c
message HistogramData { message HistogramData {
repeated uint32 bucket = 1; repeated uint32 bucket = 1;
double min_seen = 2; double min_seen = 2;
@ -52,7 +60,10 @@ message HistogramData {
} }
message ClientStats { message ClientStats {
// Latency histogram. Data points are in nanoseconds.
HistogramData latencies = 1; HistogramData latencies = 1;
// See ServerStats for details.
double time_elapsed = 2; double time_elapsed = 2;
double time_user = 3; double time_user = 3;
double time_system = 4; double time_system = 4;

@ -0,0 +1,56 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto3";
package grpc.testing;
message GaugeResponse {
string name = 1;
oneof value {
int64 long_value = 2;
double double_vale = 3;
string string_value = 4;
}
}
message GaugeRequest {
string name = 1;
}
message EmptyMessage {
}
service MetricsService {
rpc GetAllGauges(EmptyMessage) returns (stream GaugeResponse);
rpc GetGauge(GaugeRequest) returns (GaugeResponse);
}

@ -0,0 +1,216 @@
#!/usr/bin/env python2.7
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import requests
import json
_REQUEST_TIMEOUT_SECS = 10
def _make_pod_config(pod_name, image_name, container_port_list, cmd_list,
arg_list):
"""Creates a string containing the Pod defintion as required by the Kubernetes API"""
body = {
'kind': 'Pod',
'apiVersion': 'v1',
'metadata': {
'name': pod_name,
'labels': {'name': pod_name}
},
'spec': {
'containers': [
{
'name': pod_name,
'image': image_name,
'ports': []
}
]
}
}
# Populate the 'ports' list
for port in container_port_list:
port_entry = {'containerPort': port, 'protocol': 'TCP'}
body['spec']['containers'][0]['ports'].append(port_entry)
# Add the 'Command' and 'Args' attributes if they are passed.
# Note:
# - 'Command' overrides the ENTRYPOINT in the Docker Image
# - 'Args' override the COMMAND in Docker image (yes, it is confusing!)
if len(cmd_list) > 0:
body['spec']['containers'][0]['command'] = cmd_list
if len(arg_list) > 0:
body['spec']['containers'][0]['args'] = arg_list
return json.dumps(body)
def _make_service_config(service_name, pod_name, service_port_list,
container_port_list, is_headless):
"""Creates a string containing the Service definition as required by the Kubernetes API.
NOTE:
This creates either a Headless Service or 'LoadBalancer' service depending on
the is_headless parameter. For Headless services, there is no 'type' attribute
and the 'clusterIP' attribute is set to 'None'. Also, if the service is
Headless, Kubernetes creates DNS entries for Pods - i.e creates DNS A-records
mapping the service's name to the Pods' IPs
"""
if len(container_port_list) != len(service_port_list):
print(
'ERROR: container_port_list and service_port_list must be of same size')
return ''
body = {
'kind': 'Service',
'apiVersion': 'v1',
'metadata': {
'name': service_name,
'labels': {
'name': service_name
}
},
'spec': {
'ports': [],
'selector': {
'name': pod_name
}
}
}
# Populate the 'ports' list in the 'spec' section. This maps service ports
# (port numbers that are exposed by Kubernetes) to container ports (i.e port
# numbers that are exposed by your Docker image)
for idx in range(len(container_port_list)):
port_entry = {
'port': service_port_list[idx],
'targetPort': container_port_list[idx],
'protocol': 'TCP'
}
body['spec']['ports'].append(port_entry)
# Make this either a LoadBalancer service or a headless service depending on
# the is_headless parameter
if is_headless:
body['spec']['clusterIP'] = 'None'
else:
body['spec']['type'] = 'LoadBalancer'
return json.dumps(body)
def _print_connection_error(msg):
print('ERROR: Connection failed. Did you remember to run Kubenetes proxy on '
'localhost (i.e kubectl proxy --port=<proxy_port>) ?. Error: %s' % msg)
def _do_post(post_url, api_name, request_body):
"""Helper to do HTTP POST.
Note:
1) On success, Kubernetes returns a success code of 201(CREATED) not 200(OK)
2) A response code of 509(CONFLICT) is interpreted as a success code (since
the error is most likely due to the resource already existing). This makes
_do_post() idempotent which is semantically desirable.
"""
is_success = True
try:
r = requests.post(post_url, data=request_body, timeout=_REQUEST_TIMEOUT_SECS)
if r.status_code == requests.codes.conflict:
print('WARN: Looks like the resource already exists. Api: %s, url: %s' %
(api_name, post_url))
elif r.status_code != requests.codes.created:
print('ERROR: %s API returned error. HTTP response: (%d) %s' %
(api_name, r.status_code, r.text))
is_success = False
except(requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
is_success = False
_print_connection_error(str(e))
return is_success
def _do_delete(del_url, api_name):
"""Helper to do HTTP DELETE.
Note: A response code of 404(NOT_FOUND) is treated as success to keep
_do_delete() idempotent.
"""
is_success = True
try:
r = requests.delete(del_url, timeout=_REQUEST_TIMEOUT_SECS)
if r.status_code == requests.codes.not_found:
print('WARN: The resource does not exist. Api: %s, url: %s' %
(api_name, del_url))
elif r.status_code != requests.codes.ok:
print('ERROR: %s API returned error. HTTP response: %s' %
(api_name, r.text))
is_success = False
except(requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
is_success = False
_print_connection_error(str(e))
return is_success
def create_service(kube_host, kube_port, namespace, service_name, pod_name,
service_port_list, container_port_list, is_headless):
"""Creates either a Headless Service or a LoadBalancer Service depending
on the is_headless parameter.
"""
post_url = 'http://%s:%d/api/v1/namespaces/%s/services' % (
kube_host, kube_port, namespace)
request_body = _make_service_config(service_name, pod_name, service_port_list,
container_port_list, is_headless)
return _do_post(post_url, 'Create Service', request_body)
def create_pod(kube_host, kube_port, namespace, pod_name, image_name,
container_port_list, cmd_list, arg_list):
"""Creates a Kubernetes Pod.
Note that it is generally NOT considered a good practice to directly create
Pods. Typically, the recommendation is to create 'Controllers' to create and
manage Pods' lifecycle. Currently Kubernetes only supports 'Replication
Controller' which creates a configurable number of 'identical Replicas' of
Pods and automatically restarts any Pods in case of failures (for eg: Machine
failures in Kubernetes). This makes it less flexible for our test use cases
where we might want slightly different set of args to each Pod. Hence we
directly create Pods and not care much about Kubernetes failures since those
are very rare.
"""
post_url = 'http://%s:%d/api/v1/namespaces/%s/pods' % (kube_host, kube_port,
namespace)
request_body = _make_pod_config(pod_name, image_name, container_port_list,
cmd_list, arg_list)
return _do_post(post_url, 'Create Pod', request_body)
def delete_service(kube_host, kube_port, namespace, service_name):
del_url = 'http://%s:%d/api/v1/namespaces/%s/services/%s' % (
kube_host, kube_port, namespace, service_name)
return _do_delete(del_url, 'Delete Service')
def delete_pod(kube_host, kube_port, namespace, pod_name):
del_url = 'http://%s:%d/api/v1/namespaces/%s/pods/%s' % (kube_host, kube_port,
namespace, pod_name)
return _do_delete(del_url, 'Delete Pod')

@ -0,0 +1,72 @@
package http2interop
import (
"encoding/binary"
"fmt"
"io"
)
type GoAwayFrame struct {
Header FrameHeader
Reserved
StreamID
// TODO(carl-mastrangelo): make an enum out of this.
Code uint32
Data []byte
}
func (f *GoAwayFrame) GetHeader() *FrameHeader {
return &f.Header
}
func (f *GoAwayFrame) ParsePayload(r io.Reader) error {
raw := make([]byte, f.Header.Length)
if _, err := io.ReadFull(r, raw); err != nil {
return err
}
return f.UnmarshalPayload(raw)
}
func (f *GoAwayFrame) UnmarshalPayload(raw []byte) error {
if f.Header.Length != len(raw) {
return fmt.Errorf("Invalid Payload length %d != %d", f.Header.Length, len(raw))
}
if f.Header.Length < 8 {
return fmt.Errorf("Invalid Payload length %d", f.Header.Length)
}
*f = GoAwayFrame{
Reserved: Reserved(raw[0]>>7 == 1),
StreamID: StreamID(binary.BigEndian.Uint32(raw[0:4]) & 0x7fffffff),
Code: binary.BigEndian.Uint32(raw[4:8]),
Data: []byte(string(raw[8:])),
}
return nil
}
func (f *GoAwayFrame) MarshalPayload() ([]byte, error) {
raw := make([]byte, 8, 8+len(f.Data))
binary.BigEndian.PutUint32(raw[:4], uint32(f.StreamID))
binary.BigEndian.PutUint32(raw[4:8], f.Code)
raw = append(raw, f.Data...)
return raw, nil
}
func (f *GoAwayFrame) MarshalBinary() ([]byte, error) {
payload, err := f.MarshalPayload()
if err != nil {
return nil, err
}
f.Header.Length = len(payload)
f.Header.Type = GoAwayFrameType
header, err := f.Header.MarshalBinary()
if err != nil {
return nil, err
}
header = append(header, payload...)
return header, nil
}

@ -252,6 +252,58 @@ func testTLSApplicationProtocol(ctx *HTTP2InteropCtx) error {
return nil return nil
} }
func testTLSBadCipherSuites(ctx *HTTP2InteropCtx) error {
config := buildTlsConfig(ctx)
// These are the suites that Go supports, but are forbidden by http2.
config.CipherSuites = []uint16{
tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
}
conn, err := connectWithTls(ctx, config)
if err != nil {
return err
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
if err := http2Connect(conn, nil); err != nil {
return err
}
for {
f, err := parseFrame(conn)
if err != nil {
return err
}
if gf, ok := f.(*GoAwayFrame); ok {
return fmt.Errorf("Got goaway frame %d", gf.Code)
}
}
return nil
}
func http2Connect(c net.Conn, sf *SettingsFrame) error {
if _, err := c.Write([]byte(Preface)); err != nil {
return err
}
if sf == nil {
sf = &SettingsFrame{}
}
if err := streamFrame(c, sf); err != nil {
return err
}
return nil
}
func connect(ctx *HTTP2InteropCtx) (net.Conn, error) { func connect(ctx *HTTP2InteropCtx) (net.Conn, error) {
var conn net.Conn var conn net.Conn
var err error var err error

@ -3,13 +3,13 @@ package http2interop
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"strings"
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"strconv" "strconv"
"strings"
"testing" "testing"
) )
@ -17,8 +17,7 @@ var (
serverHost = flag.String("server_host", "", "The host to test") serverHost = flag.String("server_host", "", "The host to test")
serverPort = flag.Int("server_port", 443, "The port to test") serverPort = flag.Int("server_port", 443, "The port to test")
useTls = flag.Bool("use_tls", true, "Should TLS tests be run") useTls = flag.Bool("use_tls", true, "Should TLS tests be run")
// TODO: implement testCase = flag.String("test_case", "", "What test cases to run (tls, framing)")
testCase = flag.String("test_case", "", "What test cases to run")
// The rest of these are unused, but present to fulfill the client interface // The rest of these are unused, but present to fulfill the client interface
serverHostOverride = flag.String("server_host_override", "", "Unused") serverHostOverride = flag.String("server_host_override", "", "Unused")
@ -70,6 +69,9 @@ func (ctx *HTTP2InteropCtx) Close() error {
} }
func TestShortPreface(t *testing.T) { func TestShortPreface(t *testing.T) {
if *testCase != "framing" {
t.SkipNow()
}
ctx := InteropCtx(t) ctx := InteropCtx(t)
for i := 0; i < len(Preface)-1; i++ { for i := 0; i < len(Preface)-1; i++ {
if err := testShortPreface(ctx, Preface[:i]+"X"); err != io.EOF { if err := testShortPreface(ctx, Preface[:i]+"X"); err != io.EOF {
@ -79,6 +81,9 @@ func TestShortPreface(t *testing.T) {
} }
func TestUnknownFrameType(t *testing.T) { func TestUnknownFrameType(t *testing.T) {
if *testCase != "framing" {
t.SkipNow()
}
ctx := InteropCtx(t) ctx := InteropCtx(t)
if err := testUnknownFrameType(ctx); err != nil { if err := testUnknownFrameType(ctx); err != nil {
t.Fatal(err) t.Fatal(err)
@ -86,33 +91,53 @@ func TestUnknownFrameType(t *testing.T) {
} }
func TestTLSApplicationProtocol(t *testing.T) { func TestTLSApplicationProtocol(t *testing.T) {
if *testCase != "tls" {
t.SkipNow()
}
ctx := InteropCtx(t) ctx := InteropCtx(t)
err := testTLSApplicationProtocol(ctx); err := testTLSApplicationProtocol(ctx)
matchError(t, err, "EOF") matchError(t, err, "EOF")
} }
func TestTLSMaxVersion(t *testing.T) { func TestTLSMaxVersion(t *testing.T) {
if *testCase != "tls" {
t.SkipNow()
}
ctx := InteropCtx(t) ctx := InteropCtx(t)
err := testTLSMaxVersion(ctx, tls.VersionTLS11); err := testTLSMaxVersion(ctx, tls.VersionTLS11)
// TODO(carl-mastrangelo): maybe this should be some other error. If the server picks
// the wrong protocol version, thats bad too.
matchError(t, err, "EOF", "server selected unsupported protocol") matchError(t, err, "EOF", "server selected unsupported protocol")
} }
func TestTLSBadCipherSuites(t *testing.T) {
if *testCase != "tls" {
t.SkipNow()
}
ctx := InteropCtx(t)
err := testTLSBadCipherSuites(ctx)
matchError(t, err, "EOF", "Got goaway frame")
}
func TestClientPrefaceWithStreamId(t *testing.T) { func TestClientPrefaceWithStreamId(t *testing.T) {
if *testCase != "framing" {
t.SkipNow()
}
ctx := InteropCtx(t) ctx := InteropCtx(t)
err := testClientPrefaceWithStreamId(ctx) err := testClientPrefaceWithStreamId(ctx)
matchError(t, err, "EOF") matchError(t, err, "EOF")
} }
func matchError(t *testing.T, err error, matches ... string) { func matchError(t *testing.T, err error, matches ...string) {
if err == nil { if err == nil {
t.Fatal("Expected an error") t.Fatal("Expected an error")
} }
for _, s := range matches { for _, s := range matches {
if strings.Contains(err.Error(), s) { if strings.Contains(err.Error(), s) {
return return
} }
} }
t.Fatalf("Error %v not in %+v", err, matches) t.Fatalf("Error %v not in %+v", err, matches)
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

@ -47,6 +47,7 @@ def random_name(base_name):
def docker_kill(cid): def docker_kill(cid):
"""Kills a docker container. Returns True if successful.""" """Kills a docker container. Returns True if successful."""
return subprocess.call(['docker','kill', str(cid)], return subprocess.call(['docker','kill', str(cid)],
stdin=subprocess.PIPE,
stdout=_DEVNULL, stdout=_DEVNULL,
stderr=subprocess.STDOUT) == 0 stderr=subprocess.STDOUT) == 0
@ -78,6 +79,7 @@ def finish_jobs(jobs):
def image_exists(image): def image_exists(image):
"""Returns True if given docker image exists.""" """Returns True if given docker image exists."""
return subprocess.call(['docker','inspect', image], return subprocess.call(['docker','inspect', image],
stdin=subprocess.PIPE,
stdout=_DEVNULL, stdout=_DEVNULL,
stderr=subprocess.STDOUT) == 0 stderr=subprocess.STDOUT) == 0
@ -88,6 +90,7 @@ def remove_image(image, skip_nonexistent=False, max_retries=10):
return True return True
for attempt in range(0, max_retries): for attempt in range(0, max_retries):
if subprocess.call(['docker','rmi', '-f', image], if subprocess.call(['docker','rmi', '-f', image],
stdin=subprocess.PIPE,
stdout=_DEVNULL, stdout=_DEVNULL,
stderr=subprocess.STDOUT) == 0: stderr=subprocess.STDOUT) == 0:
return True return True

@ -29,6 +29,11 @@
"""Generate XML and HTML test reports.""" """Generate XML and HTML test reports."""
try:
from mako.runtime import Context
from mako.template import Template
except (ImportError):
pass # Mako not installed but it is ok.
import os import os
import string import string
import xml.etree.cElementTree as ET import xml.etree.cElementTree as ET
@ -49,7 +54,7 @@ def _filter_msg(msg, output_format):
return msg return msg
def render_xml_report(resultset, xml_report): def render_junit_xml_report(resultset, xml_report):
"""Generate JUnit-like XML report.""" """Generate JUnit-like XML report."""
root = ET.Element('testsuites') root = ET.Element('testsuites')
testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc',
@ -69,147 +74,36 @@ def render_xml_report(resultset, xml_report):
tree.write(xml_report, encoding='UTF-8') tree.write(xml_report, encoding='UTF-8')
# TODO(adelez): Use mako template. def render_interop_html_report(
def fill_one_test_result(shortname, resultset, html_str): client_langs, server_langs, test_cases, auth_test_cases, http2_cases,
if shortname in resultset: resultset, num_failures, cloud_to_prod, http2_interop):
# Because interop tests does not have runs_per_test flag, each test is run """Generate HTML report for interop tests."""
# once. So there should only be one element for each result. html_report_dir = 'reports'
result = resultset[shortname][0] template_file = os.path.join(html_report_dir, 'interop_html_report.template')
if result.state == 'PASSED': try:
html_str = '%s<td bgcolor=\"green\">PASS</td>\n' % html_str mytemplate = Template(filename=template_file, format_exceptions=True)
else: except NameError:
tooltip = '' print 'Mako template is not installed. Skipping HTML report generation.'
if result.returncode > 0 or result.message: return
if result.returncode > 0: except IOError as e:
tooltip = 'returncode: %d ' % result.returncode print 'Failed to find the template %s: %s' % (template_file, e)
if result.message: return
escaped_msg = _filter_msg(result.message, 'HTML')
tooltip = '%smessage: %s' % (tooltip, escaped_msg)
if result.state == 'FAILED':
html_str = '%s<td bgcolor=\"red\">' % html_str
if tooltip:
html_str = ('%s<a href=\"#\" data-toggle=\"tooltip\" '
'data-placement=\"auto\" title=\"%s\">FAIL</a></td>\n' %
(html_str, tooltip))
else:
html_str = '%sFAIL</td>\n' % html_str
elif result.state == 'TIMEOUT':
html_str = '%s<td bgcolor=\"yellow\">' % html_str
if tooltip:
html_str = ('%s<a href=\"#\" data-toggle=\"tooltip\" '
'data-placement=\"auto\" title=\"%s\">TIMEOUT</a></td>\n'
% (html_str, tooltip))
else:
html_str = '%sTIMEOUT</td>\n' % html_str
else:
html_str = '%s<td bgcolor=\"magenta\">Not implemented</td>\n' % html_str
return html_str
def render_html_report(client_langs, server_langs, test_cases, auth_test_cases,
http2_cases, resultset, num_failures, cloud_to_prod,
http2_interop):
"""Generate html report."""
sorted_test_cases = sorted(test_cases) sorted_test_cases = sorted(test_cases)
sorted_auth_test_cases = sorted(auth_test_cases) sorted_auth_test_cases = sorted(auth_test_cases)
sorted_http2_cases = sorted(http2_cases) sorted_http2_cases = sorted(http2_cases)
sorted_client_langs = sorted(client_langs) sorted_client_langs = sorted(client_langs)
sorted_server_langs = sorted(server_langs) sorted_server_langs = sorted(server_langs)
html_str = ('<!DOCTYPE html>\n'
'<html lang=\"en\">\n'
'<head><title>Interop Test Result</title></head>\n'
'<body>\n')
if num_failures > 1:
html_str = (
'%s<p><h2><font color=\"red\">%d tests failed!</font></h2></p>\n' %
(html_str, num_failures))
elif num_failures:
html_str = (
'%s<p><h2><font color=\"red\">%d test failed!</font></h2></p>\n' %
(html_str, num_failures))
else:
html_str = (
'%s<p><h2><font color=\"green\">All tests passed!</font></h2></p>\n' %
html_str)
if cloud_to_prod:
# Each column header is the client language.
html_str = ('%s<h2>Cloud to Prod</h2>\n'
'<table style=\"width:100%%\" border=\"1\">\n'
'<tr bgcolor=\"#00BFFF\">\n'
'<th>Client languages &#9658;</th>\n') % html_str
for client_lang in sorted_client_langs:
html_str = '%s<th>%s\n' % (html_str, client_lang)
html_str = '%s</tr>\n' % html_str
for test_case in sorted_test_cases + sorted_auth_test_cases:
html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, test_case)
for client_lang in sorted_client_langs:
if not test_case in sorted_auth_test_cases:
shortname = 'cloud_to_prod:%s:%s' % (client_lang, test_case)
else:
shortname = 'cloud_to_prod_auth:%s:%s' % (client_lang, test_case)
html_str = fill_one_test_result(shortname, resultset, html_str)
html_str = '%s</tr>\n' % html_str
html_str = '%s</table>\n' % html_str
if http2_interop:
# Each column header is the server language.
html_str = ('%s<h2>HTTP/2 Interop</h2>\n'
'<table style=\"width:100%%\" border=\"1\">\n'
'<tr bgcolor=\"#00BFFF\">\n'
'<th>Servers &#9658;<br/>'
'Test Cases &#9660;</th>\n') % html_str
for server_lang in sorted_server_langs:
html_str = '%s<th>%s\n' % (html_str, server_lang)
if cloud_to_prod:
html_str = '%s<th>%s\n' % (html_str, "prod")
html_str = '%s</tr>\n' % html_str
for test_case in sorted_http2_cases:
html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, test_case)
# Fill up the cells with test result.
for server_lang in sorted_server_langs:
shortname = 'cloud_to_cloud:%s:%s_server:%s' % (
"http2", server_lang, test_case)
html_str = fill_one_test_result(shortname, resultset, html_str)
if cloud_to_prod:
shortname = 'cloud_to_prod:%s:%s' % ("http2", test_case)
html_str = fill_one_test_result(shortname, resultset, html_str)
html_str = '%s</tr>\n' % html_str
html_str = '%s</table>\n' % html_str
if server_langs:
for test_case in sorted_test_cases:
# Each column header is the client language.
html_str = ('%s<h2>%s</h2>\n'
'<table style=\"width:100%%\" border=\"1\">\n'
'<tr bgcolor=\"#00BFFF\">\n'
'<th>Client languages &#9658;<br/>'
'Server languages &#9660;</th>\n') % (html_str, test_case)
for client_lang in sorted_client_langs:
html_str = '%s<th>%s\n' % (html_str, client_lang)
html_str = '%s</tr>\n' % html_str
# Each row head is the server language.
for server_lang in sorted_server_langs:
html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, server_lang)
# Fill up the cells with test result.
for client_lang in sorted_client_langs:
shortname = 'cloud_to_cloud:%s:%s_server:%s' % (
client_lang, server_lang, test_case)
html_str = fill_one_test_result(shortname, resultset, html_str)
html_str = '%s</tr>\n' % html_str
html_str = '%s</table>\n' % html_str
html_str = ('%s\n' args = {'client_langs': sorted_client_langs,
'<script>\n' 'server_langs': sorted_server_langs,
'$(document).ready(function(){' 'test_cases': sorted_test_cases,
'$(\'[data-toggle=\"tooltip\"]\').tooltip();\n' 'auth_test_cases': sorted_auth_test_cases,
'});\n' 'http2_cases': sorted_http2_cases,
'</script>\n' 'resultset': resultset,
'</body>\n' 'num_failures': num_failures,
'</html>') % html_str 'cloud_to_prod': cloud_to_prod,
'http2_interop': http2_interop}
# Write to reports/index.html as set up in Jenkins plugin.
html_report_dir = 'reports'
if not os.path.exists(html_report_dir):
os.mkdir(html_report_dir)
html_file_path = os.path.join(html_report_dir, 'index.html') html_file_path = os.path.join(html_report_dir, 'index.html')
with open(html_file_path, 'w') as f: with open(html_file_path, 'w') as output_file:
f.write(html_str) mytemplate.render_context(Context(output_file, **args))

@ -37,7 +37,6 @@ import jobset
import multiprocessing import multiprocessing
import os import os
import report_utils import report_utils
import subprocess
import sys import sys
import tempfile import tempfile
import time import time
@ -170,7 +169,7 @@ class Http2Client:
self.safename = str(self) self.safename = str(self)
def client_args(self): def client_args(self):
return ['tools/http2_interop/http2_interop.test'] return ['tools/http2_interop/http2_interop.test', '-test.v']
def cloud_to_prod_env(self): def cloud_to_prod_env(self):
return {} return {}
@ -306,7 +305,7 @@ _TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
_AUTH_TEST_CASES = ['compute_engine_creds', 'jwt_token_creds', _AUTH_TEST_CASES = ['compute_engine_creds', 'jwt_token_creds',
'oauth2_auth_token', 'per_rpc_creds'] 'oauth2_auth_token', 'per_rpc_creds']
_HTTP2_TEST_CASES = ["tls"] _HTTP2_TEST_CASES = ["tls", "framing"]
def docker_run_cmdline(cmdline, image, docker_args=[], cwd=None, environ=None): def docker_run_cmdline(cmdline, image, docker_args=[], cwd=None, environ=None):
"""Wraps given cmdline array to create 'docker run' cmdline from it.""" """Wraps given cmdline array to create 'docker run' cmdline from it."""
@ -686,9 +685,9 @@ try:
else: else:
jobset.message('SUCCESS', 'All tests passed', do_newline=True) jobset.message('SUCCESS', 'All tests passed', do_newline=True)
report_utils.render_xml_report(resultset, 'report.xml') report_utils.render_junit_xml_report(resultset, 'report.xml')
report_utils.render_html_report( report_utils.render_interop_html_report(
set([str(l) for l in languages]), servers, _TEST_CASES, _AUTH_TEST_CASES, set([str(l) for l in languages]), servers, _TEST_CASES, _AUTH_TEST_CASES,
_HTTP2_TEST_CASES, resultset, num_failures, _HTTP2_TEST_CASES, resultset, num_failures,
args.cloud_to_prod_auth or args.cloud_to_prod, args.http2_interop) args.cloud_to_prod_auth or args.cloud_to_prod, args.http2_interop)

@ -485,10 +485,10 @@ _CONFIGS = {
'msan': SimpleConfig('msan', timeout_multiplier=1.5), 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
'ubsan': SimpleConfig('ubsan'), 'ubsan': SimpleConfig('ubsan'),
'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={ 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
'ASAN_OPTIONS': 'detect_leaks=1:color=always:suppressions=tools/tsan_suppressions.txt', 'ASAN_OPTIONS': 'detect_leaks=1:color=always',
'LSAN_OPTIONS': 'report_objects=1'}), 'LSAN_OPTIONS': 'report_objects=1'}),
'asan-noleaks': SimpleConfig('asan', environ={ 'asan-noleaks': SimpleConfig('asan', environ={
'ASAN_OPTIONS': 'detect_leaks=0:color=always:suppressions=tools/tsan_suppressions.txt'}), 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
'gcov': SimpleConfig('gcov'), 'gcov': SimpleConfig('gcov'),
'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']), 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
'helgrind': ValgrindConfig('dbg', 'helgrind') 'helgrind': ValgrindConfig('dbg', 'helgrind')
@ -902,7 +902,7 @@ def _build_and_run(
for antagonist in antagonists: for antagonist in antagonists:
antagonist.kill() antagonist.kill()
if xml_report and resultset: if xml_report and resultset:
report_utils.render_xml_report(resultset, xml_report) report_utils.render_junit_xml_report(resultset, xml_report)
number_failures, _ = jobset.run( number_failures, _ = jobset.run(
post_tests_steps, maxjobs=1, stop_on_failure=True, post_tests_steps, maxjobs=1, stop_on_failure=True,

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

Loading…
Cancel
Save