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
- linux
- 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
build: test
language: c++
@ -2155,13 +2169,16 @@ targets:
- test/cpp/interop/client_helper.h
- test/cpp/interop/interop_client.h
- test/cpp/interop/stress_interop_client.h
- test/cpp/util/metrics_server.h
src:
- test/proto/empty.proto
- test/proto/messages.proto
- test/proto/metrics.proto
- test/proto/test.proto
- test/cpp/interop/interop_client.cc
- test/cpp/interop/stress_interop_client.cc
- test/cpp/interop/stress_test.cc
- test/cpp/util/metrics_server.cc
deps:
- grpc++_test_util
- grpc_test_util
@ -2256,6 +2273,23 @@ node_modules:
- deps:
- grpc
- 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
src:
- 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
/// channel credentials.
std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
const std::shared_ptr<ChannelCredentials>& channel_creds,
const std::shared_ptr<CallCredentials>& call_creds);
const std::shared_ptr<ChannelCredentials>& channel_creds,
const std::shared_ptr<CallCredentials>& call_creds);
/// Combines two call credentials objects into a composite call credentials.
std::shared_ptr<CallCredentials> CompositeCallCredentials(

@ -127,6 +127,12 @@ typedef struct {
/** Initial sequence number for http2 transports */
#define GRPC_ARG_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 */
#define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority"
/** 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).
*
* 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
* 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 \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. */
int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx,
grpc_subchannel *subchannel,

@ -126,7 +126,8 @@ void grpc_pollset_destroy(grpc_pollset *pollset) {}
void grpc_pollset_reset(grpc_pollset *pollset) {
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->is_iocp_worker = 0;
pollset->kicked_without_pollers = 0;

@ -39,6 +39,9 @@
/* Forward decl of 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. */
typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg,
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_tcp_server_cb on_accept_cb, void *cb_arg);
/* Add a port to the server, returning port number on success, or negative
on failure.
/* Add a port to the server, returning the newly created listener on success,
or a null pointer on failure.
The :: and 0.0.0.0 wildcard addresses are treated identically, accepting
both IPv4 and IPv6 connections, but :: is the preferred style. This usually
creates one socket, but possibly two on systems which support IPv6,
but not dualstack sockets.
For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */
but not dualstack sockets. */
/* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle
all of the multiple socket port matching logic in one place */
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
size_t addr_len);
grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s,
const void *addr, size_t addr_len);
/* Returns the file descriptor of the Nth listening socket on this server,
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,
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 */

@ -67,14 +67,13 @@
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#define INIT_PORT_CAP 2
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
static gpr_once s_init_max_accept_queue_size;
static int s_max_accept_queue_size;
/* one listening port */
typedef struct {
struct grpc_tcp_listener {
int fd;
grpc_fd *emfd;
grpc_tcp_server *server;
@ -84,9 +83,18 @@ typedef struct {
struct sockaddr_un un;
} addr;
size_t addr_len;
int port;
grpc_closure read_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) {
struct stat st;
@ -112,10 +120,9 @@ struct grpc_tcp_server {
/* is this server shutting down? (boolean) */
int shutdown;
/* all listening ports */
server_port *ports;
size_t nports;
size_t port_capacity;
/* linked list of server ports */
grpc_tcp_listener *head;
unsigned nports;
/* shutdown callback */
grpc_closure *shutdown_complete;
@ -134,9 +141,8 @@ grpc_tcp_server *grpc_tcp_server_create(void) {
s->shutdown = 0;
s->on_accept_cb = 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;
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_free(s->ports);
while (s->head) {
grpc_tcp_listener *sp = s->head;
s->head = sp->next;
grpc_tcp_listener_unref(sp);
}
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
things */
static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
size_t i;
/* delete ALL the things */
gpr_mu_lock(&s->mu);
@ -176,9 +185,9 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
return;
}
if (s->nports) {
for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i];
if (s->head) {
grpc_tcp_listener *sp;
for (sp = s->head; sp; sp = sp->next) {
if (sp->addr.sockaddr.sa_family == AF_UNIX) {
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,
grpc_closure *closure) {
size_t i;
gpr_mu_lock(&s->mu);
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 */
if (s->active_ports) {
for (i = 0; i < s->nports; i++) {
grpc_fd_shutdown(exec_ctx, s->ports[i].emfd);
grpc_tcp_listener *sp;
for (sp = s->head; sp; sp = sp->next) {
grpc_fd_shutdown(exec_ctx, sp->emfd);
}
gpr_mu_unlock(&s->mu);
} else {
@ -298,7 +307,7 @@ error:
/* event manager callback when reads are ready */
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;
size_t i;
@ -364,9 +373,10 @@ error:
}
}
static int add_socket_to_server(grpc_tcp_server *s, int fd,
const struct sockaddr *addr, size_t addr_len) {
server_port *sp;
static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd,
const struct sockaddr *addr,
size_t addr_len) {
grpc_tcp_listener *sp = NULL;
int port;
char *addr_str;
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);
gpr_asprintf(&name, "tcp-server-listener:%s", addr_str);
gpr_mu_lock(&s->mu);
s->nports++;
GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
/* append it to the list under a lock */
if (s->nports == s->port_capacity) {
s->port_capacity *= 2;
s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity);
}
sp = &s->ports[s->nports++];
sp = gpr_malloc(sizeof(grpc_tcp_listener));
sp->next = s->head;
s->head = sp;
sp->server = s;
sp->fd = fd;
sp->emfd = grpc_fd_create(fd, name);
memcpy(sp->addr.untyped, addr, 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_mu_unlock(&s->mu);
gpr_free(addr_str);
gpr_free(name);
}
return port;
return sp;
}
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
size_t addr_len) {
int allocated_port1 = -1;
int allocated_port2 = -1;
unsigned i;
grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s,
const void *addr, size_t addr_len) {
int allocated_port = -1;
grpc_tcp_listener *sp;
grpc_tcp_listener *sp2 = NULL;
int fd;
grpc_dualstack_mode dsmode;
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
as some previously created listener. */
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);
if (0 == getsockname(s->ports[i].fd, (struct sockaddr *)&sockname_temp,
if (0 == getsockname(sp->fd, (struct sockaddr *)&sockname_temp,
&sockname_len)) {
port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
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)) {
addr = (const struct sockaddr *)&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_len = sizeof(wild6);
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) {
goto done;
}
/* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
if (port == 0 && allocated_port1 > 0) {
grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port1);
if (port == 0 && allocated_port > 0) {
grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port);
sp2 = sp;
}
addr = (struct sockaddr *)&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_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:
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) {
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,
grpc_pollset **pollsets, size_t pollset_count,
grpc_tcp_server_cb on_accept_cb,
void *on_accept_cb_arg) {
size_t i, j;
size_t i;
grpc_tcp_listener *sp;
GPR_ASSERT(on_accept_cb);
gpr_mu_lock(&s->mu);
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->pollsets = pollsets;
s->pollset_count = pollset_count;
for (i = 0; i < s->nports; i++) {
for (j = 0; j < pollset_count; j++) {
grpc_pollset_add_fd(exec_ctx, pollsets[j], s->ports[i].emfd);
for (sp = s->head; sp; sp = sp->next) {
for (i = 0; i < pollset_count; i++) {
grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
}
s->ports[i].read_closure.cb = on_read;
s->ports[i].read_closure.cb_arg = &s->ports[i];
grpc_fd_notify_on_read(exec_ctx, s->ports[i].emfd,
&s->ports[i].read_closure);
sp->read_closure.cb = on_read;
sp->read_closure.cb_arg = sp;
grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
s->active_ports++;
}
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

@ -35,7 +35,8 @@
#ifdef GPR_WINSOCK_SOCKET
#define _GNU_SOURCE
#include <io.h>
#include "src/core/iomgr/sockaddr_utils.h"
#include <grpc/support/alloc.h>
@ -51,25 +52,29 @@
#include "src/core/iomgr/tcp_server.h"
#include "src/core/iomgr/tcp_windows.h"
#define INIT_PORT_CAP 2
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
/* one listening port */
typedef struct server_port {
struct grpc_tcp_listener {
/* This seemingly magic number comes from AcceptEx's documentation. each
address buffer needs to have at least 16 more bytes at their end. */
gpr_uint8 addresses[(sizeof(struct sockaddr_in6) + 16) * 2];
/* This will hold the socket for the next accept. */
SOCKET new_socket;
/* The listener winsocked. */
/* The listener winsocket. */
grpc_winsocket *socket;
/* The actual TCP port number. */
int port;
grpc_tcp_server *server;
/* The cached AcceptEx for that port. */
LPFN_ACCEPTEX AcceptEx;
int shutting_down;
/* closure for socket notification of accept being ready */
grpc_closure on_accept;
} server_port;
gpr_refcount refs;
/* linked list */
struct grpc_tcp_listener *next;
};
/* the overall server */
struct grpc_tcp_server {
@ -82,10 +87,8 @@ struct grpc_tcp_server {
/* active port count: how many ports are actually still listening */
int active_ports;
/* all listening ports */
server_port *ports;
size_t nports;
size_t port_capacity;
/* linked list of server ports */
grpc_tcp_listener *head;
/* shutdown callback */
grpc_closure *shutdown_complete;
@ -99,9 +102,7 @@ grpc_tcp_server *grpc_tcp_server_create(void) {
s->active_ports = 0;
s->on_accept_cb = NULL;
s->on_accept_cb_arg = NULL;
s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP);
s->nports = 0;
s->port_capacity = INIT_PORT_CAP;
s->head = NULL;
s->shutdown_complete = NULL;
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 finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
size_t i;
grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1);
/* 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
closed by the system. */
for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i];
while (s->head) {
grpc_tcp_listener *sp = s->head;
s->head = sp->next;
sp->next = NULL;
grpc_winsocket_destroy(sp->socket);
grpc_tcp_listener_unref(sp);
}
gpr_free(s->ports);
gpr_free(s);
}
/* Public function. Stops and destroys a grpc_tcp_server. */
void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
grpc_closure *shutdown_complete) {
size_t i;
int immediately_done = 0;
grpc_tcp_listener *sp;
gpr_mu_lock(&s->mu);
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) {
immediately_done = 1;
}
for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i];
for (sp = s->head; sp; sp = sp->next) {
sp->shutting_down = 1;
grpc_winsocket_shutdown(sp->socket);
}
@ -199,7 +199,7 @@ error:
}
static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx,
server_port *sp) {
grpc_tcp_listener *sp) {
int notify = 0;
sp->shutting_down = 0;
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
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;
char *message;
char *utf8_message;
@ -276,7 +276,7 @@ failure:
/* Event manager callback when reads are ready. */
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;
grpc_winsocket_callback_info *info = &sp->socket->read_info;
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);
}
static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
const struct sockaddr *addr, size_t addr_len) {
server_port *sp;
static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
const struct sockaddr *addr,
size_t addr_len) {
grpc_tcp_listener *sp = NULL;
int port;
int status;
GUID guid = WSAID_ACCEPTEX;
DWORD ioctl_num_bytes;
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
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_free(utf8_message);
closesocket(sock);
return -1;
return NULL;
}
port = prepare_socket(sock, addr, addr_len);
if (port >= 0) {
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
/* append it to the list under a lock */
if (s->nports == s->port_capacity) {
/* too many ports, and we need to store their address in a closure */
/* TODO(ctiller): make server_port a linked list */
abort();
}
sp = &s->ports[s->nports++];
sp = gpr_malloc(sizeof(grpc_tcp_listener));
sp->next = s->head;
s->head = sp;
sp->server = s;
sp->socket = grpc_winsocket_create(sock, "listener");
sp->shutting_down = 0;
sp->AcceptEx = AcceptEx;
sp->new_socket = INVALID_SOCKET;
sp->port = port;
gpr_ref_init(&sp->refs, 1);
grpc_closure_init(&sp->on_accept, on_accept, sp);
GPR_ASSERT(sp->socket);
gpr_mu_unlock(&s->mu);
}
return port;
return sp;
}
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
size_t addr_len) {
int allocated_port = -1;
unsigned i;
grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s,
const void *addr, size_t addr_len) {
grpc_tcp_listener *sp;
SOCKET sock;
struct sockaddr_in6 addr6_v4mapped;
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
as some previously created listener. */
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);
if (0 == getsockname(s->ports[i].socket->socket,
if (0 == getsockname(sp->socket->socket,
(struct sockaddr *)&sockname_temp, &sockname_len)) {
port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
if (port > 0) {
@ -452,33 +450,56 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
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);
return allocated_port;
return sp;
}
SOCKET
grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) {
return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET;
int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned port_index) {
grpc_tcp_listener *sp;
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,
grpc_pollset **pollset, size_t pollset_count,
grpc_tcp_server_cb on_accept_cb,
void *on_accept_cb_arg) {
size_t i;
grpc_tcp_listener *sp;
GPR_ASSERT(on_accept_cb);
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->on_accept_cb);
GPR_ASSERT(s->active_ports == 0);
s->on_accept_cb = on_accept_cb;
s->on_accept_cb_arg = on_accept_cb_arg;
for (i = 0; i < s->nports; i++) {
start_accept(exec_ctx, s->ports + i);
for (sp = s->head; sp; sp = sp->next) {
start_accept(exec_ctx, sp);
s->active_ports++;
}
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 */

@ -116,9 +116,12 @@ void grpc_call_credentials_release(grpc_call_credentials *creds) {
grpc_call_credentials_unref(creds);
}
void grpc_call_credentials_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) {
void grpc_call_credentials_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
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 (cb != NULL) {
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);
}
static void jwt_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) {
static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
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 *)creds;
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);
}
static void md_only_test_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) {
static void md_only_test_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
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;
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);
}
static void access_token_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) {
static void access_token_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
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;
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);
}
static void composite_call_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_pollset *pollset,
const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) {
static void composite_call_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
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_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,
iam_get_request_metadata};
iam_get_request_metadata};
grpc_call_credentials *grpc_google_iam_credentials_create(
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);
return &c->base;
}

@ -187,7 +187,8 @@ typedef struct {
size_t num_creds;
} 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);
/* 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:
if (result == 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. */
grpc_channel_credentials *ssl_creds =
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_call_credentials *request_metadata_creds, const grpc_ssl_config *config,
const char *target_name, const char *overridden_target_name,
grpc_channel_security_connector **sc) {
grpc_call_credentials *request_metadata_creds,
const grpc_ssl_config *config, const char *target_name,
const char *overridden_target_name, grpc_channel_security_connector **sc) {
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const unsigned char **alpn_protocol_strings =
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++) {
port_temp = grpc_tcp_server_add_port(
grpc_tcp_listener *listener;
listener = grpc_tcp_server_add_port(
tcp, (struct sockaddr *)&resolved->addrs[i].addr,
resolved->addrs[i].len);
port_temp = grpc_tcp_listener_get_port(listener);
if (port_temp >= 0) {
if (port_num == -1) {
port_num = port_temp;

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

@ -922,7 +922,8 @@ static batch_control *allocate_batch_control(grpc_call *call) {
size_t i;
for (i = 0; i < MAX_CONCURRENT_BATCHES; i++) {
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];
}
}

@ -107,9 +107,11 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) {
}
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,
resolved->addrs[i].len);
port_temp = grpc_tcp_listener_get_port(listener);
if (port_temp >= 0) {
if (port_num == -1) {
port_num = port_temp;

@ -36,8 +36,10 @@
#include <assert.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
#include "src/core/transport/chttp2/bin_encoder.h"
#include "src/core/transport/chttp2/hpack_table.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);
}
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 */
static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
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);
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
the current table, drop elements until it fits, matching the decompressor
algorithm */
/* TODO(ctiller): constant */
while (c->table_size + elem_size > 4096) {
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--;
while (c->table_size + elem_size > c->max_table_size) {
evict_entry(c);
}
GPR_ASSERT(c->table_elems < GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS);
c->table_elem_size[new_index % GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS] =
(gpr_uint16)elem_size;
GPR_ASSERT(c->table_elems < c->max_table_size);
c->table_elem_size[new_index % c->cap_table_elems] = (gpr_uint16)elem_size;
c->table_size = (gpr_uint16)(c->table_size + elem_size);
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));
}
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,
gpr_uint32 elem_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;
}
static gpr_uint32 elems_for_bytes(gpr_uint32 bytes) {
return (bytes + 31) / 32;
}
void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
grpc_mdctx *ctx) {
memset(c, 0, sizeof(*c));
c->mdctx = ctx;
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) {
@ -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]);
}
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,
@ -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
updated). After this loop, we'll do a batch unref of elements. */
begin_frame(&st);
if (c->advertise_table_size_change != 0) {
emit_advertise_table_size_change(c, &st);
}
grpc_metadata_batch_assert_ok(metadata);
for (l = metadata->list.head; l; l = l->next) {
hpack_enc(c, l->md, &st);

@ -43,14 +43,26 @@
#define GRPC_CHTTP2_HPACKC_NUM_FILTERS 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 {
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 */
gpr_uint32 tail_remote_index;
gpr_uint16 table_size;
gpr_uint16 table_elems;
gpr_uint32 table_size;
gpr_uint32 table_elems;
/* filter tables for elems: this tables provides an approximate
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_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;
void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
grpc_mdctx *mdctx);
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,
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);
static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
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,
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_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx,
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
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_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,
@ -622,13 +624,15 @@ static const gpr_uint8 inverse_base64[256] = {
};
/* emission helpers */
static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
int add_to_table) {
static int on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
int add_to_table) {
if (add_to_table) {
GRPC_MDELEM_REF(md);
grpc_chttp2_hptbl_add(&p->table, md);
if (!grpc_chttp2_hptbl_add(&p->table, md)) {
return 0;
}
}
p->on_header(p->on_header_user_data, md);
return 1;
}
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,
const gpr_uint8 *cur, const gpr_uint8 *end) {
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);
on_hdr(p, md, 0);
return parse_begin(p, cur, end);
return on_hdr(p, md, 0) && parse_begin(p, cur, end);
}
/* 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,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
1);
return parse_begin(p, cur, end);
return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
1) &&
parse_begin(p, cur, end);
}
/* finish a literal header with incremental indexing with no index */
static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key),
take_string(p, &p->value)),
1);
return parse_begin(p, cur, end);
return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key),
take_string(p, &p->value)),
1) &&
parse_begin(p, cur, end);
}
/* 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,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0);
return parse_begin(p, cur, end);
return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0) &&
parse_begin(p, cur, end);
}
/* finish a literal header without incremental indexing with index = 0 */
static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key),
take_string(p, &p->value)),
0);
return parse_begin(p, cur, end);
return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key),
take_string(p, &p->value)),
0) &&
parse_begin(p, cur, end);
}
/* 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,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0);
return parse_begin(p, cur, end);
return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0) &&
parse_begin(p, cur, end);
}
/* finish a literal header that is never indexed with an extra value */
static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key),
take_string(p, &p->value)),
0);
return parse_begin(p, cur, end);
return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
take_string(p, &p->key),
take_string(p, &p->value)),
0) &&
parse_begin(p, cur, end);
}
/* 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,
const gpr_uint8 *cur, const gpr_uint8 *end) {
gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
abort(); /* not implemented */
return parse_begin(p, cur, end);
return grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index) &&
parse_begin(p, cur, end);
}
/* parse a max table size change, max size < 15 */
static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end) {
p->index = (*cur) & 0xf;
p->index = (*cur) & 0x1f;
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[] = {
finish_max_tbl_size};
p->next_state = and_then;
p->index = 0xf;
p->index = 0x1f;
p->parsing.value = &p->index;
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;
}
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
no overflow is possible */
static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,

@ -36,7 +36,9 @@
#include <assert.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/support/murmur_hash.h"
static struct {
@ -169,12 +171,22 @@ static struct {
{"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) {
size_t i;
memset(tbl, 0, sizeof(*tbl));
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++) {
tbl->static_ents[i - 1] = grpc_mdelem_from_strings(
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]);
}
for (i = 0; i < tbl->num_ents; i++) {
GRPC_MDELEM_UNREF(
tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]);
GRPC_MDELEM_UNREF(tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]);
}
gpr_free(tbl->ents);
}
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 */
tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1);
if (tbl_index < tbl->num_ents) {
gpr_uint32 offset = (tbl->num_ents - 1u - tbl_index + tbl->first_ent) %
GRPC_CHTTP2_MAX_TABLE_COUNT;
gpr_uint32 offset =
(tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries;
return tbl->ents[offset];
}
/* Invalid entry: return error */
@ -216,21 +228,81 @@ static void evict1(grpc_chttp2_hptbl *tbl) {
GPR_SLICE_LENGTH(first_ent->value->slice) +
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
GPR_ASSERT(elem_bytes <= tbl->mem_used);
tbl->mem_used = (gpr_uint16)(tbl->mem_used - elem_bytes);
tbl->first_ent =
(gpr_uint16)((tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT);
tbl->mem_used -= (gpr_uint32)elem_bytes;
tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries);
tbl->num_ents--;
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 */
size_t elem_bytes = GPR_SLICE_LENGTH(md->key->slice) +
GPR_SLICE_LENGTH(md->value->slice) +
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 */
if (elem_bytes > tbl->max_bytes) {
if (elem_bytes > tbl->current_table_bytes) {
/* HPACK draft 10 section 4.4 states:
* 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
@ -243,44 +315,43 @@ void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
while (tbl->num_ents) {
evict1(tbl);
}
return;
return 1;
}
/* 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);
}
/* 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 */
tbl->last_ent =
(gpr_uint16)((tbl->last_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT);
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(
const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
grpc_chttp2_hptbl_find_result r = {0, 0};
gpr_uint16 i;
gpr_uint32 i;
/* See if the string is in the static table */
for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
grpc_mdelem *ent = tbl->static_ents[i];
if (md->key != ent->key) continue;
r.index = (gpr_uint16)(i + 1);
r.index = i + 1u;
r.has_value = md->value == ent->value;
if (r.has_value) return r;
}
/* Scan the dynamic table */
for (i = 0; i < tbl->num_ents; i++) {
gpr_uint16 idx =
(gpr_uint16)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY);
grpc_mdelem *ent =
tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT];
gpr_uint32 idx =
(gpr_uint32)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY);
grpc_mdelem *ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
if (md->key != ent->key) continue;
r.index = idx;
r.has_value = md->value == ent->value;

@ -49,47 +49,59 @@
#define GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE
/* Per entry overhead bytes as per the spec */
#define GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD 32
#if 0
/* Maximum number of entries we could possibly fit in the table, given defined
overheads */
#define GRPC_CHTTP2_MAX_TABLE_COUNT \
((GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / \
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD)
#endif
/* hpack decoder table */
typedef struct {
grpc_mdctx *mdctx;
/* the first used entry in ents */
gpr_uint16 first_ent;
/* the last used entry in ents */
gpr_uint16 last_ent;
gpr_uint32 first_ent;
/* 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 */
gpr_uint16 mem_used;
gpr_uint32 mem_used;
/* the max memory allowed to be used by the table, according to the hpack
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
what hpack specifies, in order to simplify table management a little...
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_chttp2_hptbl;
/* initialize a hpack table */
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_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 */
grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
gpr_uint32 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
most similar entry, or 0 if the value was not found */
typedef struct {
gpr_uint16 index;
gpr_uint8 has_value;
gpr_uint32 index;
int has_value;
} grpc_chttp2_hptbl_find_result;
grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
const grpc_chttp2_hptbl *tbl, grpc_mdelem *md);

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

@ -78,6 +78,9 @@ void grpc_chttp2_prepare_to_read(
GPR_TIMER_BEGIN("grpc_chttp2_prepare_to_read", 0);
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 */
while (grpc_chttp2_list_pop_unannounced_incoming_window_available(
@ -127,6 +130,7 @@ void grpc_chttp2_publish_reads(
transport_global->settings[GRPC_SENT_SETTINGS],
GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32));
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
@ -819,6 +823,9 @@ static int init_settings_frame_parser(
}
if (transport_parsing->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
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_data = &transport_parsing->simple.settings;

@ -45,7 +45,7 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
int grpc_chttp2_unlocking_check_writes(
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_writing *stream_writing;
@ -55,8 +55,13 @@ int grpc_chttp2_unlocking_check_writes(
gpr_slice_buffer_swap(&transport_global->qbuf, &transport_writing->outbuf);
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 &&
!transport_global->sent_local_settings) {
!transport_global->sent_local_settings && !is_parsing) {
gpr_slice_buffer_add(
&transport_writing->outbuf,
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 =
(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) {
GPR_TIMER_BEGIN("unlock", 0);
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;
REF_TRANSPORT(t, "writing");
grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, 1);
@ -735,6 +761,8 @@ static int contains_non_ok_status(
return 0;
}
static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, int success) {}
static void perform_stream_op_locked(
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
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);
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
dropped at the end of this function */
on_complete->final_data = 2;
@ -806,7 +837,7 @@ static void perform_stream_op_locked(
}
if (stream_global->write_closed) {
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));
} else if (stream_global->id != 0) {
/* 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
what we want - which is safe because we haven't told anyone
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 =
TRANSPORT_FROM_GLOBAL(transport_global)->metadata_context;
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));
}
SecureChannelCredentials* AsSecureCredentials() GRPC_OVERRIDE { return nullptr; }
SecureChannelCredentials* AsSecureCredentials() GRPC_OVERRIDE {
return nullptr;
}
};
} // namespace

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

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

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

@ -40,12 +40,12 @@ namespace Grpc.Core.Profiling
{
internal static class Profilers
{
static readonly NopProfiler defaultProfiler = new NopProfiler();
static readonly NopProfiler DefaultProfiler = new NopProfiler();
static readonly ThreadLocal<IProfiler> profilers = new ThreadLocal<IProfiler>();
public static IProfiler ForCurrentThread()
{
return profilers.Value ?? defaultProfiler;
return profilers.Value ?? DefaultProfiler;
}
public static void SetForCurrentThread(IProfiler profiler)
@ -89,15 +89,18 @@ namespace Grpc.Core.Profiling
this.entries = new ProfilerEntry[capacity];
}
public void Begin(string tag) {
public void Begin(string 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));
}
public void Mark(string tag) {
public void Mark(string 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>
</PropertyGroup>
<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">
<HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<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" />
<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>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="Google.Apis.Auth.PlatformServices">
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="Google.Apis.Core">
<HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="Google.Protobuf">
<HintPath>..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</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>
<Compile Include="..\Grpc.Core\Version.cs">
@ -104,6 +95,22 @@
<Compile Include="TestGrpc.cs" />
<Compile Include="SslCredentialsTest.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>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<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">
<SourceFileList>
<SourceFile>Messages.cs</SourceFile>
<SourceFile>Empty.cs</SourceFile>
<SourceFile>Control.cs</SourceFile>
<SourceFile>Messages.cs</SourceFile>
<SourceFile>Payloads.cs</SourceFile>
<SourceFile>Stats.cs</SourceFile>
<Settings>
<GlobalSettings>
<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
# Visual Studio 2013
# Visual Studio 2012
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck.Tests", "Grpc.HealthCheck.Tests\Grpc.HealthCheck.Tests.csproj", "{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.QpsWorker", "Grpc.IntegrationTesting.QpsWorker\Grpc.IntegrationTesting.QpsWorker.csproj", "{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -39,72 +41,78 @@ Global
ReleaseSigned|Any CPU = ReleaseSigned|Any CPU
EndGlobalSection
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.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.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.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.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.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.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.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.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.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.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.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.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.Build.0 = Debug|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.Build.0 = ReleaseSigned|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection

@ -35,14 +35,14 @@ cd $(dirname $0)
PROTOC=../../bins/opt/protobuf/protoc
PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin
EXAMPLES_DIR=Grpc.Examples
INTEROP_DIR=Grpc.IntegrationTesting
TESTING_DIR=Grpc.IntegrationTesting
HEALTHCHECK_DIR=Grpc.HealthCheck
$PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \
-I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$INTEROP_DIR --grpc_out=$INTEROP_DIR \
-I ../.. ../../test/proto/*.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \
-I ../.. ../../test/proto/*.proto ../../test/proto/benchmarks/*.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \
-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));
c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq,
"/foo", "foo.test.google.fr", gpr_inf_future(GPR_CLOCK_REALTIME),
NULL);
"/foo", "foo.test.google.fr",
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
GPR_ASSERT(c);
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) {
gpr_log(GPR_DEBUG, "PROXY UNREF %s", reason);
if (gpr_unref(&pc->refs)) {
grpc_call_destroy(pc->c2p);
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) {
gpr_log(GPR_DEBUG, "PROXY REF %s", reason);
gpr_ref(&pc->refs);
}
static void refpc(proxy_call *pc, const char *reason) { gpr_ref(&pc->refs); }
static void on_c2p_sent_initial_metadata(void *arg, int success) {
proxy_call *pc = arg;

@ -37,8 +37,8 @@ import collections
import hashlib
FixtureOptions = collections.namedtuple('FixtureOptions', 'fullstack includes_proxy dns_resolver secure platforms ci_mac')
default_unsecure_fixture_options = FixtureOptions(True, False, True, False, ['windows', 'linux', 'mac', 'posix'], True)
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, 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)
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_sockpair_1byte': 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+poll': default_secure_fixture_options._replace(platforms=['linux']),
'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,
}
TestOptions = collections.namedtuple('TestOptions', 'needs_fullstack needs_dns proxyable flaky secure')
default_test_options = TestOptions(False, False, True, False, False)
TestOptions = collections.namedtuple('TestOptions', 'needs_fullstack needs_dns proxyable flaky secure traceable')
default_test_options = TestOptions(False, False, True, False, False, True)
connectivity_test_options = default_test_options._replace(needs_fullstack=True)
# maps test names to options
@ -85,6 +85,7 @@ END2END_TESTS = {
'disappearing_server': connectivity_test_options,
'empty_batch': 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,
'invoke_large_request': default_test_options,
'large_metadata': default_test_options,
@ -117,6 +118,9 @@ def compatible(f, t):
if not END2END_TESTS[t].proxyable:
if END2END_FIXTURES[f].includes_proxy:
return False
if not END2END_TESTS[t].traceable:
if END2END_FIXTURES[f].tracing:
return False
return True

@ -166,7 +166,8 @@ static void simple_request_body(grpc_end2end_test_fixture f, size_t num_ops) {
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;
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);
}
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;
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));
return -1;
}
bytes_read += (size_t) err2;
bytes_read += (size_t)err2;
} while (bytes_read < read_size);
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);
do {
do {
err2 = read(args->fds.read_fd, buf + bytes_read,
read_size - bytes_read);
err2 =
read(args->fds.read_fd, buf + bytes_read, read_size - bytes_read);
} while (err2 < 0 && errno == EINTR);
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
done to ensure we see an EAGAIN */
} 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,
NULL);
grpc_call_credentials_get_request_metadata(&exec_ctx, creds, NULL,
test_service_url,
check_google_iam_metadata, creds);
test_service_url,
check_google_iam_metadata, creds);
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,
NULL);
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(google_iam_creds);
GPR_ASSERT(
@ -481,7 +482,8 @@ static void test_channel_oauth2_google_iam_composite_creds(void) {
grpc_call_credentials *oauth2_creds =
grpc_access_token_credentials_create("blah", NULL);
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(
test_google_iam_authorization_token, test_google_iam_authority_selector,
NULL);

@ -185,8 +185,8 @@ static void test_byte_buffer_from_reader(void) {
}
static void test_readall(void) {
char* lotsa_as[512];
char* lotsa_bs[1024];
char *lotsa_as[512];
char *lotsa_bs[1024];
gpr_slice slices[2];
grpc_byte_buffer *buffer;
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_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 */
test_vector(&parser, mode,
"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_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 */
test_vector(&parser, mode,
"4882 6402 5885 aec3 771a 4b61 96d0 7abe"

@ -143,9 +143,12 @@ static void test_many_additions(void) {
grpc_chttp2_hptbl_init(&tbl, mdctx);
for (i = 0; i < 1000000; i++) {
grpc_mdelem *elem;
gpr_asprintf(&key, "K:%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);
gpr_free(key);
gpr_free(value);
@ -173,18 +176,25 @@ static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
static void test_find(void) {
grpc_chttp2_hptbl tbl;
int i;
gpr_uint32 i;
char buffer[32];
grpc_mdctx *mdctx;
grpc_mdelem *elem;
grpc_chttp2_hptbl_find_result r;
LOG_TEST("test_find");
mdctx = grpc_mdctx_create();
grpc_chttp2_hptbl_init(&tbl, mdctx);
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "xyz"));
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "123"));
grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "x", "1"));
elem = grpc_mdelem_from_strings(mdctx, "abc", "xyz");
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
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");
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 */
for (i = 0; i < 10000; i++) {
gpr_ltoa(i, buffer);
grpc_chttp2_hptbl_add(&tbl,
grpc_mdelem_from_strings(mdctx, "test", buffer));
elem = 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");
@ -250,7 +261,7 @@ static void test_find(void) {
GPR_ASSERT(r.has_value == 1);
for (i = 0; i < tbl.num_ents; i++) {
int expect = 9999 - i;
gpr_uint32 expect = 9999 - i;
gpr_ltoa(expect, 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) {
struct sockaddr_in addr;
grpc_tcp_listener *listener;
int port_added;
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));
server->tcp_server = grpc_tcp_server_create();
port_added =
grpc_tcp_server_add_port(server->tcp_server, &addr, sizeof(addr));
listener = grpc_tcp_server_add_port(server->tcp_server, &addr, sizeof(addr));
port_added = grpc_tcp_listener_get_port(listener);
GPR_ASSERT(port_added == port);
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 "test/cpp/interop/interop_client.h"
#include "test/cpp/util/metrics_server.h"
namespace grpc {
namespace testing {
@ -81,21 +82,19 @@ TestCaseType WeightedRandomTestSelector::GetNextTest() const {
StressTestInteropClient::StressTestInteropClient(
int test_id, const grpc::string& server_address,
std::shared_ptr<Channel> channel,
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),
server_address_(server_address),
channel_(channel),
interop_client_(new InteropClient(channel, false)),
test_selector_(test_selector),
test_duration_secs_(test_duration_secs),
sleep_duration_ms_(sleep_duration_ms) {
// TODO(sreek): This will change once we add support for other tests
// that won't work with InsecureChannelCredentials()
std::shared_ptr<Channel> channel(
CreateChannel(server_address, InsecureChannelCredentials()));
interop_client_.reset(new InteropClient(channel, false));
}
sleep_duration_ms_(sleep_duration_ms),
metrics_collection_interval_secs_(metrics_collection_interval_secs) {}
void StressTestInteropClient::MainLoop() {
void StressTestInteropClient::MainLoop(std::shared_ptr<Gauge> qps_gauge) {
gpr_log(GPR_INFO, "Running test %d. ServerAddr: %s", test_id_,
server_address_.c_str());
@ -104,21 +103,38 @@ void StressTestInteropClient::MainLoop() {
gpr_time_from_seconds(test_duration_secs_, GPR_TIMESPAN));
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 ||
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
TestCaseType test_case = test_selector_.GetNextTest();
gpr_log(GPR_INFO, "%d - Executing the test case %d", test_id_, 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
if (sleep_duration_ms_ > 0) {
gpr_timespec sleep_time = gpr_time_add(
current_time, gpr_time_from_millis(sleep_duration_ms_, GPR_TIMESPAN));
gpr_timespec sleep_time =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_millis(sleep_duration_ms_, GPR_TIMESPAN));
gpr_sleep_until(sleep_time);
}
current_time = gpr_now(GPR_CLOCK_REALTIME);
}
}

@ -41,6 +41,7 @@
#include <grpc++/create_channel.h>
#include "test/cpp/interop/interop_client.h"
#include "test/cpp/util/metrics_server.h"
namespace grpc {
namespace testing {
@ -84,20 +85,26 @@ class WeightedRandomTestSelector {
class StressTestInteropClient {
public:
StressTestInteropClient(int test_id, const grpc::string& server_address,
std::shared_ptr<Channel> channel,
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:
void RunTest(TestCaseType test_case);
int test_id_;
std::unique_ptr<InteropClient> interop_client_;
const grpc::string& server_address_;
std::shared_ptr<Channel> channel_;
std::unique_ptr<InteropClient> interop_client_;
const WeightedRandomTestSelector& test_selector_;
long test_duration_secs_;
long sleep_duration_ms_;
long metrics_collection_interval_secs_;
};
} // namespace testing

@ -45,7 +45,15 @@
#include "test/cpp/interop/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/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,
"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"
" 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
DEFINE_string(test_cases, "",
"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"
" 70% of the time");
using std::make_pair;
using std::pair;
using std::vector;
using grpc::testing::kTestCaseList;
using grpc::testing::MetricsService;
using grpc::testing::MetricsServiceImpl;
using grpc::testing::StressTestInteropClient;
using grpc::testing::TestCaseType;
using grpc::testing::WeightedRandomTestSelector;
using grpc::testing::UNKNOWN_TEST;
using grpc::testing::WeightedRandomTestSelector;
TestCaseType GetTestTypeFromName(const grpc::string& test_name) {
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
bool ParseCommaDelimitedString(const grpc::string& comma_delimited_str,
vector<grpc::string>& tokens) {
std::vector<grpc::string>& tokens) {
size_t bpos = 0;
size_t epos = grpc::string::npos;
@ -122,10 +133,10 @@ bool ParseCommaDelimitedString(const grpc::string& comma_delimited_str,
// - Whether parsing was successful (return value)
// - Vector of (test_type_enum, weight) pairs returned via 'tests' parameter
bool ParseTestCasesString(const grpc::string& test_cases,
vector<pair<TestCaseType, int>>& tests) {
std::vector<std::pair<TestCaseType, int>>& tests) {
bool is_success = true;
vector<grpc::string> tokens;
std::vector<grpc::string> tokens;
ParseCommaDelimitedString(test_cases, tokens);
for (auto it = tokens.begin(); it != tokens.end(); it++) {
@ -153,8 +164,8 @@ bool ParseTestCasesString(const grpc::string& test_cases,
}
// For debugging purposes
void LogParameterInfo(const vector<grpc::string>& addresses,
const vector<pair<TestCaseType, int>>& tests) {
void LogParameterInfo(const std::vector<grpc::string>& addresses,
const std::vector<std::pair<TestCaseType, int>>& tests) {
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, "sleep_duration_ms: %d", FLAGS_sleep_duration_ms);
@ -180,7 +191,7 @@ int main(int argc, char** argv) {
srand(time(NULL));
// Parse the server addresses
vector<grpc::string> server_addresses;
std::vector<grpc::string> server_addresses;
ParseCommaDelimitedString(FLAGS_server_addresses, server_addresses);
// Parse test cases and weights
@ -189,7 +200,7 @@ int main(int argc, char** argv) {
return 1;
}
vector<pair<TestCaseType, int>> tests;
std::vector<std::pair<TestCaseType, int>> tests;
if (!ParseTestCasesString(FLAGS_test_cases, tests)) {
gpr_log(GPR_ERROR, "Error in parsing test cases string %s ",
FLAGS_test_cases.c_str());
@ -199,23 +210,48 @@ int main(int argc, char** argv) {
LogParameterInfo(server_addresses, tests);
WeightedRandomTestSelector test_selector(tests);
MetricsServiceImpl metrics_service;
gpr_log(GPR_INFO, "Starting test(s)..");
vector<grpc::thread> test_threads;
std::vector<grpc::thread> test_threads;
int thread_idx = 0;
for (auto it = server_addresses.begin(); it != server_addresses.end(); it++) {
StressTestInteropClient* client = new StressTestInteropClient(
++thread_idx, *it, test_selector, FLAGS_test_duration_secs,
FLAGS_sleep_duration_ms);
test_threads.emplace_back(
grpc::thread(&StressTestInteropClient::MainLoop, client));
// TODO(sreek): This will change once we add support for other tests
// that won't work with InsecureChannelCredentials()
std::shared_ptr<grpc::Channel> channel(
grpc::CreateChannel(*it, grpc::InsecureChannelCredentials()));
// 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++) {
it->join();
}
metrics_server->Wait();
return 0;
}

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

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

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

@ -110,7 +110,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
list<ClientContext> contexts;
// 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;
ServerConfig result_server_config;

@ -42,7 +42,10 @@ namespace testing {
class Histogram {
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() {
if (impl_) gpr_histogram_destroy(impl_);
}
@ -73,6 +76,9 @@ class Histogram {
p.sum_of_squares(), p.count());
}
static double default_resolution() { return 0.01; }
static double default_max_possible() { return 60e9; }
private:
Histogram(const Histogram&);
Histogram& operator=(const Histogram&);

@ -137,8 +137,14 @@ static void QpsDriver() {
// 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;
server_config.set_server_type(server_type);
server_config.set_host("localhost");
server_config.set_async_server_threads(FLAGS_async_server_threads);
if (FLAGS_secure_test) {

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

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

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

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

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

@ -89,7 +89,7 @@ class SynchronousServer GRPC_FINAL : public grpc::testing::Server {
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,
Server::CreateServerCredentials(config));
gpr_free(server_address);

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

@ -57,6 +57,7 @@ static void RunSynchronousUnaryPingPong() {
ServerConfig server_config;
server_config.set_server_type(SYNC_SERVER);
server_config.set_host("localhost");
const auto result =
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;
}
// Parameters of poisson process distribution, which is a good representation
// of activity coming in from independent identical stationary sources.
message PoissonParams {
// The rate of arrivals (a.k.a. lambda parameter of the exp distribution).
double offered_load = 1;
}
@ -67,6 +70,8 @@ message ParetoParams {
double alpha = 2;
}
// Once an RPC finishes, immediately start a new one.
// No configuration parameters needed.
message ClosedLoopParams {
}
@ -87,16 +92,23 @@ message SecurityParams {
}
message ClientConfig {
// List of targets to connect to. At least one target needs to be specified.
repeated string server_targets = 1;
ClientType client_type = 2;
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;
// Number of independent client channels to create.
// i-th channel will connect to server_target[i % server_targets.size()]
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;
RpcType rpc_type = 8;
// The requested load for the entire client (aggregated over all the threads).
LoadParams load_params = 10;
PayloadConfig payload_config = 11;
HistogramParams histogram_params = 12;
}
message ClientStatus {
@ -105,6 +117,7 @@ message ClientStatus {
// Request current stats
message Mark {
// if true, the stats will be reset after taking their snapshot.
bool reset = 1;
}
@ -118,10 +131,13 @@ message ClientArgs {
message ServerConfig {
ServerType server_type = 1;
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;
// only for async server
// Only for async server. Number of threads used to serve the requests.
int32 async_server_threads = 7;
// restrict core usage
// restrict core usage, currently unused
int32 core_limit = 8;
PayloadConfig payload_config = 9;
}
@ -135,6 +151,8 @@ message ServerArgs {
message ServerStatus {
ServerStats stats = 1;
// the port bound by the server
int32 port = 2;
// Number of cores on the server. See gpr_cpu_num_cores.
int32 cores = 3;
}

@ -47,9 +47,19 @@ service BenchmarkService {
}
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);
// 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);
}

@ -32,16 +32,24 @@ syntax = "proto3";
package grpc.testing;
message ServerStats {
// wall clock time
// wall clock time change in seconds since last reset
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;
// 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;
}
// 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 {
repeated uint32 bucket = 1;
double min_seen = 2;
@ -52,7 +60,10 @@ message HistogramData {
}
message ClientStats {
// Latency histogram. Data points are in nanoseconds.
HistogramData latencies = 1;
// See ServerStats for details.
double time_elapsed = 2;
double time_user = 3;
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
}
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) {
var conn net.Conn
var err error

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

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

@ -29,6 +29,11 @@
"""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 string
import xml.etree.cElementTree as ET
@ -49,7 +54,7 @@ def _filter_msg(msg, output_format):
return msg
def render_xml_report(resultset, xml_report):
def render_junit_xml_report(resultset, xml_report):
"""Generate JUnit-like XML report."""
root = ET.Element('testsuites')
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')
# TODO(adelez): Use mako template.
def fill_one_test_result(shortname, resultset, html_str):
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':
html_str = '%s<td bgcolor=\"green\">PASS</td>\n' % html_str
else:
tooltip = ''
if result.returncode > 0 or result.message:
if result.returncode > 0:
tooltip = 'returncode: %d ' % result.returncode
if result.message:
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_interop_html_report(
client_langs, server_langs, test_cases, auth_test_cases, http2_cases,
resultset, num_failures, cloud_to_prod, http2_interop):
"""Generate HTML report for interop tests."""
html_report_dir = 'reports'
template_file = os.path.join(html_report_dir, 'interop_html_report.template')
try:
mytemplate = Template(filename=template_file, format_exceptions=True)
except NameError:
print 'Mako template is not installed. Skipping HTML report generation.'
return
except IOError as e:
print 'Failed to find the template %s: %s' % (template_file, e)
return
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_auth_test_cases = sorted(auth_test_cases)
sorted_http2_cases = sorted(http2_cases)
sorted_client_langs = sorted(client_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'
'<script>\n'
'$(document).ready(function(){'
'$(\'[data-toggle=\"tooltip\"]\').tooltip();\n'
'});\n'
'</script>\n'
'</body>\n'
'</html>') % html_str
# 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)
args = {'client_langs': sorted_client_langs,
'server_langs': sorted_server_langs,
'test_cases': sorted_test_cases,
'auth_test_cases': sorted_auth_test_cases,
'http2_cases': sorted_http2_cases,
'resultset': resultset,
'num_failures': num_failures,
'cloud_to_prod': cloud_to_prod,
'http2_interop': http2_interop}
html_file_path = os.path.join(html_report_dir, 'index.html')
with open(html_file_path, 'w') as f:
f.write(html_str)
with open(html_file_path, 'w') as output_file:
mytemplate.render_context(Context(output_file, **args))

@ -37,7 +37,6 @@ import jobset
import multiprocessing
import os
import report_utils
import subprocess
import sys
import tempfile
import time
@ -170,7 +169,7 @@ class Http2Client:
self.safename = str(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):
return {}
@ -306,7 +305,7 @@ _TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
_AUTH_TEST_CASES = ['compute_engine_creds', 'jwt_token_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):
"""Wraps given cmdline array to create 'docker run' cmdline from it."""
@ -686,9 +685,9 @@ try:
else:
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,
_HTTP2_TEST_CASES, resultset, num_failures,
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),
'ubsan': SimpleConfig('ubsan'),
'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'}),
'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'),
'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
'helgrind': ValgrindConfig('dbg', 'helgrind')
@ -902,7 +902,7 @@ def _build_and_run(
for antagonist in antagonists:
antagonist.kill()
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(
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