aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler/csharp_generator.cc27
-rw-r--r--src/compiler/python_generator.cc3
-rw-r--r--src/compiler/python_generator.h1
-rw-r--r--src/compiler/python_plugin.cc2
-rw-r--r--src/core/ext/census/grpc_filter.c3
-rw-r--r--src/core/ext/client_config/channel_connectivity.c7
-rw-r--r--src/core/ext/client_config/client_channel.c6
-rw-r--r--src/core/ext/client_config/subchannel.c2
-rw-r--r--src/core/ext/client_config/subchannel_call_holder.c9
-rw-r--r--src/core/ext/transport/chttp2/server/insecure/server_chttp2.c3
-rw-r--r--src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c3
-rw-r--r--src/core/ext/transport/chttp2/transport/chttp2_transport.c254
-rw-r--r--src/core/ext/transport/chttp2/transport/internal.h6
-rw-r--r--src/core/lib/channel/channel_stack.c2
-rw-r--r--src/core/lib/debug/trace.c8
-rw-r--r--src/core/lib/iomgr/error.c83
-rw-r--r--src/core/lib/iomgr/error.h15
-rw-r--r--src/core/lib/iomgr/ev_epoll_linux.c1872
-rw-r--r--src/core/lib/iomgr/ev_epoll_linux.h (renamed from src/core/lib/surface/surface_trace.h)23
-rw-r--r--src/core/lib/iomgr/ev_posix.c15
-rw-r--r--src/core/lib/iomgr/ev_posix.h3
-rw-r--r--src/core/lib/iomgr/network_status_tracker.c121
-rw-r--r--src/core/lib/iomgr/network_status_tracker.h41
-rw-r--r--src/core/lib/iomgr/socket_utils_common_posix.c22
-rw-r--r--src/core/lib/iomgr/socket_utils_posix.h3
-rw-r--r--src/core/lib/iomgr/tcp_posix.c8
-rw-r--r--src/core/lib/iomgr/tcp_server.h3
-rw-r--r--src/core/lib/iomgr/tcp_server_posix.c123
-rw-r--r--src/core/lib/iomgr/tcp_server_windows.c3
-rw-r--r--src/core/lib/iomgr/tcp_windows.c5
-rw-r--r--src/core/lib/iomgr/udp_server.c4
-rw-r--r--src/core/lib/iomgr/wakeup_fd_eventfd.c6
-rw-r--r--src/core/lib/profiling/basic_timers.c8
-rw-r--r--src/core/lib/security/credentials/composite/composite_credentials.c7
-rw-r--r--src/core/lib/security/credentials/credentials.c2
-rw-r--r--src/core/lib/security/credentials/credentials.h9
-rw-r--r--src/core/lib/security/credentials/fake/fake_credentials.c4
-rw-r--r--src/core/lib/security/credentials/iam/iam_credentials.c2
-rw-r--r--src/core/lib/security/credentials/jwt/jwt_credentials.c13
-rw-r--r--src/core/lib/security/credentials/oauth2/oauth2_credentials.c10
-rw-r--r--src/core/lib/security/credentials/plugin/plugin_credentials.c7
-rw-r--r--src/core/lib/security/transport/client_auth_filter.c13
-rw-r--r--src/core/lib/surface/call.c106
-rw-r--r--src/core/lib/surface/call.h1
-rw-r--r--src/core/lib/surface/channel.c16
-rw-r--r--src/core/lib/surface/completion_queue.c38
-rw-r--r--src/core/lib/surface/completion_queue.h4
-rw-r--r--src/core/lib/surface/init.c7
-rw-r--r--src/core/lib/surface/version.c2
-rw-r--r--src/core/lib/transport/transport.c67
-rw-r--r--src/core/lib/transport/transport.h9
-rw-r--r--src/core/lib/transport/transport_op_string.c19
-rw-r--r--src/cpp/README.md2
-rw-r--r--src/csharp/Grpc.Auth/project.json13
-rw-r--r--src/csharp/Grpc.Core.Tests/CompressionTest.cs26
-rw-r--r--src/csharp/Grpc.Core.Tests/project.json14
-rw-r--r--src/csharp/Grpc.Core/Grpc.Core.nuspec13
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs23
-rw-r--r--src/csharp/Grpc.Core/Metadata.cs7
-rw-r--r--src/csharp/Grpc.Core/VersionInfo.cs4
-rw-r--r--src/csharp/Grpc.Core/project.json24
-rw-r--r--src/csharp/Grpc.Examples.MathClient/project.json14
-rw-r--r--src/csharp/Grpc.Examples.MathServer/project.json14
-rw-r--r--src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs2
-rw-r--r--src/csharp/Grpc.Examples.Tests/project.json14
-rw-r--r--src/csharp/Grpc.Examples/MathGrpc.cs15
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs2
-rw-r--r--src/csharp/Grpc.HealthCheck.Tests/project.json14
-rw-r--r--src/csharp/Grpc.HealthCheck/HealthGrpc.cs15
-rw-r--r--src/csharp/Grpc.HealthCheck/project.json13
-rw-r--r--src/csharp/Grpc.IntegrationTesting.Client/project.json14
-rw-r--r--src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json14
-rw-r--r--src/csharp/Grpc.IntegrationTesting.Server/project.json14
-rw-r--r--src/csharp/Grpc.IntegrationTesting.StressClient/project.json14
-rw-r--r--src/csharp/Grpc.IntegrationTesting/ClientRunners.cs6
-rw-r--r--src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs2
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropClient.cs131
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs4
-rw-r--r--src/csharp/Grpc.IntegrationTesting/Messages.cs414
-rw-r--r--src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs4
-rw-r--r--src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs15
-rw-r--r--src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs30
-rw-r--r--src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs2
-rw-r--r--src/csharp/Grpc.IntegrationTesting/StressTestClient.cs2
-rw-r--r--src/csharp/Grpc.IntegrationTesting/TestGrpc.cs45
-rw-r--r--src/csharp/Grpc.IntegrationTesting/project.json14
-rw-r--r--src/csharp/build_packages.bat2
-rw-r--r--src/csharp/ext/grpc_csharp_ext.c4
-rw-r--r--src/node/ext/node_grpc.cc127
-rw-r--r--src/node/index.js43
-rw-r--r--src/node/src/common.js21
-rw-r--r--src/node/src/credentials.js4
-rw-r--r--src/node/src/server.js4
-rw-r--r--src/node/test/credentials_test.js4
-rw-r--r--src/node/tools/package.json2
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+ChannelArg.h5
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+ChannelArg.m4
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h4
-rw-r--r--src/objective-c/GRPCClient/private/GRPCChannel.m4
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.h2
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.m21
-rw-r--r--src/objective-c/GRPCClient/private/NSData+GRPC.m36
-rw-r--r--src/objective-c/ProtoRPC/ProtoMethod.h3
-rw-r--r--src/objective-c/ProtoRPC/ProtoRPC.h3
-rw-r--r--src/objective-c/ProtoRPC/ProtoService.h3
-rw-r--r--src/objective-c/ProtoRPC/ProtoService.m24
-rw-r--r--src/objective-c/examples/RemoteTestClient/RemoteTest.podspec4
-rw-r--r--src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj50
-rw-r--r--src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme91
-rw-r--r--src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj42
-rw-r--r--src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme91
-rw-r--r--src/objective-c/examples/SwiftSample/ViewController.swift5
-rw-r--r--src/objective-c/tests/GRPCClientTests.m36
-rw-r--r--src/objective-c/tests/InteropTests.m40
-rw-r--r--src/objective-c/tests/Podfile26
-rw-r--r--src/objective-c/tests/Tests.xcodeproj/project.pbxproj106
-rwxr-xr-xsrc/objective-c/tests/build_example_test.sh74
-rwxr-xr-xsrc/objective-c/tests/build_one_example.sh62
-rw-r--r--src/php/README.md4
-rw-r--r--src/php/ext/grpc/call.c1
-rw-r--r--src/python/grpcio/README.rst2
-rw-r--r--src/python/grpcio/grpc/_channel.py111
-rw-r--r--src/python/grpcio/grpc/_common.py43
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi5
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi8
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi1
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi36
-rw-r--r--src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi2
-rw-r--r--src/python/grpcio/grpc/_cython/cygrpc.pyx34
-rw-r--r--src/python/grpcio/grpc/_cython/imports.generated.c2
-rw-r--r--src/python/grpcio/grpc/_cython/imports.generated.h3
-rw-r--r--src/python/grpcio/grpc/_cython/loader.c7
-rw-r--r--src/python/grpcio/grpc/_cython/loader.h5
-rw-r--r--src/python/grpcio/grpc/_plugin_wrapping.py14
-rw-r--r--src/python/grpcio/grpc/_server.py42
-rw-r--r--src/python/grpcio/grpc/beta/_server_adaptations.py41
-rw-r--r--src/python/grpcio/grpc_core_dependencies.py2
-rw-r--r--src/python/grpcio/grpc_version.py2
-rw-r--r--src/python/grpcio/tests/interop/methods.py9
-rw-r--r--src/python/grpcio/tests/tests.json4
-rw-r--r--src/python/grpcio/tests/unit/_compression_test.py133
-rw-r--r--src/python/grpcio/tests/unit/_cython/_cancel_many_calls_test.py4
-rw-r--r--src/python/grpcio/tests/unit/_cython/_channel_test.py2
-rw-r--r--src/python/grpcio/tests/unit/_cython/_read_some_but_not_all_responses_test.py4
-rw-r--r--src/python/grpcio/tests/unit/_cython/cygrpc_test.py40
-rw-r--r--src/python/grpcio/tests/unit/_empty_message_test.py8
-rw-r--r--src/python/grpcio/tests/unit/_exit_scenarios.py249
-rw-r--r--src/python/grpcio/tests/unit/_exit_test.py185
-rw-r--r--src/python/grpcio/tests/unit/_metadata_code_details_test.py523
-rw-r--r--src/python/grpcio/tests/unit/_metadata_test.py24
-rw-r--r--src/python/grpcio/tests/unit/_rpc_test.py96
-rw-r--r--src/python/grpcio/tests/unit/beta/_connectivity_channel_test.py153
-rw-r--r--src/python/grpcio/tests/unit/beta/_utilities_test.py21
-rw-r--r--src/python/grpcio/tests/unit/beta/test_utilities.py2
-rw-r--r--src/python/grpcio/tests/unit/test_common.py18
-rw-r--r--src/ruby/ext/grpc/rb_grpc_imports.generated.c2
-rw-r--r--src/ruby/ext/grpc/rb_grpc_imports.generated.h3
-rw-r--r--src/ruby/lib/grpc/version.rb2
-rw-r--r--src/ruby/spec/client_server_spec.rb46
-rw-r--r--src/ruby/tools/version.rb2
160 files changed, 5644 insertions, 1182 deletions
diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc
index fc8feaf0fc..f5a0876cf9 100644
--- a/src/compiler/csharp_generator.cc
+++ b/src/compiler/csharp_generator.cc
@@ -333,22 +333,28 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
out->Indent();
// constructors
+ out->Print("/// <summary>Creates a new client for $servicename$</summary>\n"
+ "/// <param name=\"channel\">The channel to use to make remote calls.</param>\n",
+ "servicename", GetServiceClassName(service));
out->Print("public $name$(Channel channel) : base(channel)\n",
"name", GetClientClassName(service));
out->Print("{\n");
out->Print("}\n");
+ out->Print("/// <summary>Creates a new client for $servicename$ that uses a custom <c>CallInvoker</c>.</summary>\n"
+ "/// <param name=\"callInvoker\">The callInvoker to use to make remote calls.</param>\n",
+ "servicename", GetServiceClassName(service));
out->Print("public $name$(CallInvoker callInvoker) : base(callInvoker)\n",
"name", GetClientClassName(service));
out->Print("{\n");
out->Print("}\n");
- out->Print("///<summary>Protected parameterless constructor to allow creation"
+ out->Print("/// <summary>Protected parameterless constructor to allow creation"
" of test doubles.</summary>\n");
out->Print("protected $name$() : base()\n",
"name", GetClientClassName(service));
out->Print("{\n");
out->Print("}\n");
- out->Print("///<summary>Protected constructor to allow creation of configured"
- " clients.</summary>\n");
+ out->Print("/// <summary>Protected constructor to allow creation of configured clients.</summary>\n"
+ "/// <param name=\"configuration\">The client configuration.</param>\n");
out->Print("protected $name$(ClientBaseConfiguration configuration)"
" : base(configuration)\n",
"name", GetClientClassName(service));
@@ -485,20 +491,6 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service) {
out->Print("\n");
}
-void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) {
- out->Print("/// <summary>Creates a new client for $servicename$</summary>\n",
- "servicename", GetServiceClassName(service));
- out->Print("public static $classname$ NewClient(Channel channel)\n",
- "classname", GetClientClassName(service));
- out->Print("{\n");
- out->Indent();
- out->Print("return new $classname$(channel);\n", "classname",
- GetClientClassName(service));
- out->Outdent();
- out->Print("}\n");
- out->Print("\n");
-}
-
void GenerateService(Printer* out, const ServiceDescriptor *service,
bool generate_client, bool generate_server,
bool internal_access) {
@@ -524,7 +516,6 @@ void GenerateService(Printer* out, const ServiceDescriptor *service,
}
if (generate_client) {
GenerateClientStub(out, service);
- GenerateNewStubMethods(out, service);
}
if (generate_server) {
GenerateBindServiceMethod(out, service);
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
index 15cda474cc..c5f1ed8061 100644
--- a/src/compiler/python_generator.cc
+++ b/src/compiler/python_generator.cc
@@ -66,6 +66,9 @@ using std::vector;
namespace grpc_python_generator {
+GeneratorConfiguration::GeneratorConfiguration()
+ : grpc_package_root("grpc"), beta_package_root("grpc.beta") {}
+
PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
: config_(config) {}
diff --git a/src/compiler/python_generator.h b/src/compiler/python_generator.h
index fc51b48dae..7ed99eff0b 100644
--- a/src/compiler/python_generator.h
+++ b/src/compiler/python_generator.h
@@ -43,6 +43,7 @@ namespace grpc_python_generator {
// Data pertaining to configuration of the generator with respect to anything
// that may be used internally at Google.
struct GeneratorConfiguration {
+ GeneratorConfiguration();
grpc::string grpc_package_root;
grpc::string beta_package_root;
};
diff --git a/src/compiler/python_plugin.cc b/src/compiler/python_plugin.cc
index fc76ee5ec6..3457aa631a 100644
--- a/src/compiler/python_plugin.cc
+++ b/src/compiler/python_plugin.cc
@@ -38,8 +38,6 @@
int main(int argc, char* argv[]) {
grpc_python_generator::GeneratorConfiguration config;
- config.grpc_package_root = "grpc";
- config.beta_package_root = "grpc.beta";
grpc_python_generator::PythonGrpcGenerator generator(config);
return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
}
diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c
index c5a6e6d3dc..55046f4df8 100644
--- a/src/core/ext/census/grpc_filter.c
+++ b/src/core/ext/census/grpc_filter.c
@@ -45,6 +45,7 @@
#include "src/core/ext/census/census_interface.h"
#include "src/core/ext/census/census_rpc_stats.h"
#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/transport/static_metadata.h"
typedef struct call_data {
@@ -92,6 +93,7 @@ static void client_start_transport_op(grpc_exec_ctx *exec_ctx,
static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr,
grpc_error *error) {
+ GPR_TIMER_BEGIN("census-server:server_on_done_recv", 0);
grpc_call_element *elem = ptr;
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
@@ -99,6 +101,7 @@ static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr,
extract_and_annotate_method_tag(calld->recv_initial_metadata, calld, chand);
}
calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error);
+ GPR_TIMER_END("census-server:server_on_done_recv", 0);
}
static void server_mutate_op(grpc_call_element *elem,
diff --git a/src/core/ext/client_config/channel_connectivity.c b/src/core/ext/client_config/channel_connectivity.c
index 20c01a9a7c..c1220e3a8c 100644
--- a/src/core/ext/client_config/channel_connectivity.c
+++ b/src/core/ext/client_config/channel_connectivity.c
@@ -189,10 +189,11 @@ void grpc_channel_watch_connectivity_state(
GRPC_API_TRACE(
"grpc_channel_watch_connectivity_state("
"channel=%p, last_observed_state=%d, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"cq=%p, tag=%p)",
- 7, (channel, (int)last_observed_state, (long long)deadline.tv_sec,
- (int)deadline.tv_nsec, (int)deadline.clock_type, cq, tag));
+ 7, (channel, (int)last_observed_state, deadline.tv_sec, deadline.tv_nsec,
+ (int)deadline.clock_type, cq, tag));
grpc_cq_begin_op(cq, tag);
diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c
index 84e3fb4da6..b7cff88a8c 100644
--- a/src/core/ext/client_config/client_channel.c
+++ b/src/core/ext/client_config/client_channel.c
@@ -367,6 +367,8 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
uint32_t initial_metadata_flags,
grpc_connected_subchannel **connected_subchannel,
grpc_closure *on_ready) {
+ GPR_TIMER_BEGIN("cc_pick_subchannel", 0);
+
grpc_call_element *elem = elemp;
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
@@ -391,6 +393,7 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
}
}
gpr_mu_unlock(&chand->mu_config);
+ GPR_TIMER_END("cc_pick_subchannel", 0);
return 1;
}
if (chand->lb_policy != NULL) {
@@ -402,6 +405,7 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
initial_metadata, initial_metadata_flags,
connected_subchannel, on_ready);
GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "cc_pick_subchannel");
+ GPR_TIMER_END("cc_pick_subchannel", 0);
return r;
}
if (chand->resolver != NULL && !chand->started_resolving) {
@@ -426,6 +430,8 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
NULL);
}
gpr_mu_unlock(&chand->mu_config);
+
+ GPR_TIMER_END("cc_pick_subchannel", 0);
return 0;
}
diff --git a/src/core/ext/client_config/subchannel.c b/src/core/ext/client_config/subchannel.c
index 495ca46cc8..df35904b85 100644
--- a/src/core/ext/client_config/subchannel.c
+++ b/src/core/ext/client_config/subchannel.c
@@ -690,9 +690,11 @@ char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx,
void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx,
grpc_subchannel_call *call,
grpc_transport_stream_op *op) {
+ GPR_TIMER_BEGIN("grpc_subchannel_call_process_op", 0);
grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call);
grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0);
top_elem->filter->start_transport_stream_op(exec_ctx, top_elem, op);
+ GPR_TIMER_END("grpc_subchannel_call_process_op", 0);
}
grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel(
diff --git a/src/core/ext/client_config/subchannel_call_holder.c b/src/core/ext/client_config/subchannel_call_holder.c
index 5b54233153..ac286825f6 100644
--- a/src/core/ext/client_config/subchannel_call_holder.c
+++ b/src/core/ext/client_config/subchannel_call_holder.c
@@ -125,17 +125,14 @@ retry:
return;
}
/* if this is a cancellation, then we can raise our cancelled flag */
- if (op->cancel_with_status != GRPC_STATUS_OK) {
+ if (op->cancel_error != GRPC_ERROR_NONE) {
if (!gpr_atm_rel_cas(&holder->subchannel_call, 0,
(gpr_atm)(uintptr_t)CANCELLED_CALL)) {
goto retry;
} else {
switch (holder->creation_phase) {
case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
- fail_locked(exec_ctx, holder,
- grpc_error_set_int(GRPC_ERROR_CREATE("Cancelled"),
- GRPC_ERROR_INT_GRPC_STATUS,
- op->cancel_with_status));
+ fail_locked(exec_ctx, holder, GRPC_ERROR_REF(op->cancel_error));
break;
case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, NULL,
@@ -210,7 +207,7 @@ static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
exec_ctx, holder->connected_subchannel, holder->pollent,
&subchannel_call);
if (new_error != GRPC_ERROR_NONE) {
- grpc_error_add_child(new_error, error);
+ new_error = grpc_error_add_child(new_error, error);
subchannel_call = CANCELLED_CALL;
fail_locked(exec_ctx, holder, new_error);
}
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
index 9bae3a94f9..e5c987925c 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
@@ -97,7 +97,8 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) {
goto error;
}
- err = grpc_tcp_server_create(NULL, &tcp);
+ err =
+ grpc_tcp_server_create(NULL, grpc_server_get_channel_args(server), &tcp);
if (err != GRPC_ERROR_NONE) {
goto error;
}
diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
index ead8a4d566..c42810e913 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -216,7 +216,8 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
state = gpr_malloc(sizeof(*state));
memset(state, 0, sizeof(*state));
grpc_closure_init(&state->destroy_closure, destroy_done, state);
- err = grpc_tcp_server_create(&state->destroy_closure, &tcp);
+ err = grpc_tcp_server_create(&state->destroy_closure,
+ grpc_server_get_channel_args(server), &tcp);
if (err != GRPC_ERROR_NONE) {
goto error;
}
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 9c99ff883a..38e782b9b4 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -106,14 +106,12 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
static void cancel_from_api(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global,
- grpc_status_code status,
- gpr_slice *optional_message);
+ grpc_error *error);
static void close_from_api(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global,
- grpc_status_code status,
- gpr_slice *optional_message);
+ grpc_error *error);
/** Add endpoint from this transport to pollset */
static void add_to_pollset_locked(grpc_exec_ctx *exec_ctx,
@@ -163,8 +161,6 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
GPR_ASSERT(t->ep == NULL);
- gpr_slice_unref(t->optional_drop_message);
-
gpr_slice_buffer_destroy(&t->global.qbuf);
gpr_slice_buffer_destroy(&t->writing.outbuf);
@@ -266,7 +262,6 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0;
t->parsing.is_first_frame = true;
t->writing.is_client = is_client;
- t->optional_drop_message = gpr_empty_slice();
grpc_connectivity_state_init(
&t->channel_callback.state_tracker, GRPC_CHANNEL_READY,
is_client ? "client_transport" : "server_transport");
@@ -594,6 +589,7 @@ static void destroy_stream_locked(grpc_exec_ctx *exec_ctx,
grpc_chttp2_incoming_metadata_buffer_destroy(
&s->global.received_trailing_metadata);
gpr_slice_buffer_destroy(&s->writing.flow_controlled_buffer);
+ GRPC_ERROR_UNREF(s->global.removal_error);
UNREF_TRANSPORT(exec_ctx, t, "stream");
@@ -642,6 +638,8 @@ static void finish_global_actions(grpc_exec_ctx *exec_ctx,
grpc_chttp2_executor_action_header *hdr;
grpc_chttp2_executor_action_header *next;
+ GPR_TIMER_BEGIN("finish_global_actions", 0);
+
for (;;) {
if (!t->executor.writing_active && !t->closed &&
grpc_chttp2_unlocking_check_writes(exec_ctx, &t->global, &t->writing)) {
@@ -659,7 +657,9 @@ static void finish_global_actions(grpc_exec_ctx *exec_ctx,
NULL;
gpr_mu_unlock(&t->executor.mu);
while (hdr != NULL) {
+ GPR_TIMER_BEGIN("chttp2:locked_action", 0);
hdr->action(exec_ctx, t, hdr->stream, hdr->arg);
+ GPR_TIMER_END("chttp2:locked_action", 0);
next = hdr->next;
gpr_free(hdr);
UNREF_TRANSPORT(exec_ctx, t, "pending_action");
@@ -672,6 +672,8 @@ static void finish_global_actions(grpc_exec_ctx *exec_ctx,
gpr_mu_unlock(&t->executor.mu);
break;
}
+
+ GPR_TIMER_END("finish_global_actions", 0);
}
void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
@@ -681,6 +683,8 @@ void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
void *arg, size_t sizeof_arg) {
grpc_chttp2_executor_action_header *hdr;
+ GPR_TIMER_BEGIN("grpc_chttp2_run_with_global_lock", 0);
+
REF_TRANSPORT(t, "run_global");
gpr_mu_lock(&t->executor.mu);
@@ -689,7 +693,9 @@ void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
t->executor.global_active = 1;
gpr_mu_unlock(&t->executor.mu);
+ GPR_TIMER_BEGIN("chttp2:locked_action", 0);
action(exec_ctx, t, optional_stream, arg);
+ GPR_TIMER_END("chttp2:locked_action", 0);
finish_global_actions(exec_ctx, t);
} else {
@@ -726,6 +732,8 @@ void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
}
UNREF_TRANSPORT(exec_ctx, t, "run_global");
+
+ GPR_TIMER_END("grpc_chttp2_run_with_global_lock", 0);
}
/*******************************************************************************
@@ -876,7 +884,9 @@ static void maybe_start_some_streams(
grpc_chttp2_list_pop_waiting_for_concurrency(transport_global,
&stream_global)) {
cancel_from_api(exec_ctx, transport_global, stream_global,
- GRPC_STATUS_UNAVAILABLE, NULL);
+ grpc_error_set_int(
+ GRPC_ERROR_CREATE("Stream IDs exhausted"),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
}
}
@@ -958,14 +968,14 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT;
}
- if (op->cancel_with_status != GRPC_STATUS_OK) {
+ if (op->cancel_error != GRPC_ERROR_NONE) {
cancel_from_api(exec_ctx, transport_global, stream_global,
- op->cancel_with_status, op->optional_close_message);
+ GRPC_ERROR_REF(op->cancel_error));
}
- if (op->close_with_status != GRPC_STATUS_OK) {
+ if (op->close_error != GRPC_ERROR_NONE) {
close_from_api(exec_ctx, transport_global, stream_global,
- op->close_with_status, op->optional_close_message);
+ GRPC_ERROR_REF(op->close_error));
}
if (op->send_initial_metadata != NULL) {
@@ -979,12 +989,16 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
transport_global->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
if (metadata_size > metadata_peer_limit) {
- gpr_log(GPR_DEBUG,
- "to-be-sent initial metadata size exceeds peer limit "
- "(%" PRIuPTR " vs. %" PRIuPTR ")",
- metadata_size, metadata_peer_limit);
- cancel_from_api(exec_ctx, transport_global, stream_global,
- GRPC_STATUS_RESOURCE_EXHAUSTED, NULL);
+ cancel_from_api(
+ exec_ctx, transport_global, stream_global,
+ grpc_error_set_int(
+ grpc_error_set_int(
+ grpc_error_set_int(
+ GRPC_ERROR_CREATE("to-be-sent initial metadata size "
+ "exceeds peer limit"),
+ GRPC_ERROR_INT_SIZE, (intptr_t)metadata_size),
+ GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
} else {
if (contains_non_ok_status(transport_global, op->send_initial_metadata)) {
stream_global->seen_error = true;
@@ -1038,12 +1052,16 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
transport_global->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
if (metadata_size > metadata_peer_limit) {
- gpr_log(GPR_DEBUG,
- "to-be-sent trailing metadata size exceeds peer limit "
- "(%" PRIuPTR " vs. %" PRIuPTR ")",
- metadata_size, metadata_peer_limit);
- cancel_from_api(exec_ctx, transport_global, stream_global,
- GRPC_STATUS_RESOURCE_EXHAUSTED, NULL);
+ cancel_from_api(
+ exec_ctx, transport_global, stream_global,
+ grpc_error_set_int(
+ grpc_error_set_int(
+ grpc_error_set_int(
+ GRPC_ERROR_CREATE("to-be-sent trailing metadata size "
+ "exceeds peer limit"),
+ GRPC_ERROR_INT_SIZE, (intptr_t)metadata_size),
+ GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
} else {
if (contains_non_ok_status(transport_global,
op->send_trailing_metadata)) {
@@ -1235,8 +1253,12 @@ static void check_read_ops(grpc_exec_ctx *exec_ctx,
incoming_byte_stream_destroy_locked(exec_ctx, NULL, NULL, bs);
}
if (stream_global->exceeded_metadata_size) {
- cancel_from_api(exec_ctx, transport_global, stream_global,
- GRPC_STATUS_RESOURCE_EXHAUSTED, NULL);
+ cancel_from_api(
+ exec_ctx, transport_global, stream_global,
+ grpc_error_set_int(
+ GRPC_ERROR_CREATE(
+ "received initial metadata size exceeds limit"),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
}
}
grpc_chttp2_incoming_metadata_buffer_publish(
@@ -1275,8 +1297,12 @@ static void check_read_ops(grpc_exec_ctx *exec_ctx,
incoming_byte_stream_destroy_locked(exec_ctx, NULL, NULL, bs);
}
if (stream_global->exceeded_metadata_size) {
- cancel_from_api(exec_ctx, transport_global, stream_global,
- GRPC_STATUS_RESOURCE_EXHAUSTED, NULL);
+ cancel_from_api(
+ exec_ctx, transport_global, stream_global,
+ grpc_error_set_int(
+ GRPC_ERROR_CREATE(
+ "received trailing metadata size exceeds limit"),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
}
}
if (stream_global->all_incoming_byte_streams_finished) {
@@ -1316,15 +1342,15 @@ static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
}
if (s->parsing.data_parser.parsing_frame != NULL) {
grpc_chttp2_incoming_byte_stream_finished(
- exec_ctx, s->parsing.data_parser.parsing_frame,
- GRPC_ERROR_CREATE_REFERENCING("Stream removed", &error, 1), 0);
+ exec_ctx, s->parsing.data_parser.parsing_frame, GRPC_ERROR_REF(error),
+ 0);
s->parsing.data_parser.parsing_frame = NULL;
}
if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) {
close_transport_locked(
- exec_ctx, t,
- GRPC_ERROR_CREATE("Last stream closed after sending GOAWAY"));
+ exec_ctx, t, GRPC_ERROR_CREATE_REFERENCING(
+ "Last stream closed after sending GOAWAY", &error, 1));
}
if (grpc_chttp2_list_remove_writable_stream(&t->global, &s->global)) {
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, &s->global, "chttp2_writing");
@@ -1340,35 +1366,67 @@ static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
GRPC_ERROR_UNREF(error);
}
+static void status_codes_from_error(grpc_error *error,
+ grpc_chttp2_error_code *http2_error,
+ grpc_status_code *grpc_status) {
+ intptr_t ip_http;
+ intptr_t ip_grpc;
+ bool have_http =
+ grpc_error_get_int(error, GRPC_ERROR_INT_HTTP2_ERROR, &ip_http);
+ bool have_grpc =
+ grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &ip_grpc);
+ if (have_http) {
+ *http2_error = (grpc_chttp2_error_code)ip_http;
+ } else if (have_grpc) {
+ *http2_error =
+ grpc_chttp2_grpc_status_to_http2_error((grpc_status_code)ip_grpc);
+ } else {
+ *http2_error = GRPC_CHTTP2_INTERNAL_ERROR;
+ }
+ if (have_grpc) {
+ *grpc_status = (grpc_status_code)ip_grpc;
+ } else if (have_http) {
+ *grpc_status =
+ grpc_chttp2_http2_error_to_grpc_status((grpc_chttp2_error_code)ip_http);
+ } else {
+ *grpc_status = GRPC_STATUS_INTERNAL;
+ }
+}
+
static void cancel_from_api(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global,
- grpc_status_code status,
- gpr_slice *optional_message) {
+ grpc_error *due_to_error) {
if (!stream_global->read_closed || !stream_global->write_closed) {
+ grpc_status_code grpc_status;
+ grpc_chttp2_error_code http_error;
+ status_codes_from_error(due_to_error, &http_error, &grpc_status);
+
if (stream_global->id != 0) {
gpr_slice_buffer_add(
&transport_global->qbuf,
- grpc_chttp2_rst_stream_create(
- stream_global->id,
- (uint32_t)grpc_chttp2_grpc_status_to_http2_error(status),
- &stream_global->stats.outgoing));
+ grpc_chttp2_rst_stream_create(stream_global->id, (uint32_t)http_error,
+ &stream_global->stats.outgoing));
}
- if (optional_message) {
- gpr_slice_ref(*optional_message);
+ const char *msg =
+ grpc_error_get_str(due_to_error, GRPC_ERROR_STR_GRPC_MESSAGE);
+ bool free_msg = false;
+ if (msg == NULL) {
+ free_msg = true;
+ msg = grpc_error_string(due_to_error);
}
- grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, status,
- optional_message);
+ gpr_slice msg_slice = gpr_slice_from_copied_string(msg);
+ grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global,
+ grpc_status, &msg_slice);
+ if (free_msg) grpc_error_free_string(msg);
}
- if (status != GRPC_STATUS_OK && !stream_global->seen_error) {
+ if (due_to_error != GRPC_ERROR_NONE && !stream_global->seen_error) {
stream_global->seen_error = true;
grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
}
- grpc_chttp2_mark_stream_closed(
- exec_ctx, transport_global, stream_global, 1, 1,
- grpc_error_set_int(GRPC_ERROR_CREATE("Cancelled"),
- GRPC_ERROR_INT_GRPC_STATUS, status));
+ grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1,
+ 1, due_to_error);
}
void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx,
@@ -1451,6 +1509,7 @@ void grpc_chttp2_mark_stream_closed(
}
}
if (stream_global->read_closed && stream_global->write_closed) {
+ stream_global->removal_error = GRPC_ERROR_REF(error);
if (stream_global->id != 0 &&
TRANSPORT_FROM_GLOBAL(transport_global)->executor.parsing_active) {
grpc_chttp2_list_add_closed_waiting_for_parsing(transport_global,
@@ -1469,15 +1528,17 @@ void grpc_chttp2_mark_stream_closed(
static void close_from_api(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global,
- grpc_status_code status,
- gpr_slice *optional_message) {
+ grpc_error *error) {
gpr_slice hdr;
gpr_slice status_hdr;
gpr_slice message_pfx;
uint8_t *p;
uint32_t len = 0;
+ grpc_status_code grpc_status;
+ grpc_chttp2_error_code http_error;
+ status_codes_from_error(error, &http_error, &grpc_status);
- GPR_ASSERT(status >= 0 && (int)status < 100);
+ GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100);
if (stream_global->id != 0 && !transport_global->is_client) {
/* Hand roll a header block.
@@ -1487,7 +1548,7 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
time we got around to sending this, so instead we ignore HPACK
compression
and just write the uncompressed bytes onto the wire. */
- status_hdr = gpr_slice_malloc(15 + (status >= 10));
+ status_hdr = gpr_slice_malloc(15 + (grpc_status >= 10));
p = GPR_SLICE_START_PTR(status_hdr);
*p++ = 0x40; /* literal header */
*p++ = 11; /* len(grpc-status) */
@@ -1502,19 +1563,23 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
*p++ = 't';
*p++ = 'u';
*p++ = 's';
- if (status < 10) {
+ if (grpc_status < 10) {
*p++ = 1;
- *p++ = (uint8_t)('0' + status);
+ *p++ = (uint8_t)('0' + grpc_status);
} else {
*p++ = 2;
- *p++ = (uint8_t)('0' + (status / 10));
- *p++ = (uint8_t)('0' + (status % 10));
+ *p++ = (uint8_t)('0' + (grpc_status / 10));
+ *p++ = (uint8_t)('0' + (grpc_status % 10));
}
GPR_ASSERT(p == GPR_SLICE_END_PTR(status_hdr));
len += (uint32_t)GPR_SLICE_LENGTH(status_hdr);
- if (optional_message) {
- GPR_ASSERT(GPR_SLICE_LENGTH(*optional_message) < 127);
+ const char *optional_message =
+ grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE);
+
+ if (optional_message != NULL) {
+ size_t msg_len = strlen(optional_message);
+ GPR_ASSERT(msg_len < 127);
message_pfx = gpr_slice_malloc(15);
p = GPR_SLICE_START_PTR(message_pfx);
*p++ = 0x40;
@@ -1531,10 +1596,10 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
*p++ = 'a';
*p++ = 'g';
*p++ = 'e';
- *p++ = (uint8_t)GPR_SLICE_LENGTH(*optional_message);
+ *p++ = (uint8_t)msg_len;
GPR_ASSERT(p == GPR_SLICE_END_PTR(message_pfx));
len += (uint32_t)GPR_SLICE_LENGTH(message_pfx);
- len += (uint32_t)GPR_SLICE_LENGTH(*optional_message);
+ len += (uint32_t)msg_len;
}
hdr = gpr_slice_malloc(9);
@@ -1555,53 +1620,53 @@ static void close_from_api(grpc_exec_ctx *exec_ctx,
if (optional_message) {
gpr_slice_buffer_add(&transport_global->qbuf, message_pfx);
gpr_slice_buffer_add(&transport_global->qbuf,
- gpr_slice_ref(*optional_message));
+ gpr_slice_from_copied_string(optional_message));
}
-
gpr_slice_buffer_add(
&transport_global->qbuf,
grpc_chttp2_rst_stream_create(stream_global->id, GRPC_CHTTP2_NO_ERROR,
&stream_global->stats.outgoing));
-
- if (optional_message) {
- gpr_slice_ref(*optional_message);
- }
}
- grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, status,
- optional_message);
- grpc_error *err = GRPC_ERROR_CREATE("Stream closed");
- err = grpc_error_set_int(err, GRPC_ERROR_INT_GRPC_STATUS, status);
- if (optional_message) {
- char *str =
- gpr_dump_slice(*optional_message, GPR_DUMP_HEX | GPR_DUMP_ASCII);
- err = grpc_error_set_str(err, GRPC_ERROR_STR_GRPC_MESSAGE, str);
- gpr_free(str);
+ const char *msg = grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE);
+ bool free_msg = false;
+ if (msg == NULL) {
+ free_msg = true;
+ msg = grpc_error_string(error);
}
+ gpr_slice msg_slice = gpr_slice_from_copied_string(msg);
+ grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global,
+ grpc_status, &msg_slice);
+ if (free_msg) grpc_error_free_string(msg);
+
grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1,
- 1, err);
+ 1, error);
}
+typedef struct {
+ grpc_exec_ctx *exec_ctx;
+ grpc_error *error;
+} cancel_stream_cb_args;
+
static void cancel_stream_cb(grpc_chttp2_transport_global *transport_global,
void *user_data,
grpc_chttp2_stream_global *stream_global) {
- grpc_chttp2_transport *transport = TRANSPORT_FROM_GLOBAL(transport_global);
- cancel_from_api(user_data, transport_global, stream_global,
- GRPC_STATUS_UNAVAILABLE,
- GPR_SLICE_IS_EMPTY(transport->optional_drop_message)
- ? NULL
- : &transport->optional_drop_message);
+ cancel_stream_cb_args *args = user_data;
+ cancel_from_api(args->exec_ctx, transport_global, stream_global,
+ GRPC_ERROR_REF(args->error));
}
-static void end_all_the_calls(grpc_exec_ctx *exec_ctx,
- grpc_chttp2_transport *t) {
- grpc_chttp2_for_all_streams(&t->global, exec_ctx, cancel_stream_cb);
+static void end_all_the_calls(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
+ grpc_error *error) {
+ cancel_stream_cb_args args = {exec_ctx, error};
+ grpc_chttp2_for_all_streams(&t->global, &args, cancel_stream_cb);
+ GRPC_ERROR_UNREF(error);
}
static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
grpc_error *error) {
- close_transport_locked(exec_ctx, t, error);
- end_all_the_calls(exec_ctx, t);
+ close_transport_locked(exec_ctx, t, GRPC_ERROR_REF(error));
+ end_all_the_calls(exec_ctx, t, error);
}
/** update window from a settings change */
@@ -1708,15 +1773,7 @@ static void parsing_action(grpc_exec_ctx *exec_ctx, void *arg,
t->read_buffer.slices[i]);
};
if (i != t->read_buffer.count) {
- gpr_slice_unref(t->optional_drop_message);
errors[2] = try_http_parsing(exec_ctx, t);
- if (errors[2] != GRPC_ERROR_NONE) {
- t->optional_drop_message = gpr_slice_from_copied_string(
- "Connection dropped: received http1.x response");
- } else {
- t->optional_drop_message = gpr_slice_from_copied_string(
- "Connection dropped: received unparseable response");
- }
}
grpc_error *err =
errors[0] == GRPC_ERROR_NONE && errors[1] == GRPC_ERROR_NONE &&
@@ -1767,7 +1824,7 @@ static void post_parse_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
GPR_ASSERT(stream_global->write_closed);
GPR_ASSERT(stream_global->read_closed);
remove_stream(exec_ctx, t, stream_global->id,
- GRPC_ERROR_CREATE("Stream removed"));
+ GRPC_ERROR_REF(stream_global->removal_error));
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2");
}
@@ -1784,6 +1841,10 @@ static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
error = GRPC_ERROR_CREATE("Transport closed");
}
if (error != GRPC_ERROR_NONE) {
+ if (!grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) {
+ error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
+ GRPC_STATUS_UNAVAILABLE);
+ }
drop_connection(exec_ctx, t, GRPC_ERROR_REF(error));
t->endpoint_reading = 0;
if (!t->executor.writing_active && t->ep) {
@@ -1798,6 +1859,7 @@ static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
prevent_endpoint_shutdown(t);
}
gpr_slice_buffer_reset_and_unref(&t->read_buffer);
+ GRPC_ERROR_UNREF(error);
if (keep_reading) {
grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, &t->reading_action);
@@ -1806,8 +1868,6 @@ static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
} else {
UNREF_TRANSPORT(exec_ctx, t, "reading_action");
}
-
- GRPC_LOG_IF_ERROR("close_transport", error);
}
/*******************************************************************************
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index d63170e350..b5180c6fc8 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -384,9 +384,6 @@ struct grpc_chttp2_transport {
/** Transport op to be applied post-parsing */
grpc_transport_op *post_parsing_op;
-
- /** Message explaining the reason of dropping connection */
- gpr_slice optional_drop_message;
};
typedef struct {
@@ -439,6 +436,9 @@ typedef struct {
bool seen_error;
bool exceeded_metadata_size;
+ /** the error that resulted in this stream being removed */
+ grpc_error *removal_error;
+
bool published_initial_metadata;
bool published_trailing_metadata;
bool final_metadata_requested;
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
index ff824c781f..ada5e8d40c 100644
--- a/src/core/lib/channel/channel_stack.c
+++ b/src/core/lib/channel/channel_stack.c
@@ -269,6 +269,6 @@ void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx,
grpc_call_element *cur_elem) {
grpc_transport_stream_op op;
memset(&op, 0, sizeof(op));
- op.cancel_with_status = GRPC_STATUS_CANCELLED;
+ op.cancel_error = GRPC_ERROR_CANCELLED;
grpc_call_next_op(exec_ctx, cur_elem, &op);
}
diff --git a/src/core/lib/debug/trace.c b/src/core/lib/debug/trace.c
index 555f497b78..c56046785b 100644
--- a/src/core/lib/debug/trace.c
+++ b/src/core/lib/debug/trace.c
@@ -88,7 +88,11 @@ static void parse(const char *s) {
split(s, &strings, &nstrings);
for (i = 0; i < nstrings; i++) {
- grpc_tracer_set_enabled(strings[i], 1);
+ if (strings[i][0] == '-') {
+ grpc_tracer_set_enabled(strings[i] + 1, 0);
+ } else {
+ grpc_tracer_set_enabled(strings[i], 1);
+ }
}
for (i = 0; i < nstrings; i++) {
@@ -117,7 +121,7 @@ int grpc_tracer_set_enabled(const char *name, int enabled) {
tracer *t;
if (0 == strcmp(name, "all")) {
for (t = tracers; t; t = t->next) {
- *t->flag = 1;
+ *t->flag = enabled;
}
} else {
int found = 0;
diff --git a/src/core/lib/iomgr/error.c b/src/core/lib/iomgr/error.c
index 1b15963e57..149c55663c 100644
--- a/src/core/lib/iomgr/error.c
+++ b/src/core/lib/iomgr/error.c
@@ -37,6 +37,7 @@
#include <stdbool.h>
#include <string.h>
+#include <grpc/status.h>
#include <grpc/support/alloc.h>
#include <grpc/support/avl.h>
#include <grpc/support/log.h>
@@ -47,6 +48,8 @@
#include <grpc/support/log_windows.h>
#endif
+#include "src/core/lib/profiling/timers.h"
+
static void destroy_integer(void *key) {}
static void *copy_integer(void *key) { return key; }
@@ -115,6 +118,8 @@ static const char *error_int_name(grpc_error_ints key) {
return "wsa_error";
case GRPC_ERROR_INT_HTTP_STATUS:
return "http_status";
+ case GRPC_ERROR_INT_LIMIT:
+ return "limit";
}
GPR_UNREACHABLE_CODE(return "unknown");
}
@@ -169,8 +174,8 @@ static bool is_special(grpc_error *err) {
grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line,
const char *func) {
if (is_special(err)) return err;
- gpr_log(GPR_DEBUG, "%p: %d -> %d [%s:%d %s]", err, err->refs.count,
- err->refs.count + 1, file, line, func);
+ gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", err,
+ err->refs.count, err->refs.count + 1, file, line, func);
gpr_ref(&err->refs);
return err;
}
@@ -195,8 +200,8 @@ static void error_destroy(grpc_error *err) {
void grpc_error_unref(grpc_error *err, const char *file, int line,
const char *func) {
if (is_special(err)) return;
- gpr_log(GPR_DEBUG, "%p: %d -> %d [%s:%d %s]", err, err->refs.count,
- err->refs.count - 1, file, line, func);
+ gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", err,
+ err->refs.count, err->refs.count - 1, file, line, func);
if (gpr_unref(&err->refs)) {
error_destroy(err);
}
@@ -213,6 +218,7 @@ void grpc_error_unref(grpc_error *err) {
grpc_error *grpc_error_create(const char *file, int line, const char *desc,
grpc_error **referencing,
size_t num_referencing) {
+ GPR_TIMER_BEGIN("grpc_error_create", 0);
grpc_error *err = gpr_malloc(sizeof(*err));
if (err == NULL) { // TODO(ctiller): make gpr_malloc return NULL
return GRPC_ERROR_OOM;
@@ -238,63 +244,91 @@ grpc_error *grpc_error_create(const char *file, int line, const char *desc,
(void *)(uintptr_t)GRPC_ERROR_TIME_CREATED,
box_time(gpr_now(GPR_CLOCK_REALTIME)));
gpr_ref_init(&err->refs, 1);
+ GPR_TIMER_END("grpc_error_create", 0);
return err;
}
static grpc_error *copy_error_and_unref(grpc_error *in) {
+ GPR_TIMER_BEGIN("copy_error_and_unref", 0);
+ grpc_error *out;
if (is_special(in)) {
- if (in == GRPC_ERROR_NONE) return GRPC_ERROR_CREATE("no error");
- if (in == GRPC_ERROR_OOM) return GRPC_ERROR_CREATE("oom");
- if (in == GRPC_ERROR_CANCELLED) return GRPC_ERROR_CREATE("cancelled");
- return GRPC_ERROR_CREATE("unknown");
- }
- grpc_error *out = gpr_malloc(sizeof(*out));
+ if (in == GRPC_ERROR_NONE)
+ out = GRPC_ERROR_CREATE("no error");
+ else if (in == GRPC_ERROR_OOM)
+ out = GRPC_ERROR_CREATE("oom");
+ else if (in == GRPC_ERROR_CANCELLED)
+ out =
+ grpc_error_set_int(GRPC_ERROR_CREATE("cancelled"),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED);
+ else
+ out = GRPC_ERROR_CREATE("unknown");
+ } else {
+ out = gpr_malloc(sizeof(*out));
#ifdef GRPC_ERROR_REFCOUNT_DEBUG
- gpr_log(GPR_DEBUG, "%p create copying", out);
+ gpr_log(GPR_DEBUG, "%p create copying", out);
#endif
- out->ints = gpr_avl_ref(in->ints);
- out->strs = gpr_avl_ref(in->strs);
- out->errs = gpr_avl_ref(in->errs);
- out->times = gpr_avl_ref(in->times);
- out->next_err = in->next_err;
- gpr_ref_init(&out->refs, 1);
- GRPC_ERROR_UNREF(in);
+ out->ints = gpr_avl_ref(in->ints);
+ out->strs = gpr_avl_ref(in->strs);
+ out->errs = gpr_avl_ref(in->errs);
+ out->times = gpr_avl_ref(in->times);
+ out->next_err = in->next_err;
+ gpr_ref_init(&out->refs, 1);
+ GRPC_ERROR_UNREF(in);
+ }
+ GPR_TIMER_END("copy_error_and_unref", 0);
return out;
}
grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which,
intptr_t value) {
+ GPR_TIMER_BEGIN("grpc_error_set_int", 0);
grpc_error *new = copy_error_and_unref(src);
new->ints = gpr_avl_add(new->ints, (void *)(uintptr_t)which, (void *)value);
+ GPR_TIMER_END("grpc_error_set_int", 0);
return new;
}
bool grpc_error_get_int(grpc_error *err, grpc_error_ints which, intptr_t *p) {
+ GPR_TIMER_BEGIN("grpc_error_get_int", 0);
void *pp;
+ if (is_special(err)) {
+ if (err == GRPC_ERROR_CANCELLED && which == GRPC_ERROR_INT_GRPC_STATUS) {
+ *p = GRPC_STATUS_CANCELLED;
+ GPR_TIMER_END("grpc_error_get_int", 0);
+ return true;
+ }
+ GPR_TIMER_END("grpc_error_get_int", 0);
+ return false;
+ }
if (gpr_avl_maybe_get(err->ints, (void *)(uintptr_t)which, &pp)) {
if (p != NULL) *p = (intptr_t)pp;
+ GPR_TIMER_END("grpc_error_get_int", 0);
return true;
}
+ GPR_TIMER_END("grpc_error_get_int", 0);
return false;
}
grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which,
const char *value) {
+ GPR_TIMER_BEGIN("grpc_error_set_str", 0);
grpc_error *new = copy_error_and_unref(src);
new->strs =
gpr_avl_add(new->strs, (void *)(uintptr_t)which, gpr_strdup(value));
+ GPR_TIMER_END("grpc_error_set_str", 0);
return new;
}
-const char *grpc_error_get_str(grpc_error *error, grpc_error_strs which) {
- void *s = NULL;
- gpr_avl_maybe_get(error->strs, (void *)(uintptr_t)which, &s);
- return s;
+const char *grpc_error_get_str(grpc_error *err, grpc_error_strs which) {
+ if (is_special(err)) return NULL;
+ return gpr_avl_get(err->strs, (void *)(uintptr_t)which);
}
grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) {
+ GPR_TIMER_BEGIN("grpc_error_add_child", 0);
grpc_error *new = copy_error_and_unref(src);
new->errs = gpr_avl_add(new->errs, (void *)(new->next_err++), child);
+ GPR_TIMER_END("grpc_error_add_child", 0);
return new;
}
@@ -486,6 +520,7 @@ void grpc_error_free_string(const char *str) {
}
const char *grpc_error_string(grpc_error *err) {
+ GPR_TIMER_BEGIN("grpc_error_string", 0);
if (err == GRPC_ERROR_NONE) return no_error_string;
if (err == GRPC_ERROR_OOM) return oom_error_string;
if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string;
@@ -502,7 +537,9 @@ const char *grpc_error_string(grpc_error *err) {
qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs);
- return finish_kvs(&kvs);
+ const char *out = finish_kvs(&kvs);
+ GPR_TIMER_END("grpc_error_string", 0);
+ return out;
}
grpc_error *grpc_os_error(const char *file, int line, int err,
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
index 4bff591fe3..bc7781250e 100644
--- a/src/core/lib/iomgr/error.h
+++ b/src/core/lib/iomgr/error.h
@@ -92,6 +92,8 @@ typedef enum {
GRPC_ERROR_INT_FD,
/// HTTP status (i.e. 404)
GRPC_ERROR_INT_HTTP_STATUS,
+ /// context sensitive limit associated with the error
+ GRPC_ERROR_INT_LIMIT,
} grpc_error_ints;
typedef enum {
@@ -163,26 +165,27 @@ void grpc_error_unref(grpc_error *err);
#endif
grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which,
- intptr_t value);
+ intptr_t value) GRPC_MUST_USE_RESULT;
bool grpc_error_get_int(grpc_error *error, grpc_error_ints which, intptr_t *p);
grpc_error *grpc_error_set_time(grpc_error *src, grpc_error_times which,
- gpr_timespec value);
+ gpr_timespec value) GRPC_MUST_USE_RESULT;
grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which,
- const char *value);
+ const char *value) GRPC_MUST_USE_RESULT;
/// Returns NULL if the specified string is not set.
/// Caller does NOT own return value.
const char *grpc_error_get_str(grpc_error *error, grpc_error_strs which);
/// Add a child error: an error that is believed to have contributed to this
/// error occurring. Allows root causing high level errors from lower level
/// errors that contributed to them.
-grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child);
+grpc_error *grpc_error_add_child(grpc_error *src,
+ grpc_error *child) GRPC_MUST_USE_RESULT;
grpc_error *grpc_os_error(const char *file, int line, int err,
- const char *call_name);
+ const char *call_name) GRPC_MUST_USE_RESULT;
/// create an error associated with errno!=0 (an 'operating system' error)
#define GRPC_OS_ERROR(err, call_name) \
grpc_os_error(__FILE__, __LINE__, err, call_name)
grpc_error *grpc_wsa_error(const char *file, int line, int err,
- const char *call_name);
+ const char *call_name) GRPC_MUST_USE_RESULT;
/// windows only: create an error associated with WSAGetLastError()!=0
#define GRPC_WSA_ERROR(err, call_name) \
grpc_wsa_error(__FILE__, __LINE__, err, call_name)
diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c
new file mode 100644
index 0000000000..cf0fe736a0
--- /dev/null
+++ b/src/core/lib/iomgr/ev_epoll_linux.c
@@ -0,0 +1,1872 @@
+/*
+ *
+ * Copyright 2016, 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 <grpc/grpc_posix.h>
+#include <grpc/support/port_platform.h>
+
+/* This polling engine is only relevant on linux kernels supporting epoll() */
+#ifdef GPR_LINUX_EPOLL
+
+#include "src/core/lib/iomgr/ev_epoll_linux.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/tls.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/block_annotate.h"
+
+/* TODO: sreek - Move this to init.c and initialize this like other tracers. */
+static int grpc_polling_trace = 0; /* Disabled by default */
+#define GRPC_POLLING_TRACE(fmt, ...) \
+ if (grpc_polling_trace) { \
+ gpr_log(GPR_INFO, (fmt), __VA_ARGS__); \
+ }
+
+static int grpc_wakeup_signal = -1;
+static bool is_grpc_wakeup_signal_initialized = false;
+
+/* Implements the function defined in grpc_posix.h. This function might be
+ * called before even calling grpc_init() to set either a different signal to
+ * use. If signum == -1, then the use of signals is disabled */
+void grpc_use_signal(int signum) {
+ grpc_wakeup_signal = signum;
+ is_grpc_wakeup_signal_initialized = true;
+
+ if (grpc_wakeup_signal < 0) {
+ gpr_log(GPR_INFO,
+ "Use of signals is disabled. Epoll engine will not be used");
+ } else {
+ gpr_log(GPR_INFO, "epoll engine will be using signal: %d",
+ grpc_wakeup_signal);
+ }
+}
+
+struct polling_island;
+
+/*******************************************************************************
+ * Fd Declarations
+ */
+struct grpc_fd {
+ int fd;
+ /* refst format:
+ bit 0 : 1=Active / 0=Orphaned
+ bits 1-n : refcount
+ Ref/Unref by two to avoid altering the orphaned bit */
+ gpr_atm refst;
+
+ gpr_mu mu;
+
+ /* Indicates that the fd is shutdown and that any pending read/write closures
+ should fail */
+ bool shutdown;
+
+ /* The fd is either closed or we relinquished control of it. In either cases,
+ this indicates that the 'fd' on this structure is no longer valid */
+ bool orphaned;
+
+ /* TODO: sreek - Move this to a lockfree implementation */
+ grpc_closure *read_closure;
+ grpc_closure *write_closure;
+
+ /* The polling island to which this fd belongs to and the mutex protecting the
+ the field */
+ gpr_mu pi_mu;
+ struct polling_island *polling_island;
+
+ struct grpc_fd *freelist_next;
+ grpc_closure *on_done_closure;
+
+ /* The pollset that last noticed that the fd is readable */
+ grpc_pollset *read_notifier_pollset;
+
+ grpc_iomgr_object iomgr_object;
+};
+
+/* Reference counting for fds */
+// #define GRPC_FD_REF_COUNT_DEBUG
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line);
+static void fd_unref(grpc_fd *fd, const char *reason, const char *file,
+ int line);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__)
+#else
+static void fd_ref(grpc_fd *fd);
+static void fd_unref(grpc_fd *fd);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd)
+#endif
+
+static void fd_global_init(void);
+static void fd_global_shutdown(void);
+
+#define CLOSURE_NOT_READY ((grpc_closure *)0)
+#define CLOSURE_READY ((grpc_closure *)1)
+
+/*******************************************************************************
+ * Polling island Declarations
+ */
+
+// #define GRPC_PI_REF_COUNT_DEBUG
+#ifdef GRPC_PI_REF_COUNT_DEBUG
+
+#define PI_ADD_REF(p, r) pi_add_ref_dbg((p), (r), __FILE__, __LINE__)
+#define PI_UNREF(p, r) pi_unref_dbg((p), (r), __FILE__, __LINE__)
+
+#else /* defined(GRPC_PI_REF_COUNT_DEBUG) */
+
+#define PI_ADD_REF(p, r) pi_add_ref((p))
+#define PI_UNREF(p, r) pi_unref((p))
+
+#endif /* !defined(GPRC_PI_REF_COUNT_DEBUG) */
+
+typedef struct polling_island {
+ gpr_mu mu;
+ /* Ref count. Use PI_ADD_REF() and PI_UNREF() macros to increment/decrement
+ the refcount.
+ Once the ref count becomes zero, this structure is destroyed which means
+ we should ensure that there is never a scenario where a PI_ADD_REF() is
+ racing with a PI_UNREF() that just made the ref_count zero. */
+ gpr_refcount ref_count;
+
+ /* Pointer to the polling_island this merged into.
+ * merged_to value is only set once in polling_island's lifetime (and that too
+ * only if the island is merged with another island). Because of this, we can
+ * use gpr_atm type here so that we can do atomic access on this and reduce
+ * lock contention on 'mu' mutex.
+ *
+ * Note that if this field is not NULL (i.e not 0), all the remaining fields
+ * (except mu and ref_count) are invalid and must be ignored. */
+ gpr_atm merged_to;
+
+ /* The fd of the underlying epoll set */
+ int epoll_fd;
+
+ /* The file descriptors in the epoll set */
+ size_t fd_cnt;
+ size_t fd_capacity;
+ grpc_fd **fds;
+
+ /* Polling islands that are no longer needed are kept in a freelist so that
+ they can be reused. This field points to the next polling island in the
+ free list */
+ struct polling_island *next_free;
+} polling_island;
+
+/*******************************************************************************
+ * Pollset Declarations
+ */
+struct grpc_pollset_worker {
+ /* Thread id of this worker */
+ pthread_t pt_id;
+
+ /* Used to prevent a worker from getting kicked multiple times */
+ gpr_atm is_kicked;
+ struct grpc_pollset_worker *next;
+ struct grpc_pollset_worker *prev;
+};
+
+struct grpc_pollset {
+ gpr_mu mu;
+ grpc_pollset_worker root_worker;
+ bool kicked_without_pollers;
+
+ bool shutting_down; /* Is the pollset shutting down ? */
+ bool finish_shutdown_called; /* Is the 'finish_shutdown_locked()' called ? */
+ grpc_closure *shutdown_done; /* Called after after shutdown is complete */
+
+ /* The polling island to which this pollset belongs to */
+ struct polling_island *polling_island;
+};
+
+/*******************************************************************************
+ * Pollset-set Declarations
+ */
+/* TODO: sreek - Change the pollset_set implementation such that a pollset_set
+ * directly points to a polling_island (and adding an fd/pollset/pollset_set to
+ * the current pollset_set would result in polling island merges. This would
+ * remove the need to maintain fd_count here. This will also significantly
+ * simplify the grpc_fd structure since we would no longer need to explicitly
+ * maintain the orphaned state */
+struct grpc_pollset_set {
+ gpr_mu mu;
+
+ size_t pollset_count;
+ size_t pollset_capacity;
+ grpc_pollset **pollsets;
+
+ size_t pollset_set_count;
+ size_t pollset_set_capacity;
+ struct grpc_pollset_set **pollset_sets;
+
+ size_t fd_count;
+ size_t fd_capacity;
+ grpc_fd **fds;
+};
+
+/*******************************************************************************
+ * Common helpers
+ */
+
+static void append_error(grpc_error **composite, grpc_error *error,
+ const char *desc) {
+ if (error == GRPC_ERROR_NONE) return;
+ if (*composite == GRPC_ERROR_NONE) {
+ *composite = GRPC_ERROR_CREATE(desc);
+ }
+ *composite = grpc_error_add_child(*composite, error);
+}
+
+/*******************************************************************************
+ * Polling island Definitions
+ */
+
+/* The wakeup fd that is used to wake up all threads in a Polling island. This
+ is useful in the polling island merge operation where we need to wakeup all
+ the threads currently polling the smaller polling island (so that they can
+ start polling the new/merged polling island)
+
+ NOTE: This fd is initialized to be readable and MUST NOT be consumed i.e the
+ threads that woke up MUST NOT call grpc_wakeup_fd_consume_wakeup() */
+static grpc_wakeup_fd polling_island_wakeup_fd;
+
+/* Polling island freelist */
+static gpr_mu g_pi_freelist_mu;
+static polling_island *g_pi_freelist = NULL;
+
+static void polling_island_delete(); /* Forward declaration */
+
+#ifdef GRPC_TSAN
+/* Currently TSAN may incorrectly flag data races between epoll_ctl and
+ epoll_wait for any grpc_fd structs that are added to the epoll set via
+ epoll_ctl and are returned (within a very short window) via epoll_wait().
+
+ To work-around this race, we establish a happens-before relation between
+ the code just-before epoll_ctl() and the code after epoll_wait() by using
+ this atomic */
+gpr_atm g_epoll_sync;
+#endif /* defined(GRPC_TSAN) */
+
+#ifdef GRPC_PI_REF_COUNT_DEBUG
+void pi_add_ref(polling_island *pi);
+void pi_unref(polling_island *pi);
+
+void pi_add_ref_dbg(polling_island *pi, char *reason, char *file, int line) {
+ long old_cnt = gpr_atm_acq_load(&(pi->ref_count.count));
+ pi_add_ref(pi);
+ gpr_log(GPR_DEBUG, "Add ref pi: %p, old: %ld -> new:%ld (%s) - (%s, %d)",
+ (void *)pi, old_cnt, old_cnt + 1, reason, file, line);
+}
+
+void pi_unref_dbg(polling_island *pi, char *reason, char *file, int line) {
+ long old_cnt = gpr_atm_acq_load(&(pi->ref_count.count));
+ pi_unref(pi);
+ gpr_log(GPR_DEBUG, "Unref pi: %p, old:%ld -> new:%ld (%s) - (%s, %d)",
+ (void *)pi, old_cnt, (old_cnt - 1), reason, file, line);
+}
+#endif
+
+void pi_add_ref(polling_island *pi) { gpr_ref(&pi->ref_count); }
+
+void pi_unref(polling_island *pi) {
+ /* If ref count went to zero, delete the polling island.
+ Note that this deletion not be done under a lock. Once the ref count goes
+ to zero, we are guaranteed that no one else holds a reference to the
+ polling island (and that there is no racing pi_add_ref() call either).
+
+ Also, if we are deleting the polling island and the merged_to field is
+ non-empty, we should remove a ref to the merged_to polling island
+ */
+ if (gpr_unref(&pi->ref_count)) {
+ polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ polling_island_delete(pi);
+ if (next != NULL) {
+ PI_UNREF(next, "pi_delete"); /* Recursive call */
+ }
+ }
+}
+
+/* The caller is expected to hold pi->mu lock before calling this function */
+static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds,
+ size_t fd_count, bool add_fd_refs,
+ grpc_error **error) {
+ int err;
+ size_t i;
+ struct epoll_event ev;
+ char *err_msg;
+ const char *err_desc = "polling_island_add_fds";
+
+#ifdef GRPC_TSAN
+ /* See the definition of g_epoll_sync for more context */
+ gpr_atm_rel_store(&g_epoll_sync, (gpr_atm)0);
+#endif /* defined(GRPC_TSAN) */
+
+ for (i = 0; i < fd_count; i++) {
+ ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET);
+ ev.data.ptr = fds[i];
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, fds[i]->fd, &ev);
+
+ if (err < 0) {
+ if (errno != EEXIST) {
+ gpr_asprintf(
+ &err_msg,
+ "epoll_ctl (epoll_fd: %d) add fd: %d failed with error: %d (%s)",
+ pi->epoll_fd, fds[i]->fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+
+ continue;
+ }
+
+ if (pi->fd_cnt == pi->fd_capacity) {
+ pi->fd_capacity = GPR_MAX(pi->fd_capacity + 8, pi->fd_cnt * 3 / 2);
+ pi->fds = gpr_realloc(pi->fds, sizeof(grpc_fd *) * pi->fd_capacity);
+ }
+
+ pi->fds[pi->fd_cnt++] = fds[i];
+ if (add_fd_refs) {
+ GRPC_FD_REF(fds[i], "polling_island");
+ }
+ }
+}
+
+/* The caller is expected to hold pi->mu before calling this */
+static void polling_island_add_wakeup_fd_locked(polling_island *pi,
+ grpc_wakeup_fd *wakeup_fd,
+ grpc_error **error) {
+ struct epoll_event ev;
+ int err;
+ char *err_msg;
+ const char *err_desc = "polling_island_add_wakeup_fd";
+
+ ev.events = (uint32_t)(EPOLLIN | EPOLLET);
+ ev.data.ptr = wakeup_fd;
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD,
+ GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), &ev);
+ if (err < 0 && errno != EEXIST) {
+ gpr_asprintf(&err_msg,
+ "epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with "
+ "error: %d (%s)",
+ pi->epoll_fd,
+ GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), errno,
+ strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+}
+
+/* The caller is expected to hold pi->mu lock before calling this function */
+static void polling_island_remove_all_fds_locked(polling_island *pi,
+ bool remove_fd_refs,
+ grpc_error **error) {
+ int err;
+ size_t i;
+ char *err_msg;
+ const char *err_desc = "polling_island_remove_fds";
+
+ for (i = 0; i < pi->fd_cnt; i++) {
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, pi->fds[i]->fd, NULL);
+ if (err < 0 && errno != ENOENT) {
+ gpr_asprintf(&err_msg,
+ "epoll_ctl (epoll_fd: %d) delete fds[%zu]: %d failed with "
+ "error: %d (%s)",
+ pi->epoll_fd, i, pi->fds[i]->fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+
+ if (remove_fd_refs) {
+ GRPC_FD_UNREF(pi->fds[i], "polling_island");
+ }
+ }
+
+ pi->fd_cnt = 0;
+}
+
+/* The caller is expected to hold pi->mu lock before calling this function */
+static void polling_island_remove_fd_locked(polling_island *pi, grpc_fd *fd,
+ bool is_fd_closed,
+ grpc_error **error) {
+ int err;
+ size_t i;
+ char *err_msg;
+ const char *err_desc = "polling_island_remove_fd";
+
+ /* If fd is already closed, then it would have been automatically been removed
+ from the epoll set */
+ if (!is_fd_closed) {
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL);
+ if (err < 0 && errno != ENOENT) {
+ gpr_asprintf(
+ &err_msg,
+ "epoll_ctl (epoll_fd: %d) del fd: %d failed with error: %d (%s)",
+ pi->epoll_fd, fd->fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+ }
+
+ for (i = 0; i < pi->fd_cnt; i++) {
+ if (pi->fds[i] == fd) {
+ pi->fds[i] = pi->fds[--pi->fd_cnt];
+ GRPC_FD_UNREF(fd, "polling_island");
+ break;
+ }
+ }
+}
+
+/* Might return NULL in case of an error */
+static polling_island *polling_island_create(grpc_fd *initial_fd,
+ grpc_error **error) {
+ polling_island *pi = NULL;
+ char *err_msg;
+ const char *err_desc = "polling_island_create";
+
+ /* Try to get one from the polling island freelist */
+ gpr_mu_lock(&g_pi_freelist_mu);
+ if (g_pi_freelist != NULL) {
+ pi = g_pi_freelist;
+ g_pi_freelist = g_pi_freelist->next_free;
+ pi->next_free = NULL;
+ }
+ gpr_mu_unlock(&g_pi_freelist_mu);
+
+ /* Create new polling island if we could not get one from the free list */
+ if (pi == NULL) {
+ pi = gpr_malloc(sizeof(*pi));
+ gpr_mu_init(&pi->mu);
+ pi->fd_cnt = 0;
+ pi->fd_capacity = 0;
+ pi->fds = NULL;
+ }
+
+ gpr_ref_init(&pi->ref_count, 0);
+ gpr_atm_rel_store(&pi->merged_to, (gpr_atm)NULL);
+
+ pi->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+
+ if (pi->epoll_fd < 0) {
+ gpr_asprintf(&err_msg, "epoll_create1 failed with error %d (%s)", errno,
+ strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ } else {
+ polling_island_add_wakeup_fd_locked(pi, &grpc_global_wakeup_fd, error);
+ pi->next_free = NULL;
+
+ if (initial_fd != NULL) {
+ /* Lock the polling island here just in case we got this structure from
+ the freelist and the polling island lock was not released yet (by the
+ code that adds the polling island to the freelist) */
+ gpr_mu_lock(&pi->mu);
+ polling_island_add_fds_locked(pi, &initial_fd, 1, true, error);
+ gpr_mu_unlock(&pi->mu);
+ }
+ }
+
+ return pi;
+}
+
+static void polling_island_delete(polling_island *pi) {
+ GPR_ASSERT(pi->fd_cnt == 0);
+
+ gpr_atm_rel_store(&pi->merged_to, (gpr_atm)NULL);
+
+ close(pi->epoll_fd);
+ pi->epoll_fd = -1;
+
+ gpr_mu_lock(&g_pi_freelist_mu);
+ pi->next_free = g_pi_freelist;
+ g_pi_freelist = pi;
+ gpr_mu_unlock(&g_pi_freelist_mu);
+}
+
+/* Attempts to gets the last polling island in the linked list (liked by the
+ * 'merged_to' field). Since this does not lock the polling island, there are no
+ * guarantees that the island returned is the last island */
+static polling_island *polling_island_maybe_get_latest(polling_island *pi) {
+ polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ while (next != NULL) {
+ pi = next;
+ next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ }
+
+ return pi;
+}
+
+/* Gets the lock on the *latest* polling island i.e the last polling island in
+ the linked list (linked by the 'merged_to' field). Call gpr_mu_unlock on the
+ returned polling island's mu.
+ Usage: To lock/unlock polling island "pi", do the following:
+ polling_island *pi_latest = polling_island_lock(pi);
+ ...
+ ... critical section ..
+ ...
+ gpr_mu_unlock(&pi_latest->mu); // NOTE: use pi_latest->mu. NOT pi->mu */
+static polling_island *polling_island_lock(polling_island *pi) {
+ polling_island *next = NULL;
+
+ while (true) {
+ next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ if (next == NULL) {
+ /* Looks like 'pi' is the last node in the linked list but unless we check
+ this by holding the pi->mu lock, we cannot be sure (i.e without the
+ pi->mu lock, we don't prevent island merges).
+ To be absolutely sure, check once more by holding the pi->mu lock */
+ gpr_mu_lock(&pi->mu);
+ next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ if (next == NULL) {
+ /* pi is infact the last node and we have the pi->mu lock. we're done */
+ break;
+ }
+
+ /* pi->merged_to is not NULL i.e pi isn't the last node anymore. pi->mu
+ * isn't the lock we are interested in. Continue traversing the list */
+ gpr_mu_unlock(&pi->mu);
+ }
+
+ pi = next;
+ }
+
+ return pi;
+}
+
+/* Gets the lock on the *latest* polling islands in the linked lists pointed by
+ *p and *q (and also updates *p and *q to point to the latest polling islands)
+
+ This function is needed because calling the following block of code to obtain
+ locks on polling islands (*p and *q) is prone to deadlocks.
+ {
+ polling_island_lock(*p, true);
+ polling_island_lock(*q, true);
+ }
+
+ Usage/example:
+ polling_island *p1;
+ polling_island *p2;
+ ..
+ polling_island_lock_pair(&p1, &p2);
+ ..
+ .. Critical section with both p1 and p2 locked
+ ..
+ // Release locks: Always call polling_island_unlock_pair() to release locks
+ polling_island_unlock_pair(p1, p2);
+*/
+static void polling_island_lock_pair(polling_island **p, polling_island **q) {
+ polling_island *pi_1 = *p;
+ polling_island *pi_2 = *q;
+ polling_island *next_1 = NULL;
+ polling_island *next_2 = NULL;
+
+ /* The algorithm is simple:
+ - Go to the last polling islands in the linked lists *pi_1 and *pi_2 (and
+ keep updating pi_1 and pi_2)
+ - Then obtain locks on the islands by following a lock order rule of
+ locking polling_island with lower address first
+ Special case: Before obtaining the locks, check if pi_1 and pi_2 are
+ pointing to the same island. If that is the case, we can just call
+ polling_island_lock()
+ - After obtaining both the locks, double check that the polling islands
+ are still the last polling islands in their respective linked lists
+ (this is because there might have been polling island merges before
+ we got the lock)
+ - If the polling islands are the last islands, we are done. If not,
+ release the locks and continue the process from the first step */
+ while (true) {
+ next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to);
+ while (next_1 != NULL) {
+ pi_1 = next_1;
+ next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to);
+ }
+
+ next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to);
+ while (next_2 != NULL) {
+ pi_2 = next_2;
+ next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to);
+ }
+
+ if (pi_1 == pi_2) {
+ pi_1 = pi_2 = polling_island_lock(pi_1);
+ break;
+ }
+
+ if (pi_1 < pi_2) {
+ gpr_mu_lock(&pi_1->mu);
+ gpr_mu_lock(&pi_2->mu);
+ } else {
+ gpr_mu_lock(&pi_2->mu);
+ gpr_mu_lock(&pi_1->mu);
+ }
+
+ next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to);
+ next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to);
+ if (next_1 == NULL && next_2 == NULL) {
+ break;
+ }
+
+ gpr_mu_unlock(&pi_1->mu);
+ gpr_mu_unlock(&pi_2->mu);
+ }
+
+ *p = pi_1;
+ *q = pi_2;
+}
+
+static void polling_island_unlock_pair(polling_island *p, polling_island *q) {
+ if (p == q) {
+ gpr_mu_unlock(&p->mu);
+ } else {
+ gpr_mu_unlock(&p->mu);
+ gpr_mu_unlock(&q->mu);
+ }
+}
+
+static polling_island *polling_island_merge(polling_island *p,
+ polling_island *q,
+ grpc_error **error) {
+ /* Get locks on both the polling islands */
+ polling_island_lock_pair(&p, &q);
+
+ if (p != q) {
+ /* Make sure that p points to the polling island with fewer fds than q */
+ if (p->fd_cnt > q->fd_cnt) {
+ GPR_SWAP(polling_island *, p, q);
+ }
+
+ /* Merge p with q i.e move all the fds from p (The one with fewer fds) to q
+ Note that the refcounts on the fds being moved will not change here.
+ This is why the last param in the following two functions is 'false') */
+ polling_island_add_fds_locked(q, p->fds, p->fd_cnt, false, error);
+ polling_island_remove_all_fds_locked(p, false, error);
+
+ /* Wakeup all the pollers (if any) on p so that they pickup this change */
+ polling_island_add_wakeup_fd_locked(p, &polling_island_wakeup_fd, error);
+
+ /* Add the 'merged_to' link from p --> q */
+ gpr_atm_rel_store(&p->merged_to, (gpr_atm)q);
+ PI_ADD_REF(q, "pi_merge"); /* To account for the new incoming ref from p */
+ }
+ /* else if p == q, nothing needs to be done */
+
+ polling_island_unlock_pair(p, q);
+
+ /* Return the merged polling island (Note that no merge would have happened
+ if p == q which is ok) */
+ return q;
+}
+
+static grpc_error *polling_island_global_init() {
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ gpr_mu_init(&g_pi_freelist_mu);
+ g_pi_freelist = NULL;
+
+ error = grpc_wakeup_fd_init(&polling_island_wakeup_fd);
+ if (error == GRPC_ERROR_NONE) {
+ error = grpc_wakeup_fd_wakeup(&polling_island_wakeup_fd);
+ }
+
+ return error;
+}
+
+static void polling_island_global_shutdown() {
+ polling_island *next;
+ gpr_mu_lock(&g_pi_freelist_mu);
+ gpr_mu_unlock(&g_pi_freelist_mu);
+ while (g_pi_freelist != NULL) {
+ next = g_pi_freelist->next_free;
+ gpr_mu_destroy(&g_pi_freelist->mu);
+ gpr_free(g_pi_freelist->fds);
+ gpr_free(g_pi_freelist);
+ g_pi_freelist = next;
+ }
+ gpr_mu_destroy(&g_pi_freelist_mu);
+
+ grpc_wakeup_fd_destroy(&polling_island_wakeup_fd);
+}
+
+/*******************************************************************************
+ * Fd Definitions
+ */
+
+/* We need to keep a freelist not because of any concerns of malloc performance
+ * but instead so that implementations with multiple threads in (for example)
+ * epoll_wait deal with the race between pollset removal and incoming poll
+ * notifications.
+ *
+ * The problem is that the poller ultimately holds a reference to this
+ * object, so it is very difficult to know when is safe to free it, at least
+ * without some expensive synchronization.
+ *
+ * If we keep the object freelisted, in the worst case losing this race just
+ * becomes a spurious read notification on a reused fd.
+ */
+
+/* The alarm system needs to be able to wakeup 'some poller' sometimes
+ * (specifically when a new alarm needs to be triggered earlier than the next
+ * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a
+ * case occurs. */
+
+/* TODO: sreek: Right now, this wakes up all pollers. In future we should make
+ * sure to wake up one polling thread (which can wake up other threads if
+ * needed) */
+grpc_wakeup_fd grpc_global_wakeup_fd;
+
+static grpc_fd *fd_freelist = NULL;
+static gpr_mu fd_freelist_mu;
+
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
+static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file,
+ int line) {
+ gpr_log(GPR_DEBUG, "FD %d %p ref %d %ld -> %ld [%s; %s:%d]", fd->fd,
+ (void *)fd, n, gpr_atm_no_barrier_load(&fd->refst),
+ gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line);
+#else
+#define REF_BY(fd, n, reason) ref_by(fd, n)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n)
+static void ref_by(grpc_fd *fd, int n) {
+#endif
+ GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0);
+}
+
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file,
+ int line) {
+ gpr_atm old;
+ gpr_log(GPR_DEBUG, "FD %d %p unref %d %ld -> %ld [%s; %s:%d]", fd->fd,
+ (void *)fd, n, gpr_atm_no_barrier_load(&fd->refst),
+ gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line);
+#else
+static void unref_by(grpc_fd *fd, int n) {
+ gpr_atm old;
+#endif
+ old = gpr_atm_full_fetch_add(&fd->refst, -n);
+ if (old == n) {
+ /* Add the fd to the freelist */
+ gpr_mu_lock(&fd_freelist_mu);
+ fd->freelist_next = fd_freelist;
+ fd_freelist = fd;
+ grpc_iomgr_unregister_object(&fd->iomgr_object);
+
+ gpr_mu_unlock(&fd_freelist_mu);
+ } else {
+ GPR_ASSERT(old > n);
+ }
+}
+
+/* Increment refcount by two to avoid changing the orphan bit */
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void fd_ref(grpc_fd *fd, const char *reason, const char *file,
+ int line) {
+ ref_by(fd, 2, reason, file, line);
+}
+
+static void fd_unref(grpc_fd *fd, const char *reason, const char *file,
+ int line) {
+ unref_by(fd, 2, reason, file, line);
+}
+#else
+static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); }
+static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); }
+#endif
+
+static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
+
+static void fd_global_shutdown(void) {
+ gpr_mu_lock(&fd_freelist_mu);
+ gpr_mu_unlock(&fd_freelist_mu);
+ while (fd_freelist != NULL) {
+ grpc_fd *fd = fd_freelist;
+ fd_freelist = fd_freelist->freelist_next;
+ gpr_mu_destroy(&fd->mu);
+ gpr_free(fd);
+ }
+ gpr_mu_destroy(&fd_freelist_mu);
+}
+
+static grpc_fd *fd_create(int fd, const char *name) {
+ grpc_fd *new_fd = NULL;
+
+ gpr_mu_lock(&fd_freelist_mu);
+ if (fd_freelist != NULL) {
+ new_fd = fd_freelist;
+ fd_freelist = fd_freelist->freelist_next;
+ }
+ gpr_mu_unlock(&fd_freelist_mu);
+
+ if (new_fd == NULL) {
+ new_fd = gpr_malloc(sizeof(grpc_fd));
+ gpr_mu_init(&new_fd->mu);
+ gpr_mu_init(&new_fd->pi_mu);
+ }
+
+ /* Note: It is not really needed to get the new_fd->mu lock here. If this is a
+ newly created fd (or an fd we got from the freelist), no one else would be
+ holding a lock to it anyway. */
+ gpr_mu_lock(&new_fd->mu);
+
+ gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1);
+ new_fd->fd = fd;
+ new_fd->shutdown = false;
+ new_fd->orphaned = false;
+ new_fd->read_closure = CLOSURE_NOT_READY;
+ new_fd->write_closure = CLOSURE_NOT_READY;
+ new_fd->polling_island = NULL;
+ new_fd->freelist_next = NULL;
+ new_fd->on_done_closure = NULL;
+ new_fd->read_notifier_pollset = NULL;
+
+ gpr_mu_unlock(&new_fd->mu);
+
+ char *fd_name;
+ gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
+ grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+ gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, (void *)new_fd, fd_name);
+#endif
+ gpr_free(fd_name);
+ return new_fd;
+}
+
+static bool fd_is_orphaned(grpc_fd *fd) {
+ return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
+}
+
+static int fd_wrapped_fd(grpc_fd *fd) {
+ int ret_fd = -1;
+ gpr_mu_lock(&fd->mu);
+ if (!fd->orphaned) {
+ ret_fd = fd->fd;
+ }
+ gpr_mu_unlock(&fd->mu);
+
+ return ret_fd;
+}
+
+static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *on_done, int *release_fd,
+ const char *reason) {
+ bool is_fd_closed = false;
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ gpr_mu_lock(&fd->mu);
+ fd->on_done_closure = on_done;
+
+ /* If release_fd is not NULL, we should be relinquishing control of the file
+ descriptor fd->fd (but we still own the grpc_fd structure). */
+ if (release_fd != NULL) {
+ *release_fd = fd->fd;
+ } else {
+ close(fd->fd);
+ is_fd_closed = true;
+ }
+
+ fd->orphaned = true;
+
+ /* Remove the active status but keep referenced. We want this grpc_fd struct
+ to be alive (and not added to freelist) until the end of this function */
+ REF_BY(fd, 1, reason);
+
+ /* Remove the fd from the polling island:
+ - Get a lock on the latest polling island (i.e the last island in the
+ linked list pointed by fd->polling_island). This is the island that
+ would actually contain the fd
+ - Remove the fd from the latest polling island
+ - Unlock the latest polling island
+ - Set fd->polling_island to NULL (but remove the ref on the polling island
+ before doing this.) */
+ gpr_mu_lock(&fd->pi_mu);
+ if (fd->polling_island != NULL) {
+ polling_island *pi_latest = polling_island_lock(fd->polling_island);
+ polling_island_remove_fd_locked(pi_latest, fd, is_fd_closed, &error);
+ gpr_mu_unlock(&pi_latest->mu);
+
+ PI_UNREF(fd->polling_island, "fd_orphan");
+ fd->polling_island = NULL;
+ }
+ gpr_mu_unlock(&fd->pi_mu);
+
+ grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, error, NULL);
+
+ gpr_mu_unlock(&fd->mu);
+ UNREF_BY(fd, 2, reason); /* Drop the reference */
+ GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error));
+}
+
+static grpc_error *fd_shutdown_error(bool shutdown) {
+ if (!shutdown) {
+ return GRPC_ERROR_NONE;
+ } else {
+ return GRPC_ERROR_CREATE("FD shutdown");
+ }
+}
+
+static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure **st, grpc_closure *closure) {
+ if (fd->shutdown) {
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"),
+ NULL);
+ } else if (*st == CLOSURE_NOT_READY) {
+ /* not ready ==> switch to a waiting state by setting the closure */
+ *st = closure;
+ } else if (*st == CLOSURE_READY) {
+ /* already ready ==> queue the closure to run immediately */
+ *st = CLOSURE_NOT_READY;
+ grpc_exec_ctx_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
+ NULL);
+ } else {
+ /* upcallptr was set to a different closure. This is an error! */
+ gpr_log(GPR_ERROR,
+ "User called a notify_on function with a previous callback still "
+ "pending");
+ abort();
+ }
+}
+
+/* returns 1 if state becomes not ready */
+static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure **st) {
+ if (*st == CLOSURE_READY) {
+ /* duplicate ready ==> ignore */
+ return 0;
+ } else if (*st == CLOSURE_NOT_READY) {
+ /* not ready, and not waiting ==> flag ready */
+ *st = CLOSURE_READY;
+ return 0;
+ } else {
+ /* waiting ==> queue closure */
+ grpc_exec_ctx_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
+ *st = CLOSURE_NOT_READY;
+ return 1;
+ }
+}
+
+static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_fd *fd) {
+ grpc_pollset *notifier = NULL;
+
+ gpr_mu_lock(&fd->mu);
+ notifier = fd->read_notifier_pollset;
+ gpr_mu_unlock(&fd->mu);
+
+ return notifier;
+}
+
+static bool fd_is_shutdown(grpc_fd *fd) {
+ gpr_mu_lock(&fd->mu);
+ const bool r = fd->shutdown;
+ gpr_mu_unlock(&fd->mu);
+ return r;
+}
+
+/* Might be called multiple times */
+static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
+ gpr_mu_lock(&fd->mu);
+ /* Do the actual shutdown only once */
+ if (!fd->shutdown) {
+ fd->shutdown = true;
+
+ shutdown(fd->fd, SHUT_RDWR);
+ /* Flush any pending read and write closures. Since fd->shutdown is 'true'
+ at this point, the closures would be called with 'success = false' */
+ set_ready_locked(exec_ctx, fd, &fd->read_closure);
+ set_ready_locked(exec_ctx, fd, &fd->write_closure);
+ }
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *closure) {
+ gpr_mu_lock(&fd->mu);
+ notify_on_locked(exec_ctx, fd, &fd->read_closure, closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *closure) {
+ gpr_mu_lock(&fd->mu);
+ notify_on_locked(exec_ctx, fd, &fd->write_closure, closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+/*******************************************************************************
+ * Pollset Definitions
+ */
+GPR_TLS_DECL(g_current_thread_pollset);
+GPR_TLS_DECL(g_current_thread_worker);
+static __thread bool g_initialized_sigmask;
+static __thread sigset_t g_orig_sigmask;
+
+static void sig_handler(int sig_num) {
+#ifdef GRPC_EPOLL_DEBUG
+ gpr_log(GPR_INFO, "Received signal %d", sig_num);
+#endif
+}
+
+static void poller_kick_init() { signal(grpc_wakeup_signal, sig_handler); }
+
+/* Global state management */
+static grpc_error *pollset_global_init(void) {
+ gpr_tls_init(&g_current_thread_pollset);
+ gpr_tls_init(&g_current_thread_worker);
+ poller_kick_init();
+ return grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
+}
+
+static void pollset_global_shutdown(void) {
+ grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd);
+ gpr_tls_destroy(&g_current_thread_pollset);
+ gpr_tls_destroy(&g_current_thread_worker);
+}
+
+static grpc_error *pollset_worker_kick(grpc_pollset_worker *worker) {
+ grpc_error *err = GRPC_ERROR_NONE;
+
+ /* Kick the worker only if it was not already kicked */
+ if (gpr_atm_no_barrier_cas(&worker->is_kicked, (gpr_atm)0, (gpr_atm)1)) {
+ GRPC_POLLING_TRACE(
+ "pollset_worker_kick: Kicking worker: %p (thread id: %ld)",
+ (void *)worker, worker->pt_id);
+ int err_num = pthread_kill(worker->pt_id, grpc_wakeup_signal);
+ if (err_num != 0) {
+ err = GRPC_OS_ERROR(err_num, "pthread_kill");
+ }
+ }
+ return err;
+}
+
+/* Return 1 if the pollset has active threads in pollset_work (pollset must
+ * be locked) */
+static int pollset_has_workers(grpc_pollset *p) {
+ return p->root_worker.next != &p->root_worker;
+}
+
+static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->prev->next = worker->next;
+ worker->next->prev = worker->prev;
+}
+
+static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) {
+ if (pollset_has_workers(p)) {
+ grpc_pollset_worker *w = p->root_worker.next;
+ remove_worker(p, w);
+ return w;
+ } else {
+ return NULL;
+ }
+}
+
+static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->next = &p->root_worker;
+ worker->prev = worker->next->prev;
+ worker->prev->next = worker->next->prev = worker;
+}
+
+static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->prev = &p->root_worker;
+ worker->next = worker->prev->next;
+ worker->prev->next = worker->next->prev = worker;
+}
+
+/* p->mu must be held before calling this function */
+static grpc_error *pollset_kick(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker) {
+ GPR_TIMER_BEGIN("pollset_kick", 0);
+ grpc_error *error = GRPC_ERROR_NONE;
+ const char *err_desc = "Kick Failure";
+ grpc_pollset_worker *worker = specific_worker;
+ if (worker != NULL) {
+ if (worker == GRPC_POLLSET_KICK_BROADCAST) {
+ if (pollset_has_workers(p)) {
+ GPR_TIMER_BEGIN("pollset_kick.broadcast", 0);
+ for (worker = p->root_worker.next; worker != &p->root_worker;
+ worker = worker->next) {
+ if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) {
+ append_error(&error, pollset_worker_kick(worker), err_desc);
+ }
+ }
+ GPR_TIMER_END("pollset_kick.broadcast", 0);
+ } else {
+ p->kicked_without_pollers = true;
+ }
+ } else {
+ GPR_TIMER_MARK("kicked_specifically", 0);
+ if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) {
+ append_error(&error, pollset_worker_kick(worker), err_desc);
+ }
+ }
+ } else if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)p) {
+ /* Since worker == NULL, it means that we can kick "any" worker on this
+ pollset 'p'. If 'p' happens to be the same pollset this thread is
+ currently polling (i.e in pollset_work() function), then there is no need
+ to kick any other worker since the current thread can just absorb the
+ kick. This is the reason why we enter this case only when
+ g_current_thread_pollset is != p */
+
+ GPR_TIMER_MARK("kick_anonymous", 0);
+ worker = pop_front_worker(p);
+ if (worker != NULL) {
+ GPR_TIMER_MARK("finally_kick", 0);
+ push_back_worker(p, worker);
+ append_error(&error, pollset_worker_kick(worker), err_desc);
+ } else {
+ GPR_TIMER_MARK("kicked_no_pollers", 0);
+ p->kicked_without_pollers = true;
+ }
+ }
+
+ GPR_TIMER_END("pollset_kick", 0);
+ GRPC_LOG_IF_ERROR("pollset_kick", GRPC_ERROR_REF(error));
+ return error;
+}
+
+static grpc_error *kick_poller(void) {
+ return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd);
+}
+
+static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
+ gpr_mu_init(&pollset->mu);
+ *mu = &pollset->mu;
+
+ pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker;
+ pollset->kicked_without_pollers = false;
+
+ pollset->shutting_down = false;
+ pollset->finish_shutdown_called = false;
+ pollset->shutdown_done = NULL;
+
+ pollset->polling_island = NULL;
+}
+
+/* Convert a timespec to milliseconds:
+ - Very small or negative poll times are clamped to zero to do a non-blocking
+ poll (which becomes spin polling)
+ - Other small values are rounded up to one millisecond
+ - Longer than a millisecond polls are rounded up to the next nearest
+ millisecond to avoid spinning
+ - Infinite timeouts are converted to -1 */
+static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
+ gpr_timespec now) {
+ gpr_timespec timeout;
+ static const int64_t max_spin_polling_us = 10;
+ if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
+ return -1;
+ }
+
+ if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros(
+ max_spin_polling_us,
+ GPR_TIMESPAN))) <= 0) {
+ return 0;
+ }
+ timeout = gpr_time_sub(deadline, now);
+ return gpr_time_to_millis(gpr_time_add(
+ timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
+}
+
+static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_pollset *notifier) {
+ /* Need the fd->mu since we might be racing with fd_notify_on_read */
+ gpr_mu_lock(&fd->mu);
+ set_ready_locked(exec_ctx, fd, &fd->read_closure);
+ fd->read_notifier_pollset = notifier;
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
+ /* Need the fd->mu since we might be racing with fd_notify_on_write */
+ gpr_mu_lock(&fd->mu);
+ set_ready_locked(exec_ctx, fd, &fd->write_closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void pollset_release_polling_island(grpc_pollset *ps, char *reason) {
+ if (ps->polling_island != NULL) {
+ PI_UNREF(ps->polling_island, reason);
+ }
+ ps->polling_island = NULL;
+}
+
+static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset) {
+ /* The pollset cannot have any workers if we are at this stage */
+ GPR_ASSERT(!pollset_has_workers(pollset));
+
+ pollset->finish_shutdown_called = true;
+
+ /* Release the ref and set pollset->polling_island to NULL */
+ pollset_release_polling_island(pollset, "ps_shutdown");
+ grpc_exec_ctx_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
+}
+
+/* pollset->mu lock must be held by the caller before calling this */
+static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_closure *closure) {
+ GPR_TIMER_BEGIN("pollset_shutdown", 0);
+ GPR_ASSERT(!pollset->shutting_down);
+ pollset->shutting_down = true;
+ pollset->shutdown_done = closure;
+ pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
+
+ /* If the pollset has any workers, we cannot call finish_shutdown_locked()
+ because it would release the underlying polling island. In such a case, we
+ let the last worker call finish_shutdown_locked() from pollset_work() */
+ if (!pollset_has_workers(pollset)) {
+ GPR_ASSERT(!pollset->finish_shutdown_called);
+ GPR_TIMER_MARK("pollset_shutdown.finish_shutdown_locked", 0);
+ finish_shutdown_locked(exec_ctx, pollset);
+ }
+ GPR_TIMER_END("pollset_shutdown", 0);
+}
+
+/* pollset_shutdown is guaranteed to be called before pollset_destroy. So other
+ * than destroying the mutexes, there is nothing special that needs to be done
+ * here */
+static void pollset_destroy(grpc_pollset *pollset) {
+ GPR_ASSERT(!pollset_has_workers(pollset));
+ gpr_mu_destroy(&pollset->mu);
+}
+
+static void pollset_reset(grpc_pollset *pollset) {
+ GPR_ASSERT(pollset->shutting_down);
+ GPR_ASSERT(!pollset_has_workers(pollset));
+ pollset->shutting_down = false;
+ pollset->finish_shutdown_called = false;
+ pollset->kicked_without_pollers = false;
+ pollset->shutdown_done = NULL;
+ pollset_release_polling_island(pollset, "ps_reset");
+}
+
+#define GRPC_EPOLL_MAX_EVENTS 1000
+/* Note: sig_mask contains the signal mask to use *during* epoll_wait() */
+static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset,
+ grpc_pollset_worker *worker, int timeout_ms,
+ sigset_t *sig_mask, grpc_error **error) {
+ struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS];
+ int epoll_fd = -1;
+ int ep_rv;
+ polling_island *pi = NULL;
+ char *err_msg;
+ const char *err_desc = "pollset_work_and_unlock";
+ GPR_TIMER_BEGIN("pollset_work_and_unlock", 0);
+
+ /* We need to get the epoll_fd to wait on. The epoll_fd is in inside the
+ latest polling island pointed by pollset->polling_island.
+
+ Since epoll_fd is immutable, we can read it without obtaining the polling
+ island lock. There is however a possibility that the polling island (from
+ which we got the epoll_fd) got merged with another island while we are
+ in this function. This is still okay because in such a case, we will wakeup
+ right-away from epoll_wait() and pick up the latest polling_island the next
+ this function (i.e pollset_work_and_unlock()) is called */
+
+ if (pollset->polling_island == NULL) {
+ pollset->polling_island = polling_island_create(NULL, error);
+ if (pollset->polling_island == NULL) {
+ GPR_TIMER_END("pollset_work_and_unlock", 0);
+ return; /* Fatal error. We cannot continue */
+ }
+
+ PI_ADD_REF(pollset->polling_island, "ps");
+ GRPC_POLLING_TRACE("pollset_work: pollset: %p created new pi: %p",
+ (void *)pollset, (void *)pollset->polling_island);
+ }
+
+ pi = polling_island_maybe_get_latest(pollset->polling_island);
+ epoll_fd = pi->epoll_fd;
+
+ /* Update the pollset->polling_island since the island being pointed by
+ pollset->polling_island maybe older than the one pointed by pi) */
+ if (pollset->polling_island != pi) {
+ /* Always do PI_ADD_REF before PI_UNREF because PI_UNREF may cause the
+ polling island to be deleted */
+ PI_ADD_REF(pi, "ps");
+ PI_UNREF(pollset->polling_island, "ps");
+ pollset->polling_island = pi;
+ }
+
+ /* Add an extra ref so that the island does not get destroyed (which means
+ the epoll_fd won't be closed) while we are are doing an epoll_wait() on the
+ epoll_fd */
+ PI_ADD_REF(pi, "ps_work");
+ gpr_mu_unlock(&pollset->mu);
+
+ do {
+ ep_rv = epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms,
+ sig_mask);
+ if (ep_rv < 0) {
+ if (errno != EINTR) {
+ gpr_asprintf(&err_msg,
+ "epoll_wait() epoll fd: %d failed with error: %d (%s)",
+ epoll_fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ } else {
+ /* We were interrupted. Save an interation by doing a zero timeout
+ epoll_wait to see if there are any other events of interest */
+ GRPC_POLLING_TRACE(
+ "pollset_work: pollset: %p, worker: %p received kick",
+ (void *)pollset, (void *)worker);
+ ep_rv = epoll_wait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0);
+ }
+ }
+
+#ifdef GRPC_TSAN
+ /* See the definition of g_poll_sync for more details */
+ gpr_atm_acq_load(&g_epoll_sync);
+#endif /* defined(GRPC_TSAN) */
+
+ for (int i = 0; i < ep_rv; ++i) {
+ void *data_ptr = ep_ev[i].data.ptr;
+ if (data_ptr == &grpc_global_wakeup_fd) {
+ append_error(error,
+ grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd),
+ err_desc);
+ } else if (data_ptr == &polling_island_wakeup_fd) {
+ GRPC_POLLING_TRACE(
+ "pollset_work: pollset: %p, worker: %p polling island (epoll_fd: "
+ "%d) got merged",
+ (void *)pollset, (void *)worker, epoll_fd);
+ /* This means that our polling island is merged with a different
+ island. We do not have to do anything here since the subsequent call
+ to the function pollset_work_and_unlock() will pick up the correct
+ epoll_fd */
+ } else {
+ grpc_fd *fd = data_ptr;
+ int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP);
+ int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI);
+ int write_ev = ep_ev[i].events & EPOLLOUT;
+ if (read_ev || cancel) {
+ fd_become_readable(exec_ctx, fd, pollset);
+ }
+ if (write_ev || cancel) {
+ fd_become_writable(exec_ctx, fd);
+ }
+ }
+ }
+ } while (ep_rv == GRPC_EPOLL_MAX_EVENTS);
+
+ GPR_ASSERT(pi != NULL);
+
+ /* Before leaving, release the extra ref we added to the polling island. It
+ is important to use "pi" here (i.e our old copy of pollset->polling_island
+ that we got before releasing the polling island lock). This is because
+ pollset->polling_island pointer might get udpated in other parts of the
+ code when there is an island merge while we are doing epoll_wait() above */
+ PI_UNREF(pi, "ps_work");
+
+ GPR_TIMER_END("pollset_work_and_unlock", 0);
+}
+
+/* pollset->mu lock must be held by the caller before calling this.
+ The function pollset_work() may temporarily release the lock (pollset->mu)
+ during the course of its execution but it will always re-acquire the lock and
+ ensure that it is held by the time the function returns */
+static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker_hdl,
+ gpr_timespec now, gpr_timespec deadline) {
+ GPR_TIMER_BEGIN("pollset_work", 0);
+ grpc_error *error = GRPC_ERROR_NONE;
+ int timeout_ms = poll_deadline_to_millis_timeout(deadline, now);
+
+ sigset_t new_mask;
+
+ grpc_pollset_worker worker;
+ worker.next = worker.prev = NULL;
+ worker.pt_id = pthread_self();
+ gpr_atm_no_barrier_store(&worker.is_kicked, (gpr_atm)0);
+
+ *worker_hdl = &worker;
+
+ gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset);
+ gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker);
+
+ if (pollset->kicked_without_pollers) {
+ /* If the pollset was kicked without pollers, pretend that the current
+ worker got the kick and skip polling. A kick indicates that there is some
+ work that needs attention like an event on the completion queue or an
+ alarm */
+ GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0);
+ pollset->kicked_without_pollers = 0;
+ } else if (!pollset->shutting_down) {
+ /* We use the posix-signal with number 'grpc_wakeup_signal' for waking up
+ (i.e 'kicking') a worker in the pollset. A 'kick' is a way to inform the
+ worker that there is some pending work that needs immediate attention
+ (like an event on the completion queue, or a polling island merge that
+ results in a new epoll-fd to wait on) and that the worker should not
+ spend time waiting in epoll_pwait().
+
+ A worker can be kicked anytime from the point it is added to the pollset
+ via push_front_worker() (or push_back_worker()) to the point it is
+ removed via remove_worker().
+ If the worker is kicked before/during it calls epoll_pwait(), it should
+ immediately exit from epoll_wait(). If the worker is kicked after it
+ returns from epoll_wait(), then nothing really needs to be done.
+
+ To accomplish this, we mask 'grpc_wakeup_signal' on this thread at all
+ times *except* when it is in epoll_pwait(). This way, the worker never
+ misses acting on a kick */
+
+ if (!g_initialized_sigmask) {
+ sigemptyset(&new_mask);
+ sigaddset(&new_mask, grpc_wakeup_signal);
+ pthread_sigmask(SIG_BLOCK, &new_mask, &g_orig_sigmask);
+ sigdelset(&g_orig_sigmask, grpc_wakeup_signal);
+ g_initialized_sigmask = true;
+ /* new_mask: The new thread mask which blocks 'grpc_wakeup_signal'.
+ This is the mask used at all times *except during
+ epoll_wait()*"
+ g_orig_sigmask: The thread mask which allows 'grpc_wakeup_signal' and
+ this is the mask to use *during epoll_wait()*
+
+ The new_mask is set on the worker before it is added to the pollset
+ (i.e before it can be kicked) */
+ }
+
+ push_front_worker(pollset, &worker); /* Add worker to pollset */
+
+ pollset_work_and_unlock(exec_ctx, pollset, &worker, timeout_ms,
+ &g_orig_sigmask, &error);
+ grpc_exec_ctx_flush(exec_ctx);
+
+ gpr_mu_lock(&pollset->mu);
+
+ /* Note: There is no need to reset worker.is_kicked to 0 since we are no
+ longer going to use this worker */
+ remove_worker(pollset, &worker);
+ }
+
+ /* If we are the last worker on the pollset (i.e pollset_has_workers() is
+ false at this point) and the pollset is shutting down, we may have to
+ finish the shutdown process by calling finish_shutdown_locked().
+ See pollset_shutdown() for more details.
+
+ Note: Continuing to access pollset here is safe; it is the caller's
+ responsibility to not destroy a pollset when it has outstanding calls to
+ pollset_work() */
+ if (pollset->shutting_down && !pollset_has_workers(pollset) &&
+ !pollset->finish_shutdown_called) {
+ GPR_TIMER_MARK("pollset_work.finish_shutdown_locked", 0);
+ finish_shutdown_locked(exec_ctx, pollset);
+
+ gpr_mu_unlock(&pollset->mu);
+ grpc_exec_ctx_flush(exec_ctx);
+ gpr_mu_lock(&pollset->mu);
+ }
+
+ *worker_hdl = NULL;
+
+ gpr_tls_set(&g_current_thread_pollset, (intptr_t)0);
+ gpr_tls_set(&g_current_thread_worker, (intptr_t)0);
+
+ GPR_TIMER_END("pollset_work", 0);
+
+ GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error));
+ return error;
+}
+
+static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_fd *fd) {
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ gpr_mu_lock(&pollset->mu);
+ gpr_mu_lock(&fd->pi_mu);
+
+ polling_island *pi_new = NULL;
+
+ /* 1) If fd->polling_island and pollset->polling_island are both non-NULL and
+ * equal, do nothing.
+ * 2) If fd->polling_island and pollset->polling_island are both NULL, create
+ * a new polling island (with a refcount of 2) and make the polling_island
+ * fields in both fd and pollset to point to the new island
+ * 3) If one of fd->polling_island or pollset->polling_island is NULL, update
+ * the NULL polling_island field to point to the non-NULL polling_island
+ * field (ensure that the refcount on the polling island is incremented by
+ * 1 to account for the newly added reference)
+ * 4) Finally, if fd->polling_island and pollset->polling_island are non-NULL
+ * and different, merge both the polling islands and update the
+ * polling_island fields in both fd and pollset to point to the merged
+ * polling island.
+ */
+ if (fd->polling_island == pollset->polling_island) {
+ pi_new = fd->polling_island;
+ if (pi_new == NULL) {
+ pi_new = polling_island_create(fd, &error);
+
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: Created new polling island. pi_new: %p (fd: %d, "
+ "pollset: %p)",
+ (void *)pi_new, fd->fd, (void *)pollset);
+ }
+ } else if (fd->polling_island == NULL) {
+ pi_new = polling_island_lock(pollset->polling_island);
+ polling_island_add_fds_locked(pi_new, &fd, 1, true, &error);
+ gpr_mu_unlock(&pi_new->mu);
+
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: fd->pi was NULL. pi_new: %p (fd: %d, pollset: %p, "
+ "pollset->pi: %p)",
+ (void *)pi_new, fd->fd, (void *)pollset,
+ (void *)pollset->polling_island);
+ } else if (pollset->polling_island == NULL) {
+ pi_new = polling_island_lock(fd->polling_island);
+ gpr_mu_unlock(&pi_new->mu);
+
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: pollset->pi was NULL. pi_new: %p (fd: %d, pollset: "
+ "%p, fd->pi: %p",
+ (void *)pi_new, fd->fd, (void *)pollset, (void *)fd->polling_island);
+ } else {
+ pi_new = polling_island_merge(fd->polling_island, pollset->polling_island,
+ &error);
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: polling islands merged. pi_new: %p (fd: %d, pollset: "
+ "%p, fd->pi: %p, pollset->pi: %p)",
+ (void *)pi_new, fd->fd, (void *)pollset, (void *)fd->polling_island,
+ (void *)pollset->polling_island);
+ }
+
+ /* At this point, pi_new is the polling island that both fd->polling_island
+ and pollset->polling_island must be pointing to */
+
+ if (fd->polling_island != pi_new) {
+ PI_ADD_REF(pi_new, "fd");
+ if (fd->polling_island != NULL) {
+ PI_UNREF(fd->polling_island, "fd");
+ }
+ fd->polling_island = pi_new;
+ }
+
+ if (pollset->polling_island != pi_new) {
+ PI_ADD_REF(pi_new, "ps");
+ if (pollset->polling_island != NULL) {
+ PI_UNREF(pollset->polling_island, "ps");
+ }
+ pollset->polling_island = pi_new;
+ }
+
+ gpr_mu_unlock(&fd->pi_mu);
+ gpr_mu_unlock(&pollset->mu);
+}
+
+/*******************************************************************************
+ * Pollset-set Definitions
+ */
+
+static grpc_pollset_set *pollset_set_create(void) {
+ grpc_pollset_set *pollset_set = gpr_malloc(sizeof(*pollset_set));
+ memset(pollset_set, 0, sizeof(*pollset_set));
+ gpr_mu_init(&pollset_set->mu);
+ return pollset_set;
+}
+
+static void pollset_set_destroy(grpc_pollset_set *pollset_set) {
+ size_t i;
+ gpr_mu_destroy(&pollset_set->mu);
+ for (i = 0; i < pollset_set->fd_count; i++) {
+ GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+ }
+ gpr_free(pollset_set->pollsets);
+ gpr_free(pollset_set->pollset_sets);
+ gpr_free(pollset_set->fds);
+ gpr_free(pollset_set);
+}
+
+static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set, grpc_fd *fd) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ if (pollset_set->fd_count == pollset_set->fd_capacity) {
+ pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity);
+ pollset_set->fds = gpr_realloc(
+ pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds));
+ }
+ GRPC_FD_REF(fd, "pollset_set");
+ pollset_set->fds[pollset_set->fd_count++] = fd;
+ for (i = 0; i < pollset_set->pollset_count; i++) {
+ pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd);
+ }
+ for (i = 0; i < pollset_set->pollset_set_count; i++) {
+ pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set, grpc_fd *fd) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ for (i = 0; i < pollset_set->fd_count; i++) {
+ if (pollset_set->fds[i] == fd) {
+ pollset_set->fd_count--;
+ GPR_SWAP(grpc_fd *, pollset_set->fds[i],
+ pollset_set->fds[pollset_set->fd_count]);
+ GRPC_FD_UNREF(fd, "pollset_set");
+ break;
+ }
+ }
+ for (i = 0; i < pollset_set->pollset_set_count; i++) {
+ pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set,
+ grpc_pollset *pollset) {
+ size_t i, j;
+ gpr_mu_lock(&pollset_set->mu);
+ if (pollset_set->pollset_count == pollset_set->pollset_capacity) {
+ pollset_set->pollset_capacity =
+ GPR_MAX(8, 2 * pollset_set->pollset_capacity);
+ pollset_set->pollsets =
+ gpr_realloc(pollset_set->pollsets, pollset_set->pollset_capacity *
+ sizeof(*pollset_set->pollsets));
+ }
+ pollset_set->pollsets[pollset_set->pollset_count++] = pollset;
+ for (i = 0, j = 0; i < pollset_set->fd_count; i++) {
+ if (fd_is_orphaned(pollset_set->fds[i])) {
+ GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+ } else {
+ pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]);
+ pollset_set->fds[j++] = pollset_set->fds[i];
+ }
+ }
+ pollset_set->fd_count = j;
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set,
+ grpc_pollset *pollset) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ for (i = 0; i < pollset_set->pollset_count; i++) {
+ if (pollset_set->pollsets[i] == pollset) {
+ pollset_set->pollset_count--;
+ GPR_SWAP(grpc_pollset *, pollset_set->pollsets[i],
+ pollset_set->pollsets[pollset_set->pollset_count]);
+ break;
+ }
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *bag,
+ grpc_pollset_set *item) {
+ size_t i, j;
+ gpr_mu_lock(&bag->mu);
+ if (bag->pollset_set_count == bag->pollset_set_capacity) {
+ bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity);
+ bag->pollset_sets =
+ gpr_realloc(bag->pollset_sets,
+ bag->pollset_set_capacity * sizeof(*bag->pollset_sets));
+ }
+ bag->pollset_sets[bag->pollset_set_count++] = item;
+ for (i = 0, j = 0; i < bag->fd_count; i++) {
+ if (fd_is_orphaned(bag->fds[i])) {
+ GRPC_FD_UNREF(bag->fds[i], "pollset_set");
+ } else {
+ pollset_set_add_fd(exec_ctx, item, bag->fds[i]);
+ bag->fds[j++] = bag->fds[i];
+ }
+ }
+ bag->fd_count = j;
+ gpr_mu_unlock(&bag->mu);
+}
+
+static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *bag,
+ grpc_pollset_set *item) {
+ size_t i;
+ gpr_mu_lock(&bag->mu);
+ for (i = 0; i < bag->pollset_set_count; i++) {
+ if (bag->pollset_sets[i] == item) {
+ bag->pollset_set_count--;
+ GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i],
+ bag->pollset_sets[bag->pollset_set_count]);
+ break;
+ }
+ }
+ gpr_mu_unlock(&bag->mu);
+}
+
+/* Test helper functions
+ * */
+void *grpc_fd_get_polling_island(grpc_fd *fd) {
+ polling_island *pi;
+
+ gpr_mu_lock(&fd->pi_mu);
+ pi = fd->polling_island;
+ gpr_mu_unlock(&fd->pi_mu);
+
+ return pi;
+}
+
+void *grpc_pollset_get_polling_island(grpc_pollset *ps) {
+ polling_island *pi;
+
+ gpr_mu_lock(&ps->mu);
+ pi = ps->polling_island;
+ gpr_mu_unlock(&ps->mu);
+
+ return pi;
+}
+
+bool grpc_are_polling_islands_equal(void *p, void *q) {
+ polling_island *p1 = p;
+ polling_island *p2 = q;
+
+ /* Note: polling_island_lock_pair() may change p1 and p2 to point to the
+ latest polling islands in their respective linked lists */
+ polling_island_lock_pair(&p1, &p2);
+ polling_island_unlock_pair(p1, p2);
+
+ return p1 == p2;
+}
+
+/*******************************************************************************
+ * Event engine binding
+ */
+
+static void shutdown_engine(void) {
+ fd_global_shutdown();
+ pollset_global_shutdown();
+ polling_island_global_shutdown();
+}
+
+static const grpc_event_engine_vtable vtable = {
+ .pollset_size = sizeof(grpc_pollset),
+
+ .fd_create = fd_create,
+ .fd_wrapped_fd = fd_wrapped_fd,
+ .fd_orphan = fd_orphan,
+ .fd_shutdown = fd_shutdown,
+ .fd_is_shutdown = fd_is_shutdown,
+ .fd_notify_on_read = fd_notify_on_read,
+ .fd_notify_on_write = fd_notify_on_write,
+ .fd_get_read_notifier_pollset = fd_get_read_notifier_pollset,
+
+ .pollset_init = pollset_init,
+ .pollset_shutdown = pollset_shutdown,
+ .pollset_reset = pollset_reset,
+ .pollset_destroy = pollset_destroy,
+ .pollset_work = pollset_work,
+ .pollset_kick = pollset_kick,
+ .pollset_add_fd = pollset_add_fd,
+
+ .pollset_set_create = pollset_set_create,
+ .pollset_set_destroy = pollset_set_destroy,
+ .pollset_set_add_pollset = pollset_set_add_pollset,
+ .pollset_set_del_pollset = pollset_set_del_pollset,
+ .pollset_set_add_pollset_set = pollset_set_add_pollset_set,
+ .pollset_set_del_pollset_set = pollset_set_del_pollset_set,
+ .pollset_set_add_fd = pollset_set_add_fd,
+ .pollset_set_del_fd = pollset_set_del_fd,
+
+ .kick_poller = kick_poller,
+
+ .shutdown_engine = shutdown_engine,
+};
+
+/* It is possible that GLIBC has epoll but the underlying kernel doesn't.
+ * Create a dummy epoll_fd to make sure epoll support is available */
+static bool is_epoll_available() {
+ int fd = epoll_create1(EPOLL_CLOEXEC);
+ if (fd < 0) {
+ gpr_log(
+ GPR_ERROR,
+ "epoll_create1 failed with error: %d. Not using epoll polling engine",
+ fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+const grpc_event_engine_vtable *grpc_init_epoll_linux(void) {
+ /* If use of signals is disabled, we cannot use epoll engine*/
+ if (is_grpc_wakeup_signal_initialized && grpc_wakeup_signal < 0) {
+ return NULL;
+ }
+
+ if (!is_epoll_available()) {
+ return NULL;
+ }
+
+ if (!is_grpc_wakeup_signal_initialized) {
+ grpc_use_signal(SIGRTMIN + 2);
+ }
+
+ fd_global_init();
+
+ if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
+ return NULL;
+ }
+
+ if (!GRPC_LOG_IF_ERROR("polling_island_global_init",
+ polling_island_global_init())) {
+ return NULL;
+ }
+
+ return &vtable;
+}
+
+#else /* defined(GPR_LINUX_EPOLL) */
+#if defined(GPR_POSIX_SOCKET)
+#include "src/core/lib/iomgr/ev_posix.h"
+/* If GPR_LINUX_EPOLL is not defined, it means epoll is not available. Return
+ * NULL */
+const grpc_event_engine_vtable *grpc_init_epoll_linux(void) { return NULL; }
+#endif /* defined(GPR_POSIX_SOCKET) */
+
+void grpc_use_signal(int signum) {}
+#endif /* !defined(GPR_LINUX_EPOLL) */
diff --git a/src/core/lib/surface/surface_trace.h b/src/core/lib/iomgr/ev_epoll_linux.h
index a69a0fff57..7a494aba19 100644
--- a/src/core/lib/surface/surface_trace.h
+++ b/src/core/lib/iomgr/ev_epoll_linux.h
@@ -31,18 +31,17 @@
*
*/
-#ifndef GRPC_CORE_LIB_SURFACE_SURFACE_TRACE_H
-#define GRPC_CORE_LIB_SURFACE_SURFACE_TRACE_H
+#ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H
+#define GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H
-#include <grpc/support/log.h>
-#include "src/core/lib/debug/trace.h"
-#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/iomgr/ev_posix.h"
-#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \
- if (grpc_api_trace) { \
- char *_ev = grpc_event_string(event); \
- gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \
- gpr_free(_ev); \
- }
+const grpc_event_engine_vtable *grpc_init_epoll_linux(void);
-#endif /* GRPC_CORE_LIB_SURFACE_SURFACE_TRACE_H */
+#ifdef GPR_LINUX_EPOLL
+void *grpc_fd_get_polling_island(grpc_fd *fd);
+void *grpc_pollset_get_polling_island(grpc_pollset *ps);
+bool grpc_are_polling_islands_equal(void *p, void *q);
+#endif /* defined(GPR_LINUX_EPOLL) */
+
+#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H */
diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c
index 4c360f13a3..a3c1e9db9a 100644
--- a/src/core/lib/iomgr/ev_posix.c
+++ b/src/core/lib/iomgr/ev_posix.c
@@ -44,6 +44,7 @@
#include <grpc/support/string_util.h>
#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/ev_epoll_linux.h"
#include "src/core/lib/iomgr/ev_poll_and_epoll_posix.h"
#include "src/core/lib/iomgr/ev_poll_posix.h"
#include "src/core/lib/support/env.h"
@@ -53,6 +54,7 @@
grpc_poll_function_type grpc_poll_function = poll;
static const grpc_event_engine_vtable *g_event_engine;
+static const char *g_poll_strategy_name = NULL;
typedef const grpc_event_engine_vtable *(*event_engine_factory_fn)(void);
@@ -62,7 +64,9 @@ typedef struct {
} event_engine_factory;
static const event_engine_factory g_factories[] = {
- {"poll", grpc_init_poll_posix}, {"legacy", grpc_init_poll_and_epoll_posix},
+ {"epoll", grpc_init_epoll_linux},
+ {"poll", grpc_init_poll_posix},
+ {"legacy", grpc_init_poll_and_epoll_posix},
};
static void add(const char *beg, const char *end, char ***ss, size_t *ns) {
@@ -98,6 +102,7 @@ static void try_engine(const char *engine) {
for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) {
if (is(engine, g_factories[i].name)) {
if ((g_event_engine = g_factories[i].factory())) {
+ g_poll_strategy_name = g_factories[i].name;
gpr_log(GPR_DEBUG, "Using polling engine: %s", g_factories[i].name);
return;
}
@@ -105,6 +110,9 @@ static void try_engine(const char *engine) {
}
}
+/* Call this only after calling grpc_event_engine_init() */
+const char *grpc_get_poll_strategy_name() { return g_poll_strategy_name; }
+
void grpc_event_engine_init(void) {
char *s = gpr_getenv("GRPC_POLL_STRATEGY");
if (s == NULL) {
@@ -167,11 +175,6 @@ void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
g_event_engine->fd_notify_on_write(exec_ctx, fd, closure);
}
-grpc_pollset *grpc_fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
- grpc_fd *fd) {
- return g_event_engine->fd_get_read_notifier_pollset(exec_ctx, fd);
-}
-
size_t grpc_pollset_size(void) { return g_event_engine->pollset_size; }
void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index 87e9bc4d46..579c84ef70 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -99,6 +99,9 @@ typedef struct grpc_event_engine_vtable {
void grpc_event_engine_init(void);
void grpc_event_engine_shutdown(void);
+/* Return the name of the poll strategy */
+const char *grpc_get_poll_strategy_name();
+
/* Create a wrapped file descriptor.
Requires fd is a non-blocking file descriptor.
This takes ownership of closing fd. */
diff --git a/src/core/lib/iomgr/network_status_tracker.c b/src/core/lib/iomgr/network_status_tracker.c
new file mode 100644
index 0000000000..38a1c9b7d4
--- /dev/null
+++ b/src/core/lib/iomgr/network_status_tracker.c
@@ -0,0 +1,121 @@
+/*
+ *
+ * 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 <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/iomgr/endpoint.h"
+
+typedef struct endpoint_ll_node {
+ grpc_endpoint *ep;
+ struct endpoint_ll_node *next;
+} endpoint_ll_node;
+
+static endpoint_ll_node *head = NULL;
+static gpr_mu g_endpoint_mutex;
+static bool g_init_done = false;
+
+void grpc_initialize_network_status_monitor() {
+ g_init_done = true;
+ gpr_mu_init(&g_endpoint_mutex);
+ // TODO(makarandd): Install callback with OS to monitor network status.
+}
+
+void grpc_destroy_network_status_monitor() {
+ for (endpoint_ll_node *curr = head; curr != NULL;) {
+ endpoint_ll_node *next = curr->next;
+ gpr_free(curr);
+ curr = next;
+ }
+ gpr_mu_destroy(&g_endpoint_mutex);
+}
+
+void grpc_network_status_register_endpoint(grpc_endpoint *ep) {
+ if (!g_init_done) {
+ grpc_initialize_network_status_monitor();
+ }
+ gpr_mu_lock(&g_endpoint_mutex);
+ if (head == NULL) {
+ head = (endpoint_ll_node *)gpr_malloc(sizeof(endpoint_ll_node));
+ head->ep = ep;
+ head->next = NULL;
+ } else {
+ endpoint_ll_node *prev_head = head;
+ head = (endpoint_ll_node *)gpr_malloc(sizeof(endpoint_ll_node));
+ head->ep = ep;
+ head->next = prev_head;
+ }
+ gpr_mu_unlock(&g_endpoint_mutex);
+}
+
+void grpc_network_status_unregister_endpoint(grpc_endpoint *ep) {
+ gpr_mu_lock(&g_endpoint_mutex);
+ GPR_ASSERT(head);
+ bool found = false;
+ endpoint_ll_node *prev = head;
+ // if we're unregistering the head, just move head to the next
+ if (ep == head->ep) {
+ head = head->next;
+ gpr_free(prev);
+ found = true;
+ } else {
+ for (endpoint_ll_node *curr = head->next; curr != NULL; curr = curr->next) {
+ if (ep == curr->ep) {
+ prev->next = curr->next;
+ gpr_free(curr);
+ found = true;
+ break;
+ }
+ prev = curr;
+ }
+ }
+ gpr_mu_unlock(&g_endpoint_mutex);
+ GPR_ASSERT(found);
+}
+
+// Walk the linked-list from head and execute shutdown. It is possible that
+// other threads might be in the process of shutdown as well, but that has
+// no side effect since endpoint shutdown is idempotent.
+void grpc_network_status_shutdown_all_endpoints() {
+ gpr_mu_lock(&g_endpoint_mutex);
+ if (head == NULL) {
+ gpr_mu_unlock(&g_endpoint_mutex);
+ return;
+ }
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+ for (endpoint_ll_node *curr = head; curr != NULL; curr = curr->next) {
+ curr->ep->vtable->shutdown(&exec_ctx, curr->ep);
+ }
+ gpr_mu_unlock(&g_endpoint_mutex);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
diff --git a/src/core/lib/iomgr/network_status_tracker.h b/src/core/lib/iomgr/network_status_tracker.h
new file mode 100644
index 0000000000..74a1aa8135
--- /dev/null
+++ b/src/core/lib/iomgr/network_status_tracker.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
+#define GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
+#include "src/core/lib/iomgr/endpoint.h"
+
+void grpc_network_status_register_endpoint(grpc_endpoint *ep);
+void grpc_network_status_unregister_endpoint(grpc_endpoint *ep);
+void grpc_network_status_shutdown_all_endpoints();
+#endif /* GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H */
diff --git a/src/core/lib/iomgr/socket_utils_common_posix.c b/src/core/lib/iomgr/socket_utils_common_posix.c
index 3a1371617e..d2f6261e2a 100644
--- a/src/core/lib/iomgr/socket_utils_common_posix.c
+++ b/src/core/lib/iomgr/socket_utils_common_posix.c
@@ -169,6 +169,28 @@ grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse) {
return GRPC_ERROR_NONE;
}
+/* set a socket to reuse old addresses */
+grpc_error *grpc_set_socket_reuse_port(int fd, int reuse) {
+#ifndef SO_REUSEPORT
+ return GRPC_ERROR_CREATE("SO_REUSEPORT unavailable on compiling system");
+#else
+ int val = (reuse != 0);
+ int newval;
+ socklen_t intlen = sizeof(newval);
+ if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) {
+ return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)");
+ }
+ if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) {
+ return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)");
+ }
+ if ((newval != 0) != val) {
+ return GRPC_ERROR_CREATE("Failed to set SO_REUSEPORT");
+ }
+
+ return GRPC_ERROR_NONE;
+#endif
+}
+
/* disable nagle */
grpc_error *grpc_set_socket_low_latency(int fd, int low_latency) {
int val = (low_latency != 0);
diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h
index 30ff39dfa3..7bcc2219ae 100644
--- a/src/core/lib/iomgr/socket_utils_posix.h
+++ b/src/core/lib/iomgr/socket_utils_posix.h
@@ -55,6 +55,9 @@ grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse);
/* disable nagle */
grpc_error *grpc_set_socket_low_latency(int fd, int low_latency);
+/* set SO_REUSEPORT */
+grpc_error *grpc_set_socket_reuse_port(int fd, int reuse);
+
/* Returns true if this system can create AF_INET6 sockets bound to ::1.
The value is probed once, and cached for the life of the process.
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index 017f52c367..2ab45e33ce 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -35,6 +35,7 @@
#ifdef GPR_POSIX_SOCKET
+#include "src/core/lib/iomgr/network_status_tracker.h"
#include "src/core/lib/iomgr/tcp_posix.h"
#include <errno.h>
@@ -152,6 +153,7 @@ static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); }
#endif
static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
+ grpc_network_status_unregister_endpoint(ep);
grpc_tcp *tcp = (grpc_tcp *)ep;
TCP_UNREF(exec_ctx, tcp, "destroy");
}
@@ -160,7 +162,7 @@ static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp,
grpc_error *error) {
grpc_closure *cb = tcp->read_cb;
- if (false && grpc_tcp_trace) {
+ if (grpc_tcp_trace) {
size_t i;
const char *str = grpc_error_string(error);
gpr_log(GPR_DEBUG, "read: error=%s", str);
@@ -394,7 +396,7 @@ static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
grpc_tcp *tcp = (grpc_tcp *)ep;
grpc_error *error = GRPC_ERROR_NONE;
- if (false && grpc_tcp_trace) {
+ if (grpc_tcp_trace) {
size_t i;
for (i = 0; i < buf->count; i++) {
@@ -474,6 +476,8 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size,
tcp->write_closure.cb = tcp_handle_write;
tcp->write_closure.cb_arg = tcp;
gpr_slice_buffer_init(&tcp->last_read_buffer);
+ /* Tell network status tracker about new endpoint */
+ grpc_network_status_register_endpoint(&tcp->base);
return &tcp->base;
}
diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h
index 1d6e70dbe9..875a6ca14a 100644
--- a/src/core/lib/iomgr/tcp_server.h
+++ b/src/core/lib/iomgr/tcp_server.h
@@ -34,6 +34,8 @@
#ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
#define GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
+#include <grpc/grpc.h>
+
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/endpoint.h"
@@ -59,6 +61,7 @@ typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg,
If shutdown_complete is not NULL, it will be used by
grpc_tcp_server_unref() when the ref count reaches zero. */
grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+ const grpc_channel_args *args,
grpc_tcp_server **server);
/* Start listening to bound ports */
diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c
index 6fb547bb36..a1a463550a 100644
--- a/src/core/lib/iomgr/tcp_server_posix.c
+++ b/src/core/lib/iomgr/tcp_server_posix.c
@@ -112,8 +112,10 @@ struct grpc_tcp_server {
/* destroyed port count: how many ports are completely destroyed */
size_t destroyed_ports;
- /* is this server shutting down? (boolean) */
- int shutdown;
+ /* is this server shutting down? */
+ bool shutdown;
+ /* use SO_REUSEPORT */
+ bool so_reuseport;
/* linked list of server ports */
grpc_tcp_listener *head;
@@ -135,14 +137,42 @@ struct grpc_tcp_server {
size_t next_pollset_to_assign;
};
+static gpr_once check_init = GPR_ONCE_INIT;
+static bool has_so_reuseport;
+
+static void init(void) {
+ int s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s >= 0) {
+ has_so_reuseport = GRPC_LOG_IF_ERROR("check for SO_REUSEPORT",
+ grpc_set_socket_reuse_port(s, 1));
+ close(s);
+ }
+}
+
grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+ const grpc_channel_args *args,
grpc_tcp_server **server) {
+ gpr_once_init(&check_init, init);
+
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
+ s->so_reuseport = has_so_reuseport;
+ for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) {
+ if (0 == strcmp(GRPC_ARG_ALLOW_REUSEPORT, args->args[i].key)) {
+ if (args->args[i].type == GRPC_ARG_INTEGER) {
+ s->so_reuseport =
+ has_so_reuseport && (args->args[i].value.integer != 0);
+ } else {
+ gpr_free(s);
+ return GRPC_ERROR_CREATE(GRPC_ARG_ALLOW_REUSEPORT
+ " must be an integer");
+ }
+ }
+ }
gpr_ref_init(&s->refs, 1);
gpr_mu_init(&s->mu);
s->active_ports = 0;
s->destroyed_ports = 0;
- s->shutdown = 0;
+ s->shutdown = false;
s->shutdown_starting.head = NULL;
s->shutdown_starting.tail = NULL;
s->shutdown_complete = shutdown_complete;
@@ -218,7 +248,7 @@ static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->shutdown);
- s->shutdown = 1;
+ s->shutdown = true;
/* shutdown all fd's */
if (s->active_ports) {
@@ -268,13 +298,19 @@ static int get_max_accept_queue_size(void) {
/* Prepare a recently-created socket for listening. */
static grpc_error *prepare_socket(int fd, const struct sockaddr *addr,
- size_t addr_len, int *port) {
+ size_t addr_len, bool so_reuseport,
+ int *port) {
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
grpc_error *err = GRPC_ERROR_NONE;
GPR_ASSERT(fd >= 0);
+ if (so_reuseport) {
+ err = grpc_set_socket_reuse_port(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
+ }
+
err = grpc_set_socket_nonblocking(fd, 1);
if (err != GRPC_ERROR_NONE) goto error;
err = grpc_set_socket_cloexec(fd, 1);
@@ -407,7 +443,7 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd,
char *addr_str;
char *name;
- grpc_error *err = prepare_socket(fd, addr, addr_len, &port);
+ grpc_error *err = prepare_socket(fd, addr, addr_len, s->so_reuseport, &port);
if (err == GRPC_ERROR_NONE) {
GPR_ASSERT(port > 0);
grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1);
@@ -443,6 +479,52 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd,
return err;
}
+static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) {
+ grpc_tcp_listener *sp = NULL;
+ char *addr_str;
+ char *name;
+ grpc_error *err;
+
+ for (grpc_tcp_listener *l = listener->next; l && l->is_sibling; l = l->next) {
+ l->fd_index += count;
+ }
+
+ for (unsigned i = 0; i < count; i++) {
+ int fd, port;
+ grpc_dualstack_mode dsmode;
+ err = grpc_create_dualstack_socket(&listener->addr.sockaddr, SOCK_STREAM, 0,
+ &dsmode, &fd);
+ if (err != GRPC_ERROR_NONE) return err;
+ err = prepare_socket(fd, &listener->addr.sockaddr, listener->addr_len, true,
+ &port);
+ if (err != GRPC_ERROR_NONE) return err;
+ listener->server->nports++;
+ grpc_sockaddr_to_string(&addr_str, &listener->addr.sockaddr, 1);
+ gpr_asprintf(&name, "tcp-server-listener:%s/clone-%d", addr_str, i);
+ sp = gpr_malloc(sizeof(grpc_tcp_listener));
+ sp->next = listener->next;
+ listener->next = sp;
+ sp->server = listener->server;
+ sp->fd = fd;
+ sp->emfd = grpc_fd_create(fd, name);
+ memcpy(sp->addr.untyped, listener->addr.untyped, listener->addr_len);
+ sp->addr_len = listener->addr_len;
+ sp->port = port;
+ sp->port_index = listener->port_index;
+ sp->fd_index = listener->fd_index + count - i;
+ sp->is_sibling = 1;
+ sp->sibling = listener->is_sibling ? listener->sibling : listener;
+ GPR_ASSERT(sp->emfd);
+ while (listener->server->tail->next != NULL) {
+ listener->server->tail = listener->server->tail->next;
+ }
+ gpr_free(addr_str);
+ gpr_free(name);
+ }
+
+ return GRPC_ERROR_NONE;
+}
+
grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
size_t addr_len, int *out_port) {
grpc_tcp_listener *sp;
@@ -599,14 +681,29 @@ 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 (sp = s->head; sp; sp = sp->next) {
- for (i = 0; i < pollset_count; i++) {
- grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
+ sp = s->head;
+ while (sp != NULL) {
+ if (s->so_reuseport && pollset_count > 1) {
+ GPR_ASSERT(GRPC_LOG_IF_ERROR(
+ "clone_port", clone_port(sp, (unsigned)(pollset_count - 1))));
+ for (i = 0; i < pollset_count; i++) {
+ grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
+ 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++;
+ sp = sp->next;
+ }
+ } else {
+ for (i = 0; i < pollset_count; i++) {
+ grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
+ }
+ 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++;
+ sp = sp->next;
}
- 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);
}
diff --git a/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c
index 2a51671ec7..7b0966704c 100644
--- a/src/core/lib/iomgr/tcp_server_windows.c
+++ b/src/core/lib/iomgr/tcp_server_windows.c
@@ -103,6 +103,7 @@ struct grpc_tcp_server {
/* Public function. Allocates the proper data structures to hold a
grpc_tcp_server. */
grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+ const grpc_channel_args *args,
grpc_tcp_server **server) {
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
gpr_ref_init(&s->refs, 1);
@@ -396,7 +397,7 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
size_t addr_len, unsigned port_index,
grpc_tcp_listener **listener) {
grpc_tcp_listener *sp = NULL;
- int port;
+ int port = -1;
int status;
GUID guid = WSAID_ACCEPTEX;
DWORD ioctl_num_bytes;
diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c
index b2af8030aa..37ab59021e 100644
--- a/src/core/lib/iomgr/tcp_windows.c
+++ b/src/core/lib/iomgr/tcp_windows.c
@@ -37,6 +37,7 @@
#include <limits.h>
+#include "src/core/lib/iomgr/network_status_tracker.h"
#include "src/core/lib/iomgr/sockaddr_windows.h"
#include <grpc/support/alloc.h>
@@ -378,6 +379,7 @@ static void win_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
}
static void win_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
+ grpc_network_status_unregister_endpoint(ep);
grpc_tcp *tcp = (grpc_tcp *)ep;
TCP_UNREF(tcp, "destroy");
}
@@ -401,6 +403,9 @@ grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) {
grpc_closure_init(&tcp->on_read, on_read, tcp);
grpc_closure_init(&tcp->on_write, on_write, tcp);
tcp->peer_string = gpr_strdup(peer_string);
+ /* Tell network status tracking code about the new endpoint */
+ grpc_network_status_register_endpoint(&tcp->base);
+
return &tcp->base;
}
diff --git a/src/core/lib/iomgr/udp_server.c b/src/core/lib/iomgr/udp_server.c
index 16150687d3..1ebccf2ee2 100644
--- a/src/core/lib/iomgr/udp_server.c
+++ b/src/core/lib/iomgr/udp_server.c
@@ -243,13 +243,13 @@ static int prepare_socket(int fd, const struct sockaddr *addr,
if (!grpc_set_socket_sndbuf(fd, buffer_size_bytes)) {
gpr_log(GPR_ERROR, "Failed to set send buffer size to %d bytes",
- buf_size_bytes);
+ buffer_size_bytes);
goto error;
}
if (!grpc_set_socket_rcvbuf(fd, buffer_size_bytes)) {
gpr_log(GPR_ERROR, "Failed to set receive buffer size to %d bytes",
- buf_size_bytes);
+ buffer_size_bytes);
goto error;
}
diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.c b/src/core/lib/iomgr/wakeup_fd_eventfd.c
index 667b4a5f90..95f6102330 100644
--- a/src/core/lib/iomgr/wakeup_fd_eventfd.c
+++ b/src/core/lib/iomgr/wakeup_fd_eventfd.c
@@ -84,8 +84,10 @@ static void eventfd_destroy(grpc_wakeup_fd* fd_info) {
}
static int eventfd_check_availability(void) {
- /* TODO(klempner): Actually check if eventfd is available */
- return 1;
+ const int efd = eventfd(0, 0);
+ const int is_available = efd >= 0;
+ if (is_available) close(efd);
+ return is_available;
}
const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
diff --git a/src/core/lib/profiling/basic_timers.c b/src/core/lib/profiling/basic_timers.c
index 50082cd7ee..51813d0461 100644
--- a/src/core/lib/profiling/basic_timers.c
+++ b/src/core/lib/profiling/basic_timers.c
@@ -141,11 +141,11 @@ static void write_log(gpr_timer_log *log) {
entry->tm = gpr_time_0(entry->tm.clock_type);
}
fprintf(output_file,
- "{\"t\": %lld.%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": "
+ "{\"t\": %" PRId64
+ ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": "
"\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n",
- (long long)entry->tm.tv_sec, (int)entry->tm.tv_nsec, entry->thd,
- entry->type, entry->tagstr, entry->file, entry->line,
- entry->important);
+ entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type,
+ entry->tagstr, entry->file, entry->line, entry->important);
}
}
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.c b/src/core/lib/security/credentials/composite/composite_credentials.c
index 07db8bfd75..850e41e646 100644
--- a/src/core/lib/security/credentials/composite/composite_credentials.c
+++ b/src/core/lib/security/credentials/composite/composite_credentials.c
@@ -72,11 +72,12 @@ static void composite_call_md_context_destroy(
static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
grpc_credentials_md *md_elems,
size_t num_md,
- grpc_credentials_status status) {
+ grpc_credentials_status status,
+ const char *error_details) {
grpc_composite_call_credentials_metadata_context *ctx =
(grpc_composite_call_credentials_metadata_context *)user_data;
if (status != GRPC_CREDENTIALS_OK) {
- ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status);
+ ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status, error_details);
return;
}
@@ -101,7 +102,7 @@ static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
/* We're done!. */
ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries,
- ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK);
+ ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK, NULL);
composite_call_md_context_destroy(ctx);
}
diff --git a/src/core/lib/security/credentials/credentials.c b/src/core/lib/security/credentials/credentials.c
index 0eadaec191..029a357261 100644
--- a/src/core/lib/security/credentials/credentials.c
+++ b/src/core/lib/security/credentials/credentials.c
@@ -117,7 +117,7 @@ void grpc_call_credentials_get_request_metadata(
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);
+ cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK, NULL);
}
return;
}
diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h
index ce235e3a1d..8e9d842ead 100644
--- a/src/core/lib/security/credentials/credentials.h
+++ b/src/core/lib/security/credentials/credentials.h
@@ -156,11 +156,10 @@ void grpc_credentials_md_store_unref(grpc_credentials_md_store *store);
/* --- grpc_call_credentials. --- */
-typedef void (*grpc_credentials_metadata_cb)(grpc_exec_ctx *exec_ctx,
- void *user_data,
- grpc_credentials_md *md_elems,
- size_t num_md,
- grpc_credentials_status status);
+/* error_details must be NULL if status is GRPC_CREDENTIALS_OK. */
+typedef void (*grpc_credentials_metadata_cb)(
+ grpc_exec_ctx *exec_ctx, void *user_data, grpc_credentials_md *md_elems,
+ size_t num_md, grpc_credentials_status status, const char *error_details);
typedef struct {
void (*destruct)(grpc_call_credentials *c);
diff --git a/src/core/lib/security/credentials/fake/fake_credentials.c b/src/core/lib/security/credentials/fake/fake_credentials.c
index ee6d964de1..51cafd986f 100644
--- a/src/core/lib/security/credentials/fake/fake_credentials.c
+++ b/src/core/lib/security/credentials/fake/fake_credentials.c
@@ -100,7 +100,7 @@ static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx,
(grpc_credentials_metadata_request *)user_data;
grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds;
r->cb(exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries,
- GRPC_CREDENTIALS_OK);
+ GRPC_CREDENTIALS_OK, NULL);
grpc_credentials_metadata_request_destroy(r);
}
@@ -117,7 +117,7 @@ static void md_only_test_get_request_metadata(
grpc_closure_create(on_simulated_token_fetch_done, cb_arg),
GRPC_ERROR_NONE);
} else {
- cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK);
+ cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK, NULL);
}
}
diff --git a/src/core/lib/security/credentials/iam/iam_credentials.c b/src/core/lib/security/credentials/iam/iam_credentials.c
index 64d5871844..370a384d0e 100644
--- a/src/core/lib/security/credentials/iam/iam_credentials.c
+++ b/src/core/lib/security/credentials/iam/iam_credentials.c
@@ -55,7 +55,7 @@ static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx,
void *user_data) {
grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds;
cb(exec_ctx, user_data, c->iam_md->entries, c->iam_md->num_entries,
- GRPC_CREDENTIALS_OK);
+ GRPC_CREDENTIALS_OK, NULL);
}
static grpc_call_credentials_vtable iam_vtable = {iam_destruct,
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.c b/src/core/lib/security/credentials/jwt/jwt_credentials.c
index 973fb75eaa..f87ba0ce8d 100644
--- a/src/core/lib/security/credentials/jwt/jwt_credentials.c
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.c
@@ -113,10 +113,11 @@ static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
if (jwt_md != NULL) {
cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries,
- GRPC_CREDENTIALS_OK);
+ GRPC_CREDENTIALS_OK, NULL);
grpc_credentials_md_store_unref(jwt_md);
} else {
- cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
+ cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR,
+ "Could not generate JWT.");
}
}
@@ -149,11 +150,11 @@ grpc_call_credentials *grpc_service_account_jwt_access_credentials_create(
"grpc_service_account_jwt_access_credentials_create("
"json_key=%s, "
"token_lifetime="
- "gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
- 5,
- (json_key, (long long)token_lifetime.tv_sec, (int)token_lifetime.tv_nsec,
- (int)token_lifetime.clock_type, reserved));
+ 5, (json_key, token_lifetime.tv_sec, token_lifetime.tv_nsec,
+ (int)token_lifetime.clock_type, reserved));
GPR_ASSERT(reserved == NULL);
return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
grpc_auth_json_key_create_from_string(json_key), token_lifetime);
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
index 1102553dd3..c22ea5c468 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
@@ -235,10 +235,11 @@ static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx,
c->token_expiration =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime);
r->cb(exec_ctx, r->user_data, c->access_token_md->entries,
- c->access_token_md->num_entries, status);
+ c->access_token_md->num_entries, GRPC_CREDENTIALS_OK, NULL);
} else {
c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
- r->cb(exec_ctx, r->user_data, NULL, 0, status);
+ r->cb(exec_ctx, r->user_data, NULL, 0, status,
+ "Error occured when fetching oauth2 token.");
}
gpr_mu_unlock(&c->mu);
grpc_credentials_metadata_request_destroy(r);
@@ -266,7 +267,7 @@ static void oauth2_token_fetcher_get_request_metadata(
}
if (cached_access_token_md != NULL) {
cb(exec_ctx, user_data, cached_access_token_md->entries,
- cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK);
+ cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK, NULL);
grpc_credentials_md_store_unref(cached_access_token_md);
} else {
c->fetch_func(
@@ -404,7 +405,8 @@ static void access_token_get_request_metadata(
grpc_polling_entity *pollent, grpc_auth_metadata_context context,
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);
+ cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK,
+ NULL);
}
static grpc_call_credentials_vtable access_token_vtable = {
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c
index 9fb55e8466..824ff081dc 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.c
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c
@@ -67,7 +67,8 @@ static void plugin_md_request_metadata_ready(void *request,
gpr_log(GPR_ERROR, "Getting metadata from plugin failed with error: %s",
error_details);
}
- r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
+ r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR,
+ error_details);
} else {
size_t i;
grpc_credentials_md *md_array = NULL;
@@ -79,7 +80,7 @@ static void plugin_md_request_metadata_ready(void *request,
gpr_slice_from_copied_buffer(md[i].value, md[i].value_length);
}
}
- r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK);
+ r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK, NULL);
if (md_array != NULL) {
for (i = 0; i < num_md; i++) {
gpr_slice_unref(md_array[i].key);
@@ -107,7 +108,7 @@ static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx,
c->plugin.get_metadata(c->plugin.state, context,
plugin_md_request_metadata_ready, request);
} else {
- cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+ cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK, NULL);
}
}
diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c
index bccb8f755e..88e8de1449 100644
--- a/src/core/lib/security/transport/client_auth_filter.c
+++ b/src/core/lib/security/transport/client_auth_filter.c
@@ -91,14 +91,16 @@ static void bubble_up_error(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_status_code status, const char *error_msg) {
call_data *calld = elem->call_data;
gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg);
- grpc_transport_stream_op_add_cancellation(&calld->op, status);
+ gpr_slice error_slice = gpr_slice_from_copied_string(error_msg);
+ grpc_transport_stream_op_add_close(&calld->op, status, &error_slice);
grpc_call_next_op(exec_ctx, elem, &calld->op);
}
static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
grpc_credentials_md *md_elems,
size_t num_md,
- grpc_credentials_status status) {
+ grpc_credentials_status status,
+ const char *error_details) {
grpc_call_element *elem = (grpc_call_element *)user_data;
call_data *calld = elem->call_data;
grpc_transport_stream_op *op = &calld->op;
@@ -107,7 +109,9 @@ static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
reset_auth_metadata_context(&calld->auth_md_context);
if (status != GRPC_CREDENTIALS_OK) {
bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED,
- "Credentials failed to get metadata.");
+ (error_details != NULL && strlen(error_details) > 0)
+ ? error_details
+ : "Credentials failed to get metadata.");
return;
}
GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
@@ -220,8 +224,7 @@ static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_linked_mdelem *l;
grpc_client_security_context *sec_ctx = NULL;
- if (calld->security_context_set == 0 &&
- op->cancel_with_status == GRPC_STATUS_OK) {
+ if (calld->security_context_set == 0 && op->cancel_error == GRPC_ERROR_NONE) {
calld->security_context_set = 1;
GPR_ASSERT(op->context);
if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) {
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index cdb896909a..309c4434f9 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -412,8 +412,51 @@ static void set_status_code(grpc_call *call, status_source source,
call->status[source].is_set = 1;
call->status[source].code = (grpc_status_code)status;
+}
- /* TODO(ctiller): what to do about the flush that was previously here */
+static void set_status_details(grpc_call *call, status_source source,
+ grpc_mdstr *status) {
+ if (call->status[source].details != NULL) {
+ GRPC_MDSTR_UNREF(status);
+ } else {
+ call->status[source].details = status;
+ }
+}
+
+static void get_final_status(grpc_call *call,
+ void (*set_value)(grpc_status_code code,
+ void *user_data),
+ void *set_value_user_data) {
+ int i;
+ for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
+ if (call->status[i].is_set) {
+ set_value(call->status[i].code, set_value_user_data);
+ return;
+ }
+ }
+ if (call->is_client) {
+ set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
+ } else {
+ set_value(GRPC_STATUS_OK, set_value_user_data);
+ }
+}
+
+static void set_status_from_error(grpc_call *call, status_source source,
+ grpc_error *error) {
+ intptr_t status;
+ if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &status)) {
+ set_status_code(call, source, (uint32_t)status);
+ } else {
+ set_status_code(call, source, GRPC_STATUS_INTERNAL);
+ }
+ const char *msg = grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE);
+ bool free_msg = false;
+ if (msg == NULL) {
+ free_msg = true;
+ msg = grpc_error_string(error);
+ }
+ set_status_details(call, source, grpc_mdstr_from_string(msg));
+ if (free_msg) grpc_error_free_string(msg);
}
static void set_incoming_compression_algorithm(
@@ -502,32 +545,6 @@ uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call) {
return encodings_accepted_by_peer;
}
-static void set_status_details(grpc_call *call, status_source source,
- grpc_mdstr *status) {
- if (call->status[source].details != NULL) {
- GRPC_MDSTR_UNREF(call->status[source].details);
- }
- call->status[source].details = status;
-}
-
-static void get_final_status(grpc_call *call,
- void (*set_value)(grpc_status_code code,
- void *user_data),
- void *set_value_user_data) {
- int i;
- for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
- if (call->status[i].is_set) {
- set_value(call->status[i].code, set_value_user_data);
- return;
- }
- }
- if (call->is_client) {
- set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
- } else {
- set_value(GRPC_STATUS_OK, set_value_user_data);
- }
-}
-
static void get_final_details(grpc_call *call, char **out_details,
size_t *out_details_capacity) {
int i;
@@ -751,8 +768,7 @@ grpc_call_error grpc_call_cancel_with_status(grpc_call *c,
typedef struct termination_closure {
grpc_closure closure;
grpc_call *call;
- grpc_status_code status;
- gpr_slice optional_message;
+ grpc_error *error;
grpc_closure *op_closure;
enum { TC_CANCEL, TC_CLOSE } type;
} termination_closure;
@@ -768,7 +784,7 @@ static void done_termination(grpc_exec_ctx *exec_ctx, void *tcp,
GRPC_CALL_INTERNAL_UNREF(exec_ctx, tc->call, "close");
break;
}
- gpr_slice_unref(tc->optional_message);
+ GRPC_ERROR_UNREF(tc->error);
grpc_exec_ctx_sched(exec_ctx, tc->op_closure, GRPC_ERROR_NONE, NULL);
gpr_free(tc);
}
@@ -777,7 +793,7 @@ static void send_cancel(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
grpc_transport_stream_op op;
termination_closure *tc = tcp;
memset(&op, 0, sizeof(op));
- op.cancel_with_status = tc->status;
+ op.cancel_error = tc->error;
/* reuse closure to catch completion */
grpc_closure_init(&tc->closure, done_termination, tc);
op.on_complete = &tc->closure;
@@ -788,8 +804,7 @@ static void send_close(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
grpc_transport_stream_op op;
termination_closure *tc = tcp;
memset(&op, 0, sizeof(op));
- tc->optional_message = gpr_slice_ref(tc->optional_message);
- grpc_transport_stream_op_add_close(&op, tc->status, &tc->optional_message);
+ op.close_error = tc->error;
/* reuse closure to catch completion */
grpc_closure_init(&tc->closure, done_termination, tc);
tc->op_closure = op.on_complete;
@@ -799,14 +814,7 @@ static void send_close(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
static grpc_call_error terminate_with_status(grpc_exec_ctx *exec_ctx,
termination_closure *tc) {
- grpc_mdstr *details = NULL;
- if (GPR_SLICE_LENGTH(tc->optional_message) > 0) {
- tc->optional_message = gpr_slice_ref(tc->optional_message);
- details = grpc_mdstr_from_slice(tc->optional_message);
- }
-
- set_status_code(tc->call, STATUS_FROM_API_OVERRIDE, (uint32_t)tc->status);
- set_status_details(tc->call, STATUS_FROM_API_OVERRIDE, details);
+ set_status_from_error(tc->call, STATUS_FROM_API_OVERRIDE, tc->error);
if (tc->type == TC_CANCEL) {
grpc_closure_init(&tc->closure, send_cancel, tc);
@@ -822,13 +830,15 @@ static grpc_call_error terminate_with_status(grpc_exec_ctx *exec_ctx,
static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
grpc_status_code status,
const char *description) {
+ GPR_ASSERT(status != GRPC_STATUS_OK);
termination_closure *tc = gpr_malloc(sizeof(*tc));
memset(tc, 0, sizeof(termination_closure));
tc->type = TC_CANCEL;
tc->call = c;
- tc->optional_message = gpr_slice_from_copied_string(description);
- GPR_ASSERT(status != GRPC_STATUS_OK);
- tc->status = status;
+ tc->error = grpc_error_set_int(
+ grpc_error_set_str(GRPC_ERROR_CREATE(description),
+ GRPC_ERROR_STR_GRPC_MESSAGE, description),
+ GRPC_ERROR_INT_GRPC_STATUS, status);
return terminate_with_status(exec_ctx, tc);
}
@@ -836,13 +846,15 @@ static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
static grpc_call_error close_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
grpc_status_code status,
const char *description) {
+ GPR_ASSERT(status != GRPC_STATUS_OK);
termination_closure *tc = gpr_malloc(sizeof(*tc));
memset(tc, 0, sizeof(termination_closure));
tc->type = TC_CLOSE;
tc->call = c;
- tc->optional_message = gpr_slice_from_copied_string(description);
- GPR_ASSERT(status != GRPC_STATUS_OK);
- tc->status = status;
+ tc->error = grpc_error_set_int(
+ grpc_error_set_str(GRPC_ERROR_CREATE(description),
+ GRPC_ERROR_STR_GRPC_MESSAGE, description),
+ GRPC_ERROR_INT_GRPC_STATUS, status);
return terminate_with_status(exec_ctx, tc);
}
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index b640345c21..3a78fe3aa3 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -37,7 +37,6 @@
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/surface/api_trace.h"
-#include "src/core/lib/surface/surface_trace.h"
#include <grpc/grpc.h>
#include <grpc/impl/codegen/compression_types.h>
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index 8936e7164f..2cf6d8890a 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -223,11 +223,12 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
"grpc_channel_create_call("
"channel=%p, parent_call=%p, propagation_mask=%x, cq=%p, method=%s, "
"host=%s, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
- 10, (channel, parent_call, (unsigned)propagation_mask, cq, method, host,
- (long long)deadline.tv_sec, (int)deadline.tv_nsec,
- (int)deadline.clock_type, reserved));
+ 10,
+ (channel, parent_call, (unsigned)propagation_mask, cq, method, host,
+ deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, reserved));
GPR_ASSERT(!reserved);
return grpc_channel_create_call_internal(
channel, parent_call, propagation_mask, cq, NULL,
@@ -282,11 +283,12 @@ grpc_call *grpc_channel_create_registered_call(
"grpc_channel_create_registered_call("
"channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, "
"registered_call_handle=%p, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
9, (channel, parent_call, (unsigned)propagation_mask, completion_queue,
- registered_call_handle, (long long)deadline.tv_sec,
- (int)deadline.tv_nsec, (int)deadline.clock_type, reserved));
+ registered_call_handle, deadline.tv_sec, deadline.tv_nsec,
+ (int)deadline.clock_type, reserved));
GPR_ASSERT(!reserved);
return grpc_channel_create_call_internal(
channel, parent_call, propagation_mask, completion_queue, NULL,
diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c
index 2cc6aa74e0..5978884db8 100644
--- a/src/core/lib/surface/completion_queue.c
+++ b/src/core/lib/surface/completion_queue.c
@@ -48,7 +48,6 @@
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/event_string.h"
-#include "src/core/lib/surface/surface_trace.h"
int grpc_trace_operation_failures;
@@ -93,6 +92,17 @@ struct grpc_completion_queue {
static gpr_mu g_freelist_mu;
static grpc_completion_queue *g_freelist;
+int grpc_cq_pluck_trace;
+int grpc_cq_event_timeout_trace;
+
+#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \
+ if (grpc_api_trace && \
+ (grpc_cq_pluck_trace || (event)->type != GRPC_QUEUE_TIMEOUT)) { \
+ char *_ev = grpc_event_string(event); \
+ gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \
+ gpr_free(_ev); \
+ }
+
static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cc,
grpc_error *error);
@@ -240,7 +250,7 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
"grpc_cq_end_op(exec_ctx=%p, cc=%p, tag=%p, error=%s, done=%p, "
"done_arg=%p, storage=%p)",
7, (exec_ctx, cc, tag, errmsg, done, done_arg, storage));
- if (grpc_trace_operation_failures) {
+ if (grpc_trace_operation_failures && error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
}
grpc_error_free_string(errmsg);
@@ -316,10 +326,11 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
GRPC_API_TRACE(
"grpc_completion_queue_next("
"cc=%p, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
- 5, (cc, (long long)deadline.tv_sec, (int)deadline.tv_nsec,
- (int)deadline.clock_type, reserved));
+ 5, (cc, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type,
+ reserved));
GPR_ASSERT(!reserved);
deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
@@ -425,13 +436,16 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0);
- GRPC_API_TRACE(
- "grpc_completion_queue_pluck("
- "cc=%p, tag=%p, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
- "reserved=%p)",
- 6, (cc, tag, (long long)deadline.tv_sec, (int)deadline.tv_nsec,
- (int)deadline.clock_type, reserved));
+ if (grpc_cq_pluck_trace) {
+ GRPC_API_TRACE(
+ "grpc_completion_queue_pluck("
+ "cc=%p, tag=%p, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
+ "reserved=%p)",
+ 6, (cc, tag, deadline.tv_sec, deadline.tv_nsec,
+ (int)deadline.clock_type, reserved));
+ }
GPR_ASSERT(!reserved);
deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index b9dd1092b6..3049284f68 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -39,6 +39,10 @@
#include <grpc/grpc.h>
#include "src/core/lib/iomgr/pollset.h"
+/* These trace flags default to 1. The corresponding lines are only traced
+ if grpc_api_trace is also truthy */
+extern int grpc_cq_pluck_trace;
+extern int grpc_cq_event_timeout_trace;
extern int grpc_trace_operation_failures;
typedef struct grpc_cq_completion {
diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c
index f07039cb94..5397913a21 100644
--- a/src/core/lib/surface/init.c
+++ b/src/core/lib/surface/init.c
@@ -57,7 +57,6 @@
#include "src/core/lib/surface/init.h"
#include "src/core/lib/surface/lame_client.h"
#include "src/core/lib/surface/server.h"
-#include "src/core/lib/surface/surface_trace.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/transport_impl.h"
@@ -165,6 +164,12 @@ void grpc_init(void) {
&grpc_trace_channel_stack_builder);
grpc_register_tracer("http1", &grpc_http1_trace);
grpc_register_tracer("compression", &grpc_compression_trace);
+ grpc_register_tracer("queue_pluck", &grpc_cq_pluck_trace);
+ // Default pluck trace to 1
+ grpc_cq_pluck_trace = 1;
+ grpc_register_tracer("queue_timeout", &grpc_cq_event_timeout_trace);
+ // Default timeout trace to 1
+ grpc_cq_event_timeout_trace = 1;
grpc_register_tracer("op_failure", &grpc_trace_operation_failures);
grpc_security_pre_init();
grpc_iomgr_init();
diff --git a/src/core/lib/surface/version.c b/src/core/lib/surface/version.c
index aca76d2bb7..53f3c43854 100644
--- a/src/core/lib/surface/version.c
+++ b/src/core/lib/surface/version.c
@@ -36,4 +36,4 @@
#include <grpc/grpc.h>
-const char *grpc_version_string(void) { return "0.15.0-dev"; }
+const char *grpc_version_string(void) { return "0.16.0-dev"; }
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
index 1105494a85..f8ddf5774a 100644
--- a/src/core/lib/transport/transport.c
+++ b/src/core/lib/transport/transport.c
@@ -36,6 +36,7 @@
#include <grpc/support/atm.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
+#include "src/core/lib/support/string.h"
#include "src/core/lib/transport/transport_impl.h"
#ifdef GRPC_STREAM_REFCOUNT_DEBUG
@@ -162,55 +163,63 @@ void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx,
grpc_exec_ctx_sched(exec_ctx, op->on_complete, error, NULL);
}
-void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
- grpc_status_code status) {
- GPR_ASSERT(status != GRPC_STATUS_OK);
- if (op->cancel_with_status == GRPC_STATUS_OK) {
- op->cancel_with_status = status;
- }
- if (op->close_with_status != GRPC_STATUS_OK) {
- op->close_with_status = GRPC_STATUS_OK;
- if (op->optional_close_message != NULL) {
- gpr_slice_unref(*op->optional_close_message);
- op->optional_close_message = NULL;
- }
- }
-}
-
typedef struct {
- gpr_slice message;
+ grpc_error *error;
grpc_closure *then_call;
grpc_closure closure;
} close_message_data;
static void free_message(grpc_exec_ctx *exec_ctx, void *p, grpc_error *error) {
close_message_data *cmd = p;
- gpr_slice_unref(cmd->message);
+ GRPC_ERROR_UNREF(cmd->error);
if (cmd->then_call != NULL) {
- cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, GRPC_ERROR_REF(error));
+ cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, error);
}
gpr_free(cmd);
}
+static void add_error(grpc_transport_stream_op *op, grpc_error **which,
+ grpc_error *error) {
+ close_message_data *cmd;
+ cmd = gpr_malloc(sizeof(*cmd));
+ cmd->error = error;
+ cmd->then_call = op->on_complete;
+ grpc_closure_init(&cmd->closure, free_message, cmd);
+ op->on_complete = &cmd->closure;
+ *which = error;
+}
+
+void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
+ grpc_status_code status) {
+ GPR_ASSERT(status != GRPC_STATUS_OK);
+ if (op->cancel_error == GRPC_ERROR_NONE) {
+ op->cancel_error = grpc_error_set_int(GRPC_ERROR_CANCELLED,
+ GRPC_ERROR_INT_GRPC_STATUS, status);
+ op->close_error = GRPC_ERROR_NONE;
+ }
+}
+
void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op,
grpc_status_code status,
gpr_slice *optional_message) {
- close_message_data *cmd;
GPR_ASSERT(status != GRPC_STATUS_OK);
- if (op->cancel_with_status != GRPC_STATUS_OK ||
- op->close_with_status != GRPC_STATUS_OK) {
+ if (op->cancel_error != GRPC_ERROR_NONE ||
+ op->close_error != GRPC_ERROR_NONE) {
if (optional_message) {
gpr_slice_unref(*optional_message);
}
return;
}
- if (optional_message) {
- cmd = gpr_malloc(sizeof(*cmd));
- cmd->message = *optional_message;
- cmd->then_call = op->on_complete;
- grpc_closure_init(&cmd->closure, free_message, cmd);
- op->on_complete = &cmd->closure;
- op->optional_close_message = &cmd->message;
+ grpc_error *error;
+ if (optional_message != NULL) {
+ char *msg = gpr_dump_slice(*optional_message, GPR_DUMP_ASCII);
+ error = grpc_error_set_str(GRPC_ERROR_CREATE(msg),
+ GRPC_ERROR_STR_GRPC_MESSAGE, msg);
+ gpr_free(msg);
+ gpr_slice_unref(*optional_message);
+ } else {
+ error = GRPC_ERROR_CREATE("Call force closed");
}
- op->close_with_status = status;
+ error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, status);
+ add_error(op, &op->close_error, error);
}
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index a46ccb643c..d2f6344ee3 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -135,13 +135,12 @@ typedef struct grpc_transport_stream_op {
/** Collect any stats into provided buffer, zero internal stat counters */
grpc_transport_stream_stats *collect_stats;
- /** If != GRPC_STATUS_OK, cancel this stream */
- grpc_status_code cancel_with_status;
+ /** If != GRPC_ERROR_NONE, cancel this stream */
+ grpc_error *cancel_error;
- /** If != GRPC_STATUS_OK, send grpc-status, grpc-message, and close this
+ /** If != GRPC_ERROR, send grpc-status, grpc-message, and close this
stream for both reading and writing */
- grpc_status_code close_with_status;
- gpr_slice *optional_close_message;
+ grpc_error *close_error;
/* Indexes correspond to grpc_context_index enum values */
grpc_call_context_element *context;
diff --git a/src/core/lib/transport/transport_op_string.c b/src/core/lib/transport/transport_op_string.c
index df04c61127..138591db2a 100644
--- a/src/core/lib/transport/transport_op_string.c
+++ b/src/core/lib/transport/transport_op_string.c
@@ -63,8 +63,8 @@ static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) {
}
if (gpr_time_cmp(md.deadline, gpr_inf_future(md.deadline.clock_type)) != 0) {
char *tmp;
- gpr_asprintf(&tmp, " deadline=%lld.%09d", (long long)md.deadline.tv_sec,
- (int)md.deadline.tv_nsec);
+ gpr_asprintf(&tmp, " deadline=%" PRId64 ".%09d", md.deadline.tv_sec,
+ md.deadline.tv_nsec);
gpr_strvec_add(b, tmp);
}
}
@@ -119,10 +119,21 @@ char *grpc_transport_stream_op_string(grpc_transport_stream_op *op) {
gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA"));
}
- if (op->cancel_with_status != GRPC_STATUS_OK) {
+ if (op->cancel_error != GRPC_ERROR_NONE) {
if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
first = 0;
- gpr_asprintf(&tmp, "CANCEL:%d", op->cancel_with_status);
+ const char *msg = grpc_error_string(op->cancel_error);
+ gpr_asprintf(&tmp, "CANCEL:%s", msg);
+ grpc_error_free_string(msg);
+ gpr_strvec_add(&b, tmp);
+ }
+
+ if (op->close_error != GRPC_ERROR_NONE) {
+ if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+ first = 0;
+ const char *msg = grpc_error_string(op->close_error);
+ gpr_asprintf(&tmp, "CLOSE:%s", msg);
+ grpc_error_free_string(msg);
gpr_strvec_add(&b, tmp);
}
diff --git a/src/cpp/README.md b/src/cpp/README.md
index f2935e52d9..8c0f85e5ff 100644
--- a/src/cpp/README.md
+++ b/src/cpp/README.md
@@ -51,7 +51,7 @@ below.
#Build from Source
```sh
- $ git clone https://github.com/grpc/grpc.git
+ $ git clone -b $(curl -L http://grpc.io/release) https://github.com/grpc/grpc
$ cd grpc
$ git submodule update --init
$ make
diff --git a/src/csharp/Grpc.Auth/project.json b/src/csharp/Grpc.Auth/project.json
index 1677565824..72c258a91a 100644
--- a/src/csharp/Grpc.Auth/project.json
+++ b/src/csharp/Grpc.Auth/project.json
@@ -1,5 +1,5 @@
{
- "version": "0.15.0-dev",
+ "version": "0.16.0-dev",
"title": "gRPC C# Auth",
"authors": [ "Google Inc." ],
"copyright": "Copyright 2015, Google Inc.",
@@ -12,8 +12,17 @@
"requireLicenseAcceptance": false,
"tags": [ "gRPC RPC Protocol HTTP/2 Auth OAuth2" ],
},
+ "buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ }
+ },
"dependencies": {
- "Grpc.Core": "0.15.0-dev",
+ "Grpc.Core": "0.16.0-dev",
"Google.Apis.Auth": "1.11.1"
},
"frameworks": {
diff --git a/src/csharp/Grpc.Core.Tests/CompressionTest.cs b/src/csharp/Grpc.Core.Tests/CompressionTest.cs
index 378c81851c..16be210508 100644
--- a/src/csharp/Grpc.Core.Tests/CompressionTest.cs
+++ b/src/csharp/Grpc.Core.Tests/CompressionTest.cs
@@ -34,6 +34,7 @@
using System;
using System.Diagnostics;
using System.Linq;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
@@ -118,5 +119,30 @@ namespace Grpc.Core.Tests
await call.ResponseStream.ToListAsync();
}
+
+ [Test]
+ public void CanReadCompressedMessages()
+ {
+ var compressionMetadata = new Metadata
+ {
+ { new Metadata.Entry(Metadata.CompressionRequestAlgorithmMetadataKey, "gzip") }
+ };
+
+ helper.UnaryHandler = new UnaryServerMethod<string, string>(async (req, context) =>
+ {
+ await context.WriteResponseHeadersAsync(compressionMetadata);
+ return req;
+ });
+
+ var stringBuilder = new StringBuilder();
+ for (int i = 0; i < 200000; i++)
+ {
+ stringBuilder.Append('a');
+ }
+ var request = stringBuilder.ToString();
+ var response = Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(compressionMetadata)), request);
+
+ Assert.AreEqual(request, response);
+ }
}
}
diff --git a/src/csharp/Grpc.Core.Tests/project.json b/src/csharp/Grpc.Core.Tests/project.json
index 3ad081df39..f58bcbb515 100644
--- a/src/csharp/Grpc.Core.Tests/project.json
+++ b/src/csharp/Grpc.Core.Tests/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
@@ -17,6 +24,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec
index fa2c1fbff2..47593f787b 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.nuspec
+++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec
@@ -24,11 +24,12 @@
<file src="bin/ReleaseSigned/Grpc.Core.xml" target="lib/net45" />
<file src="**\*.cs" target="src" />
<file src="Grpc.Core.targets" target="\build\net45\Grpc.Core.targets" />
- <file src="../nativelibs/windows_x86/grpc_csharp_ext.dll" target="/build/native/bin/windows_x86/grpc_csharp_ext.dll" />
- <file src="../nativelibs/windows_x64/grpc_csharp_ext.dll" target="/build/native/bin/windows_x64/grpc_csharp_ext.dll" />
- <file src="../nativelibs/linux_x86/libgrpc_csharp_ext.so" target="/build/native/bin/linux_x86/libgrpc_csharp_ext.so" />
- <file src="../nativelibs/linux_x64/libgrpc_csharp_ext.so" target="/build/native/bin/linux_x64/libgrpc_csharp_ext.so" />
- <file src="../nativelibs/macosx_x86/libgrpc_csharp_ext.dylib" target="/build/native/bin/macosx_x86/libgrpc_csharp_ext.dylib" />
- <file src="../nativelibs/macosx_x64/libgrpc_csharp_ext.dylib" target="/build/native/bin/macosx_x64/libgrpc_csharp_ext.dylib" />
+ <!-- without backslashes in the the source path, nuget won't copy the files -->
+ <file src="..\nativelibs\windows_x86\grpc_csharp_ext.dll" target="/build/native/bin/windows_x86/grpc_csharp_ext.dll" />
+ <file src="..\nativelibs\windows_x64\grpc_csharp_ext.dll" target="/build/native/bin/windows_x64/grpc_csharp_ext.dll" />
+ <file src="..\nativelibs\linux_x86\libgrpc_csharp_ext.so" target="/build/native/bin/linux_x86/libgrpc_csharp_ext.so" />
+ <file src="..\nativelibs\linux_x64\libgrpc_csharp_ext.so" target="/build/native/bin/linux_x64/libgrpc_csharp_ext.so" />
+ <file src="..\nativelibs\macosx_x86\libgrpc_csharp_ext.dylib" target="/build/native/bin/macosx_x86/libgrpc_csharp_ext.dylib" />
+ <file src="..\nativelibs\macosx_x64\libgrpc_csharp_ext.dylib" target="/build/native/bin/macosx_x64/libgrpc_csharp_ext.dylib" />
</files>
</package>
diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
index 8581302706..014a8db78f 100644
--- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
@@ -54,7 +54,10 @@ namespace Grpc.Core.Internal
public void RegisterCompletionQueue(CompletionQueueSafeHandle cq)
{
- Native.grpcsharp_server_register_completion_queue(this, cq);
+ using (cq.NewScope())
+ {
+ Native.grpcsharp_server_register_completion_queue(this, cq);
+ }
}
public int AddInsecurePort(string addr)
@@ -74,16 +77,22 @@ namespace Grpc.Core.Internal
public void ShutdownAndNotify(BatchCompletionDelegate callback, CompletionQueueSafeHandle completionQueue)
{
- var ctx = BatchContextSafeHandle.Create();
- completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
- Native.grpcsharp_server_shutdown_and_notify_callback(this, completionQueue, ctx);
+ using (completionQueue.NewScope())
+ {
+ var ctx = BatchContextSafeHandle.Create();
+ completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ Native.grpcsharp_server_shutdown_and_notify_callback(this, completionQueue, ctx);
+ }
}
public void RequestCall(BatchCompletionDelegate callback, CompletionQueueSafeHandle completionQueue)
{
- var ctx = BatchContextSafeHandle.Create();
- completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
- Native.grpcsharp_server_request_call(this, completionQueue, ctx).CheckOk();
+ using (completionQueue.NewScope())
+ {
+ var ctx = BatchContextSafeHandle.Create();
+ completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
+ Native.grpcsharp_server_request_call(this, completionQueue, ctx).CheckOk();
+ }
}
protected override bool ReleaseHandle()
diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs
index f73f720094..915bec146c 100644
--- a/src/csharp/Grpc.Core/Metadata.cs
+++ b/src/csharp/Grpc.Core/Metadata.cs
@@ -63,6 +63,13 @@ namespace Grpc.Core
/// </summary>
public static readonly Metadata Empty = new Metadata().Freeze();
+ /// <summary>
+ /// To be used in initial metadata to request specific compression algorithm
+ /// for given call. Direct selection of compression algorithms is an internal
+ /// feature and is not part of public API.
+ /// </summary>
+ internal const string CompressionRequestAlgorithmMetadataKey = "grpc-internal-encoding-request";
+
readonly List<Entry> entries;
bool readOnly;
diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs
index e1609341d9..cb20967680 100644
--- a/src/csharp/Grpc.Core/VersionInfo.cs
+++ b/src/csharp/Grpc.Core/VersionInfo.cs
@@ -48,11 +48,11 @@ namespace Grpc.Core
/// <summary>
/// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
/// </summary>
- public const string CurrentAssemblyFileVersion = "0.15.0.0";
+ public const string CurrentAssemblyFileVersion = "0.16.0.0";
/// <summary>
/// Current version of gRPC C#
/// </summary>
- public const string CurrentVersion = "0.15.0-dev";
+ public const string CurrentVersion = "0.16.0-dev";
}
}
diff --git a/src/csharp/Grpc.Core/project.json b/src/csharp/Grpc.Core/project.json
index 7253107e04..201e548801 100644
--- a/src/csharp/Grpc.Core/project.json
+++ b/src/csharp/Grpc.Core/project.json
@@ -1,5 +1,5 @@
{
- "version": "0.15.0-dev",
+ "version": "0.16.0-dev",
"title": "gRPC C# Core",
"authors": [ "Google Inc." ],
"copyright": "Copyright 2015, Google Inc.",
@@ -12,17 +12,23 @@
"requireLicenseAcceptance": false,
"tags": [ "gRPC RPC Protocol HTTP/2" ],
"files": {
- "build/net45/": "Grpc.Core.targets",
- "build/native/bin/windows_x86/": "../nativelibs/windows_x86/grpc_csharp_ext.dll",
- "build/native/bin/windows_x64/": "../nativelibs/windows_x64/grpc_csharp_ext.dll",
- "build/native/bin/linux_x86/": "../nativelibs/linux_x86/libgrpc_csharp_ext.so",
- "build/native/bin/linux_x64/": "../nativelibs/linux_x64/libgrpc_csharp_ext.so",
- "build/native/bin/macosx_x86/": "../nativelibs/macosx_x86/libgrpc_csharp_ext.dylib",
- "build/native/bin/macosx_x64/": "../nativelibs/macosx_x64/libgrpc_csharp_ext.dylib"
+ "mappings": {
+ "build/net45/": "Grpc.Core.targets",
+ "build/native/bin/windows_x86/": "../nativelibs/windows_x86/grpc_csharp_ext.dll",
+ "build/native/bin/windows_x64/": "../nativelibs/windows_x64/grpc_csharp_ext.dll",
+ "build/native/bin/linux_x86/": "../nativelibs/linux_x86/libgrpc_csharp_ext.so",
+ "build/native/bin/linux_x64/": "../nativelibs/linux_x64/libgrpc_csharp_ext.so",
+ "build/native/bin/macosx_x86/": "../nativelibs/macosx_x86/libgrpc_csharp_ext.dylib",
+ "build/native/bin/macosx_x64/": "../nativelibs/macosx_x64/libgrpc_csharp_ext.dylib"
+ }
}
},
"buildOptions": {
- "embed": [ "../../../etc/roots.pem" ]
+ "embed": [ "../../../etc/roots.pem" ],
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true
},
"dependencies": {
"Ix-Async": "1.2.5"
diff --git a/src/csharp/Grpc.Examples.MathClient/project.json b/src/csharp/Grpc.Examples.MathClient/project.json
index b254f15af8..b865cd5011 100644
--- a/src/csharp/Grpc.Examples.MathClient/project.json
+++ b/src/csharp/Grpc.Examples.MathClient/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
@@ -17,6 +24,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
diff --git a/src/csharp/Grpc.Examples.MathServer/project.json b/src/csharp/Grpc.Examples.MathServer/project.json
index b254f15af8..b865cd5011 100644
--- a/src/csharp/Grpc.Examples.MathServer/project.json
+++ b/src/csharp/Grpc.Examples.MathServer/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
@@ -17,6 +24,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
index 324c209ca1..50dacc2eaa 100644
--- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
+++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
@@ -62,7 +62,7 @@ namespace Math.Tests
};
server.Start();
channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
- client = Math.NewClient(channel);
+ client = new Math.MathClient(channel);
}
[TestFixtureTearDown]
diff --git a/src/csharp/Grpc.Examples.Tests/project.json b/src/csharp/Grpc.Examples.Tests/project.json
index d2779e814f..cc518eb6ff 100644
--- a/src/csharp/Grpc.Examples.Tests/project.json
+++ b/src/csharp/Grpc.Examples.Tests/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
@@ -17,6 +24,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs
index 4bbefcbe01..25abc51419 100644
--- a/src/csharp/Grpc.Examples/MathGrpc.cs
+++ b/src/csharp/Grpc.Examples/MathGrpc.cs
@@ -128,17 +128,22 @@ namespace Math {
/// <summary>Client for Math</summary>
public class MathClient : ClientBase<MathClient>
{
+ /// <summary>Creates a new client for Math</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public MathClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for Math that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public MathClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected MathClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected MathClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -235,12 +240,6 @@ namespace Math {
}
}
- /// <summary>Creates a new client for Math</summary>
- public static MathClient NewClient(Channel channel)
- {
- return new MathClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(MathBase serviceImpl)
{
diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
index 070674bae9..25a58fb386 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
+++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
@@ -65,7 +65,7 @@ namespace Grpc.HealthCheck.Tests
server.Start();
channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
- client = Grpc.Health.V1.Health.NewClient(channel);
+ client = new Grpc.Health.V1.Health.HealthClient(channel);
}
[TestFixtureTearDown]
diff --git a/src/csharp/Grpc.HealthCheck.Tests/project.json b/src/csharp/Grpc.HealthCheck.Tests/project.json
index 74599bd4b9..fbf8d92f04 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/project.json
+++ b/src/csharp/Grpc.HealthCheck.Tests/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
@@ -17,6 +24,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"mappings": {
"nativelibs/windows_x64/grpc_csharp_ext.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
index d0ade7d02b..43eea0f22f 100644
--- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
+++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs
@@ -71,17 +71,22 @@ namespace Grpc.Health.V1 {
/// <summary>Client for Health</summary>
public class HealthClient : ClientBase<HealthClient>
{
+ /// <summary>Creates a new client for Health</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public HealthClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for Health that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public HealthClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected HealthClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected HealthClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -108,12 +113,6 @@ namespace Grpc.Health.V1 {
}
}
- /// <summary>Creates a new client for Health</summary>
- public static HealthClient NewClient(Channel channel)
- {
- return new HealthClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(HealthBase serviceImpl)
{
diff --git a/src/csharp/Grpc.HealthCheck/project.json b/src/csharp/Grpc.HealthCheck/project.json
index eb57608957..d9daef720f 100644
--- a/src/csharp/Grpc.HealthCheck/project.json
+++ b/src/csharp/Grpc.HealthCheck/project.json
@@ -1,5 +1,5 @@
{
- "version": "0.15.0-dev",
+ "version": "0.16.0-dev",
"title": "gRPC C# Healthchecking",
"authors": [ "Google Inc." ],
"copyright": "Copyright 2015, Google Inc.",
@@ -12,8 +12,17 @@
"requireLicenseAcceptance": false,
"tags": [ "gRPC health check" ]
},
+ "buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ }
+ },
"dependencies": {
- "Grpc.Core": "0.15.0-dev",
+ "Grpc.Core": "0.16.0-dev",
"Google.Protobuf": "3.0.0-beta3"
},
"frameworks": {
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/project.json b/src/csharp/Grpc.IntegrationTesting.Client/project.json
index e5ba04d717..4a2846feea 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.Client/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
@@ -18,6 +25,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json b/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json
index e5ba04d717..4a2846feea 100644
--- a/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
@@ -18,6 +25,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/project.json b/src/csharp/Grpc.IntegrationTesting.Server/project.json
index e5ba04d717..4a2846feea 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.Server/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
@@ -18,6 +25,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
diff --git a/src/csharp/Grpc.IntegrationTesting.StressClient/project.json b/src/csharp/Grpc.IntegrationTesting.StressClient/project.json
index e5ba04d717..4a2846feea 100644
--- a/src/csharp/Grpc.IntegrationTesting.StressClient/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.StressClient/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
@@ -18,6 +25,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
diff --git a/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs b/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
index 39b9ae08e6..b9c0fe6d0d 100644
--- a/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
@@ -211,7 +211,7 @@ namespace Grpc.IntegrationTesting
bool profilerReset = false;
- var client = BenchmarkService.NewClient(channel);
+ var client = new BenchmarkService.BenchmarkServiceClient(channel);
var request = CreateSimpleRequest();
var stopwatch = new Stopwatch();
@@ -237,7 +237,7 @@ namespace Grpc.IntegrationTesting
private async Task RunUnaryAsync(Channel channel, IInterarrivalTimer timer)
{
- var client = BenchmarkService.NewClient(channel);
+ var client = new BenchmarkService.BenchmarkServiceClient(channel);
var request = CreateSimpleRequest();
var stopwatch = new Stopwatch();
@@ -256,7 +256,7 @@ namespace Grpc.IntegrationTesting
private async Task RunStreamingPingPongAsync(Channel channel, IInterarrivalTimer timer)
{
- var client = BenchmarkService.NewClient(channel);
+ var client = new BenchmarkService.BenchmarkServiceClient(channel);
var request = CreateSimpleRequest();
var stopwatch = new Stopwatch();
diff --git a/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs b/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs
index 001533ce31..4216dc1d6b 100644
--- a/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/GeneratedServiceBaseTest.cs
@@ -61,7 +61,7 @@ namespace Grpc.IntegrationTesting
};
server.Start();
channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
- client = TestService.NewClient(channel);
+ client = new TestService.TestServiceClient(channel);
}
[TearDown]
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index d273867a6a..e27fe5b3d8 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -222,6 +222,12 @@ namespace Grpc.IntegrationTesting
case "unimplemented_method":
RunUnimplementedMethod(new UnimplementedService.UnimplementedServiceClient(channel));
break;
+ case "client_compressed_unary":
+ RunClientCompressedUnary(client);
+ break;
+ case "client_compressed_streaming":
+ await RunClientCompressedStreamingAsync(client);
+ break;
default:
throw new ArgumentException("Unknown test case " + options.TestCase);
}
@@ -240,13 +246,11 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("running large_unary");
var request = new SimpleRequest
{
- ResponseType = PayloadType.Compressable,
ResponseSize = 314159,
Payload = CreateZerosPayload(271828)
};
var response = client.UnaryCall(request);
- Assert.AreEqual(PayloadType.Compressable, response.Payload.Type);
Assert.AreEqual(314159, response.Payload.Body.Length);
Console.WriteLine("Passed!");
}
@@ -275,17 +279,12 @@ namespace Grpc.IntegrationTesting
var request = new StreamingOutputCallRequest
{
- ResponseType = PayloadType.Compressable,
ResponseParameters = { bodySizes.Select((size) => new ResponseParameters { Size = size }) }
};
using (var call = client.StreamingOutputCall(request))
{
var responseList = await call.ResponseStream.ToListAsync();
- foreach (var res in responseList)
- {
- Assert.AreEqual(PayloadType.Compressable, res.Payload.Type);
- }
CollectionAssert.AreEqual(bodySizes, responseList.Select((item) => item.Payload.Body.Length));
}
Console.WriteLine("Passed!");
@@ -299,46 +298,38 @@ namespace Grpc.IntegrationTesting
{
await call.RequestStream.WriteAsync(new StreamingOutputCallRequest
{
- ResponseType = PayloadType.Compressable,
ResponseParameters = { new ResponseParameters { Size = 31415 } },
Payload = CreateZerosPayload(27182)
});
Assert.IsTrue(await call.ResponseStream.MoveNext());
- Assert.AreEqual(PayloadType.Compressable, call.ResponseStream.Current.Payload.Type);
Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length);
await call.RequestStream.WriteAsync(new StreamingOutputCallRequest
{
- ResponseType = PayloadType.Compressable,
ResponseParameters = { new ResponseParameters { Size = 9 } },
Payload = CreateZerosPayload(8)
});
Assert.IsTrue(await call.ResponseStream.MoveNext());
- Assert.AreEqual(PayloadType.Compressable, call.ResponseStream.Current.Payload.Type);
Assert.AreEqual(9, call.ResponseStream.Current.Payload.Body.Length);
await call.RequestStream.WriteAsync(new StreamingOutputCallRequest
{
- ResponseType = PayloadType.Compressable,
ResponseParameters = { new ResponseParameters { Size = 2653 } },
Payload = CreateZerosPayload(1828)
});
Assert.IsTrue(await call.ResponseStream.MoveNext());
- Assert.AreEqual(PayloadType.Compressable, call.ResponseStream.Current.Payload.Type);
Assert.AreEqual(2653, call.ResponseStream.Current.Payload.Body.Length);
await call.RequestStream.WriteAsync(new StreamingOutputCallRequest
{
- ResponseType = PayloadType.Compressable,
ResponseParameters = { new ResponseParameters { Size = 58979 } },
Payload = CreateZerosPayload(45904)
});
Assert.IsTrue(await call.ResponseStream.MoveNext());
- Assert.AreEqual(PayloadType.Compressable, call.ResponseStream.Current.Payload.Type);
Assert.AreEqual(58979, call.ResponseStream.Current.Payload.Body.Length);
await call.RequestStream.CompleteAsync();
@@ -367,7 +358,6 @@ namespace Grpc.IntegrationTesting
var request = new SimpleRequest
{
- ResponseType = PayloadType.Compressable,
ResponseSize = 314159,
Payload = CreateZerosPayload(271828),
FillUsername = true,
@@ -377,7 +367,6 @@ namespace Grpc.IntegrationTesting
// not setting credentials here because they were set on channel already
var response = client.UnaryCall(request);
- Assert.AreEqual(PayloadType.Compressable, response.Payload.Type);
Assert.AreEqual(314159, response.Payload.Body.Length);
Assert.False(string.IsNullOrEmpty(response.OauthScope));
Assert.True(oauthScope.Contains(response.OauthScope));
@@ -391,7 +380,6 @@ namespace Grpc.IntegrationTesting
var request = new SimpleRequest
{
- ResponseType = PayloadType.Compressable,
ResponseSize = 314159,
Payload = CreateZerosPayload(271828),
FillUsername = true,
@@ -400,7 +388,6 @@ namespace Grpc.IntegrationTesting
// not setting credentials here because they were set on channel already
var response = client.UnaryCall(request);
- Assert.AreEqual(PayloadType.Compressable, response.Payload.Type);
Assert.AreEqual(314159, response.Payload.Body.Length);
Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username);
Console.WriteLine("Passed!");
@@ -480,13 +467,11 @@ namespace Grpc.IntegrationTesting
{
await call.RequestStream.WriteAsync(new StreamingOutputCallRequest
{
- ResponseType = PayloadType.Compressable,
ResponseParameters = { new ResponseParameters { Size = 31415 } },
Payload = CreateZerosPayload(27182)
});
Assert.IsTrue(await call.ResponseStream.MoveNext());
- Assert.AreEqual(PayloadType.Compressable, call.ResponseStream.Current.Payload.Type);
Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length);
cts.Cancel();
@@ -546,7 +531,6 @@ namespace Grpc.IntegrationTesting
// step 1: test unary call
var request = new SimpleRequest
{
- ResponseType = PayloadType.Compressable,
ResponseSize = 314159,
Payload = CreateZerosPayload(271828)
};
@@ -565,7 +549,6 @@ namespace Grpc.IntegrationTesting
// step 2: test full duplex call
var request = new StreamingOutputCallRequest
{
- ResponseType = PayloadType.Compressable,
ResponseParameters = { new ResponseParameters { Size = 31415 } },
Payload = CreateZerosPayload(27182)
};
@@ -638,11 +621,113 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!");
}
+ public static void RunClientCompressedUnary(TestService.TestServiceClient client)
+ {
+ Console.WriteLine("running client_compressed_unary");
+ var probeRequest = new SimpleRequest
+ {
+ ExpectCompressed = new BoolValue
+ {
+ Value = true // lie about compression
+ },
+ ResponseSize = 314159,
+ Payload = CreateZerosPayload(271828)
+ };
+ var e = Assert.Throws<RpcException>(() => client.UnaryCall(probeRequest, CreateClientCompressionMetadata(false)));
+ Assert.AreEqual(StatusCode.InvalidArgument, e.Status.StatusCode);
+
+ var compressedRequest = new SimpleRequest
+ {
+ ExpectCompressed = new BoolValue
+ {
+ Value = true
+ },
+ ResponseSize = 314159,
+ Payload = CreateZerosPayload(271828)
+ };
+ var response1 = client.UnaryCall(compressedRequest, CreateClientCompressionMetadata(true));
+ Assert.AreEqual(314159, response1.Payload.Body.Length);
+
+ var uncompressedRequest = new SimpleRequest
+ {
+ ExpectCompressed = new BoolValue
+ {
+ Value = false
+ },
+ ResponseSize = 314159,
+ Payload = CreateZerosPayload(271828)
+ };
+ var response2 = client.UnaryCall(uncompressedRequest, CreateClientCompressionMetadata(false));
+ Assert.AreEqual(314159, response2.Payload.Body.Length);
+
+ Console.WriteLine("Passed!");
+ }
+
+ public static async Task RunClientCompressedStreamingAsync(TestService.TestServiceClient client)
+ {
+ Console.WriteLine("running client_compressed_streaming");
+ try
+ {
+ var probeCall = client.StreamingInputCall(CreateClientCompressionMetadata(false));
+ await probeCall.RequestStream.WriteAsync(new StreamingInputCallRequest
+ {
+ ExpectCompressed = new BoolValue
+ {
+ Value = true
+ },
+ Payload = CreateZerosPayload(27182)
+ });
+
+ // cannot use Assert.ThrowsAsync because it uses Task.Wait and would deadlock.
+ await probeCall;
+ Assert.Fail();
+ }
+ catch (RpcException e)
+ {
+ Assert.AreEqual(StatusCode.InvalidArgument, e.Status.StatusCode);
+ }
+
+ var call = client.StreamingInputCall(CreateClientCompressionMetadata(true));
+ await call.RequestStream.WriteAsync(new StreamingInputCallRequest
+ {
+ ExpectCompressed = new BoolValue
+ {
+ Value = true
+ },
+ Payload = CreateZerosPayload(27182)
+ });
+
+ call.RequestStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
+ await call.RequestStream.WriteAsync(new StreamingInputCallRequest
+ {
+ ExpectCompressed = new BoolValue
+ {
+ Value = false
+ },
+ Payload = CreateZerosPayload(45904)
+ });
+ await call.RequestStream.CompleteAsync();
+
+ var response = await call.ResponseAsync;
+ Assert.AreEqual(73086, response.AggregatedPayloadSize);
+
+ Console.WriteLine("Passed!");
+ }
+
private static Payload CreateZerosPayload(int size)
{
return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
}
+ private static Metadata CreateClientCompressionMetadata(bool compressed)
+ {
+ var algorithmName = compressed ? "gzip" : "identity";
+ return new Metadata
+ {
+ { new Metadata.Entry(Metadata.CompressionRequestAlgorithmMetadataKey, algorithmName) }
+ };
+ }
+
// extracts the client_email field from service account file used for auth test cases
private static string GetEmailFromServiceAccountFile()
{
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
index 4ee1ff5ec8..f907f630da 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
@@ -69,7 +69,7 @@ namespace Grpc.IntegrationTesting
};
int port = server.Ports.Single().BoundPort;
channel = new Channel(Host, port, TestCredentials.CreateSslCredentials(), options);
- client = TestService.NewClient(channel);
+ client = new TestService.TestServiceClient(channel);
}
[TestFixtureTearDown]
@@ -148,7 +148,7 @@ namespace Grpc.IntegrationTesting
[Test]
public void UnimplementedMethod()
{
- InteropClient.RunUnimplementedMethod(UnimplementedService.NewClient(channel));
+ InteropClient.RunUnimplementedMethod(new UnimplementedService.UnimplementedServiceClient(channel));
}
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/Messages.cs b/src/csharp/Grpc.IntegrationTesting/Messages.cs
index d42501aa5b..1240db128b 100644
--- a/src/csharp/Grpc.IntegrationTesting/Messages.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Messages.cs
@@ -24,46 +24,48 @@ namespace Grpc.Testing {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CiVzcmMvcHJvdG8vZ3JwYy90ZXN0aW5nL21lc3NhZ2VzLnByb3RvEgxncnBj",
- "LnRlc3RpbmciQAoHUGF5bG9hZBInCgR0eXBlGAEgASgOMhkuZ3JwYy50ZXN0",
- "aW5nLlBheWxvYWRUeXBlEgwKBGJvZHkYAiABKAwiKwoKRWNob1N0YXR1cxIM",
- "CgRjb2RlGAEgASgFEg8KB21lc3NhZ2UYAiABKAkioQIKDVNpbXBsZVJlcXVl",
- "c3QSMAoNcmVzcG9uc2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXls",
- "b2FkVHlwZRIVCg1yZXNwb25zZV9zaXplGAIgASgFEiYKB3BheWxvYWQYAyAB",
- "KAsyFS5ncnBjLnRlc3RpbmcuUGF5bG9hZBIVCg1maWxsX3VzZXJuYW1lGAQg",
- "ASgIEhgKEGZpbGxfb2F1dGhfc2NvcGUYBSABKAgSOwoUcmVzcG9uc2VfY29t",
- "cHJlc3Npb24YBiABKA4yHS5ncnBjLnRlc3RpbmcuQ29tcHJlc3Npb25UeXBl",
- "EjEKD3Jlc3BvbnNlX3N0YXR1cxgHIAEoCzIYLmdycGMudGVzdGluZy5FY2hv",
- "U3RhdHVzIl8KDlNpbXBsZVJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5n",
- "cnBjLnRlc3RpbmcuUGF5bG9hZBIQCgh1c2VybmFtZRgCIAEoCRITCgtvYXV0",
- "aF9zY29wZRgDIAEoCSJDChlTdHJlYW1pbmdJbnB1dENhbGxSZXF1ZXN0EiYK",
- "B3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3RpbmcuUGF5bG9hZCI9ChpTdHJl",
- "YW1pbmdJbnB1dENhbGxSZXNwb25zZRIfChdhZ2dyZWdhdGVkX3BheWxvYWRf",
- "c2l6ZRgBIAEoBSI3ChJSZXNwb25zZVBhcmFtZXRlcnMSDAoEc2l6ZRgBIAEo",
- "BRITCgtpbnRlcnZhbF91cxgCIAEoBSKlAgoaU3RyZWFtaW5nT3V0cHV0Q2Fs",
- "bFJlcXVlc3QSMAoNcmVzcG9uc2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGlu",
- "Zy5QYXlsb2FkVHlwZRI9ChNyZXNwb25zZV9wYXJhbWV0ZXJzGAIgAygLMiAu",
- "Z3JwYy50ZXN0aW5nLlJlc3BvbnNlUGFyYW1ldGVycxImCgdwYXlsb2FkGAMg",
- "ASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxvYWQSOwoUcmVzcG9uc2VfY29tcHJl",
- "c3Npb24YBiABKA4yHS5ncnBjLnRlc3RpbmcuQ29tcHJlc3Npb25UeXBlEjEK",
- "D3Jlc3BvbnNlX3N0YXR1cxgHIAEoCzIYLmdycGMudGVzdGluZy5FY2hvU3Rh",
- "dHVzIkUKG1N0cmVhbWluZ091dHB1dENhbGxSZXNwb25zZRImCgdwYXlsb2Fk",
- "GAEgASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxvYWQiMwoPUmVjb25uZWN0UGFy",
- "YW1zEiAKGG1heF9yZWNvbm5lY3RfYmFja29mZl9tcxgBIAEoBSIzCg1SZWNv",
- "bm5lY3RJbmZvEg4KBnBhc3NlZBgBIAEoCBISCgpiYWNrb2ZmX21zGAIgAygF",
- "Kj8KC1BheWxvYWRUeXBlEhAKDENPTVBSRVNTQUJMRRAAEhIKDlVOQ09NUFJF",
- "U1NBQkxFEAESCgoGUkFORE9NEAIqMgoPQ29tcHJlc3Npb25UeXBlEggKBE5P",
- "TkUQABIICgRHWklQEAESCwoHREVGTEFURRACYgZwcm90bzM="));
+ "LnRlc3RpbmciGgoJQm9vbFZhbHVlEg0KBXZhbHVlGAEgASgIIkAKB1BheWxv",
+ "YWQSJwoEdHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlwZRIM",
+ "CgRib2R5GAIgASgMIisKCkVjaG9TdGF0dXMSDAoEY29kZRgBIAEoBRIPCgdt",
+ "ZXNzYWdlGAIgASgJIs4CCg1TaW1wbGVSZXF1ZXN0EjAKDXJlc3BvbnNlX3R5",
+ "cGUYASABKA4yGS5ncnBjLnRlc3RpbmcuUGF5bG9hZFR5cGUSFQoNcmVzcG9u",
+ "c2Vfc2l6ZRgCIAEoBRImCgdwYXlsb2FkGAMgASgLMhUuZ3JwYy50ZXN0aW5n",
+ "LlBheWxvYWQSFQoNZmlsbF91c2VybmFtZRgEIAEoCBIYChBmaWxsX29hdXRo",
+ "X3Njb3BlGAUgASgIEjQKE3Jlc3BvbnNlX2NvbXByZXNzZWQYBiABKAsyFy5n",
+ "cnBjLnRlc3RpbmcuQm9vbFZhbHVlEjEKD3Jlc3BvbnNlX3N0YXR1cxgHIAEo",
+ "CzIYLmdycGMudGVzdGluZy5FY2hvU3RhdHVzEjIKEWV4cGVjdF9jb21wcmVz",
+ "c2VkGAggASgLMhcuZ3JwYy50ZXN0aW5nLkJvb2xWYWx1ZSJfCg5TaW1wbGVS",
+ "ZXNwb25zZRImCgdwYXlsb2FkGAEgASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxv",
+ "YWQSEAoIdXNlcm5hbWUYAiABKAkSEwoLb2F1dGhfc2NvcGUYAyABKAkidwoZ",
+ "U3RyZWFtaW5nSW5wdXRDYWxsUmVxdWVzdBImCgdwYXlsb2FkGAEgASgLMhUu",
+ "Z3JwYy50ZXN0aW5nLlBheWxvYWQSMgoRZXhwZWN0X2NvbXByZXNzZWQYAiAB",
+ "KAsyFy5ncnBjLnRlc3RpbmcuQm9vbFZhbHVlIj0KGlN0cmVhbWluZ0lucHV0",
+ "Q2FsbFJlc3BvbnNlEh8KF2FnZ3JlZ2F0ZWRfcGF5bG9hZF9zaXplGAEgASgF",
+ "ImQKElJlc3BvbnNlUGFyYW1ldGVycxIMCgRzaXplGAEgASgFEhMKC2ludGVy",
+ "dmFsX3VzGAIgASgFEisKCmNvbXByZXNzZWQYAyABKAsyFy5ncnBjLnRlc3Rp",
+ "bmcuQm9vbFZhbHVlIugBChpTdHJlYW1pbmdPdXRwdXRDYWxsUmVxdWVzdBIw",
+ "Cg1yZXNwb25zZV90eXBlGAEgASgOMhkuZ3JwYy50ZXN0aW5nLlBheWxvYWRU",
+ "eXBlEj0KE3Jlc3BvbnNlX3BhcmFtZXRlcnMYAiADKAsyIC5ncnBjLnRlc3Rp",
+ "bmcuUmVzcG9uc2VQYXJhbWV0ZXJzEiYKB3BheWxvYWQYAyABKAsyFS5ncnBj",
+ "LnRlc3RpbmcuUGF5bG9hZBIxCg9yZXNwb25zZV9zdGF0dXMYByABKAsyGC5n",
+ "cnBjLnRlc3RpbmcuRWNob1N0YXR1cyJFChtTdHJlYW1pbmdPdXRwdXRDYWxs",
+ "UmVzcG9uc2USJgoHcGF5bG9hZBgBIAEoCzIVLmdycGMudGVzdGluZy5QYXls",
+ "b2FkIjMKD1JlY29ubmVjdFBhcmFtcxIgChhtYXhfcmVjb25uZWN0X2JhY2tv",
+ "ZmZfbXMYASABKAUiMwoNUmVjb25uZWN0SW5mbxIOCgZwYXNzZWQYASABKAgS",
+ "EgoKYmFja29mZl9tcxgCIAMoBSofCgtQYXlsb2FkVHlwZRIQCgxDT01QUkVT",
+ "U0FCTEUQAGIGcHJvdG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), typeof(global::Grpc.Testing.CompressionType), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.BoolValue), global::Grpc.Testing.BoolValue.Parser, new[]{ "Value" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.Payload), global::Grpc.Testing.Payload.Parser, new[]{ "Type", "Body" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.EchoStatus), global::Grpc.Testing.EchoStatus.Parser, new[]{ "Code", "Message" }, null, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.SimpleRequest), global::Grpc.Testing.SimpleRequest.Parser, new[]{ "ResponseType", "ResponseSize", "Payload", "FillUsername", "FillOauthScope", "ResponseCompression", "ResponseStatus" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.SimpleRequest), global::Grpc.Testing.SimpleRequest.Parser, new[]{ "ResponseType", "ResponseSize", "Payload", "FillUsername", "FillOauthScope", "ResponseCompressed", "ResponseStatus", "ExpectCompressed" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.SimpleResponse), global::Grpc.Testing.SimpleResponse.Parser, new[]{ "Payload", "Username", "OauthScope" }, null, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.StreamingInputCallRequest), global::Grpc.Testing.StreamingInputCallRequest.Parser, new[]{ "Payload" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.StreamingInputCallRequest), global::Grpc.Testing.StreamingInputCallRequest.Parser, new[]{ "Payload", "ExpectCompressed" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.StreamingInputCallResponse), global::Grpc.Testing.StreamingInputCallResponse.Parser, new[]{ "AggregatedPayloadSize" }, null, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ResponseParameters), global::Grpc.Testing.ResponseParameters.Parser, new[]{ "Size", "IntervalUs" }, null, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), global::Grpc.Testing.StreamingOutputCallRequest.Parser, new[]{ "ResponseType", "ResponseParameters", "Payload", "ResponseCompression", "ResponseStatus" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ResponseParameters), global::Grpc.Testing.ResponseParameters.Parser, new[]{ "Size", "IntervalUs", "Compressed" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), global::Grpc.Testing.StreamingOutputCallRequest.Parser, new[]{ "ResponseType", "ResponseParameters", "Payload", "ResponseStatus" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.StreamingOutputCallResponse), global::Grpc.Testing.StreamingOutputCallResponse.Parser, new[]{ "Payload" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ReconnectParams), global::Grpc.Testing.ReconnectParams.Parser, new[]{ "MaxReconnectBackoffMs" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ReconnectInfo), global::Grpc.Testing.ReconnectInfo.Parser, new[]{ "Passed", "BackoffMs" }, null, null, null)
@@ -74,6 +76,7 @@ namespace Grpc.Testing {
}
#region Enums
/// <summary>
+ /// DEPRECATED, don't use. To be removed shortly.
/// The type of payload that should be returned.
/// </summary>
public enum PayloadType {
@@ -81,31 +84,122 @@ namespace Grpc.Testing {
/// Compressable text format.
/// </summary>
[pbr::OriginalName("COMPRESSABLE")] Compressable = 0,
- /// <summary>
- /// Uncompressable binary format.
- /// </summary>
- [pbr::OriginalName("UNCOMPRESSABLE")] Uncompressable = 1,
- /// <summary>
- /// Randomly chosen from all other formats defined in this enum.
- /// </summary>
- [pbr::OriginalName("RANDOM")] Random = 2,
}
+ #endregion
+
+ #region Messages
/// <summary>
- /// Compression algorithms
+ /// TODO(dgq): Go back to using well-known types once
+ /// https://github.com/grpc/grpc/issues/6980 has been fixed.
+ /// import "google/protobuf/wrappers.proto";
/// </summary>
- public enum CompressionType {
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class BoolValue : pb::IMessage<BoolValue> {
+ private static readonly pb::MessageParser<BoolValue> _parser = new pb::MessageParser<BoolValue>(() => new BoolValue());
+ public static pb::MessageParser<BoolValue> Parser { get { return _parser; } }
+
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[0]; }
+ }
+
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ public BoolValue() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ public BoolValue(BoolValue other) : this() {
+ value_ = other.value_;
+ }
+
+ public BoolValue Clone() {
+ return new BoolValue(this);
+ }
+
+ /// <summary>Field number for the "value" field.</summary>
+ public const int ValueFieldNumber = 1;
+ private bool value_;
/// <summary>
- /// No compression
+ /// The bool value.
/// </summary>
- [pbr::OriginalName("NONE")] None = 0,
- [pbr::OriginalName("GZIP")] Gzip = 1,
- [pbr::OriginalName("DEFLATE")] Deflate = 2,
- }
+ public bool Value {
+ get { return value_; }
+ set {
+ value_ = value;
+ }
+ }
- #endregion
+ public override bool Equals(object other) {
+ return Equals(other as BoolValue);
+ }
+
+ public bool Equals(BoolValue other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (Value != other.Value) return false;
+ return true;
+ }
+
+ public override int GetHashCode() {
+ int hash = 1;
+ if (Value != false) hash ^= Value.GetHashCode();
+ return hash;
+ }
+
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ public void WriteTo(pb::CodedOutputStream output) {
+ if (Value != false) {
+ output.WriteRawTag(8);
+ output.WriteBool(Value);
+ }
+ }
+
+ public int CalculateSize() {
+ int size = 0;
+ if (Value != false) {
+ size += 1 + 1;
+ }
+ return size;
+ }
+
+ public void MergeFrom(BoolValue other) {
+ if (other == null) {
+ return;
+ }
+ if (other.Value != false) {
+ Value = other.Value;
+ }
+ }
+
+ public void MergeFrom(pb::CodedInputStream input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ input.SkipLastField();
+ break;
+ case 8: {
+ Value = input.ReadBool();
+ break;
+ }
+ }
+ }
+ }
+
+ }
- #region Messages
/// <summary>
/// A block of data, to simply increase gRPC message size.
/// </summary>
@@ -115,7 +209,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<Payload> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[0]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[1]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -141,6 +235,7 @@ namespace Grpc.Testing {
public const int TypeFieldNumber = 1;
private global::Grpc.Testing.PayloadType type_ = 0;
/// <summary>
+ /// DEPRECATED, don't use. To be removed shortly.
/// The type of data in body.
/// </summary>
public global::Grpc.Testing.PayloadType Type {
@@ -255,7 +350,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<EchoStatus> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[1]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[2]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -388,7 +483,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<SimpleRequest> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[2]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[3]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -407,8 +502,9 @@ namespace Grpc.Testing {
Payload = other.payload_ != null ? other.Payload.Clone() : null;
fillUsername_ = other.fillUsername_;
fillOauthScope_ = other.fillOauthScope_;
- responseCompression_ = other.responseCompression_;
+ ResponseCompressed = other.responseCompressed_ != null ? other.ResponseCompressed.Clone() : null;
ResponseStatus = other.responseStatus_ != null ? other.ResponseStatus.Clone() : null;
+ ExpectCompressed = other.expectCompressed_ != null ? other.ExpectCompressed.Clone() : null;
}
public SimpleRequest Clone() {
@@ -419,6 +515,7 @@ namespace Grpc.Testing {
public const int ResponseTypeFieldNumber = 1;
private global::Grpc.Testing.PayloadType responseType_ = 0;
/// <summary>
+ /// DEPRECATED, don't use. To be removed shortly.
/// Desired payload type in the response from the server.
/// If response_type is RANDOM, server randomly chooses one from other formats.
/// </summary>
@@ -434,7 +531,6 @@ namespace Grpc.Testing {
private int responseSize_;
/// <summary>
/// Desired payload size in the response from the server.
- /// If response_type is COMPRESSABLE, this denotes the size before compression.
/// </summary>
public int ResponseSize {
get { return responseSize_; }
@@ -482,16 +578,19 @@ namespace Grpc.Testing {
}
}
- /// <summary>Field number for the "response_compression" field.</summary>
- public const int ResponseCompressionFieldNumber = 6;
- private global::Grpc.Testing.CompressionType responseCompression_ = 0;
+ /// <summary>Field number for the "response_compressed" field.</summary>
+ public const int ResponseCompressedFieldNumber = 6;
+ private global::Grpc.Testing.BoolValue responseCompressed_;
/// <summary>
- /// Compression algorithm to be used by the server for the response (stream)
+ /// Whether to request the server to compress the response. This field is
+ /// "nullable" in order to interoperate seamlessly with clients not able to
+ /// implement the full compression tests by introspecting the call to verify
+ /// the response's compression status.
/// </summary>
- public global::Grpc.Testing.CompressionType ResponseCompression {
- get { return responseCompression_; }
+ public global::Grpc.Testing.BoolValue ResponseCompressed {
+ get { return responseCompressed_; }
set {
- responseCompression_ = value;
+ responseCompressed_ = value;
}
}
@@ -508,6 +607,19 @@ namespace Grpc.Testing {
}
}
+ /// <summary>Field number for the "expect_compressed" field.</summary>
+ public const int ExpectCompressedFieldNumber = 8;
+ private global::Grpc.Testing.BoolValue expectCompressed_;
+ /// <summary>
+ /// Whether the server should expect this request to be compressed.
+ /// </summary>
+ public global::Grpc.Testing.BoolValue ExpectCompressed {
+ get { return expectCompressed_; }
+ set {
+ expectCompressed_ = value;
+ }
+ }
+
public override bool Equals(object other) {
return Equals(other as SimpleRequest);
}
@@ -524,8 +636,9 @@ namespace Grpc.Testing {
if (!object.Equals(Payload, other.Payload)) return false;
if (FillUsername != other.FillUsername) return false;
if (FillOauthScope != other.FillOauthScope) return false;
- if (ResponseCompression != other.ResponseCompression) return false;
+ if (!object.Equals(ResponseCompressed, other.ResponseCompressed)) return false;
if (!object.Equals(ResponseStatus, other.ResponseStatus)) return false;
+ if (!object.Equals(ExpectCompressed, other.ExpectCompressed)) return false;
return true;
}
@@ -536,8 +649,9 @@ namespace Grpc.Testing {
if (payload_ != null) hash ^= Payload.GetHashCode();
if (FillUsername != false) hash ^= FillUsername.GetHashCode();
if (FillOauthScope != false) hash ^= FillOauthScope.GetHashCode();
- if (ResponseCompression != 0) hash ^= ResponseCompression.GetHashCode();
+ if (responseCompressed_ != null) hash ^= ResponseCompressed.GetHashCode();
if (responseStatus_ != null) hash ^= ResponseStatus.GetHashCode();
+ if (expectCompressed_ != null) hash ^= ExpectCompressed.GetHashCode();
return hash;
}
@@ -566,14 +680,18 @@ namespace Grpc.Testing {
output.WriteRawTag(40);
output.WriteBool(FillOauthScope);
}
- if (ResponseCompression != 0) {
- output.WriteRawTag(48);
- output.WriteEnum((int) ResponseCompression);
+ if (responseCompressed_ != null) {
+ output.WriteRawTag(50);
+ output.WriteMessage(ResponseCompressed);
}
if (responseStatus_ != null) {
output.WriteRawTag(58);
output.WriteMessage(ResponseStatus);
}
+ if (expectCompressed_ != null) {
+ output.WriteRawTag(66);
+ output.WriteMessage(ExpectCompressed);
+ }
}
public int CalculateSize() {
@@ -593,12 +711,15 @@ namespace Grpc.Testing {
if (FillOauthScope != false) {
size += 1 + 1;
}
- if (ResponseCompression != 0) {
- size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseCompression);
+ if (responseCompressed_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(ResponseCompressed);
}
if (responseStatus_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(ResponseStatus);
}
+ if (expectCompressed_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(ExpectCompressed);
+ }
return size;
}
@@ -624,8 +745,11 @@ namespace Grpc.Testing {
if (other.FillOauthScope != false) {
FillOauthScope = other.FillOauthScope;
}
- if (other.ResponseCompression != 0) {
- ResponseCompression = other.ResponseCompression;
+ if (other.responseCompressed_ != null) {
+ if (responseCompressed_ == null) {
+ responseCompressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ ResponseCompressed.MergeFrom(other.ResponseCompressed);
}
if (other.responseStatus_ != null) {
if (responseStatus_ == null) {
@@ -633,6 +757,12 @@ namespace Grpc.Testing {
}
ResponseStatus.MergeFrom(other.ResponseStatus);
}
+ if (other.expectCompressed_ != null) {
+ if (expectCompressed_ == null) {
+ expectCompressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ ExpectCompressed.MergeFrom(other.ExpectCompressed);
+ }
}
public void MergeFrom(pb::CodedInputStream input) {
@@ -665,8 +795,11 @@ namespace Grpc.Testing {
FillOauthScope = input.ReadBool();
break;
}
- case 48: {
- responseCompression_ = (global::Grpc.Testing.CompressionType) input.ReadEnum();
+ case 50: {
+ if (responseCompressed_ == null) {
+ responseCompressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ input.ReadMessage(responseCompressed_);
break;
}
case 58: {
@@ -676,6 +809,13 @@ namespace Grpc.Testing {
input.ReadMessage(responseStatus_);
break;
}
+ case 66: {
+ if (expectCompressed_ == null) {
+ expectCompressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ input.ReadMessage(expectCompressed_);
+ break;
+ }
}
}
}
@@ -691,7 +831,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<SimpleResponse> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[3]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[4]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -867,7 +1007,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingInputCallRequest> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[4]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[5]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -882,6 +1022,7 @@ namespace Grpc.Testing {
public StreamingInputCallRequest(StreamingInputCallRequest other) : this() {
Payload = other.payload_ != null ? other.Payload.Clone() : null;
+ ExpectCompressed = other.expectCompressed_ != null ? other.ExpectCompressed.Clone() : null;
}
public StreamingInputCallRequest Clone() {
@@ -901,6 +1042,22 @@ namespace Grpc.Testing {
}
}
+ /// <summary>Field number for the "expect_compressed" field.</summary>
+ public const int ExpectCompressedFieldNumber = 2;
+ private global::Grpc.Testing.BoolValue expectCompressed_;
+ /// <summary>
+ /// Whether the server should expect this request to be compressed. This field
+ /// is "nullable" in order to interoperate seamlessly with servers not able to
+ /// implement the full compression tests by introspecting the call to verify
+ /// the request's compression status.
+ /// </summary>
+ public global::Grpc.Testing.BoolValue ExpectCompressed {
+ get { return expectCompressed_; }
+ set {
+ expectCompressed_ = value;
+ }
+ }
+
public override bool Equals(object other) {
return Equals(other as StreamingInputCallRequest);
}
@@ -913,12 +1070,14 @@ namespace Grpc.Testing {
return true;
}
if (!object.Equals(Payload, other.Payload)) return false;
+ if (!object.Equals(ExpectCompressed, other.ExpectCompressed)) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (payload_ != null) hash ^= Payload.GetHashCode();
+ if (expectCompressed_ != null) hash ^= ExpectCompressed.GetHashCode();
return hash;
}
@@ -931,6 +1090,10 @@ namespace Grpc.Testing {
output.WriteRawTag(10);
output.WriteMessage(Payload);
}
+ if (expectCompressed_ != null) {
+ output.WriteRawTag(18);
+ output.WriteMessage(ExpectCompressed);
+ }
}
public int CalculateSize() {
@@ -938,6 +1101,9 @@ namespace Grpc.Testing {
if (payload_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload);
}
+ if (expectCompressed_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(ExpectCompressed);
+ }
return size;
}
@@ -951,6 +1117,12 @@ namespace Grpc.Testing {
}
Payload.MergeFrom(other.Payload);
}
+ if (other.expectCompressed_ != null) {
+ if (expectCompressed_ == null) {
+ expectCompressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ ExpectCompressed.MergeFrom(other.ExpectCompressed);
+ }
}
public void MergeFrom(pb::CodedInputStream input) {
@@ -967,6 +1139,13 @@ namespace Grpc.Testing {
input.ReadMessage(payload_);
break;
}
+ case 18: {
+ if (expectCompressed_ == null) {
+ expectCompressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ input.ReadMessage(expectCompressed_);
+ break;
+ }
}
}
}
@@ -982,7 +1161,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingInputCallResponse> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[5]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[6]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -1091,7 +1270,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<ResponseParameters> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[6]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[7]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -1107,6 +1286,7 @@ namespace Grpc.Testing {
public ResponseParameters(ResponseParameters other) : this() {
size_ = other.size_;
intervalUs_ = other.intervalUs_;
+ Compressed = other.compressed_ != null ? other.Compressed.Clone() : null;
}
public ResponseParameters Clone() {
@@ -1118,7 +1298,6 @@ namespace Grpc.Testing {
private int size_;
/// <summary>
/// Desired payload sizes in responses from the server.
- /// If response_type is COMPRESSABLE, this denotes the size before compression.
/// </summary>
public int Size {
get { return size_; }
@@ -1141,6 +1320,22 @@ namespace Grpc.Testing {
}
}
+ /// <summary>Field number for the "compressed" field.</summary>
+ public const int CompressedFieldNumber = 3;
+ private global::Grpc.Testing.BoolValue compressed_;
+ /// <summary>
+ /// Whether to request the server to compress the response. This field is
+ /// "nullable" in order to interoperate seamlessly with clients not able to
+ /// implement the full compression tests by introspecting the call to verify
+ /// the response's compression status.
+ /// </summary>
+ public global::Grpc.Testing.BoolValue Compressed {
+ get { return compressed_; }
+ set {
+ compressed_ = value;
+ }
+ }
+
public override bool Equals(object other) {
return Equals(other as ResponseParameters);
}
@@ -1154,6 +1349,7 @@ namespace Grpc.Testing {
}
if (Size != other.Size) return false;
if (IntervalUs != other.IntervalUs) return false;
+ if (!object.Equals(Compressed, other.Compressed)) return false;
return true;
}
@@ -1161,6 +1357,7 @@ namespace Grpc.Testing {
int hash = 1;
if (Size != 0) hash ^= Size.GetHashCode();
if (IntervalUs != 0) hash ^= IntervalUs.GetHashCode();
+ if (compressed_ != null) hash ^= Compressed.GetHashCode();
return hash;
}
@@ -1177,6 +1374,10 @@ namespace Grpc.Testing {
output.WriteRawTag(16);
output.WriteInt32(IntervalUs);
}
+ if (compressed_ != null) {
+ output.WriteRawTag(26);
+ output.WriteMessage(Compressed);
+ }
}
public int CalculateSize() {
@@ -1187,6 +1388,9 @@ namespace Grpc.Testing {
if (IntervalUs != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(IntervalUs);
}
+ if (compressed_ != null) {
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(Compressed);
+ }
return size;
}
@@ -1200,6 +1404,12 @@ namespace Grpc.Testing {
if (other.IntervalUs != 0) {
IntervalUs = other.IntervalUs;
}
+ if (other.compressed_ != null) {
+ if (compressed_ == null) {
+ compressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ Compressed.MergeFrom(other.Compressed);
+ }
}
public void MergeFrom(pb::CodedInputStream input) {
@@ -1217,6 +1427,13 @@ namespace Grpc.Testing {
IntervalUs = input.ReadInt32();
break;
}
+ case 26: {
+ if (compressed_ == null) {
+ compressed_ = new global::Grpc.Testing.BoolValue();
+ }
+ input.ReadMessage(compressed_);
+ break;
+ }
}
}
}
@@ -1232,7 +1449,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingOutputCallRequest> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[7]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[8]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -1249,7 +1466,6 @@ namespace Grpc.Testing {
responseType_ = other.responseType_;
responseParameters_ = other.responseParameters_.Clone();
Payload = other.payload_ != null ? other.Payload.Clone() : null;
- responseCompression_ = other.responseCompression_;
ResponseStatus = other.responseStatus_ != null ? other.ResponseStatus.Clone() : null;
}
@@ -1261,6 +1477,7 @@ namespace Grpc.Testing {
public const int ResponseTypeFieldNumber = 1;
private global::Grpc.Testing.PayloadType responseType_ = 0;
/// <summary>
+ /// DEPRECATED, don't use. To be removed shortly.
/// Desired payload type in the response from the server.
/// If response_type is RANDOM, the payload from each response in the stream
/// might be of different types. This is to simulate a mixed type of payload
@@ -1298,19 +1515,6 @@ namespace Grpc.Testing {
}
}
- /// <summary>Field number for the "response_compression" field.</summary>
- public const int ResponseCompressionFieldNumber = 6;
- private global::Grpc.Testing.CompressionType responseCompression_ = 0;
- /// <summary>
- /// Compression algorithm to be used by the server for the response (stream)
- /// </summary>
- public global::Grpc.Testing.CompressionType ResponseCompression {
- get { return responseCompression_; }
- set {
- responseCompression_ = value;
- }
- }
-
/// <summary>Field number for the "response_status" field.</summary>
public const int ResponseStatusFieldNumber = 7;
private global::Grpc.Testing.EchoStatus responseStatus_;
@@ -1338,7 +1542,6 @@ namespace Grpc.Testing {
if (ResponseType != other.ResponseType) return false;
if(!responseParameters_.Equals(other.responseParameters_)) return false;
if (!object.Equals(Payload, other.Payload)) return false;
- if (ResponseCompression != other.ResponseCompression) return false;
if (!object.Equals(ResponseStatus, other.ResponseStatus)) return false;
return true;
}
@@ -1348,7 +1551,6 @@ namespace Grpc.Testing {
if (ResponseType != 0) hash ^= ResponseType.GetHashCode();
hash ^= responseParameters_.GetHashCode();
if (payload_ != null) hash ^= Payload.GetHashCode();
- if (ResponseCompression != 0) hash ^= ResponseCompression.GetHashCode();
if (responseStatus_ != null) hash ^= ResponseStatus.GetHashCode();
return hash;
}
@@ -1367,10 +1569,6 @@ namespace Grpc.Testing {
output.WriteRawTag(26);
output.WriteMessage(Payload);
}
- if (ResponseCompression != 0) {
- output.WriteRawTag(48);
- output.WriteEnum((int) ResponseCompression);
- }
if (responseStatus_ != null) {
output.WriteRawTag(58);
output.WriteMessage(ResponseStatus);
@@ -1386,9 +1584,6 @@ namespace Grpc.Testing {
if (payload_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload);
}
- if (ResponseCompression != 0) {
- size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseCompression);
- }
if (responseStatus_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(ResponseStatus);
}
@@ -1409,9 +1604,6 @@ namespace Grpc.Testing {
}
Payload.MergeFrom(other.Payload);
}
- if (other.ResponseCompression != 0) {
- ResponseCompression = other.ResponseCompression;
- }
if (other.responseStatus_ != null) {
if (responseStatus_ == null) {
responseStatus_ = new global::Grpc.Testing.EchoStatus();
@@ -1442,10 +1634,6 @@ namespace Grpc.Testing {
input.ReadMessage(payload_);
break;
}
- case 48: {
- responseCompression_ = (global::Grpc.Testing.CompressionType) input.ReadEnum();
- break;
- }
case 58: {
if (responseStatus_ == null) {
responseStatus_ = new global::Grpc.Testing.EchoStatus();
@@ -1468,7 +1656,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingOutputCallResponse> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[8]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[9]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -1584,7 +1772,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<ReconnectParams> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[9]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[10]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@@ -1692,7 +1880,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<ReconnectInfo> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
- get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[10]; }
+ get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[11]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
index 9fd575f190..eb3bb8a28b 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
@@ -88,7 +88,7 @@ namespace Grpc.IntegrationTesting
var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
CallCredentials.FromInterceptor(asyncAuthInterceptor));
channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
- client = TestService.NewClient(channel);
+ client = new TestService.TestServiceClient(channel);
client.UnaryCall(new SimpleRequest { });
}
@@ -97,7 +97,7 @@ namespace Grpc.IntegrationTesting
public void MetadataCredentials_PerCall()
{
channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options);
- client = TestService.NewClient(channel);
+ client = new TestService.TestServiceClient(channel);
var callCredentials = CallCredentials.FromInterceptor(asyncAuthInterceptor);
client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: callCredentials));
diff --git a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
index 22bd27ec0a..040798e3c2 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs
@@ -97,17 +97,22 @@ namespace Grpc.Testing {
/// <summary>Client for MetricsService</summary>
public class MetricsServiceClient : ClientBase<MetricsServiceClient>
{
+ /// <summary>Creates a new client for MetricsService</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public MetricsServiceClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for MetricsService that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public MetricsServiceClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected MetricsServiceClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected MetricsServiceClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -162,12 +167,6 @@ namespace Grpc.Testing {
}
}
- /// <summary>Creates a new client for MetricsService</summary>
- public static MetricsServiceClient NewClient(Channel channel)
- {
- return new MetricsServiceClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(MetricsServiceBase serviceImpl)
{
diff --git a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
index 9c99296115..e205dea93e 100644
--- a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs
@@ -93,17 +93,22 @@ namespace Grpc.Testing {
/// <summary>Client for BenchmarkService</summary>
public class BenchmarkServiceClient : ClientBase<BenchmarkServiceClient>
{
+ /// <summary>Creates a new client for BenchmarkService</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public BenchmarkServiceClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for BenchmarkService that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public BenchmarkServiceClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected BenchmarkServiceClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected BenchmarkServiceClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -162,12 +167,6 @@ namespace Grpc.Testing {
}
}
- /// <summary>Creates a new client for BenchmarkService</summary>
- public static BenchmarkServiceClient NewClient(Channel channel)
- {
- return new BenchmarkServiceClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(BenchmarkServiceBase serviceImpl)
{
@@ -273,17 +272,22 @@ namespace Grpc.Testing {
/// <summary>Client for WorkerService</summary>
public class WorkerServiceClient : ClientBase<WorkerServiceClient>
{
+ /// <summary>Creates a new client for WorkerService</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public WorkerServiceClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for WorkerService that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public WorkerServiceClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected WorkerServiceClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected WorkerServiceClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -398,12 +402,6 @@ namespace Grpc.Testing {
}
}
- /// <summary>Creates a new client for WorkerService</summary>
- public static WorkerServiceClient NewClient(Channel channel)
- {
- return new WorkerServiceClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(WorkerServiceBase serviceImpl)
{
diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
index 3df45b5f70..f85e272711 100644
--- a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
@@ -79,7 +79,7 @@ namespace Grpc.IntegrationTesting
};
channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
- client = TestService.NewClient(channel);
+ client = new TestService.TestServiceClient(channel);
}
[TestFixtureTearDown]
diff --git a/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs b/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
index 4d6ca7ece5..74ee040ae4 100644
--- a/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
@@ -148,7 +148,7 @@ namespace Grpc.IntegrationTesting
channels.Add(channel);
for (int j = 0; j < options.NumStubsPerChannel; j++)
{
- var client = TestService.NewClient(channel);
+ var client = new TestService.TestServiceClient(channel);
var task = Task.Factory.StartNew(() => RunBodyAsync(client).GetAwaiter().GetResult(),
TaskCreationOptions.LongRunning);
tasks.Add(task);
diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
index 6c252013f8..3e149da3e0 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
@@ -168,17 +168,22 @@ namespace Grpc.Testing {
/// <summary>Client for TestService</summary>
public class TestServiceClient : ClientBase<TestServiceClient>
{
+ /// <summary>Creates a new client for TestService</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public TestServiceClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for TestService that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public TestServiceClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected TestServiceClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected TestServiceClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -315,12 +320,6 @@ namespace Grpc.Testing {
}
}
- /// <summary>Creates a new client for TestService</summary>
- public static TestServiceClient NewClient(Channel channel)
- {
- return new TestServiceClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(TestServiceBase serviceImpl)
{
@@ -373,17 +372,22 @@ namespace Grpc.Testing {
/// <summary>Client for UnimplementedService</summary>
public class UnimplementedServiceClient : ClientBase<UnimplementedServiceClient>
{
+ /// <summary>Creates a new client for UnimplementedService</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public UnimplementedServiceClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for UnimplementedService that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public UnimplementedServiceClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected UnimplementedServiceClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected UnimplementedServiceClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -422,12 +426,6 @@ namespace Grpc.Testing {
}
}
- /// <summary>Creates a new client for UnimplementedService</summary>
- public static UnimplementedServiceClient NewClient(Channel channel)
- {
- return new UnimplementedServiceClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(UnimplementedServiceBase serviceImpl)
{
@@ -485,17 +483,22 @@ namespace Grpc.Testing {
/// <summary>Client for ReconnectService</summary>
public class ReconnectServiceClient : ClientBase<ReconnectServiceClient>
{
+ /// <summary>Creates a new client for ReconnectService</summary>
+ /// <param name="channel">The channel to use to make remote calls.</param>
public ReconnectServiceClient(Channel channel) : base(channel)
{
}
+ /// <summary>Creates a new client for ReconnectService that uses a custom <c>CallInvoker</c>.</summary>
+ /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
public ReconnectServiceClient(CallInvoker callInvoker) : base(callInvoker)
{
}
- ///<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+ /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
protected ReconnectServiceClient() : base()
{
}
- ///<summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <summary>Protected constructor to allow creation of configured clients.</summary>
+ /// <param name="configuration">The client configuration.</param>
protected ReconnectServiceClient(ClientBaseConfiguration configuration) : base(configuration)
{
}
@@ -538,12 +541,6 @@ namespace Grpc.Testing {
}
}
- /// <summary>Creates a new client for ReconnectService</summary>
- public static ReconnectServiceClient NewClient(Channel channel)
- {
- return new ReconnectServiceClient(channel);
- }
-
/// <summary>Creates service definition that can be registered with a server</summary>
public static ServerServiceDefinition BindService(ReconnectServiceBase serviceImpl)
{
diff --git a/src/csharp/Grpc.IntegrationTesting/project.json b/src/csharp/Grpc.IntegrationTesting/project.json
index 3493ab0c22..6297600ddc 100644
--- a/src/csharp/Grpc.IntegrationTesting/project.json
+++ b/src/csharp/Grpc.IntegrationTesting/project.json
@@ -5,6 +5,13 @@
"configurations": {
"Debug": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
@@ -18,6 +25,13 @@
},
"Release": {
"buildOptions": {
+ "define": [ "SIGNED" ],
+ "keyFile": "../keys/Grpc.snk",
+ "publicSign": true,
+ "xmlDoc": true,
+ "compile": {
+ "includeFiles": [ "../Grpc.Core/Version.cs" ]
+ },
"copyToOutput": {
"include": "data/*",
"mappings": {
diff --git a/src/csharp/build_packages.bat b/src/csharp/build_packages.bat
index 63f8c30bc7..272b30f385 100644
--- a/src/csharp/build_packages.bat
+++ b/src/csharp/build_packages.bat
@@ -30,7 +30,7 @@
@rem Builds gRPC NuGet packages
@rem Current package versions
-set VERSION=0.15.0-dev
+set VERSION=0.16.0-dev
set PROTOBUF_VERSION=3.0.0-beta3
@rem Packages that depend on prerelease packages (like Google.Protobuf) need to have prerelease suffix as well.
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 4782654250..9b8d050ea5 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -249,10 +249,12 @@ grpcsharp_batch_context_recv_initial_metadata(
GPR_EXPORT intptr_t GPR_CALLTYPE grpcsharp_batch_context_recv_message_length(
const grpcsharp_batch_context *ctx) {
+ grpc_byte_buffer_reader reader;
if (!ctx->recv_message) {
return -1;
}
- return (intptr_t)grpc_byte_buffer_length(ctx->recv_message);
+ grpc_byte_buffer_reader_init(&reader, ctx->recv_message);
+ return (intptr_t)grpc_byte_buffer_length(reader.buffer_out);
}
/*
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index f18ce01c6f..745b5023d5 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -31,12 +31,16 @@
*
*/
+#include <list>
+
#include <node.h>
#include <nan.h>
#include <v8.h>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/alloc.h"
+#include "grpc/support/log.h"
+#include "grpc/support/time.h"
#include "call.h"
#include "call_credentials.h"
@@ -45,14 +49,32 @@
#include "server.h"
#include "completion_queue_async_worker.h"
#include "server_credentials.h"
+#include "timeval.h"
using v8::FunctionTemplate;
using v8::Local;
using v8::Value;
+using v8::Number;
using v8::Object;
using v8::Uint32;
using v8::String;
+typedef struct log_args {
+ gpr_log_func_args core_args;
+ gpr_timespec timestamp;
+} log_args;
+
+typedef struct logger_state {
+ Nan::Callback *callback;
+ std::list<log_args *> *pending_args;
+ uv_mutex_t mutex;
+ uv_async_t async;
+ // Indicates that a logger has been set
+ bool logger_set;
+} logger_state;
+
+logger_state grpc_logger_state;
+
static char *pem_root_certs = NULL;
void InitStatusConstants(Local<Object> exports) {
@@ -235,6 +257,18 @@ void InitWriteFlags(Local<Object> exports) {
Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS);
}
+void InitLogConstants(Local<Object> exports) {
+ Nan::HandleScope scope;
+ Local<Object> log_verbosity = Nan::New<Object>();
+ Nan::Set(exports, Nan::New("logVerbosity").ToLocalChecked(), log_verbosity);
+ Local<Value> DEBUG(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_DEBUG));
+ Nan::Set(log_verbosity, Nan::New("DEBUG").ToLocalChecked(), DEBUG);
+ Local<Value> INFO(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_INFO));
+ Nan::Set(log_verbosity, Nan::New("INFO").ToLocalChecked(), INFO);
+ Local<Value> LOG_ERROR(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_ERROR));
+ Nan::Set(log_verbosity, Nan::New("ERROR").ToLocalChecked(), LOG_ERROR);
+}
+
NAN_METHOD(MetadataKeyIsLegal) {
if (!info[0]->IsString()) {
return Nan::ThrowTypeError(
@@ -298,16 +332,101 @@ NAN_METHOD(SetDefaultRootsPem) {
}
}
+NAUV_WORK_CB(LogMessagesCallback) {
+ Nan::HandleScope scope;
+ std::list<log_args *> args;
+ uv_mutex_lock(&grpc_logger_state.mutex);
+ args.splice(args.begin(), *grpc_logger_state.pending_args);
+ uv_mutex_unlock(&grpc_logger_state.mutex);
+ /* Call the callback with each log message */
+ while (!args.empty()) {
+ log_args *arg = args.front();
+ args.pop_front();
+ Local<Value> file = Nan::New(arg->core_args.file).ToLocalChecked();
+ Local<Value> line = Nan::New<Uint32, uint32_t>(arg->core_args.line);
+ Local<Value> severity = Nan::New(
+ gpr_log_severity_string(arg->core_args.severity)).ToLocalChecked();
+ Local<Value> message = Nan::New(arg->core_args.message).ToLocalChecked();
+ Local<Value> timestamp = Nan::New<v8::Date>(
+ grpc::node::TimespecToMilliseconds(arg->timestamp)).ToLocalChecked();
+ const int argc = 5;
+ Local<Value> argv[argc] = {file, line, severity, message, timestamp};
+ grpc_logger_state.callback->Call(argc, argv);
+ delete[] arg->core_args.message;
+ delete arg;
+ }
+}
+
+void node_log_func(gpr_log_func_args *args) {
+ // TODO(mlumish): Use the core's log formatter when it becomes available
+ log_args *args_copy = new log_args;
+ size_t message_len = strlen(args->message) + 1;
+ char *message = new char[message_len];
+ memcpy(message, args->message, message_len);
+ memcpy(&args_copy->core_args, args, sizeof(gpr_log_func_args));
+ args_copy->core_args.message = message;
+ args_copy->timestamp = gpr_now(GPR_CLOCK_REALTIME);
+
+ uv_mutex_lock(&grpc_logger_state.mutex);
+ grpc_logger_state.pending_args->push_back(args_copy);
+ uv_mutex_unlock(&grpc_logger_state.mutex);
+
+ uv_async_send(&grpc_logger_state.async);
+}
+
+void init_logger() {
+ memset(&grpc_logger_state, 0, sizeof(logger_state));
+ grpc_logger_state.pending_args = new std::list<log_args *>();
+ uv_mutex_init(&grpc_logger_state.mutex);
+ uv_async_init(uv_default_loop(),
+ &grpc_logger_state.async,
+ LogMessagesCallback);
+ uv_unref((uv_handle_t*)&grpc_logger_state.async);
+ grpc_logger_state.logger_set = false;
+
+ gpr_log_verbosity_init();
+}
+
+/* This registers a JavaScript logger for messages from the gRPC core. Because
+ that handler has to be run in the context of the JavaScript event loop, it
+ will be run asynchronously. To minimize the problems that could cause for
+ debugging, we leave core to do its default synchronous logging until a
+ JavaScript logger is set */
+NAN_METHOD(SetDefaultLoggerCallback) {
+ if (!info[0]->IsFunction()) {
+ return Nan::ThrowTypeError(
+ "setDefaultLoggerCallback's argument must be a function");
+ }
+ if (!grpc_logger_state.logger_set) {
+ gpr_set_log_function(node_log_func);
+ grpc_logger_state.logger_set = true;
+ }
+ grpc_logger_state.callback = new Nan::Callback(info[0].As<v8::Function>());
+}
+
+NAN_METHOD(SetLogVerbosity) {
+ if (!info[0]->IsUint32()) {
+ return Nan::ThrowTypeError(
+ "setLogVerbosity's argument must be a number");
+ }
+ gpr_log_severity severity = static_cast<gpr_log_severity>(
+ Nan::To<uint32_t>(info[0]).FromJust());
+ gpr_set_log_verbosity(severity);
+}
+
void init(Local<Object> exports) {
Nan::HandleScope scope;
grpc_init();
grpc_set_ssl_roots_override_callback(get_ssl_roots_override);
+ init_logger();
+
InitStatusConstants(exports);
InitCallErrorConstants(exports);
InitOpTypeConstants(exports);
InitPropagateConstants(exports);
InitConnectivityStateConstants(exports);
InitWriteFlags(exports);
+ InitLogConstants(exports);
grpc::node::Call::Init(exports);
grpc::node::CallCredentials::Init(exports);
@@ -333,6 +452,14 @@ void init(Local<Object> exports) {
Nan::GetFunction(
Nan::New<FunctionTemplate>(SetDefaultRootsPem)
).ToLocalChecked());
+ Nan::Set(exports, Nan::New("setDefaultLoggerCallback").ToLocalChecked(),
+ Nan::GetFunction(
+ Nan::New<FunctionTemplate>(SetDefaultLoggerCallback)
+ ).ToLocalChecked());
+ Nan::Set(exports, Nan::New("setLogVerbosity").ToLocalChecked(),
+ Nan::GetFunction(
+ Nan::New<FunctionTemplate>(SetLogVerbosity)
+ ).ToLocalChecked());
}
NODE_MODULE(grpc_node, init)
diff --git a/src/node/index.js b/src/node/index.js
index 66664d94b5..9fb6faa5d7 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -46,6 +46,8 @@ var client = require('./src/client.js');
var server = require('./src/server.js');
+var common = require('./src/common.js');
+
var Metadata = require('./src/metadata.js');
var grpc = require('./src/grpc_extension');
@@ -122,6 +124,42 @@ exports.load = function load(filename, format, options) {
return loadObject(builder.ns, options);
};
+var log_template = _.template(
+ '{severity} {timestamp}\t{file}:{line}]\t{message}',
+ {interpolate: /{([\s\S]+?)}/g});
+
+/**
+ * Sets the logger function for the gRPC module. For debugging purposes, the C
+ * core will log synchronously directly to stdout unless this function is
+ * called. Note: the output format here is intended to be informational, and
+ * is not guaranteed to stay the same in the future.
+ * Logs will be directed to logger.error.
+ * @param {Console} logger A Console-like object.
+ */
+exports.setLogger = function setLogger(logger) {
+ common.logger = logger;
+ grpc.setDefaultLoggerCallback(function(file, line, severity,
+ message, timestamp) {
+ logger.error(log_template({
+ file: path.basename(file),
+ line: line,
+ severity: severity,
+ message: message,
+ timestamp: timestamp.toISOString()
+ }));
+ });
+};
+
+/**
+ * Sets the logger verbosity for gRPC module logging. The options are members
+ * of the grpc.logVerbosity map.
+ * @param {Number} verbosity The minimum severity to log
+ */
+exports.setLogVerbosity = function setLogVerbosity(verbosity) {
+ common.logVerbosity = verbosity;
+ grpc.setLogVerbosity(verbosity);
+};
+
/**
* @see module:src/server.Server
*/
@@ -153,6 +191,11 @@ exports.callError = grpc.callError;
exports.writeFlags = grpc.writeFlags;
/**
+ * Log verbosity setting name to code number mapping
+ */
+exports.logVerbosity = grpc.logVerbosity;
+
+/**
* Credentials factories
*/
exports.credentials = require('./src/credentials.js');
diff --git a/src/node/src/common.js b/src/node/src/common.js
index 8cf43b7a84..22159dd39f 100644
--- a/src/node/src/common.js
+++ b/src/node/src/common.js
@@ -157,3 +157,24 @@ exports.getProtobufServiceAttrs = function getProtobufServiceAttrs(service,
}];
}));
};
+
+/**
+ * The logger object for the gRPC module. Defaults to console.
+ */
+exports.logger = console;
+
+/**
+ * The current logging verbosity. 0 corresponds to logging everything
+ */
+exports.logVerbosity = 0;
+
+/**
+ * Log a message if the severity is at least as high as the current verbosity
+ * @param {Number} severity A value of the grpc.logVerbosity map
+ * @param {String} message The message to log
+ */
+exports.log = function log(severity, message) {
+ if (severity >= exports.logVerbosity) {
+ exports.logger.error(message);
+ }
+};
diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js
index a12eade4e1..b746d0625d 100644
--- a/src/node/src/credentials.js
+++ b/src/node/src/credentials.js
@@ -69,6 +69,8 @@ var ChannelCredentials = grpc.ChannelCredentials;
var Metadata = require('./metadata.js');
+var common = require('./common.js');
+
/**
* Create an SSL Credentials object. If using a client-side certificate, both
* the second and third arguments must be passed.
@@ -120,7 +122,7 @@ exports.createFromGoogleCredential = function(google_credential) {
var service_url = auth_context.service_url;
google_credential.getRequestMetadata(service_url, function(err, header) {
if (err) {
- console.log('Auth error:', err);
+ common.log(grpc.logVerbosity.INFO, 'Auth error:' + err);
callback(err);
return;
}
diff --git a/src/node/src/server.js b/src/node/src/server.js
index fd5153f124..b3b414969a 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -735,8 +735,8 @@ Server.prototype.addService = function(service, implementation) {
}
var impl;
if (implementation[name] === undefined) {
- console.warn('Method handler for %s expected but not provided',
- attrs.path);
+ common.log(grpc.logVerbosity.ERROR, 'Method handler for ' +
+ attrs.path + ' expected but not provided');
impl = defaultHandler[method_type];
} else {
impl = _.bind(implementation[name], implementation);
diff --git a/src/node/test/credentials_test.js b/src/node/test/credentials_test.js
index 794215b246..0a21572582 100644
--- a/src/node/test/credentials_test.js
+++ b/src/node/test/credentials_test.js
@@ -318,7 +318,7 @@ describe('client credentials', function() {
done();
});
});
- it.skip('should propagate errors that the updater emits', function(done) {
+ it('should propagate errors that the updater emits', function(done) {
var metadataUpdater = function(service_url, callback) {
var error = new Error('Authentication error');
error.code = grpc.status.UNAUTHENTICATED;
@@ -370,7 +370,7 @@ describe('client credentials', function() {
done();
});
});
- it.skip('should get an error from a Google credential', function(done) {
+ it('should get an error from a Google credential', function(done) {
var creds = grpc.credentials.createFromGoogleCredential(
fakeFailingGoogleCredentials);
var combined_creds = grpc.credentials.combineChannelCredentials(
diff --git a/src/node/tools/package.json b/src/node/tools/package.json
index c34b259e2e..7c256d7ba0 100644
--- a/src/node/tools/package.json
+++ b/src/node/tools/package.json
@@ -1,6 +1,6 @@
{
"name": "grpc-tools",
- "version": "0.15.0-dev",
+ "version": "0.16.0-dev",
"author": "Google Inc.",
"description": "Tools for developing with gRPC on Node.js",
"homepage": "http://www.grpc.io/",
diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
index bd6b064f16..646bf43b54 100644
--- a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
+++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
@@ -32,6 +32,8 @@
*/
#import "GRPCCall.h"
+#include <AvailabilityMacros.h>
+
/**
* Methods to configure GRPC channel options.
*/
@@ -43,4 +45,7 @@
*/
+ (void)setUserAgentPrefix:(NSString *)userAgentPrefix forHost:(NSString *)host;
++ (void)closeOpenConnections DEPRECATED_MSG_ATTRIBUTE("The API for this feature is experimental, "
+ "and might be removed or modified at any "
+ "time.");
@end
diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
index 5f9932d86d..bcc3b91507 100644
--- a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
+++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
@@ -46,4 +46,8 @@
hostConfig.userAgentPrefix = userAgentPrefix;
}
++ (void)closeOpenConnections {
+ [GRPCHost flushChannelCache];
+}
+
@end
diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h
index 343dd48a14..ac2a37d75f 100644
--- a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h
+++ b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h
@@ -41,7 +41,7 @@
*/
+ (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCert
forHost:(nonnull NSString *)host
- error:(NSError **)errorPtr;
+ error:(NSError * _Nullable * _Nullable)errorPtr;
/**
* Configures @c host with TLS/SSL Client Credentials and optionally trusted root Certificate
* Authorities. If @c pemRootCerts is nil, the default CA Certificates bundled with gRPC will be
@@ -51,6 +51,6 @@
withPrivateKey:(nullable NSString *)pemPrivateKey
withCertChain:(nullable NSString *)pemCertChain
forHost:(nonnull NSString *)host
- error:(NSError **)errorPtr;
+ error:(NSError * _Nullable * _Nullable)errorPtr;
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m
index d3192c983d..7b7b79e1c6 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.m
@@ -199,9 +199,7 @@ grpc_channel_args * buildChannelArgs(NSDictionary *dictionary) {
NULL, GRPC_PROPAGATE_DEFAULTS,
queue.unmanagedQueue,
path.UTF8String,
- // Get "host" from "host:port"
- // TODO(jcanizales): Use NSURLs throughout, to clarify these.
- [_host componentsSeparatedByString:@":"][0].UTF8String,
+ NULL, // Passing NULL for host
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
}
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h
index 9220e2a33d..350c69bf8e 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.h
+++ b/src/objective-c/GRPCClient/private/GRPCHost.h
@@ -41,6 +41,8 @@ struct grpc_channel_credentials;
@interface GRPCHost : NSObject
++ (void)flushChannelCache;
+
@property(nonatomic, readonly) NSString *address;
@property(nonatomic, copy, nullable) NSString *userAgentPrefix;
@property(nonatomic, nullable) struct grpc_channel_credentials *channelCreds;
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
index fef6385cea..08c699f99e 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.m
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -51,6 +51,8 @@ NS_ASSUME_NONNULL_BEGIN
// templates/src/core/surface/version.c.template .
#define GRPC_OBJC_VERSION_STRING @"0.13.0"
+static NSMutableDictionary *kHostCache;
+
@implementation GRPCHost {
// TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
GRPCChannel *_channel;
@@ -82,13 +84,12 @@ NS_ASSUME_NONNULL_BEGIN
}
// Look up the GRPCHost in the cache.
- static NSMutableDictionary *hostCache;
static dispatch_once_t cacheInitialization;
dispatch_once(&cacheInitialization, ^{
- hostCache = [NSMutableDictionary dictionary];
+ kHostCache = [NSMutableDictionary dictionary];
});
- @synchronized(hostCache) {
- GRPCHost *cachedHost = hostCache[address];
+ @synchronized(kHostCache) {
+ GRPCHost *cachedHost = kHostCache[address];
if (cachedHost) {
return cachedHost;
}
@@ -96,12 +97,22 @@ NS_ASSUME_NONNULL_BEGIN
if ((self = [super init])) {
_address = address;
_secure = YES;
- hostCache[address] = self;
+ kHostCache[address] = self;
}
}
return self;
}
++ (void)flushChannelCache {
+ @synchronized(kHostCache) {
+ [kHostCache enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key,
+ GRPCHost * _Nonnull host,
+ BOOL * _Nonnull stop) {
+ [host disconnect];
+ }];
+ }
+}
+
- (nullable grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue {
GRPCChannel *channel;
diff --git a/src/objective-c/GRPCClient/private/NSData+GRPC.m b/src/objective-c/GRPCClient/private/NSData+GRPC.m
index e6a6c3c605..1238374af3 100644
--- a/src/objective-c/GRPCClient/private/NSData+GRPC.m
+++ b/src/objective-c/GRPCClient/private/NSData+GRPC.m
@@ -39,17 +39,21 @@
// TODO(jcanizales): Move these two incantations to the C library.
-static void CopyByteBufferToCharArray(grpc_byte_buffer *buffer, char *array) {
- size_t offset = 0;
+static void MallocAndCopyByteBufferToCharArray(grpc_byte_buffer *buffer,
+ size_t *length, char **array) {
grpc_byte_buffer_reader reader;
grpc_byte_buffer_reader_init(&reader, buffer);
- gpr_slice next;
- while (grpc_byte_buffer_reader_next(&reader, &next) != 0){
- memcpy(array + offset, GPR_SLICE_START_PTR(next),
- (size_t)GPR_SLICE_LENGTH(next));
- offset += GPR_SLICE_LENGTH(next);
- gpr_slice_unref(next);
+ // The slice contains uncompressed data even if compressed data was received
+ // because the reader takes care of automatically decompressing it
+ gpr_slice slice = grpc_byte_buffer_reader_readall(&reader);
+ size_t uncompressed_length = GPR_SLICE_LENGTH(slice);
+ char *result = malloc(uncompressed_length);
+ if (result) {
+ memcpy(result, GPR_SLICE_START_PTR(slice), uncompressed_length);
}
+ gpr_slice_unref(slice);
+ *array = result;
+ *length = uncompressed_length;
}
static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array,
@@ -65,8 +69,9 @@ static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array,
if (buffer == NULL) {
return nil;
}
- NSUInteger length = grpc_byte_buffer_length(buffer);
- char *array = malloc(length * sizeof(*array));
+ char *array;
+ size_t length;
+ MallocAndCopyByteBufferToCharArray(buffer, &length, &array);
if (!array) {
// TODO(jcanizales): grpc_byte_buffer is reference-counted, so we can
// prevent this memory problem by implementing a subclass of NSData
@@ -74,8 +79,9 @@ static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array,
// can be implemented using a grpc_byte_buffer_reader.
return nil;
}
- CopyByteBufferToCharArray(buffer, array);
- return [self dataWithBytesNoCopy:array length:length freeWhenDone:YES];
+ // Not depending upon size assumption of NSUInteger
+ NSUInteger length_max = MIN(length, UINT_MAX);
+ return [self dataWithBytesNoCopy:array length:length_max freeWhenDone:YES];
}
- (grpc_byte_buffer *)grpc_byteBuffer {
@@ -85,8 +91,10 @@ static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array,
// The following implementation is thus not optimal, sometimes requiring two
// copies (one by self.bytes and another by gpr_slice_from_copied_buffer).
// If it turns out to be an issue, we can use enumerateByteRangesUsingblock:
- // to create an array of gpr_slice objects to pass to grpc_raw_byte_buffer_create.
+ // to create an array of gpr_slice objects to pass to
+ // grpc_raw_byte_buffer_create.
// That would make it do exactly one copy, always.
- return CopyCharArrayToNewByteBuffer((const char *)self.bytes, (size_t)self.length);
+ return CopyCharArrayToNewByteBuffer((const char *)self.bytes,
+ (size_t)self.length);
}
@end
diff --git a/src/objective-c/ProtoRPC/ProtoMethod.h b/src/objective-c/ProtoRPC/ProtoMethod.h
index f9fdbb35ff..ae3a2723fc 100644
--- a/src/objective-c/ProtoRPC/ProtoMethod.h
+++ b/src/objective-c/ProtoRPC/ProtoMethod.h
@@ -54,6 +54,9 @@ __attribute__((deprecated("Please use GRPCProtoMethod.")))
* This subclass is empty now. Eventually we'll remove ProtoMethod class
* to avoid potential naming conflict
*/
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@interface GRPCProtoMethod : ProtoMethod
+#pragma clang diagnostic pop
@end
diff --git a/src/objective-c/ProtoRPC/ProtoRPC.h b/src/objective-c/ProtoRPC/ProtoRPC.h
index 5f91f6bce1..04fc1e45f1 100644
--- a/src/objective-c/ProtoRPC/ProtoRPC.h
+++ b/src/objective-c/ProtoRPC/ProtoRPC.h
@@ -56,6 +56,9 @@ __attribute__((deprecated("Please use GRPCProtoCall.")))
* This subclass is empty now. Eventually we'll remove ProtoRPC class
* to avoid potential naming conflict
*/
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@interface GRPCProtoCall : ProtoRPC
+#pragma clang diagnostic pop
@end
diff --git a/src/objective-c/ProtoRPC/ProtoService.h b/src/objective-c/ProtoRPC/ProtoService.h
index 87d06e1ae5..7faae1b49c 100644
--- a/src/objective-c/ProtoRPC/ProtoService.h
+++ b/src/objective-c/ProtoRPC/ProtoService.h
@@ -55,6 +55,9 @@ __attribute__((deprecated("Please use GRPCProtoService.")))
* This subclass is empty now. Eventually we'll remove ProtoService class
* to avoid potential naming conflict
*/
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@interface GRPCProtoService : ProtoService
+#pragma clang diagnostic pop
@end
diff --git a/src/objective-c/ProtoRPC/ProtoService.m b/src/objective-c/ProtoRPC/ProtoService.m
index 597c3cf0fe..3487fac59d 100644
--- a/src/objective-c/ProtoRPC/ProtoService.m
+++ b/src/objective-c/ProtoRPC/ProtoService.m
@@ -65,18 +65,18 @@
return self;
}
-- (ProtoRPC *)RPCToMethod:(NSString *)method
- requestsWriter:(GRXWriter *)requestsWriter
- responseClass:(Class)responseClass
- responsesWriteable:(id<GRXWriteable>)responsesWriteable {
- ProtoMethod *methodName = [[ProtoMethod alloc] initWithPackage:_packageName
- service:_serviceName
- method:method];
- return [[ProtoRPC alloc] initWithHost:_host
- method:methodName
- requestsWriter:requestsWriter
- responseClass:responseClass
- responsesWriteable:responsesWriteable];
+- (GRPCProtoCall *)RPCToMethod:(NSString *)method
+ requestsWriter:(GRXWriter *)requestsWriter
+ responseClass:(Class)responseClass
+ responsesWriteable:(id<GRXWriteable>)responsesWriteable {
+ GRPCProtoMethod *methodName = [[GRPCProtoMethod alloc] initWithPackage:_packageName
+ service:_serviceName
+ method:method];
+ return [[GRPCProtoCall alloc] initWithHost:_host
+ method:methodName
+ requestsWriter:requestsWriter
+ responseClass:responseClass
+ responsesWriteable:responsesWriteable];
}
@end
diff --git a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
index 5addf26fc4..107e6de4e2 100644
--- a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
+++ b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
@@ -2,6 +2,10 @@ Pod::Spec.new do |s|
s.name = "RemoteTest"
s.version = "0.0.1"
s.license = "New BSD"
+ s.authors = { 'gRPC contributors' => 'grpc-io@googlegroups.com' }
+ s.homepage = "http://www.grpc.io/"
+ s.summary = "RemoteTest example"
+ s.source = { :git => 'https://github.com/grpc/grpc.git' }
s.ios.deployment_target = '7.1'
s.osx.deployment_target = '10.9'
diff --git a/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj b/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj
index 611eb6032d..5c2a6d14f9 100644
--- a/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj
+++ b/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj
@@ -7,16 +7,16 @@
objects = {
/* Begin PBXBuildFile section */
+ 426A5020E0E158A101BCA1D9 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C20055928615A6F8434E26B4 /* libPods-Sample.a */; };
6369A2701A9322E20015FC5C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6369A26F1A9322E20015FC5C /* main.m */; };
6369A2731A9322E20015FC5C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6369A2721A9322E20015FC5C /* AppDelegate.m */; };
6369A2761A9322E20015FC5C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6369A2751A9322E20015FC5C /* ViewController.m */; };
6369A2791A9322E20015FC5C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6369A2771A9322E20015FC5C /* Main.storyboard */; };
6369A27B1A9322E20015FC5C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6369A27A1A9322E20015FC5C /* Images.xcassets */; };
- FC81FE63CA655031F3524EC0 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DC7B7C4C0410F43B9621631 /* libPods.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
- 2DC7B7C4C0410F43B9621631 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5A8C9F4B28733B249DE4AB6D /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
6369A26A1A9322E20015FC5C /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
6369A26E1A9322E20015FC5C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6369A26F1A9322E20015FC5C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@@ -26,8 +26,8 @@
6369A2751A9322E20015FC5C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
6369A2781A9322E20015FC5C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
6369A27A1A9322E20015FC5C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
- AC29DD6FCDF962F519FEBB0D /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
- C68330F8D451CC6ACEABA09F /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
+ C20055928615A6F8434E26B4 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ E3C01DF315C4E7433BCEC6E6 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -35,7 +35,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- FC81FE63CA655031F3524EC0 /* libPods.a in Frameworks */,
+ 426A5020E0E158A101BCA1D9 /* libPods-Sample.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -86,8 +86,8 @@
AB3331C9AE6488E61B2B094E /* Pods */ = {
isa = PBXGroup;
children = (
- AC29DD6FCDF962F519FEBB0D /* Pods.debug.xcconfig */,
- C68330F8D451CC6ACEABA09F /* Pods.release.xcconfig */,
+ E3C01DF315C4E7433BCEC6E6 /* Pods-Sample.debug.xcconfig */,
+ 5A8C9F4B28733B249DE4AB6D /* Pods-Sample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
@@ -95,7 +95,7 @@
C4C2C5219053E079C9EFB930 /* Frameworks */ = {
isa = PBXGroup;
children = (
- 2DC7B7C4C0410F43B9621631 /* libPods.a */,
+ C20055928615A6F8434E26B4 /* libPods-Sample.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -107,11 +107,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 6369A28D1A9322E20015FC5C /* Build configuration list for PBXNativeTarget "Sample" */;
buildPhases = (
- 41F7486D8F66994B0BFB84AF /* Check Pods Manifest.lock */,
+ 41F7486D8F66994B0BFB84AF /* [CP] Check Pods Manifest.lock */,
6369A2661A9322E20015FC5C /* Sources */,
6369A2671A9322E20015FC5C /* Frameworks */,
6369A2681A9322E20015FC5C /* Resources */,
- 04554623324BE4A838846086 /* Copy Pods Resources */,
+ 04554623324BE4A838846086 /* [CP] Copy Pods Resources */,
+ C7FAD018D05AB5F0B0FE81E2 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -167,29 +168,29 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 04554623324BE4A838846086 /* Copy Pods Resources */ = {
+ 04554623324BE4A838846086 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- 41F7486D8F66994B0BFB84AF /* Check Pods Manifest.lock */ = {
+ 41F7486D8F66994B0BFB84AF /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -197,6 +198,21 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
+ C7FAD018D05AB5F0B0FE81E2 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -304,7 +320,7 @@
};
6369A28E1A9322E20015FC5C /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = AC29DD6FCDF962F519FEBB0D /* Pods.debug.xcconfig */;
+ baseConfigurationReference = E3C01DF315C4E7433BCEC6E6 /* Pods-Sample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Sample/Info.plist;
@@ -315,7 +331,7 @@
};
6369A28F1A9322E20015FC5C /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = C68330F8D451CC6ACEABA09F /* Pods.release.xcconfig */;
+ baseConfigurationReference = 5A8C9F4B28733B249DE4AB6D /* Pods-Sample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Sample/Info.plist;
diff --git a/src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme
new file mode 100644
index 0000000000..d399e22e46
--- /dev/null
+++ b/src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6369A2691A9322E20015FC5C"
+ BuildableName = "Sample.app"
+ BlueprintName = "Sample"
+ ReferencedContainer = "container:Sample.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6369A2691A9322E20015FC5C"
+ BuildableName = "Sample.app"
+ BlueprintName = "Sample"
+ ReferencedContainer = "container:Sample.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6369A2691A9322E20015FC5C"
+ BuildableName = "Sample.app"
+ BlueprintName = "Sample"
+ ReferencedContainer = "container:Sample.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6369A2691A9322E20015FC5C"
+ BuildableName = "Sample.app"
+ BlueprintName = "Sample"
+ ReferencedContainer = "container:Sample.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
index 2f5716082b..2a1b30f2cf 100644
--- a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
+++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
@@ -7,15 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
- 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC58ACA18DCCB1553531B885 /* libPods.a */; };
633BFFC81B950B210007E424 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC71B950B210007E424 /* AppDelegate.swift */; };
633BFFCA1B950B210007E424 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC91B950B210007E424 /* ViewController.swift */; };
633BFFCD1B950B210007E424 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCB1B950B210007E424 /* Main.storyboard */; };
633BFFCF1B950B210007E424 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCE1B950B210007E424 /* Images.xcassets */; };
+ 92EDB1408A1E1E7DDAB25D9C /* libPods-SwiftSample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 69BB5C6CA3C1F97E007AC527 /* libPods-SwiftSample.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
- 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
633BFFC21B950B210007E424 /* SwiftSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
633BFFC61B950B210007E424 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
633BFFC71B950B210007E424 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -23,8 +22,9 @@
633BFFCC1B950B210007E424 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
633BFFCE1B950B210007E424 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
6367AD231B951655007FD3A4 /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
- C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
- DC58ACA18DCCB1553531B885 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 69BB5C6CA3C1F97E007AC527 /* libPods-SwiftSample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SwiftSample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ A7E614A494D89D01BB395761 /* Pods-SwiftSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftSample/Pods-SwiftSample.debug.xcconfig"; sourceTree = "<group>"; };
+ C314E3E246AF23AC29B38FCF /* Pods-SwiftSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftSample.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftSample/Pods-SwiftSample.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */,
+ 92EDB1408A1E1E7DDAB25D9C /* libPods-SwiftSample.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -42,8 +42,8 @@
31F283C976AE97586C17CCD9 /* Pods */ = {
isa = PBXGroup;
children = (
- 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */,
- C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */,
+ A7E614A494D89D01BB395761 /* Pods-SwiftSample.debug.xcconfig */,
+ C314E3E246AF23AC29B38FCF /* Pods-SwiftSample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
@@ -90,7 +90,7 @@
9D63A7F6423989BA306810CA /* Frameworks */ = {
isa = PBXGroup;
children = (
- DC58ACA18DCCB1553531B885 /* libPods.a */,
+ 69BB5C6CA3C1F97E007AC527 /* libPods-SwiftSample.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -102,12 +102,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */;
buildPhases = (
- 6BEEB33CA2705D7D2F2210E6 /* Check Pods Manifest.lock */,
+ 6BEEB33CA2705D7D2F2210E6 /* [CP] Check Pods Manifest.lock */,
633BFFBE1B950B210007E424 /* Sources */,
633BFFBF1B950B210007E424 /* Frameworks */,
633BFFC01B950B210007E424 /* Resources */,
- AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */,
- A1738A987353B0BF2C64F0F7 /* Embed Pods Frameworks */,
+ AC2F6F9AB1C090BB0BEE6E4D /* [CP] Copy Pods Resources */,
+ A1738A987353B0BF2C64F0F7 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -164,14 +164,14 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 6BEEB33CA2705D7D2F2210E6 /* Check Pods Manifest.lock */ = {
+ 6BEEB33CA2705D7D2F2210E6 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -179,34 +179,34 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- A1738A987353B0BF2C64F0F7 /* Embed Pods Frameworks */ = {
+ A1738A987353B0BF2C64F0F7 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Embed Pods Frameworks";
+ name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftSample/Pods-SwiftSample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */ = {
+ AC2F6F9AB1C090BB0BEE6E4D /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftSample/Pods-SwiftSample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -320,7 +320,7 @@
};
633BFFE21B950B210007E424 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */;
+ baseConfigurationReference = A7E614A494D89D01BB395761 /* Pods-SwiftSample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Info.plist;
@@ -333,7 +333,7 @@
};
633BFFE31B950B210007E424 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */;
+ baseConfigurationReference = C314E3E246AF23AC29B38FCF /* Pods-SwiftSample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Info.plist;
diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme
new file mode 100644
index 0000000000..bba6a02b2b
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "633BFFC11B950B210007E424"
+ BuildableName = "SwiftSample.app"
+ BlueprintName = "SwiftSample"
+ ReferencedContainer = "container:SwiftSample.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "633BFFC11B950B210007E424"
+ BuildableName = "SwiftSample.app"
+ BlueprintName = "SwiftSample"
+ ReferencedContainer = "container:SwiftSample.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "633BFFC11B950B210007E424"
+ BuildableName = "SwiftSample.app"
+ BlueprintName = "SwiftSample"
+ ReferencedContainer = "container:SwiftSample.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "633BFFC11B950B210007E424"
+ BuildableName = "SwiftSample.app"
+ BlueprintName = "SwiftSample"
+ ReferencedContainer = "container:SwiftSample.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/examples/SwiftSample/ViewController.swift b/src/objective-c/examples/SwiftSample/ViewController.swift
index a21ce07978..2a95d2de51 100644
--- a/src/objective-c/examples/SwiftSample/ViewController.swift
+++ b/src/objective-c/examples/SwiftSample/ViewController.swift
@@ -71,7 +71,8 @@ class ViewController: UIViewController {
NSLog("2. Response trailers: \(RPC.responseTrailers)")
}
- RPC.requestHeaders["My-Header"] = "My value"
+ // TODO(jcanizales): Revert to using subscript syntax once XCode 8 is released.
+ RPC.requestHeaders.setObject("My value", forKey: "My-Header")
RPC.start()
@@ -84,7 +85,7 @@ class ViewController: UIViewController {
let call = GRPCCall(host: RemoteHost, path: method.HTTPPath, requestsWriter: requestsWriter)
- call.requestHeaders["My-Header"] = "My value"
+ call.requestHeaders.setObject("My value", forKey: "My-Header")
call.startWithWriteable(GRXWriteable { response, error in
if let response = response as? NSData {
diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m
index 9a8d425324..1167a715bb 100644
--- a/src/objective-c/tests/GRPCClientTests.m
+++ b/src/objective-c/tests/GRPCClientTests.m
@@ -48,9 +48,9 @@ static NSString * const kPackage = @"grpc.testing";
static NSString * const kService = @"TestService";
static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.googleapis.com";
-static ProtoMethod *kInexistentMethod;
-static ProtoMethod *kEmptyCallMethod;
-static ProtoMethod *kUnaryCallMethod;
+static GRPCProtoMethod *kInexistentMethod;
+static GRPCProtoMethod *kEmptyCallMethod;
+static GRPCProtoMethod *kUnaryCallMethod;
/** Observer class for testing that responseMetadata is KVO-compliant */
@interface PassthroughObserver : NSObject
@@ -109,15 +109,15 @@ static ProtoMethod *kUnaryCallMethod;
[GRPCCall useInsecureConnectionsForHost:kHostAddress];
// This method isn't implemented by the remote server.
- kInexistentMethod = [[ProtoMethod alloc] initWithPackage:kPackage
- service:kService
- method:@"Inexistent"];
- kEmptyCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
- service:kService
- method:@"EmptyCall"];
- kUnaryCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
- service:kService
- method:@"UnaryCall"];
+ kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
+ service:kService
+ method:@"Inexistent"];
+ kEmptyCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
+ service:kService
+ method:@"EmptyCall"];
+ kUnaryCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
+ service:kService
+ method:@"UnaryCall"];
}
- (void)testConnectionToRemoteServer {
@@ -303,9 +303,9 @@ static ProtoMethod *kUnaryCallMethod;
// Try to set parameters to nil for GRPCCall. This should cause an exception
@try {
- GRPCCall *call = [[GRPCCall alloc] initWithHost:nil
- path:nil
- requestsWriter:nil];
+ (void)[[GRPCCall alloc] initWithHost:nil
+ path:nil
+ requestsWriter:nil];
XCTFail(@"Did not receive an exception when parameters are nil");
} @catch(NSException *theException) {
NSLog(@"Received exception as expected: %@", theException.name);
@@ -316,9 +316,9 @@ static ProtoMethod *kUnaryCallMethod;
GRXWriter *requestsWriter = [GRXWriter emptyWriter];
[requestsWriter finishWithError:nil];
@try {
- GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
- path:kUnaryCallMethod.HTTPPath
- requestsWriter:requestsWriter];
+ (void)[[GRPCCall alloc] initWithHost:kHostAddress
+ path:kUnaryCallMethod.HTTPPath
+ requestsWriter:requestsWriter];
XCTFail(@"Did not receive an exception when GRXWriter has incorrect state.");
} @catch(NSException *theException) {
NSLog(@"Received exception as expected: %@", theException.name);
diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m
index 781c500f73..a503f02059 100644
--- a/src/objective-c/tests/InteropTests.m
+++ b/src/objective-c/tests/InteropTests.m
@@ -36,6 +36,7 @@
#include <grpc/status.h>
#import <Cronet/Cronet.h>
+#import <GRPCClient/GRPCCall+ChannelArg.h>
#import <GRPCClient/GRPCCall+Tests.h>
#import <GRPCClient/GRPCCall+Cronet.h>
#import <ProtoRPC/ProtoRPC.h>
@@ -58,7 +59,7 @@
requestedResponseSize:(NSNumber *)responseSize {
RMTStreamingOutputCallRequest *request = [self message];
RMTResponseParameters *parameters = [RMTResponseParameters message];
- parameters.size = responseSize.integerValue;
+ parameters.size = responseSize.intValue;
[request.responseParametersArray addObject:parameters];
request.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue];
return request;
@@ -80,7 +81,9 @@
#pragma mark Tests
+#ifdef GRPC_COMPILE_WITH_CRONET
static cronet_engine *cronetEngine = NULL;
+#endif
@implementation InteropTests {
RMTTestService *_service;
@@ -186,7 +189,7 @@ static cronet_engine *cronetEngine = NULL;
RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
for (NSNumber *size in expectedSizes) {
RMTResponseParameters *parameters = [RMTResponseParameters message];
- parameters.size = [size integerValue];
+ parameters.size = [size intValue];
[request.responseParametersArray addObject:parameters];
}
@@ -282,9 +285,10 @@ static cronet_engine *cronetEngine = NULL;
// A buffered pipe to which we never write any value acts as a writer that just hangs.
GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init];
- ProtoRPC *call = [_service RPCToStreamingInputCallWithRequestsWriter:requestsBuffer
- handler:^(RMTStreamingInputCallResponse *response,
- NSError *error) {
+ GRPCProtoCall *call =
+ [_service RPCToStreamingInputCallWithRequestsWriter:requestsBuffer
+ handler:^(RMTStreamingInputCallResponse *response,
+ NSError *error) {
XCTAssertEqual(error.code, GRPC_STATUS_CANCELLED);
[expectation fulfill];
}];
@@ -313,7 +317,7 @@ static cronet_engine *cronetEngine = NULL;
[requestsBuffer writeValue:request];
- __block ProtoRPC *call =
+ __block GRPCProtoCall *call =
[_service RPCToFullDuplexCallWithRequestsWriter:requestsBuffer
eventHandler:^(BOOL done,
RMTStreamingOutputCallResponse *response,
@@ -334,4 +338,28 @@ static cronet_engine *cronetEngine = NULL;
[self waitForExpectationsWithTimeout:8 handler:nil];
}
+- (void)testRPCAfterClosingOpenConnections {
+ XCTAssertNotNil(self.class.host);
+ __weak XCTestExpectation *expectation =
+ [self expectationWithDescription:@"RPC after closing connection"];
+
+ RMTEmpty *request = [RMTEmpty message];
+
+ [_service emptyCallWithRequest:request handler:^(RMTEmpty *response, NSError *error) {
+ XCTAssertNil(error, @"First RPC finished with unexpected error: %@", error);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ [GRPCCall closeOpenConnections];
+#pragma clang diagnostic pop
+
+ [_service emptyCallWithRequest:request handler:^(RMTEmpty *response, NSError *error) {
+ XCTAssertNil(error, @"Second RPC finished with unexpected error: %@", error);
+ [expectation fulfill];
+ }];
+ }];
+
+ [self waitForExpectationsWithTimeout:4 handler:nil];
+}
+
@end
diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile
index a7a88a3b9d..6d5f94cbda 100644
--- a/src/objective-c/tests/Podfile
+++ b/src/objective-c/tests/Podfile
@@ -4,11 +4,11 @@ platform :ios, '8.0'
install! 'cocoapods', :deterministic_uuids => false
def shared_pods
- pod 'Protobuf', :path => "../../../third_party/protobuf"
- pod 'BoringSSL', :podspec => ".."
- pod 'CronetFramework', :podspec => ".."
- pod 'gRPC', :path => "../../.."
- pod 'RemoteTest', :path => "RemoteTestClient"
+ pod 'Protobuf', :path => "../../../third_party/protobuf", :inhibit_warnings => true
+ pod 'BoringSSL', :podspec => "..", :inhibit_warnings => true
+ pod 'CronetFramework', :podspec => ".."
+ pod 'gRPC', :path => "../../.."
+ pod 'RemoteTest', :path => "RemoteTestClient"
end
target 'Tests' do
@@ -34,3 +34,19 @@ end
target 'InteropTestsLocalCleartext' do
shared_pods
end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ target.build_configurations.each do |config|
+ config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES'
+ end
+ if target.name == 'gRPC'
+ target.build_configurations.each do |config|
+ # TODO(zyc) Remove this setting after the issue is resolved
+ # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void
+ # function" warning
+ config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO'
+ end
+ end
+ end
+end
diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
index 89e0ea60b9..f9389a4977 100644
--- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
+++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
@@ -274,12 +274,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 63423F4D1B150A5F006CF63C /* Build configuration list for PBXNativeTarget "AllTests" */;
buildPhases = (
- 914ADDD7106BA9BB8A7E569F /* 📦 Check Pods Manifest.lock */,
+ 914ADDD7106BA9BB8A7E569F /* [CP] Check Pods Manifest.lock */,
63423F401B150A5F006CF63C /* Sources */,
63423F411B150A5F006CF63C /* Frameworks */,
63423F421B150A5F006CF63C /* Resources */,
- A441F71824DCB9D0CA297748 /* 📦 Copy Pods Resources */,
- 5F14F59509E10C2852014F9E /* 📦 Embed Pods Frameworks */,
+ A441F71824DCB9D0CA297748 /* [CP] Copy Pods Resources */,
+ 5F14F59509E10C2852014F9E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -295,11 +295,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 635697DB1B14FC11007A7283 /* Build configuration list for PBXNativeTarget "Tests" */;
buildPhases = (
- 796680C7599CB4ED736DD62A /* 📦 Check Pods Manifest.lock */,
+ 796680C7599CB4ED736DD62A /* [CP] Check Pods Manifest.lock */,
635697C31B14FC11007A7283 /* Sources */,
635697C41B14FC11007A7283 /* Frameworks */,
635697C51B14FC11007A7283 /* CopyFiles */,
- AEEBFC914CBAEE347382E8C4 /* 📦 Copy Pods Resources */,
+ AEEBFC914CBAEE347382E8C4 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -314,12 +314,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */;
buildPhases = (
- B2986CEEE8CDD4901C97598B /* 📦 Check Pods Manifest.lock */,
+ B2986CEEE8CDD4901C97598B /* [CP] Check Pods Manifest.lock */,
63DC840F1BE15179000708E8 /* Sources */,
63DC84101BE15179000708E8 /* Frameworks */,
63DC84111BE15179000708E8 /* Resources */,
- 4F5690DC0E6AD6663FE78B8B /* 📦 Embed Pods Frameworks */,
- C977426A8727267BBAC7D48E /* 📦 Copy Pods Resources */,
+ 4F5690DC0E6AD6663FE78B8B /* [CP] Embed Pods Frameworks */,
+ C977426A8727267BBAC7D48E /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -335,12 +335,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */;
buildPhases = (
- 4C406327D3907A5E5FBA8AC9 /* 📦 Check Pods Manifest.lock */,
+ 4C406327D3907A5E5FBA8AC9 /* [CP] Check Pods Manifest.lock */,
63DC841F1BE15267000708E8 /* Sources */,
63DC84201BE15267000708E8 /* Frameworks */,
63DC84211BE15267000708E8 /* Resources */,
- 900B6EDD4D16BE7D765C3885 /* 📦 Embed Pods Frameworks */,
- C2E09DC4BD239F71160F0CC1 /* 📦 Copy Pods Resources */,
+ 900B6EDD4D16BE7D765C3885 /* [CP] Embed Pods Frameworks */,
+ C2E09DC4BD239F71160F0CC1 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -356,12 +356,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */;
buildPhases = (
- 5C20DCCB71C3991E6FE78C22 /* 📦 Check Pods Manifest.lock */,
+ 5C20DCCB71C3991E6FE78C22 /* [CP] Check Pods Manifest.lock */,
63DC84301BE15294000708E8 /* Sources */,
63DC84311BE15294000708E8 /* Frameworks */,
63DC84321BE15294000708E8 /* Resources */,
- C591129ACE9F6CC5EE03FCDE /* 📦 Embed Pods Frameworks */,
- 693DD0B453431D64EA24FD66 /* 📦 Copy Pods Resources */,
+ C591129ACE9F6CC5EE03FCDE /* [CP] Embed Pods Frameworks */,
+ 693DD0B453431D64EA24FD66 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -377,12 +377,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */;
buildPhases = (
- 7418AC7B3844B29E48D24FC7 /* 📦 Check Pods Manifest.lock */,
+ 7418AC7B3844B29E48D24FC7 /* [CP] Check Pods Manifest.lock */,
63DC843F1BE152B5000708E8 /* Sources */,
63DC84401BE152B5000708E8 /* Frameworks */,
63DC84411BE152B5000708E8 /* Resources */,
- A8E3AC66DF770B774114A30E /* 📦 Embed Pods Frameworks */,
- 8AD3130D3C58A0FB32FF2A36 /* 📦 Copy Pods Resources */,
+ A8E3AC66DF770B774114A30E /* [CP] Embed Pods Frameworks */,
+ 8AD3130D3C58A0FB32FF2A36 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -486,14 +486,14 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 4C406327D3907A5E5FBA8AC9 /* 📦 Check Pods Manifest.lock */ = {
+ 4C406327D3907A5E5FBA8AC9 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -501,14 +501,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- 4F5690DC0E6AD6663FE78B8B /* 📦 Embed Pods Frameworks */ = {
+ 4F5690DC0E6AD6663FE78B8B /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Embed Pods Frameworks";
+ name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -516,14 +516,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 5C20DCCB71C3991E6FE78C22 /* 📦 Check Pods Manifest.lock */ = {
+ 5C20DCCB71C3991E6FE78C22 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -531,14 +531,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- 5F14F59509E10C2852014F9E /* 📦 Embed Pods Frameworks */ = {
+ 5F14F59509E10C2852014F9E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Embed Pods Frameworks";
+ name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -546,14 +546,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AllTests/Pods-AllTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 693DD0B453431D64EA24FD66 /* 📦 Copy Pods Resources */ = {
+ 693DD0B453431D64EA24FD66 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -561,14 +561,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- 7418AC7B3844B29E48D24FC7 /* 📦 Check Pods Manifest.lock */ = {
+ 7418AC7B3844B29E48D24FC7 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -576,14 +576,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- 796680C7599CB4ED736DD62A /* 📦 Check Pods Manifest.lock */ = {
+ 796680C7599CB4ED736DD62A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -591,14 +591,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- 8AD3130D3C58A0FB32FF2A36 /* 📦 Copy Pods Resources */ = {
+ 8AD3130D3C58A0FB32FF2A36 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -606,14 +606,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- 900B6EDD4D16BE7D765C3885 /* 📦 Embed Pods Frameworks */ = {
+ 900B6EDD4D16BE7D765C3885 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Embed Pods Frameworks";
+ name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -621,14 +621,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 914ADDD7106BA9BB8A7E569F /* 📦 Check Pods Manifest.lock */ = {
+ 914ADDD7106BA9BB8A7E569F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -636,14 +636,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- A441F71824DCB9D0CA297748 /* 📦 Copy Pods Resources */ = {
+ A441F71824DCB9D0CA297748 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -651,14 +651,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AllTests/Pods-AllTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- A8E3AC66DF770B774114A30E /* 📦 Embed Pods Frameworks */ = {
+ A8E3AC66DF770B774114A30E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Embed Pods Frameworks";
+ name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -666,14 +666,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- AEEBFC914CBAEE347382E8C4 /* 📦 Copy Pods Resources */ = {
+ AEEBFC914CBAEE347382E8C4 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -681,14 +681,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests/Pods-Tests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- B2986CEEE8CDD4901C97598B /* 📦 Check Pods Manifest.lock */ = {
+ B2986CEEE8CDD4901C97598B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Check Pods Manifest.lock";
+ name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -696,14 +696,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- C2E09DC4BD239F71160F0CC1 /* 📦 Copy Pods Resources */ = {
+ C2E09DC4BD239F71160F0CC1 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -711,14 +711,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- C591129ACE9F6CC5EE03FCDE /* 📦 Embed Pods Frameworks */ = {
+ C591129ACE9F6CC5EE03FCDE /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Embed Pods Frameworks";
+ name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -726,14 +726,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- C977426A8727267BBAC7D48E /* 📦 Copy Pods Resources */ = {
+ C977426A8727267BBAC7D48E /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "📦 Copy Pods Resources";
+ name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -893,6 +893,7 @@
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -929,6 +930,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -946,6 +948,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 060EF32D7EC0DF67ED617507 /* Pods-Tests.debug.xcconfig */;
buildSettings = {
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
@@ -955,6 +958,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = E6733B838B28453434B556E2 /* Pods-Tests.release.xcconfig */;
buildSettings = {
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
diff --git a/src/objective-c/tests/build_example_test.sh b/src/objective-c/tests/build_example_test.sh
new file mode 100755
index 0000000000..5c3766b4c0
--- /dev/null
+++ b/src/objective-c/tests/build_example_test.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+# Copyright 2016, 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.
+
+# Don't run this script standalone. Instead, run from the repository root:
+# ./tools/run_tests/run_tests.py -l objc
+
+set -eo pipefail
+
+cd `dirname $0`
+
+BINDIR=`pwd`/../../../bins/$CONFIG
+TMP_PATH=$PATH
+
+# If `protoc` is not found, add bins/$CONFIG/protobuf/protoc to the search path
+hash protoc 2>/dev/null || TMP_PATH=$BINDIR/protobuf:$TMP_PATH
+
+# If `protoc-gen-objcgrpc` is not found, make a symlink from
+# bins/$CONGIF/grpc_objective_c_plugin and add it to the search path
+PATH=$TMP_PATH hash protoc-gen-objcgrpc 2>/dev/null || {
+ ln -sf $BINDIR/grpc_objective_c_plugin $BINDIR/protoc-gen-objcgrpc
+ TMP_PATH=$BINDIR:$TMP_PATH
+}
+
+SCHEME=HelloWorld \
+ EXAMPLE_PATH=examples/objective-c/helloworld \
+ PATH=$TMP_PATH \
+ ./build_one_example.sh
+
+SCHEME=RouteGuideClient \
+ EXAMPLE_PATH=examples/objective-c/route_guide \
+ PATH=$TMP_PATH \
+ ./build_one_example.sh
+
+SCHEME=AuthSample \
+ EXAMPLE_PATH=examples/objective-c/auth_sample \
+ PATH=$TMP_PATH \
+ ./build_one_example.sh
+
+SCHEME=Sample \
+ EXAMPLE_PATH=src/objective-c/examples/Sample \
+ PATH=$TMP_PATH \
+ ./build_one_example.sh
+
+SCHEME=SwiftSample \
+ EXAMPLE_PATH=src/objective-c/examples/SwiftSample \
+ PATH=$TMP_PATH \
+ ./build_one_example.sh
diff --git a/src/objective-c/tests/build_one_example.sh b/src/objective-c/tests/build_one_example.sh
new file mode 100755
index 0000000000..24fb8b7bab
--- /dev/null
+++ b/src/objective-c/tests/build_one_example.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+# Copyright 2016, 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.
+
+# Don't run this script standalone. Instead, run from the repository root:
+# ./tools/run_tests/run_tests.py -l objc
+
+set -e
+
+# Params:
+# EXAMPLE_PATH - directory of the example
+# SCHEME - scheme of the example, used by xcodebuild
+
+# CocoaPods requires the terminal to be using UTF-8 encoding.
+export LANG=en_US.UTF-8
+
+cd `dirname $0`/../../..
+
+cd $EXAMPLE_PATH
+
+# clean the directory
+rm -rf Pods
+rm -rf $SCHEME.xcworkspace
+rm -f Podfile.lock
+
+pod install
+
+set -o pipefail
+XCODEBUILD_FILTER='(^===|^\*\*|\bfatal\b|\berror\b|\bwarning\b|\bfail)'
+xcodebuild \
+ clean build \
+ -workspace *.xcworkspace \
+ -scheme $SCHEME \
+ -destination name="iPhone 6" \
+ | egrep "$XCODEBUILD_FILTER" \
+ | egrep -v "(GPBDictionary|GPBArray)" -
diff --git a/src/php/README.md b/src/php/README.md
index cf8f2c11b0..6cc1ba4d46 100644
--- a/src/php/README.md
+++ b/src/php/README.md
@@ -58,7 +58,7 @@ To run tests with generated stub code from `.proto` files, you will also need th
Clone this repository
```sh
-$ git clone https://github.com/grpc/grpc.git
+$ git clone -b $(curl -L http://grpc.io/release) https://github.com/grpc/grpc
```
Build and install the gRPC C core library
@@ -101,7 +101,7 @@ extension=grpc.so
You will need the source code to run tests
```sh
-$ git clone https://github.com/grpc/grpc.git
+$ git clone -b $(curl -L http://grpc.io/release) https://github.com/grpc/grpc
$ cd grpc
$ git pull --recurse-submodules && git submodule update --init --recursive
```
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 0ec502262d..2cd45f10dc 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -248,6 +248,7 @@ PHP_METHOD(Call, __construct) {
call->wrapped = grpc_channel_create_call(
channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, completion_queue, method,
host_override, deadline->wrapped, NULL);
+ call->owned = true;
}
/**
diff --git a/src/python/grpcio/README.rst b/src/python/grpcio/README.rst
index afc4fe6a37..3fc318539e 100644
--- a/src/python/grpcio/README.rst
+++ b/src/python/grpcio/README.rst
@@ -46,7 +46,7 @@ package named :code:`python-dev`).
::
$ export REPO_ROOT=grpc # REPO_ROOT can be any directory of your choice
- $ git clone https://github.com/grpc/grpc.git $REPO_ROOT
+ $ git clone -b $(curl -L http://grpc.io/release) https://github.com/grpc/grpc $REPO_ROOT
$ cd $REPO_ROOT
$ git submodule update --init
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index 1f34beeb2c..a89b501303 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -179,6 +179,7 @@ def _event_handler(state, call, response_deserializer):
def _consume_request_iterator(
request_iterator, state, call, request_serializer):
event_handler = _event_handler(state, call, None)
+
def consume_request_iterator():
for request in request_iterator:
serialized_request = _common.serialize(request, request_serializer)
@@ -212,8 +213,18 @@ def _consume_request_iterator(
)
call.start_batch(cygrpc.Operations(operations), event_handler)
state.due.add(cygrpc.OperationType.send_close_from_client)
- thread = threading.Thread(target=consume_request_iterator)
- thread.start()
+
+ def stop_consumption_thread(timeout):
+ with state.condition:
+ if state.code is None:
+ call.cancel()
+ state.cancelled = True
+ _abort(state, grpc.StatusCode.CANCELLED, 'Cancelled!')
+ state.condition.notify_all()
+
+ consumption_thread = _common.CleanupThread(
+ stop_consumption_thread, target=consume_request_iterator)
+ consumption_thread.start()
class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call):
@@ -353,13 +364,13 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call):
with self._state.condition:
while self._state.initial_metadata is None:
self._state.condition.wait()
- return self._state.initial_metadata
+ return _common.application_metadata(self._state.initial_metadata)
def trailing_metadata(self):
with self._state.condition:
while self._state.trailing_metadata is None:
self._state.condition.wait()
- return self._state.trailing_metadata
+ return _common.application_metadata(self._state.trailing_metadata)
def code(self):
with self._state.condition:
@@ -371,7 +382,7 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call):
with self._state.condition:
while self._state.details is None:
self._state.condition.wait()
- return self._state.details
+ return _common.decode(self._state.details)
def _repr(self):
with self._state.condition:
@@ -379,7 +390,7 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call):
return '<_Rendezvous object of in-flight RPC>'
else:
return '<_Rendezvous of RPC that terminated with ({}, {})>'.format(
- self._state.code, self._state.details)
+ self._state.code, _common.decode(self._state.details))
def __repr__(self):
return self._repr()
@@ -440,7 +451,7 @@ class _UnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
state = _RPCState(_UNARY_UNARY_INITIAL_DUE, None, None, None, None)
operations = (
cygrpc.operation_send_initial_metadata(
- _common.metadata(metadata), _EMPTY_FLAGS),
+ _common.cygrpc_metadata(metadata), _EMPTY_FLAGS),
cygrpc.operation_send_message(serialized_request, _EMPTY_FLAGS),
cygrpc.operation_send_close_from_client(_EMPTY_FLAGS),
cygrpc.operation_receive_initial_metadata(_EMPTY_FLAGS),
@@ -518,7 +529,7 @@ class _UnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
event_handler)
operations = (
cygrpc.operation_send_initial_metadata(
- _common.metadata(metadata), _EMPTY_FLAGS),
+ _common.cygrpc_metadata(metadata), _EMPTY_FLAGS),
cygrpc.operation_send_message(serialized_request, _EMPTY_FLAGS),
cygrpc.operation_send_close_from_client(_EMPTY_FLAGS),
cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
@@ -553,7 +564,7 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
None)
operations = (
cygrpc.operation_send_initial_metadata(
- _common.metadata(metadata), _EMPTY_FLAGS),
+ _common.cygrpc_metadata(metadata), _EMPTY_FLAGS),
cygrpc.operation_receive_message(_EMPTY_FLAGS),
cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
)
@@ -597,7 +608,7 @@ class _StreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
event_handler)
operations = (
cygrpc.operation_send_initial_metadata(
- _common.metadata(metadata), _EMPTY_FLAGS),
+ _common.cygrpc_metadata(metadata), _EMPTY_FLAGS),
cygrpc.operation_receive_message(_EMPTY_FLAGS),
cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
)
@@ -634,7 +645,7 @@ class _StreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
event_handler)
operations = (
cygrpc.operation_send_initial_metadata(
- _common.metadata(metadata), _EMPTY_FLAGS),
+ _common.cygrpc_metadata(metadata), _EMPTY_FLAGS),
cygrpc.operation_receive_status_on_client(_EMPTY_FLAGS),
)
call.start_batch(cygrpc.Operations(operations), event_handler)
@@ -652,16 +663,27 @@ class _ChannelCallState(object):
self.managed_calls = None
-def _call_spin(state):
- while True:
- event = state.completion_queue.poll()
- completed_call = event.tag(event)
- if completed_call is not None:
- with state.lock:
- state.managed_calls.remove(completed_call)
- if not state.managed_calls:
- state.managed_calls = None
- return
+def _run_channel_spin_thread(state):
+ def channel_spin():
+ while True:
+ event = state.completion_queue.poll()
+ completed_call = event.tag(event)
+ if completed_call is not None:
+ with state.lock:
+ state.managed_calls.remove(completed_call)
+ if not state.managed_calls:
+ state.managed_calls = None
+ return
+
+ def stop_channel_spin(timeout):
+ with state.lock:
+ if state.managed_calls is not None:
+ for call in state.managed_calls:
+ call.cancel()
+
+ channel_spin_thread = _common.CleanupThread(
+ stop_channel_spin, target=channel_spin)
+ channel_spin_thread.start()
def _create_channel_managed_call(state):
@@ -690,8 +712,7 @@ def _create_channel_managed_call(state):
parent, flags, state.completion_queue, method, host, deadline)
if state.managed_calls is None:
state.managed_calls = set((call,))
- spin_thread = threading.Thread(target=_call_spin, args=(state,))
- spin_thread.start()
+ _run_channel_spin_thread(state)
else:
state.managed_calls.add(call)
return call
@@ -784,11 +805,18 @@ def _poll_connectivity(state, channel, initial_try_to_connect):
_spawn_delivery(state, callbacks)
+def _moot(state):
+ with state.lock:
+ del state.callbacks_and_connectivities[:]
+
+
def _subscribe(state, callback, try_to_connect):
with state.lock:
if not state.callbacks_and_connectivities and not state.polling:
- polling_thread = threading.Thread(
- target=_poll_connectivity,
+ def cancel_all_subscriptions(timeout):
+ _moot(state)
+ polling_thread = _common.CleanupThread(
+ cancel_all_subscriptions, target=_poll_connectivity,
args=(state, state.channel, bool(try_to_connect)))
polling_thread.start()
state.polling = True
@@ -812,19 +840,19 @@ def _unsubscribe(state, callback):
break
-def _moot(state):
- with state.lock:
- del state.callbacks_and_connectivities[:]
-
-
def _options(options):
if options is None:
pairs = ((cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT),)
else:
pairs = list(options) + [
(cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)]
- return cygrpc.ChannelArgs(
- cygrpc.ChannelArg(arg_name, arg_value) for arg_name, arg_value in pairs)
+ encoded_pairs = [
+ (_common.encode(arg_name), arg_value) if isinstance(arg_value, int)
+ else (_common.encode(arg_name), _common.encode(arg_value))
+ for arg_name, arg_value in pairs]
+ return cygrpc.ChannelArgs([
+ cygrpc.ChannelArg(arg_name, arg_value)
+ for arg_name, arg_value in encoded_pairs])
class Channel(grpc.Channel):
@@ -837,7 +865,8 @@ class Channel(grpc.Channel):
options: Configuration options for the channel.
credentials: A cygrpc.ChannelCredentials or None.
"""
- self._channel = cygrpc.Channel(target, _options(options), credentials)
+ self._channel = cygrpc.Channel(
+ _common.encode(target), _options(options), credentials)
self._call_state = _ChannelCallState(self._channel)
self._connectivity_state = _ChannelConnectivityState(self._channel)
@@ -850,26 +879,26 @@ class Channel(grpc.Channel):
def unary_unary(
self, method, request_serializer=None, response_deserializer=None):
return _UnaryUnaryMultiCallable(
- self._channel, _create_channel_managed_call(self._call_state), method,
- request_serializer, response_deserializer)
+ self._channel, _create_channel_managed_call(self._call_state),
+ _common.encode(method), request_serializer, response_deserializer)
def unary_stream(
self, method, request_serializer=None, response_deserializer=None):
return _UnaryStreamMultiCallable(
- self._channel, _create_channel_managed_call(self._call_state), method,
- request_serializer, response_deserializer)
+ self._channel, _create_channel_managed_call(self._call_state),
+ _common.encode(method), request_serializer, response_deserializer)
def stream_unary(
self, method, request_serializer=None, response_deserializer=None):
return _StreamUnaryMultiCallable(
- self._channel, _create_channel_managed_call(self._call_state), method,
- request_serializer, response_deserializer)
+ self._channel, _create_channel_managed_call(self._call_state),
+ _common.encode(method), request_serializer, response_deserializer)
def stream_stream(
self, method, request_serializer=None, response_deserializer=None):
return _StreamStreamMultiCallable(
- self._channel, _create_channel_managed_call(self._call_state), method,
- request_serializer, response_deserializer)
+ self._channel, _create_channel_managed_call(self._call_state),
+ _common.encode(method), request_serializer, response_deserializer)
def __del__(self):
_moot(self._connectivity_state)
diff --git a/src/python/grpcio/grpc/_common.py b/src/python/grpcio/grpc/_common.py
index f351bea9e3..4d7d521419 100644
--- a/src/python/grpcio/grpc/_common.py
+++ b/src/python/grpcio/grpc/_common.py
@@ -76,9 +76,37 @@ STATUS_CODE_TO_CYGRPC_STATUS_CODE = {
}
-def metadata(application_metadata):
+def encode(s):
+ if isinstance(s, bytes):
+ return s
+ else:
+ return s.encode('ascii')
+
+
+def decode(b):
+ if isinstance(b, str):
+ return b
+ else:
+ try:
+ return b.decode('utf8')
+ except UnicodeDecodeError:
+ logging.exception('Invalid encoding on {}'.format(b))
+ return b.decode('latin1')
+
+
+def cygrpc_metadata(application_metadata):
return _EMPTY_METADATA if application_metadata is None else cygrpc.Metadata(
- cygrpc.Metadatum(key, value) for key, value in application_metadata)
+ cygrpc.Metadatum(encode(key), encode(value))
+ for key, value in application_metadata)
+
+
+def application_metadata(cygrpc_metadata):
+ if cygrpc_metadata is None:
+ return ()
+ else:
+ return tuple(
+ (decode(key), value if key[-4:] == b'-bin' else decode(value))
+ for key, value in cygrpc_metadata)
def _transform(message, transformer, exception_message):
@@ -101,17 +129,8 @@ def deserialize(serialized_message, deserializer):
'Exception deserializing message!')
-def _encode(s):
- if isinstance(s, bytes):
- return s
- else:
- return s.encode('ascii')
-
-
def fully_qualified_method(group, method):
- group = _encode(group)
- method = _encode(method)
- return b'/' + group + b'/' + method
+ return '/{}/{}'.format(group, method)
class CleanupThread(threading.Thread):
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
index 866cff0d01..1406696510 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
@@ -32,9 +32,8 @@ cimport cpython
cdef class Channel:
- def __cinit__(self, target, ChannelArgs arguments=None,
+ def __cinit__(self, bytes target, ChannelArgs arguments=None,
ChannelCredentials channel_credentials=None):
- target = str_to_bytes(target)
cdef grpc_channel_args *c_arguments = NULL
cdef char *c_target = NULL
self.c_channel = NULL
@@ -57,8 +56,6 @@ cdef class Channel:
def create_call(self, Call parent, int flags,
CompletionQueue queue not None,
method, host, Timespec deadline not None):
- method = str_to_bytes(method)
- host = str_to_bytes(host)
if queue.is_shutting_down:
raise ValueError("queue must not be shutting down or shutdown")
cdef char *method_c_string = method
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
index 470382d609..b24e69243e 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
@@ -82,7 +82,7 @@ cdef class ServerCredentials:
cdef class CredentialsMetadataPlugin:
- def __cinit__(self, object plugin_callback, name):
+ def __cinit__(self, object plugin_callback, bytes name):
"""
Args:
plugin_callback (callable): Callback accepting a service URL (str/bytes)
@@ -91,9 +91,8 @@ cdef class CredentialsMetadataPlugin:
when called should be non-blocking and eventually call the callback
object with the appropriate status code/details and metadata (if
successful).
- name (str): Plugin name.
+ name (bytes): Plugin name.
"""
- name = str_to_bytes(name)
if not callable(plugin_callback):
raise ValueError('expected callable plugin_callback')
self.plugin_callback = plugin_callback
@@ -130,8 +129,7 @@ cdef void plugin_get_metadata(
grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil:
def python_callback(
Metadata metadata, grpc_status_code status,
- error_details):
- error_details = str_to_bytes(error_details)
+ bytes error_details):
cb(user_data, metadata.c_metadata_array.metadata,
metadata.c_metadata_array.count, status, error_details)
cdef CredentialsMetadataPlugin self = <CredentialsMetadataPlugin>state
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 168b9751aa..f3b3d61273 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -37,6 +37,7 @@ cdef extern from "grpc/_cython/loader.h":
ctypedef long int64_t
int pygrpc_load_core(char*)
+ int pygrpc_initialize_core()
void *gpr_malloc(size_t size) nogil
void gpr_free(void *ptr) nogil
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
index 0055d0d3a2..8e651e880f 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
@@ -231,17 +231,10 @@ cdef class Event:
cdef class ByteBuffer:
- def __cinit__(self, data):
+ def __cinit__(self, bytes data):
if data is None:
self.c_byte_buffer = NULL
return
- if isinstance(data, ByteBuffer):
- data = (<ByteBuffer>data).bytes()
- if data is None:
- self.c_byte_buffer = NULL
- return
- else:
- data = str_to_bytes(data)
cdef char *c_data = data
cdef gpr_slice data_slice
@@ -296,26 +289,28 @@ cdef class ByteBuffer:
cdef class SslPemKeyCertPair:
- def __cinit__(self, private_key, certificate_chain):
- self.private_key = str_to_bytes(private_key)
- self.certificate_chain = str_to_bytes(certificate_chain)
+ def __cinit__(self, bytes private_key, bytes certificate_chain):
+ self.private_key = private_key
+ self.certificate_chain = certificate_chain
self.c_pair.private_key = self.private_key
self.c_pair.certificate_chain = self.certificate_chain
cdef class ChannelArg:
- def __cinit__(self, key, value):
- self.key = str_to_bytes(key)
+ def __cinit__(self, bytes key, value):
+ self.key = key
self.c_arg.key = self.key
if isinstance(value, int):
- self.value = int(value)
+ self.value = value
self.c_arg.type = GRPC_ARG_INTEGER
self.c_arg.value.integer = self.value
- else:
- self.value = str_to_bytes(value)
+ elif isinstance(value, bytes):
+ self.value = value
self.c_arg.type = GRPC_ARG_STRING
self.c_arg.value.string = self.value
+ else:
+ raise TypeError('Expected int or bytes, got {}'.format(type(value)))
cdef class ChannelArgs:
@@ -347,9 +342,9 @@ cdef class ChannelArgs:
cdef class Metadatum:
- def __cinit__(self, key, value):
- self._key = str_to_bytes(key)
- self._value = str_to_bytes(value)
+ def __cinit__(self, bytes key, bytes value):
+ self._key = key
+ self._value = value
self.c_metadata.key = self._key
self.c_metadata.value = self._value
self.c_metadata.value_length = len(self._value)
@@ -563,8 +558,7 @@ def operation_send_close_from_client(int flags):
return op
def operation_send_status_from_server(
- Metadata metadata, grpc_status_code code, details, int flags):
- details = str_to_bytes(details)
+ Metadata metadata, grpc_status_code code, bytes details, int flags):
cdef Operation op = Operation()
op.c_op.type = GRPC_OP_SEND_STATUS_FROM_SERVER
op.c_op.flags = flags
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
index 42afeb8498..3e03b6efe1 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
@@ -101,7 +101,7 @@ cdef class Server:
# Ensure the core has gotten a chance to do the start-up work
self.backup_shutdown_queue.poll(Timespec(None))
- def add_http2_port(self, address,
+ def add_http2_port(self, bytes address,
ServerCredentials server_credentials=None):
address = str_to_bytes(address)
self.references.append(address)
diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx
index cf146f5a04..7a8d0dd8a1 100644
--- a/src/python/grpcio/grpc/_cython/cygrpc.pyx
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx
@@ -45,30 +45,22 @@ include "grpc/_cython/_cygrpc/security.pyx.pxi"
include "grpc/_cython/_cygrpc/server.pyx.pxi"
#
-# Global state
+# initialize gRPC
#
-cdef class _ModuleState:
- cdef bint is_loaded
+def _initialize():
+ if 'win32' in sys.platform:
+ filename = pkg_resources.resource_filename(
+ 'grpc._cython', '_windows/grpc_c.64.python')
+ if not isinstance(filename, bytes):
+ filename = filename.encode()
+ if not pygrpc_load_core(filename):
+ raise ImportError('failed to load core gRPC library')
+ if not pygrpc_initialize_core():
+ raise ImportError('failed to initialize core gRPC library')
- def __cinit__(self):
- if 'win32' in sys.platform:
- filename = pkg_resources.resource_filename(
- 'grpc._cython', '_windows/grpc_c.64.python')
- if not pygrpc_load_core(filename):
- raise ImportError('failed to load core gRPC library')
- with nogil:
- grpc_init()
- self.is_loaded = True
- with nogil:
- grpc_set_ssl_roots_override_callback(
+ grpc_set_ssl_roots_override_callback(
<grpc_ssl_roots_override_callback>ssl_roots_override_callback)
- def __dealloc__(self):
- if self.is_loaded:
- with nogil:
- grpc_shutdown()
-
-_module_state = _ModuleState()
-
+_initialize()
diff --git a/src/python/grpcio/grpc/_cython/imports.generated.c b/src/python/grpcio/grpc/_cython/imports.generated.c
index 8437e74ba0..d78ec2f66e 100644
--- a/src/python/grpcio/grpc/_cython/imports.generated.c
+++ b/src/python/grpcio/grpc/_cython/imports.generated.c
@@ -128,6 +128,7 @@ grpc_is_binary_header_type grpc_is_binary_header_import;
grpc_call_error_to_string_type grpc_call_error_to_string_import;
grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import;
grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import;
+grpc_use_signal_type grpc_use_signal_import;
grpc_auth_property_iterator_next_type grpc_auth_property_iterator_next_import;
grpc_auth_context_property_iterator_type grpc_auth_context_property_iterator_import;
grpc_auth_context_peer_identity_type grpc_auth_context_peer_identity_import;
@@ -403,6 +404,7 @@ void pygrpc_load_imports(HMODULE library) {
grpc_call_error_to_string_import = (grpc_call_error_to_string_type) GetProcAddress(library, "grpc_call_error_to_string");
grpc_insecure_channel_create_from_fd_import = (grpc_insecure_channel_create_from_fd_type) GetProcAddress(library, "grpc_insecure_channel_create_from_fd");
grpc_server_add_insecure_channel_from_fd_import = (grpc_server_add_insecure_channel_from_fd_type) GetProcAddress(library, "grpc_server_add_insecure_channel_from_fd");
+ grpc_use_signal_import = (grpc_use_signal_type) GetProcAddress(library, "grpc_use_signal");
grpc_auth_property_iterator_next_import = (grpc_auth_property_iterator_next_type) GetProcAddress(library, "grpc_auth_property_iterator_next");
grpc_auth_context_property_iterator_import = (grpc_auth_context_property_iterator_type) GetProcAddress(library, "grpc_auth_context_property_iterator");
grpc_auth_context_peer_identity_import = (grpc_auth_context_peer_identity_type) GetProcAddress(library, "grpc_auth_context_peer_identity");
diff --git a/src/python/grpcio/grpc/_cython/imports.generated.h b/src/python/grpcio/grpc/_cython/imports.generated.h
index d52e8591b3..b3e341fe25 100644
--- a/src/python/grpcio/grpc/_cython/imports.generated.h
+++ b/src/python/grpcio/grpc/_cython/imports.generated.h
@@ -335,6 +335,9 @@ extern grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_fr
typedef void(*grpc_server_add_insecure_channel_from_fd_type)(grpc_server *server, grpc_completion_queue *cq, int fd);
extern grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import;
#define grpc_server_add_insecure_channel_from_fd grpc_server_add_insecure_channel_from_fd_import
+typedef void(*grpc_use_signal_type)(int signum);
+extern grpc_use_signal_type grpc_use_signal_import;
+#define grpc_use_signal grpc_use_signal_import
typedef const grpc_auth_property *(*grpc_auth_property_iterator_next_type)(grpc_auth_property_iterator *it);
extern grpc_auth_property_iterator_next_type grpc_auth_property_iterator_next_import;
#define grpc_auth_property_iterator_next grpc_auth_property_iterator_next_import
diff --git a/src/python/grpcio/grpc/_cython/loader.c b/src/python/grpcio/grpc/_cython/loader.c
index b909ad594e..86b70dbb02 100644
--- a/src/python/grpcio/grpc/_cython/loader.c
+++ b/src/python/grpcio/grpc/_cython/loader.c
@@ -31,6 +31,7 @@
*
*/
+#include <Python.h>
#include "loader.h"
#ifdef __cplusplus
@@ -62,6 +63,12 @@ int pygrpc_load_core(char *path) { return 1; }
#endif /* !GPR_WINDOWS */
+// Cython doesn't have Py_AtExit bindings, so we call the C_API directly
+int pygrpc_initialize_core(void) {
+ grpc_init();
+ return Py_AtExit(grpc_shutdown) < 0 ? 0 : 1;
+}
+
#ifdef __cplusplus
}
#endif /* __cpluslus */
diff --git a/src/python/grpcio/grpc/_cython/loader.h b/src/python/grpcio/grpc/_cython/loader.h
index 3b8796d39f..eb4b1a1b01 100644
--- a/src/python/grpcio/grpc/_cython/loader.h
+++ b/src/python/grpcio/grpc/_cython/loader.h
@@ -46,6 +46,11 @@ extern "C" {
/* Attempts to load the core if necessary, and return non-zero upon succes. */
int pygrpc_load_core(char *path);
+/* Initializes grpc and registers grpc_shutdown() to be called right before
+ * interpreter exit. Returns non-zero upon success.
+ */
+int pygrpc_initialize_core(void);
+
#ifdef __cplusplus
}
#endif /* __cpluslus */
diff --git a/src/python/grpcio/grpc/_plugin_wrapping.py b/src/python/grpcio/grpc/_plugin_wrapping.py
index 4e9cfe710c..7cb5218c22 100644
--- a/src/python/grpcio/grpc/_plugin_wrapping.py
+++ b/src/python/grpcio/grpc/_plugin_wrapping.py
@@ -31,6 +31,7 @@ import collections
import threading
import grpc
+from grpc import _common
from grpc._cython import cygrpc
@@ -62,17 +63,16 @@ class _WrappedCygrpcCallback(object):
# TODO(atash) translate different Exception superclasses into different
# status codes.
self.cygrpc_callback(
- cygrpc.Metadata([]), cygrpc.StatusCode.internal, error.message)
+ _common.EMPTY_METADATA, cygrpc.StatusCode.internal,
+ _common.encode(str(error)))
def _invoke_success(self, metadata):
try:
- cygrpc_metadata = cygrpc.Metadata(
- cygrpc.Metadatum(key, value)
- for key, value in metadata)
+ cygrpc_metadata = _common.cygrpc_metadata(metadata)
except Exception as error:
self._invoke_failure(error)
return
- self.cygrpc_callback(cygrpc_metadata, cygrpc.StatusCode.ok, '')
+ self.cygrpc_callback(cygrpc_metadata, cygrpc.StatusCode.ok, b'')
def __call__(self, metadata, error):
with self.is_called_lock:
@@ -101,7 +101,7 @@ class _WrappedPlugin(object):
def __call__(self, context, cygrpc_callback):
wrapped_cygrpc_callback = _WrappedCygrpcCallback(cygrpc_callback)
wrapped_context = AuthMetadataContext(
- context.service_url, context.method_name)
+ _common.decode(context.service_url), _common.decode(context.method_name))
try:
self.plugin(
wrapped_context, AuthMetadataPluginCallback(wrapped_cygrpc_callback))
@@ -120,4 +120,4 @@ def call_credentials_metadata_plugin(plugin, name):
plugin's invocation must be non-blocking.
"""
return cygrpc.call_credentials_metadata_plugin(
- cygrpc.CredentialsMetadataPlugin(_WrappedPlugin(plugin), name))
+ cygrpc.CredentialsMetadataPlugin(_WrappedPlugin(plugin), _common.encode(name)))
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index bf20f15f72..f4c114056f 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -87,7 +87,7 @@ def _abortion_code(state, code):
def _details(state):
- return '' if state.details is None else state.details
+ return b'' if state.details is None else state.details
class _HandlerCallDetails(
@@ -146,14 +146,14 @@ def _abort(state, call, code, details):
cygrpc.operation_send_initial_metadata(
_EMPTY_METADATA, _EMPTY_FLAGS),
cygrpc.operation_send_status_from_server(
- _common.metadata(state.trailing_metadata), effective_code,
+ _common.cygrpc_metadata(state.trailing_metadata), effective_code,
effective_details, _EMPTY_FLAGS),
)
token = _SEND_INITIAL_METADATA_AND_SEND_STATUS_FROM_SERVER_TOKEN
else:
operations = (
cygrpc.operation_send_status_from_server(
- _common.metadata(state.trailing_metadata), effective_code,
+ _common.cygrpc_metadata(state.trailing_metadata), effective_code,
effective_details, _EMPTY_FLAGS),
)
token = _SEND_STATUS_FROM_SERVER_TOKEN
@@ -191,7 +191,7 @@ def _receive_message(state, call, request_deserializer):
if request is None:
_abort(
state, call, cygrpc.StatusCode.internal,
- 'Exception deserializing request!')
+ b'Exception deserializing request!')
else:
state.request = request
state.condition.notify_all()
@@ -244,10 +244,10 @@ class _Context(grpc.ServicerContext):
self._state.disable_next_compression = True
def invocation_metadata(self):
- return self._rpc_event.request_metadata
+ return _common.application_metadata(self._rpc_event.request_metadata)
def peer(self):
- return self._rpc_event.operation_call.peer()
+ return _common.decode(self._rpc_event.operation_call.peer())
def send_initial_metadata(self, initial_metadata):
with self._state.condition:
@@ -256,7 +256,7 @@ class _Context(grpc.ServicerContext):
else:
if self._state.initial_metadata_allowed:
operation = cygrpc.operation_send_initial_metadata(
- _common.metadata(initial_metadata), _EMPTY_FLAGS)
+ _common.cygrpc_metadata(initial_metadata), _EMPTY_FLAGS)
self._rpc_event.operation_call.start_batch(
cygrpc.Operations((operation,)),
_send_initial_metadata(self._state))
@@ -267,7 +267,8 @@ class _Context(grpc.ServicerContext):
def set_trailing_metadata(self, trailing_metadata):
with self._state.condition:
- self._state.trailing_metadata = trailing_metadata
+ self._state.trailing_metadata = _common.cygrpc_metadata(
+ trailing_metadata)
def set_code(self, code):
with self._state.condition:
@@ -275,7 +276,7 @@ class _Context(grpc.ServicerContext):
def set_details(self, details):
with self._state.condition:
- self._state.details = details
+ self._state.details = _common.encode(details)
class _RequestIterator(object):
@@ -346,7 +347,7 @@ def _unary_request(rpc_event, state, request_deserializer):
rpc_event.request_call_details.method)
_abort(
state, rpc_event.operation_call,
- cygrpc.StatusCode.unimplemented, details)
+ cygrpc.StatusCode.unimplemented, _common.encode(details))
return None
elif state.client is _CANCELLED:
return None
@@ -366,8 +367,8 @@ def _call_behavior(rpc_event, state, behavior, argument, request_deserializer):
if e not in state.rpc_errors:
details = 'Exception calling application: {}'.format(e)
logging.exception(details)
- _abort(
- state, rpc_event.operation_call, cygrpc.StatusCode.unknown, details)
+ _abort(state, rpc_event.operation_call,
+ cygrpc.StatusCode.unknown, _common.encode(details))
return None, False
@@ -381,8 +382,8 @@ def _take_response_from_response_iterator(rpc_event, state, response_iterator):
if e not in state.rpc_errors:
details = 'Exception iterating responses: {}'.format(e)
logging.exception(details)
- _abort(
- state, rpc_event.operation_call, cygrpc.StatusCode.unknown, details)
+ _abort(state, rpc_event.operation_call,
+ cygrpc.StatusCode.unknown, _common.encode(details))
return None, False
@@ -392,7 +393,7 @@ def _serialize_response(rpc_event, state, response, response_serializer):
with state.condition:
_abort(
state, rpc_event.operation_call, cygrpc.StatusCode.internal,
- 'Failed to serialize response!')
+ b'Failed to serialize response!')
return None
else:
return serialized_response
@@ -428,7 +429,7 @@ def _send_response(rpc_event, state, serialized_response):
def _status(rpc_event, state, serialized_response):
with state.condition:
if state.client is not _CANCELLED:
- trailing_metadata = _common.metadata(state.trailing_metadata)
+ trailing_metadata = _common.cygrpc_metadata(state.trailing_metadata)
code = _completion_code(state)
details = _details(state)
operations = [
@@ -532,7 +533,8 @@ def _find_method_handler(rpc_event, generic_handlers):
for generic_handler in generic_handlers:
method_handler = generic_handler.service(
_HandlerCallDetails(
- rpc_event.request_call_details.method, rpc_event.request_metadata))
+ _common.decode(rpc_event.request_call_details.method),
+ rpc_event.request_metadata))
if method_handler is not None:
return method_handler
else:
@@ -545,7 +547,7 @@ def _handle_unrecognized_method(rpc_event):
cygrpc.operation_receive_close_on_server(_EMPTY_FLAGS),
cygrpc.operation_send_status_from_server(
_EMPTY_METADATA, cygrpc.StatusCode.unimplemented,
- 'Method not found!', _EMPTY_FLAGS),
+ b'Method not found!', _EMPTY_FLAGS),
)
rpc_state = _RPCState()
rpc_event.operation_call.start_batch(
@@ -740,10 +742,10 @@ class Server(grpc.Server):
_add_generic_handlers(self._state, generic_rpc_handlers)
def add_insecure_port(self, address):
- return _add_insecure_port(self._state, address)
+ return _add_insecure_port(self._state, _common.encode(address))
def add_secure_port(self, address, server_credentials):
- return _add_secure_port(self._state, address, server_credentials)
+ return _add_secure_port(self._state, _common.encode(address), server_credentials)
def start(self):
_start(self._state)
diff --git a/src/python/grpcio/grpc/beta/_server_adaptations.py b/src/python/grpcio/grpc/beta/_server_adaptations.py
index 79e6ca87eb..1e1f80156a 100644
--- a/src/python/grpcio/grpc/beta/_server_adaptations.py
+++ b/src/python/grpcio/grpc/beta/_server_adaptations.py
@@ -79,7 +79,8 @@ class _FaceServicerContext(face.ServicerContext):
return _ServerProtocolContext(self._servicer_context)
def invocation_metadata(self):
- return self._servicer_context.invocation_metadata()
+ return _common.cygrpc_metadata(
+ self._servicer_context.invocation_metadata())
def initial_metadata(self, initial_metadata):
self._servicer_context.send_initial_metadata(initial_metadata)
@@ -161,14 +162,24 @@ class _Callback(stream.Consumer):
self._condition.wait()
-def _pipe_requests(request_iterator, request_consumer, servicer_context):
- for request in request_iterator:
- if not servicer_context.is_active():
- return
- request_consumer.consume(request)
- if not servicer_context.is_active():
- return
- request_consumer.terminate()
+def _run_request_pipe_thread(request_iterator, request_consumer,
+ servicer_context):
+ thread_joined = threading.Event()
+ def pipe_requests():
+ for request in request_iterator:
+ if not servicer_context.is_active() or thread_joined.is_set():
+ return
+ request_consumer.consume(request)
+ if not servicer_context.is_active() or thread_joined.is_set():
+ return
+ request_consumer.terminate()
+
+ def stop_request_pipe(timeout):
+ thread_joined.set()
+
+ request_pipe_thread = _common.CleanupThread(
+ stop_request_pipe, target=pipe_requests)
+ request_pipe_thread.start()
def _adapt_unary_unary_event(unary_unary_event):
@@ -206,10 +217,8 @@ def _adapt_stream_unary_event(stream_unary_event):
raise abandonment.Abandoned()
request_consumer = stream_unary_event(
callback.consume_and_terminate, _FaceServicerContext(servicer_context))
- request_pipe_thread = threading.Thread(
- target=_pipe_requests,
- args=(request_iterator, request_consumer, servicer_context,))
- request_pipe_thread.start()
+ _run_request_pipe_thread(
+ request_iterator, request_consumer, servicer_context)
return callback.draw_all_values()[0]
return adaptation
@@ -221,10 +230,8 @@ def _adapt_stream_stream_event(stream_stream_event):
raise abandonment.Abandoned()
request_consumer = stream_stream_event(
callback, _FaceServicerContext(servicer_context))
- request_pipe_thread = threading.Thread(
- target=_pipe_requests,
- args=(request_iterator, request_consumer, servicer_context,))
- request_pipe_thread.start()
+ _run_request_pipe_thread(
+ request_iterator, request_consumer, servicer_context)
while True:
response = callback.draw_one_value()
if response is None:
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index 839c555f05..b37e27c27e 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -94,6 +94,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/iomgr/endpoint_pair_posix.c',
'src/core/lib/iomgr/endpoint_pair_windows.c',
'src/core/lib/iomgr/error.c',
+ 'src/core/lib/iomgr/ev_epoll_linux.c',
'src/core/lib/iomgr/ev_poll_and_epoll_posix.c',
'src/core/lib/iomgr/ev_poll_posix.c',
'src/core/lib/iomgr/ev_posix.c',
@@ -104,6 +105,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/iomgr/iomgr_posix.c',
'src/core/lib/iomgr/iomgr_windows.c',
'src/core/lib/iomgr/load_file.c',
+ 'src/core/lib/iomgr/network_status_tracker.c',
'src/core/lib/iomgr/polling_entity.c',
'src/core/lib/iomgr/pollset_set_windows.c',
'src/core/lib/iomgr/pollset_windows.c',
diff --git a/src/python/grpcio/grpc_version.py b/src/python/grpcio/grpc_version.py
index 0c13104d9d..0f4db9d972 100644
--- a/src/python/grpcio/grpc_version.py
+++ b/src/python/grpcio/grpc_version.py
@@ -29,4 +29,4 @@
# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_version.py.template`!!!
-VERSION='0.15.0.dev0'
+VERSION='0.16.0.dev0'
diff --git a/src/python/grpcio/tests/interop/methods.py b/src/python/grpcio/tests/interop/methods.py
index 7eac511525..86aa0495a2 100644
--- a/src/python/grpcio/tests/interop/methods.py
+++ b/src/python/grpcio/tests/interop/methods.py
@@ -79,10 +79,11 @@ class TestService(test_pb2.BetaTestServiceServicer):
def FullDuplexCall(self, request_iterator, context):
for request in request_iterator:
- yield messages_pb2.StreamingOutputCallResponse(
- payload=messages_pb2.Payload(
- type=request.payload.type,
- body=b'\x00' * request.response_parameters[0].size))
+ for response_parameters in request.response_parameters:
+ yield messages_pb2.StreamingOutputCallResponse(
+ payload=messages_pb2.Payload(
+ type=request.payload.type,
+ body=b'\x00' * response_parameters.size))
# NOTE(nathaniel): Apparently this is the same as the full-duplex call?
# NOTE(atash): It isn't even called in the interop spec (Oct 22 2015)...
diff --git a/src/python/grpcio/tests/tests.json b/src/python/grpcio/tests/tests.json
index 8e509621a8..45eb75b242 100644
--- a/src/python/grpcio/tests/tests.json
+++ b/src/python/grpcio/tests/tests.json
@@ -10,9 +10,10 @@
"_channel_connectivity_test.ChannelConnectivityTest",
"_channel_ready_future_test.ChannelReadyFutureTest",
"_channel_test.ChannelTest",
- "_connectivity_channel_test.ChannelConnectivityTest",
+ "_compression_test.CompressionTest",
"_connectivity_channel_test.ConnectivityStatesTest",
"_empty_message_test.EmptyMessageTest",
+ "_exit_test.ExitTest",
"_face_interface_test.DynamicInvokerBlockingInvocationInlineServiceTest",
"_face_interface_test.DynamicInvokerFutureInvocationAsynchronousEventServiceTest",
"_face_interface_test.GenericInvokerBlockingInvocationInlineServiceTest",
@@ -24,6 +25,7 @@
"_implementations_test.ChannelCredentialsTest",
"_insecure_interop_test.InsecureInteropTest",
"_logging_pool_test.LoggingPoolTest",
+ "_metadata_code_details_test.MetadataCodeDetailsTest",
"_metadata_test.MetadataTest",
"_not_found_test.NotFoundTest",
"_python_plugin_test.PythonPluginTest",
diff --git a/src/python/grpcio/tests/unit/_compression_test.py b/src/python/grpcio/tests/unit/_compression_test.py
new file mode 100644
index 0000000000..9e8b8578c1
--- /dev/null
+++ b/src/python/grpcio/tests/unit/_compression_test.py
@@ -0,0 +1,133 @@
+# Copyright 2016, 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.
+"""Tests server and client side compression."""
+
+import unittest
+
+import grpc
+from grpc import _grpcio_metadata
+from grpc.framework.foundation import logging_pool
+
+from tests.unit import test_common
+from tests.unit.framework.common import test_constants
+
+_UNARY_UNARY = '/test/UnaryUnary'
+_STREAM_STREAM = '/test/StreamStream'
+
+
+def handle_unary(request, servicer_context):
+ servicer_context.send_initial_metadata([
+ ('grpc-internal-encoding-request', 'gzip')])
+ return request
+
+
+def handle_stream(request_iterator, servicer_context):
+ # TODO(issue:#6891) We should be able to remove this loop,
+ # and replace with return; yield
+ servicer_context.send_initial_metadata([
+ ('grpc-internal-encoding-request', 'gzip')])
+ for request in request_iterator:
+ yield request
+
+
+class _MethodHandler(grpc.RpcMethodHandler):
+
+ def __init__(self, request_streaming, response_streaming):
+ self.request_streaming = request_streaming
+ self.response_streaming = response_streaming
+ self.request_deserializer = None
+ self.response_serializer = None
+ self.unary_unary = None
+ self.unary_stream = None
+ self.stream_unary = None
+ self.stream_stream = None
+ if self.request_streaming and self.response_streaming:
+ self.stream_stream = lambda x, y: handle_stream(x, y)
+ elif not self.request_streaming and not self.response_streaming:
+ self.unary_unary = lambda x, y: handle_unary(x, y)
+
+
+class _GenericHandler(grpc.GenericRpcHandler):
+
+ def service(self, handler_call_details):
+ if handler_call_details.method == _UNARY_UNARY:
+ return _MethodHandler(False, False)
+ elif handler_call_details.method == _STREAM_STREAM:
+ return _MethodHandler(True, True)
+ else:
+ return None
+
+
+class CompressionTest(unittest.TestCase):
+
+ def setUp(self):
+ self._server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
+ self._server = grpc.server((_GenericHandler(),), self._server_pool)
+ self._port = self._server.add_insecure_port('[::]:0')
+ self._server.start()
+
+ def testUnary(self):
+ request = b'\x00' * 100
+
+ # Client -> server compressed through default client channel compression
+ # settings. Server -> client compressed via server-side metadata setting.
+ # TODO(https://github.com/grpc/grpc/issues/4078): replace the "1" integer
+ # literal with proper use of the public API.
+ compressed_channel = grpc.insecure_channel('localhost:%d' % self._port,
+ options=[('grpc.default_compression_algorithm', 1)])
+ multi_callable = compressed_channel.unary_unary(_UNARY_UNARY)
+ response = multi_callable(request)
+ self.assertEqual(request, response)
+
+ # Client -> server compressed through client metadata setting. Server ->
+ # client compressed via server-side metadata setting.
+ # TODO(https://github.com/grpc/grpc/issues/4078): replace the "0" integer
+ # literal with proper use of the public API.
+ uncompressed_channel = grpc.insecure_channel('localhost:%d' % self._port,
+ options=[('grpc.default_compression_algorithm', 0)])
+ multi_callable = compressed_channel.unary_unary(_UNARY_UNARY)
+ response = multi_callable(request, metadata=[
+ ('grpc-internal-encoding-request', 'gzip')])
+ self.assertEqual(request, response)
+
+ def testStreaming(self):
+ request = b'\x00' * 100
+
+ # TODO(https://github.com/grpc/grpc/issues/4078): replace the "1" integer
+ # literal with proper use of the public API.
+ compressed_channel = grpc.insecure_channel('localhost:%d' % self._port,
+ options=[('grpc.default_compression_algorithm', 1)])
+ multi_callable = compressed_channel.stream_stream(_STREAM_STREAM)
+ call = multi_callable([request] * test_constants.STREAM_LENGTH)
+ for response in call:
+ self.assertEqual(request, response)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio/tests/unit/_cython/_cancel_many_calls_test.py b/src/python/grpcio/tests/unit/_cython/_cancel_many_calls_test.py
index c1de779014..cac0c8b3b9 100644
--- a/src/python/grpcio/tests/unit/_cython/_cancel_many_calls_test.py
+++ b/src/python/grpcio/tests/unit/_cython/_cancel_many_calls_test.py
@@ -159,9 +159,9 @@ class CancelManyCallsTest(unittest.TestCase):
server_completion_queue = cygrpc.CompletionQueue()
server = cygrpc.Server()
server.register_completion_queue(server_completion_queue)
- port = server.add_http2_port('[::]:0')
+ port = server.add_http2_port(b'[::]:0')
server.start()
- channel = cygrpc.Channel('localhost:{}'.format(port))
+ channel = cygrpc.Channel('localhost:{}'.format(port).encode())
state = _State()
diff --git a/src/python/grpcio/tests/unit/_cython/_channel_test.py b/src/python/grpcio/tests/unit/_cython/_channel_test.py
index 3dc7a246ae..f9c8a3ac62 100644
--- a/src/python/grpcio/tests/unit/_cython/_channel_test.py
+++ b/src/python/grpcio/tests/unit/_cython/_channel_test.py
@@ -37,7 +37,7 @@ from tests.unit.framework.common import test_constants
def _channel_and_completion_queue():
- channel = cygrpc.Channel('localhost:54321', cygrpc.ChannelArgs(()))
+ channel = cygrpc.Channel(b'localhost:54321', cygrpc.ChannelArgs(()))
completion_queue = cygrpc.CompletionQueue()
return channel, completion_queue
diff --git a/src/python/grpcio/tests/unit/_cython/_read_some_but_not_all_responses_test.py b/src/python/grpcio/tests/unit/_cython/_read_some_but_not_all_responses_test.py
index 6ae7a90fbe..27fcee0d6f 100644
--- a/src/python/grpcio/tests/unit/_cython/_read_some_but_not_all_responses_test.py
+++ b/src/python/grpcio/tests/unit/_cython/_read_some_but_not_all_responses_test.py
@@ -126,9 +126,9 @@ class ReadSomeButNotAllResponsesTest(unittest.TestCase):
server_completion_queue = cygrpc.CompletionQueue()
server = cygrpc.Server()
server.register_completion_queue(server_completion_queue)
- port = server.add_http2_port('[::]:0')
+ port = server.add_http2_port(b'[::]:0')
server.start()
- channel = cygrpc.Channel('localhost:{}'.format(port))
+ channel = cygrpc.Channel('localhost:{}'.format(port).encode())
server_shutdown_tag = 'server_shutdown_tag'
server_driver = _ServerDriver(server_completion_queue, server_shutdown_tag)
diff --git a/src/python/grpcio/tests/unit/_cython/cygrpc_test.py b/src/python/grpcio/tests/unit/_cython/cygrpc_test.py
index a006a20ce3..b740695e35 100644
--- a/src/python/grpcio/tests/unit/_cython/cygrpc_test.py
+++ b/src/python/grpcio/tests/unit/_cython/cygrpc_test.py
@@ -46,38 +46,38 @@ def _metadata_plugin_callback(context, callback):
callback(cygrpc.Metadata(
[cygrpc.Metadatum(_CALL_CREDENTIALS_METADATA_KEY,
_CALL_CREDENTIALS_METADATA_VALUE)]),
- cygrpc.StatusCode.ok, '')
+ cygrpc.StatusCode.ok, b'')
class TypeSmokeTest(unittest.TestCase):
def testStringsInUtilitiesUpDown(self):
self.assertEqual(0, cygrpc.StatusCode.ok)
- metadatum = cygrpc.Metadatum('a', 'b')
- self.assertEqual('a'.encode(), metadatum.key)
- self.assertEqual('b'.encode(), metadatum.value)
+ metadatum = cygrpc.Metadatum(b'a', b'b')
+ self.assertEqual(b'a', metadatum.key)
+ self.assertEqual(b'b', metadatum.value)
metadata = cygrpc.Metadata([metadatum])
self.assertEqual(1, len(metadata))
self.assertEqual(metadatum.key, metadata[0].key)
def testMetadataIteration(self):
metadata = cygrpc.Metadata([
- cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')])
+ cygrpc.Metadatum(b'a', b'b'), cygrpc.Metadatum(b'c', b'd')])
iterator = iter(metadata)
metadatum = next(iterator)
self.assertIsInstance(metadatum, cygrpc.Metadatum)
- self.assertEqual(metadatum.key, 'a'.encode())
- self.assertEqual(metadatum.value, 'b'.encode())
+ self.assertEqual(metadatum.key, b'a')
+ self.assertEqual(metadatum.value, b'b')
metadatum = next(iterator)
self.assertIsInstance(metadatum, cygrpc.Metadatum)
- self.assertEqual(metadatum.key, 'c'.encode())
- self.assertEqual(metadatum.value, 'd'.encode())
+ self.assertEqual(metadatum.key, b'c')
+ self.assertEqual(metadatum.value, b'd')
with self.assertRaises(StopIteration):
next(iterator)
def testOperationsIteration(self):
operations = cygrpc.Operations([
- cygrpc.operation_send_message('asdf', _EMPTY_FLAGS)])
+ cygrpc.operation_send_message(b'asdf', _EMPTY_FLAGS)])
iterator = iter(operations)
operation = next(iterator)
self.assertIsInstance(operation, cygrpc.Operation)
@@ -87,7 +87,7 @@ class TypeSmokeTest(unittest.TestCase):
next(iterator)
def testOperationFlags(self):
- operation = cygrpc.operation_send_message('asdf',
+ operation = cygrpc.operation_send_message(b'asdf',
cygrpc.WriteFlag.no_compress)
self.assertEqual(cygrpc.WriteFlag.no_compress, operation.flags)
@@ -105,16 +105,16 @@ class TypeSmokeTest(unittest.TestCase):
del server
def testChannelUpDown(self):
- channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([]))
+ channel = cygrpc.Channel(b'[::]:0', cygrpc.ChannelArgs([]))
del channel
def testCredentialsMetadataPluginUpDown(self):
plugin = cygrpc.CredentialsMetadataPlugin(
- lambda ignored_a, ignored_b: None, '')
+ lambda ignored_a, ignored_b: None, b'')
del plugin
def testCallCredentialsFromPluginUpDown(self):
- plugin = cygrpc.CredentialsMetadataPlugin(_metadata_plugin_callback, '')
+ plugin = cygrpc.CredentialsMetadataPlugin(_metadata_plugin_callback, b'')
call_credentials = cygrpc.call_credentials_metadata_plugin(plugin)
del plugin
del call_credentials
@@ -123,7 +123,7 @@ class TypeSmokeTest(unittest.TestCase):
server = cygrpc.Server()
completion_queue = cygrpc.CompletionQueue()
server.register_completion_queue(completion_queue)
- port = server.add_http2_port('[::]:0')
+ port = server.add_http2_port(b'[::]:0')
self.assertIsInstance(port, int)
server.start()
del server
@@ -131,7 +131,7 @@ class TypeSmokeTest(unittest.TestCase):
def testServerStartShutdown(self):
completion_queue = cygrpc.CompletionQueue()
server = cygrpc.Server()
- server.add_http2_port('[::]:0')
+ server.add_http2_port(b'[::]:0')
server.register_completion_queue(completion_queue)
server.start()
shutdown_tag = object()
@@ -150,9 +150,9 @@ class ServerClientMixin(object):
self.server = cygrpc.Server()
self.server.register_completion_queue(self.server_completion_queue)
if server_credentials:
- self.port = self.server.add_http2_port('[::]:0', server_credentials)
+ self.port = self.server.add_http2_port(b'[::]:0', server_credentials)
else:
- self.port = self.server.add_http2_port('[::]:0')
+ self.port = self.server.add_http2_port(b'[::]:0')
self.server.start()
self.client_completion_queue = cygrpc.CompletionQueue()
if client_credentials:
@@ -160,10 +160,10 @@ class ServerClientMixin(object):
cygrpc.ChannelArg(cygrpc.ChannelArgKey.ssl_target_name_override,
host_override)])
self.client_channel = cygrpc.Channel(
- 'localhost:{}'.format(self.port), client_channel_arguments,
+ 'localhost:{}'.format(self.port).encode(), client_channel_arguments,
client_credentials)
else:
- self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port))
+ self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port).encode())
if host_override:
self.host_argument = None # default host
self.expected_host = host_override
diff --git a/src/python/grpcio/tests/unit/_empty_message_test.py b/src/python/grpcio/tests/unit/_empty_message_test.py
index f324f6216b..8c7d697728 100644
--- a/src/python/grpcio/tests/unit/_empty_message_test.py
+++ b/src/python/grpcio/tests/unit/_empty_message_test.py
@@ -37,10 +37,10 @@ from tests.unit.framework.common import test_constants
_REQUEST = b''
_RESPONSE = b''
-_UNARY_UNARY = b'/test/UnaryUnary'
-_UNARY_STREAM = b'/test/UnaryStream'
-_STREAM_UNARY = b'/test/StreamUnary'
-_STREAM_STREAM = b'/test/StreamStream'
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
def handle_unary_unary(request, servicer_context):
diff --git a/src/python/grpcio/tests/unit/_exit_scenarios.py b/src/python/grpcio/tests/unit/_exit_scenarios.py
new file mode 100644
index 0000000000..24a2faef85
--- /dev/null
+++ b/src/python/grpcio/tests/unit/_exit_scenarios.py
@@ -0,0 +1,249 @@
+# Copyright 2016, 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.
+
+"""Defines a number of module-scope gRPC scenarios to test clean exit."""
+
+import argparse
+import threading
+import time
+
+import grpc
+
+from tests.unit.framework.common import test_constants
+
+WAIT_TIME = 1000
+
+REQUEST = b'request'
+
+UNSTARTED_SERVER = 'unstarted_server'
+RUNNING_SERVER = 'running_server'
+POLL_CONNECTIVITY_NO_SERVER = 'poll_connectivity_no_server'
+POLL_CONNECTIVITY = 'poll_connectivity'
+IN_FLIGHT_UNARY_UNARY_CALL = 'in_flight_unary_unary_call'
+IN_FLIGHT_UNARY_STREAM_CALL = 'in_flight_unary_stream_call'
+IN_FLIGHT_STREAM_UNARY_CALL = 'in_flight_stream_unary_call'
+IN_FLIGHT_STREAM_STREAM_CALL = 'in_flight_stream_stream_call'
+IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL = 'in_flight_partial_unary_stream_call'
+IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL = 'in_flight_partial_stream_unary_call'
+IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL = 'in_flight_partial_stream_stream_call'
+
+UNARY_UNARY = b'/test/UnaryUnary'
+UNARY_STREAM = b'/test/UnaryStream'
+STREAM_UNARY = b'/test/StreamUnary'
+STREAM_STREAM = b'/test/StreamStream'
+PARTIAL_UNARY_STREAM = b'/test/PartialUnaryStream'
+PARTIAL_STREAM_UNARY = b'/test/PartialStreamUnary'
+PARTIAL_STREAM_STREAM = b'/test/PartialStreamStream'
+
+TEST_TO_METHOD = {
+ IN_FLIGHT_UNARY_UNARY_CALL: UNARY_UNARY,
+ IN_FLIGHT_UNARY_STREAM_CALL: UNARY_STREAM,
+ IN_FLIGHT_STREAM_UNARY_CALL: STREAM_UNARY,
+ IN_FLIGHT_STREAM_STREAM_CALL: STREAM_STREAM,
+ IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL: PARTIAL_UNARY_STREAM,
+ IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL: PARTIAL_STREAM_UNARY,
+ IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL: PARTIAL_STREAM_STREAM,
+}
+
+
+def hang_unary_unary(request, servicer_context):
+ time.sleep(WAIT_TIME)
+
+
+def hang_unary_stream(request, servicer_context):
+ time.sleep(WAIT_TIME)
+
+
+def hang_partial_unary_stream(request, servicer_context):
+ for _ in range(test_constants.STREAM_LENGTH // 2):
+ yield request
+ time.sleep(WAIT_TIME)
+
+
+def hang_stream_unary(request_iterator, servicer_context):
+ time.sleep(WAIT_TIME)
+
+
+def hang_partial_stream_unary(request_iterator, servicer_context):
+ for _ in range(test_constants.STREAM_LENGTH // 2):
+ next(request_iterator)
+ time.sleep(WAIT_TIME)
+
+
+def hang_stream_stream(request_iterator, servicer_context):
+ time.sleep(WAIT_TIME)
+
+
+def hang_partial_stream_stream(request_iterator, servicer_context):
+ for _ in range(test_constants.STREAM_LENGTH // 2):
+ yield next(request_iterator)
+ time.sleep(WAIT_TIME)
+
+
+class MethodHandler(grpc.RpcMethodHandler):
+
+ def __init__(self, request_streaming, response_streaming, partial_hang):
+ self.request_streaming = request_streaming
+ self.response_streaming = response_streaming
+ self.request_deserializer = None
+ self.response_serializer = None
+ self.unary_unary = None
+ self.unary_stream = None
+ self.stream_unary = None
+ self.stream_stream = None
+ if self.request_streaming and self.response_streaming:
+ if partial_hang:
+ self.stream_stream = hang_partial_stream_stream
+ else:
+ self.stream_stream = hang_stream_stream
+ elif self.request_streaming:
+ if partial_hang:
+ self.stream_unary = hang_partial_stream_unary
+ else:
+ self.stream_unary = hang_stream_unary
+ elif self.response_streaming:
+ if partial_hang:
+ self.unary_stream = hang_partial_unary_stream
+ else:
+ self.unary_stream = hang_unary_stream
+ else:
+ self.unary_unary = hang_unary_unary
+
+
+class GenericHandler(grpc.GenericRpcHandler):
+
+ def service(self, handler_call_details):
+ if handler_call_details.method == UNARY_UNARY:
+ return MethodHandler(False, False, False)
+ elif handler_call_details.method == UNARY_STREAM:
+ return MethodHandler(False, True, False)
+ elif handler_call_details.method == STREAM_UNARY:
+ return MethodHandler(True, False, False)
+ elif handler_call_details.method == STREAM_STREAM:
+ return MethodHandler(True, True, False)
+ elif handler_call_details.method == PARTIAL_UNARY_STREAM:
+ return MethodHandler(False, True, True)
+ elif handler_call_details.method == PARTIAL_STREAM_UNARY:
+ return MethodHandler(True, False, True)
+ elif handler_call_details.method == PARTIAL_STREAM_STREAM:
+ return MethodHandler(True, True, True)
+ else:
+ return None
+
+
+# Traditional executors will not exit until all their
+# current jobs complete. Because we submit jobs that will
+# never finish, we don't want to block exit on these jobs.
+class DaemonPool(object):
+
+ def submit(self, fn, *args, **kwargs):
+ thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
+ thread.daemon = True
+ thread.start()
+
+ def shutdown(self, wait=True):
+ pass
+
+
+def infinite_request_iterator():
+ while True:
+ yield REQUEST
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('scenario', type=str)
+ parser.add_argument(
+ '--wait_for_interrupt', dest='wait_for_interrupt', action='store_true')
+ args = parser.parse_args()
+
+ if args.scenario == UNSTARTED_SERVER:
+ server = grpc.server((), DaemonPool())
+ if args.wait_for_interrupt:
+ time.sleep(WAIT_TIME)
+ elif args.scenario == RUNNING_SERVER:
+ server = grpc.server((), DaemonPool())
+ port = server.add_insecure_port('[::]:0')
+ server.start()
+ if args.wait_for_interrupt:
+ time.sleep(WAIT_TIME)
+ elif args.scenario == POLL_CONNECTIVITY_NO_SERVER:
+ channel = grpc.insecure_channel('localhost:12345')
+
+ def connectivity_callback(connectivity):
+ pass
+
+ channel.subscribe(connectivity_callback, try_to_connect=True)
+ if args.wait_for_interrupt:
+ time.sleep(WAIT_TIME)
+ elif args.scenario == POLL_CONNECTIVITY:
+ server = grpc.server((), DaemonPool())
+ port = server.add_insecure_port('[::]:0')
+ server.start()
+ channel = grpc.insecure_channel('localhost:%d' % port)
+
+ def connectivity_callback(connectivity):
+ pass
+
+ channel.subscribe(connectivity_callback, try_to_connect=True)
+ if args.wait_for_interrupt:
+ time.sleep(WAIT_TIME)
+
+ else:
+ handler = GenericHandler()
+ server = grpc.server((), DaemonPool())
+ port = server.add_insecure_port('[::]:0')
+ server.add_generic_rpc_handlers((handler,))
+ server.start()
+ channel = grpc.insecure_channel('localhost:%d' % port)
+
+ method = TEST_TO_METHOD[args.scenario]
+
+ if args.scenario == IN_FLIGHT_UNARY_UNARY_CALL:
+ multi_callable = channel.unary_unary(method)
+ future = multi_callable.future(REQUEST)
+ result, call = multi_callable.with_call(REQUEST)
+ elif (args.scenario == IN_FLIGHT_UNARY_STREAM_CALL or
+ args.scenario == IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL):
+ multi_callable = channel.unary_stream(method)
+ response_iterator = multi_callable(REQUEST)
+ for response in response_iterator:
+ pass
+ elif (args.scenario == IN_FLIGHT_STREAM_UNARY_CALL or
+ args.scenario == IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL):
+ multi_callable = channel.stream_unary(method)
+ future = multi_callable.future(infinite_request_iterator())
+ result, call = multi_callable.with_call(
+ [REQUEST] * test_constants.STREAM_LENGTH)
+ elif (args.scenario == IN_FLIGHT_STREAM_STREAM_CALL or
+ args.scenario == IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL):
+ multi_callable = channel.stream_stream(method)
+ response_iterator = multi_callable(infinite_request_iterator())
+ for response in response_iterator:
+ pass
diff --git a/src/python/grpcio/tests/unit/_exit_test.py b/src/python/grpcio/tests/unit/_exit_test.py
new file mode 100644
index 0000000000..b0d6af73e5
--- /dev/null
+++ b/src/python/grpcio/tests/unit/_exit_test.py
@@ -0,0 +1,185 @@
+# Copyright 2016, 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.
+
+"""Tests clean exit of server/client on Python Interpreter exit/sigint.
+
+The tests in this module spawn a subprocess for each test case, the
+test is considered successful if it doesn't hang/timeout.
+"""
+
+import atexit
+import os
+import signal
+import six
+import subprocess
+import sys
+import threading
+import time
+import unittest
+
+from tests.unit import _exit_scenarios
+
+SCENARIO_FILE = os.path.abspath(os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), '_exit_scenarios.py'))
+INTERPRETER = sys.executable
+BASE_COMMAND = [INTERPRETER, SCENARIO_FILE]
+BASE_SIGTERM_COMMAND = BASE_COMMAND + ['--wait_for_interrupt']
+
+INIT_TIME = 1.0
+
+
+processes = []
+process_lock = threading.Lock()
+
+
+# Make sure we attempt to clean up any
+# processes we may have left running
+def cleanup_processes():
+ with process_lock:
+ for process in processes:
+ try:
+ process.kill()
+ except Exception:
+ pass
+atexit.register(cleanup_processes)
+
+
+def interrupt_and_wait(process):
+ with process_lock:
+ processes.append(process)
+ time.sleep(INIT_TIME)
+ os.kill(process.pid, signal.SIGINT)
+ process.wait()
+
+
+def wait(process):
+ with process_lock:
+ processes.append(process)
+ process.wait()
+
+
+class ExitTest(unittest.TestCase):
+
+ def test_unstarted_server(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.UNSTARTED_SERVER],
+ stdout=sys.stdout, stderr=sys.stderr)
+ wait(process)
+
+ def test_unstarted_server_terminate(self):
+ process = subprocess.Popen(
+ BASE_SIGTERM_COMMAND + [_exit_scenarios.UNSTARTED_SERVER],
+ stdout=sys.stdout)
+ interrupt_and_wait(process)
+
+ def test_running_server(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.RUNNING_SERVER],
+ stdout=sys.stdout, stderr=sys.stderr)
+ wait(process)
+
+ def test_running_server_terminate(self):
+ process = subprocess.Popen(
+ BASE_SIGTERM_COMMAND + [_exit_scenarios.RUNNING_SERVER],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ def test_poll_connectivity_no_server(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY_NO_SERVER],
+ stdout=sys.stdout, stderr=sys.stderr)
+ wait(process)
+
+ def test_poll_connectivity_no_server_terminate(self):
+ process = subprocess.Popen(
+ BASE_SIGTERM_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY_NO_SERVER],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ def test_poll_connectivity(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY],
+ stdout=sys.stdout, stderr=sys.stderr)
+ wait(process)
+
+ def test_poll_connectivity_terminate(self):
+ process = subprocess.Popen(
+ BASE_SIGTERM_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ def test_in_flight_unary_unary_call(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_UNARY_UNARY_CALL],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999')
+ def test_in_flight_unary_stream_call(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_UNARY_STREAM_CALL],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ def test_in_flight_stream_unary_call(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_STREAM_UNARY_CALL],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999')
+ def test_in_flight_stream_stream_call(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_STREAM_STREAM_CALL],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999')
+ def test_in_flight_partial_unary_stream_call(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ def test_in_flight_partial_stream_unary_call(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+ @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999')
+ def test_in_flight_partial_stream_stream_call(self):
+ process = subprocess.Popen(
+ BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL],
+ stdout=sys.stdout, stderr=sys.stderr)
+ interrupt_and_wait(process)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio/tests/unit/_metadata_code_details_test.py b/src/python/grpcio/tests/unit/_metadata_code_details_test.py
new file mode 100644
index 0000000000..0fd02d2a22
--- /dev/null
+++ b/src/python/grpcio/tests/unit/_metadata_code_details_test.py
@@ -0,0 +1,523 @@
+# Copyright 2016, 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.
+
+"""Tests application-provided metadata, status code, and details."""
+
+import threading
+import unittest
+
+import grpc
+from grpc.framework.foundation import logging_pool
+
+from tests.unit import test_common
+from tests.unit.framework.common import test_constants
+from tests.unit.framework.common import test_control
+
+_SERIALIZED_REQUEST = b'\x46\x47\x48'
+_SERIALIZED_RESPONSE = b'\x49\x50\x51'
+
+_REQUEST_SERIALIZER = lambda unused_request: _SERIALIZED_REQUEST
+_REQUEST_DESERIALIZER = lambda unused_serialized_request: object()
+_RESPONSE_SERIALIZER = lambda unused_response: _SERIALIZED_RESPONSE
+_RESPONSE_DESERIALIZER = lambda unused_serialized_resopnse: object()
+
+_SERVICE = 'test.TestService'
+_UNARY_UNARY = 'UnaryUnary'
+_UNARY_STREAM = 'UnaryStream'
+_STREAM_UNARY = 'StreamUnary'
+_STREAM_STREAM = 'StreamStream'
+
+_CLIENT_METADATA = (
+ ('client-md-key', 'client-md-key'),
+ ('client-md-key-bin', b'\x00\x01')
+)
+
+_SERVER_INITIAL_METADATA = (
+ ('server-initial-md-key', 'server-initial-md-value'),
+ ('server-initial-md-key-bin', b'\x00\x02')
+)
+
+_SERVER_TRAILING_METADATA = (
+ ('server-trailing-md-key', 'server-trailing-md-value'),
+ ('server-trailing-md-key-bin', b'\x00\x03')
+)
+
+_NON_OK_CODE = grpc.StatusCode.NOT_FOUND
+_DETAILS = 'Test details!'
+
+
+class _Servicer(object):
+
+ def __init__(self):
+ self._lock = threading.Lock()
+ self._code = None
+ self._details = None
+ self._exception = False
+ self._return_none = False
+ self._received_client_metadata = None
+
+ def unary_unary(self, request, context):
+ with self._lock:
+ self._received_client_metadata = context.invocation_metadata()
+ context.send_initial_metadata(_SERVER_INITIAL_METADATA)
+ context.set_trailing_metadata(_SERVER_TRAILING_METADATA)
+ if self._code is not None:
+ context.set_code(self._code)
+ if self._details is not None:
+ context.set_details(self._details)
+ if self._exception:
+ raise test_control.Defect()
+ else:
+ return None if self._return_none else object()
+
+ def unary_stream(self, request, context):
+ with self._lock:
+ self._received_client_metadata = context.invocation_metadata()
+ context.send_initial_metadata(_SERVER_INITIAL_METADATA)
+ context.set_trailing_metadata(_SERVER_TRAILING_METADATA)
+ if self._code is not None:
+ context.set_code(self._code)
+ if self._details is not None:
+ context.set_details(self._details)
+ for _ in range(test_constants.STREAM_LENGTH // 2):
+ yield _SERIALIZED_RESPONSE
+ if self._exception:
+ raise test_control.Defect()
+
+ def stream_unary(self, request_iterator, context):
+ with self._lock:
+ self._received_client_metadata = context.invocation_metadata()
+ context.send_initial_metadata(_SERVER_INITIAL_METADATA)
+ context.set_trailing_metadata(_SERVER_TRAILING_METADATA)
+ if self._code is not None:
+ context.set_code(self._code)
+ if self._details is not None:
+ context.set_details(self._details)
+ # TODO(https://github.com/grpc/grpc/issues/6891): just ignore the
+ # request iterator.
+ for ignored_request in request_iterator:
+ pass
+ if self._exception:
+ raise test_control.Defect()
+ else:
+ return None if self._return_none else _SERIALIZED_RESPONSE
+
+ def stream_stream(self, request_iterator, context):
+ with self._lock:
+ self._received_client_metadata = context.invocation_metadata()
+ context.send_initial_metadata(_SERVER_INITIAL_METADATA)
+ context.set_trailing_metadata(_SERVER_TRAILING_METADATA)
+ if self._code is not None:
+ context.set_code(self._code)
+ if self._details is not None:
+ context.set_details(self._details)
+ # TODO(https://github.com/grpc/grpc/issues/6891): just ignore the
+ # request iterator.
+ for ignored_request in request_iterator:
+ pass
+ for _ in range(test_constants.STREAM_LENGTH // 3):
+ yield object()
+ if self._exception:
+ raise test_control.Defect()
+
+ def set_code(self, code):
+ with self._lock:
+ self._code = code
+
+ def set_details(self, details):
+ with self._lock:
+ self._details = details
+
+ def set_exception(self):
+ with self._lock:
+ self._exception = True
+
+ def set_return_none(self):
+ with self._lock:
+ self._return_none = True
+
+ def received_client_metadata(self):
+ with self._lock:
+ return self._received_client_metadata
+
+
+def _generic_handler(servicer):
+ method_handlers = {
+ _UNARY_UNARY: grpc.unary_unary_rpc_method_handler(
+ servicer.unary_unary, request_deserializer=_REQUEST_DESERIALIZER,
+ response_serializer=_RESPONSE_SERIALIZER),
+ _UNARY_STREAM: grpc.unary_stream_rpc_method_handler(
+ servicer.unary_stream),
+ _STREAM_UNARY: grpc.stream_unary_rpc_method_handler(
+ servicer.stream_unary),
+ _STREAM_STREAM: grpc.stream_stream_rpc_method_handler(
+ servicer.stream_stream, request_deserializer=_REQUEST_DESERIALIZER,
+ response_serializer=_RESPONSE_SERIALIZER),
+ }
+ return grpc.method_handlers_generic_handler(_SERVICE, method_handlers)
+
+
+class MetadataCodeDetailsTest(unittest.TestCase):
+
+ def setUp(self):
+ self._servicer = _Servicer()
+ self._server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
+ self._server = grpc.server(
+ (_generic_handler(self._servicer),), self._server_pool)
+ port = self._server.add_insecure_port('[::]:0')
+ self._server.start()
+
+ channel = grpc.insecure_channel('localhost:{}'.format(port))
+ self._unary_unary = channel.unary_unary(
+ '/'.join(('', _SERVICE, _UNARY_UNARY,)),
+ request_serializer=_REQUEST_SERIALIZER,
+ response_deserializer=_RESPONSE_DESERIALIZER,)
+ self._unary_stream = channel.unary_stream(
+ '/'.join(('', _SERVICE, _UNARY_STREAM,)),)
+ self._stream_unary = channel.stream_unary(
+ '/'.join(('', _SERVICE, _STREAM_UNARY,)),)
+ self._stream_stream = channel.stream_stream(
+ '/'.join(('', _SERVICE, _STREAM_STREAM,)),
+ request_serializer=_REQUEST_SERIALIZER,
+ response_deserializer=_RESPONSE_DESERIALIZER,)
+
+
+ def testSuccessfulUnaryUnary(self):
+ self._servicer.set_details(_DETAILS)
+
+ unused_response, call = self._unary_unary.with_call(
+ object(), metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, call.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA, call.trailing_metadata()))
+ self.assertIs(grpc.StatusCode.OK, call.code())
+ self.assertEqual(_DETAILS, call.details())
+
+ def testSuccessfulUnaryStream(self):
+ self._servicer.set_details(_DETAILS)
+
+ call = self._unary_stream(_SERIALIZED_REQUEST, metadata=_CLIENT_METADATA)
+ received_initial_metadata = call.initial_metadata()
+ for _ in call:
+ pass
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, received_initial_metadata))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA, call.trailing_metadata()))
+ self.assertIs(grpc.StatusCode.OK, call.code())
+ self.assertEqual(_DETAILS, call.details())
+
+ def testSuccessfulStreamUnary(self):
+ self._servicer.set_details(_DETAILS)
+
+ unused_response, call = self._stream_unary.with_call(
+ iter([_SERIALIZED_REQUEST] * test_constants.STREAM_LENGTH),
+ metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, call.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA, call.trailing_metadata()))
+ self.assertIs(grpc.StatusCode.OK, call.code())
+ self.assertEqual(_DETAILS, call.details())
+
+ def testSuccessfulStreamStream(self):
+ self._servicer.set_details(_DETAILS)
+
+ call = self._stream_stream(
+ iter([object()] * test_constants.STREAM_LENGTH),
+ metadata=_CLIENT_METADATA)
+ received_initial_metadata = call.initial_metadata()
+ for _ in call:
+ pass
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, received_initial_metadata))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA, call.trailing_metadata()))
+ self.assertIs(grpc.StatusCode.OK, call.code())
+ self.assertEqual(_DETAILS, call.details())
+
+ def testCustomCodeUnaryUnary(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+
+ with self.assertRaises(grpc.RpcError) as exception_context:
+ self._unary_unary.with_call(object(), metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA,
+ exception_context.exception.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA,
+ exception_context.exception.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, exception_context.exception.code())
+ self.assertEqual(_DETAILS, exception_context.exception.details())
+
+ def testCustomCodeUnaryStream(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+
+ call = self._unary_stream(_SERIALIZED_REQUEST, metadata=_CLIENT_METADATA)
+ received_initial_metadata = call.initial_metadata()
+ with self.assertRaises(grpc.RpcError):
+ for _ in call:
+ pass
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, received_initial_metadata))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA, call.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, call.code())
+ self.assertEqual(_DETAILS, call.details())
+
+ def testCustomCodeStreamUnary(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+
+ with self.assertRaises(grpc.RpcError) as exception_context:
+ self._stream_unary.with_call(
+ iter([_SERIALIZED_REQUEST] * test_constants.STREAM_LENGTH),
+ metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA,
+ exception_context.exception.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA,
+ exception_context.exception.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, exception_context.exception.code())
+ self.assertEqual(_DETAILS, exception_context.exception.details())
+
+ def testCustomCodeStreamStream(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+
+ call = self._stream_stream(
+ iter([object()] * test_constants.STREAM_LENGTH),
+ metadata=_CLIENT_METADATA)
+ received_initial_metadata = call.initial_metadata()
+ with self.assertRaises(grpc.RpcError) as exception_context:
+ for _ in call:
+ pass
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, received_initial_metadata))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA,
+ exception_context.exception.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, exception_context.exception.code())
+ self.assertEqual(_DETAILS, exception_context.exception.details())
+
+ def testCustomCodeExceptionUnaryUnary(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+ self._servicer.set_exception()
+
+ with self.assertRaises(grpc.RpcError) as exception_context:
+ self._unary_unary.with_call(object(), metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA,
+ exception_context.exception.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA,
+ exception_context.exception.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, exception_context.exception.code())
+ self.assertEqual(_DETAILS, exception_context.exception.details())
+
+ def testCustomCodeExceptionUnaryStream(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+ self._servicer.set_exception()
+
+ call = self._unary_stream(_SERIALIZED_REQUEST, metadata=_CLIENT_METADATA)
+ received_initial_metadata = call.initial_metadata()
+ with self.assertRaises(grpc.RpcError):
+ for _ in call:
+ pass
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, received_initial_metadata))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA, call.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, call.code())
+ self.assertEqual(_DETAILS, call.details())
+
+ def testCustomCodeExceptionStreamUnary(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+ self._servicer.set_exception()
+
+ with self.assertRaises(grpc.RpcError) as exception_context:
+ self._stream_unary.with_call(
+ iter([_SERIALIZED_REQUEST] * test_constants.STREAM_LENGTH),
+ metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA,
+ exception_context.exception.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA,
+ exception_context.exception.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, exception_context.exception.code())
+ self.assertEqual(_DETAILS, exception_context.exception.details())
+
+ def testCustomCodeExceptionStreamStream(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+ self._servicer.set_exception()
+
+ call = self._stream_stream(
+ iter([object()] * test_constants.STREAM_LENGTH),
+ metadata=_CLIENT_METADATA)
+ received_initial_metadata = call.initial_metadata()
+ with self.assertRaises(grpc.RpcError):
+ for _ in call:
+ pass
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA, received_initial_metadata))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA, call.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, call.code())
+ self.assertEqual(_DETAILS, call.details())
+
+ def testCustomCodeReturnNoneUnaryUnary(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+ self._servicer.set_return_none()
+
+ with self.assertRaises(grpc.RpcError) as exception_context:
+ self._unary_unary.with_call(object(), metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA,
+ exception_context.exception.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA,
+ exception_context.exception.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, exception_context.exception.code())
+ self.assertEqual(_DETAILS, exception_context.exception.details())
+
+ def testCustomCodeReturnNoneStreamUnary(self):
+ self._servicer.set_code(_NON_OK_CODE)
+ self._servicer.set_details(_DETAILS)
+ self._servicer.set_return_none()
+
+ with self.assertRaises(grpc.RpcError) as exception_context:
+ self._stream_unary.with_call(
+ iter([_SERIALIZED_REQUEST] * test_constants.STREAM_LENGTH),
+ metadata=_CLIENT_METADATA)
+
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _CLIENT_METADATA, self._servicer.received_client_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_INITIAL_METADATA,
+ exception_context.exception.initial_metadata()))
+ self.assertTrue(
+ test_common.metadata_transmitted(
+ _SERVER_TRAILING_METADATA,
+ exception_context.exception.trailing_metadata()))
+ self.assertIs(_NON_OK_CODE, exception_context.exception.code())
+ self.assertEqual(_DETAILS, exception_context.exception.details())
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/python/grpcio/tests/unit/_metadata_test.py b/src/python/grpcio/tests/unit/_metadata_test.py
index 2cb13f236b..c637a28039 100644
--- a/src/python/grpcio/tests/unit/_metadata_test.py
+++ b/src/python/grpcio/tests/unit/_metadata_test.py
@@ -44,33 +44,33 @@ _CHANNEL_ARGS = (('grpc.primary_user_agent', 'primary-agent'),
_REQUEST = b'\x00\x00\x00'
_RESPONSE = b'\x00\x00\x00'
-_UNARY_UNARY = b'/test/UnaryUnary'
-_UNARY_STREAM = b'/test/UnaryStream'
-_STREAM_UNARY = b'/test/StreamUnary'
-_STREAM_STREAM = b'/test/StreamStream'
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
_USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__)
_CLIENT_METADATA = (
- (b'client-md-key', b'client-md-key'),
- (b'client-md-key-bin', b'\x00\x01')
+ ('client-md-key', 'client-md-key'),
+ ('client-md-key-bin', b'\x00\x01')
)
_SERVER_INITIAL_METADATA = (
- (b'server-initial-md-key', b'server-initial-md-value'),
- (b'server-initial-md-key-bin', b'\x00\x02')
+ ('server-initial-md-key', 'server-initial-md-value'),
+ ('server-initial-md-key-bin', b'\x00\x02')
)
_SERVER_TRAILING_METADATA = (
- (b'server-trailing-md-key', b'server-trailing-md-value'),
- (b'server-trailing-md-key-bin', b'\x00\x03')
+ ('server-trailing-md-key', 'server-trailing-md-value'),
+ ('server-trailing-md-key-bin', b'\x00\x03')
)
def user_agent(metadata):
for key, val in metadata:
- if key == b'user-agent':
- return val.decode('ascii')
+ if key == 'user-agent':
+ return val
raise KeyError('No user agent!')
diff --git a/src/python/grpcio/tests/unit/_rpc_test.py b/src/python/grpcio/tests/unit/_rpc_test.py
index 9814504edf..c70d65a6df 100644
--- a/src/python/grpcio/tests/unit/_rpc_test.py
+++ b/src/python/grpcio/tests/unit/_rpc_test.py
@@ -45,10 +45,10 @@ _DESERIALIZE_REQUEST = lambda bytestring: bytestring[len(bytestring) // 2:]
_SERIALIZE_RESPONSE = lambda bytestring: bytestring * 3
_DESERIALIZE_RESPONSE = lambda bytestring: bytestring[:len(bytestring) // 3]
-_UNARY_UNARY = b'/test/UnaryUnary'
-_UNARY_STREAM = b'/test/UnaryStream'
-_STREAM_UNARY = b'/test/StreamUnary'
-_STREAM_STREAM = b'/test/StreamStream'
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
class _Callback(object):
@@ -79,7 +79,7 @@ class _Handler(object):
def handle_unary_unary(self, request, servicer_context):
self._control.control()
if servicer_context is not None:
- servicer_context.set_trailing_metadata(((b'testkey', b'testvalue',),))
+ servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
return request
def handle_unary_stream(self, request, servicer_context):
@@ -88,7 +88,7 @@ class _Handler(object):
yield request
self._control.control()
if servicer_context is not None:
- servicer_context.set_trailing_metadata(((b'testkey', b'testvalue',),))
+ servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
def handle_stream_unary(self, request_iterator, servicer_context):
if servicer_context is not None:
@@ -100,13 +100,13 @@ class _Handler(object):
response_elements.append(request)
self._control.control()
if servicer_context is not None:
- servicer_context.set_trailing_metadata(((b'testkey', b'testvalue',),))
+ servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
return b''.join(response_elements)
def handle_stream_stream(self, request_iterator, servicer_context):
self._control.control()
if servicer_context is not None:
- servicer_context.set_trailing_metadata(((b'testkey', b'testvalue',),))
+ servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
for request in request_iterator:
self._control.control()
yield request
@@ -185,7 +185,7 @@ class RPCTest(unittest.TestCase):
self._server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
self._server = grpc.server((), self._server_pool)
- port = self._server.add_insecure_port(b'[::]:0')
+ port = self._server.add_insecure_port('[::]:0')
self._server.add_generic_rpc_handlers((_GenericHandler(self._handler),))
self._server.start()
@@ -195,7 +195,7 @@ class RPCTest(unittest.TestCase):
request = b'abc'
with self.assertRaises(grpc.RpcError) as exception_context:
- self._channel.unary_unary(b'NoSuchMethod')(request)
+ self._channel.unary_unary('NoSuchMethod')(request)
self.assertEqual(
grpc.StatusCode.UNIMPLEMENTED, exception_context.exception.code())
@@ -207,7 +207,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _unary_unary_multi_callable(self._channel)
response = multi_callable(
request, metadata=(
- (b'test', b'SuccessfulUnaryRequestBlockingUnaryResponse'),))
+ ('test', 'SuccessfulUnaryRequestBlockingUnaryResponse'),))
self.assertEqual(expected_response, response)
@@ -218,7 +218,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _unary_unary_multi_callable(self._channel)
response, call = multi_callable.with_call(
request, metadata=(
- (b'test', b'SuccessfulUnaryRequestBlockingUnaryResponseWithCall'),))
+ ('test', 'SuccessfulUnaryRequestBlockingUnaryResponseWithCall'),))
self.assertEqual(expected_response, response)
self.assertIs(grpc.StatusCode.OK, call.code())
@@ -230,7 +230,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _unary_unary_multi_callable(self._channel)
response_future = multi_callable.future(
request, metadata=(
- (b'test', b'SuccessfulUnaryRequestFutureUnaryResponse'),))
+ ('test', 'SuccessfulUnaryRequestFutureUnaryResponse'),))
response = response_future.result()
self.assertEqual(expected_response, response)
@@ -242,7 +242,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _unary_stream_multi_callable(self._channel)
response_iterator = multi_callable(
request,
- metadata=((b'test', b'SuccessfulUnaryRequestStreamResponse'),))
+ metadata=(('test', 'SuccessfulUnaryRequestStreamResponse'),))
responses = tuple(response_iterator)
self.assertSequenceEqual(expected_responses, responses)
@@ -255,7 +255,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _stream_unary_multi_callable(self._channel)
response = multi_callable(
request_iterator,
- metadata=((b'test', b'SuccessfulStreamRequestBlockingUnaryResponse'),))
+ metadata=(('test', 'SuccessfulStreamRequestBlockingUnaryResponse'),))
self.assertEqual(expected_response, response)
@@ -268,7 +268,7 @@ class RPCTest(unittest.TestCase):
response, call = multi_callable.with_call(
request_iterator,
metadata=(
- (b'test', b'SuccessfulStreamRequestBlockingUnaryResponseWithCall'),
+ ('test', 'SuccessfulStreamRequestBlockingUnaryResponseWithCall'),
))
self.assertEqual(expected_response, response)
@@ -283,7 +283,7 @@ class RPCTest(unittest.TestCase):
response_future = multi_callable.future(
request_iterator,
metadata=(
- (b'test', b'SuccessfulStreamRequestFutureUnaryResponse'),))
+ ('test', 'SuccessfulStreamRequestFutureUnaryResponse'),))
response = response_future.result()
self.assertEqual(expected_response, response)
@@ -297,7 +297,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _stream_stream_multi_callable(self._channel)
response_iterator = multi_callable(
request_iterator,
- metadata=((b'test', b'SuccessfulStreamRequestStreamResponse'),))
+ metadata=(('test', 'SuccessfulStreamRequestStreamResponse'),))
responses = tuple(response_iterator)
self.assertSequenceEqual(expected_responses, responses)
@@ -312,9 +312,9 @@ class RPCTest(unittest.TestCase):
multi_callable = _unary_unary_multi_callable(self._channel)
first_response = multi_callable(
- first_request, metadata=((b'test', b'SequentialInvocations'),))
+ first_request, metadata=(('test', 'SequentialInvocations'),))
second_response = multi_callable(
- second_request, metadata=((b'test', b'SequentialInvocations'),))
+ second_request, metadata=(('test', 'SequentialInvocations'),))
self.assertEqual(expected_first_response, first_response)
self.assertEqual(expected_second_response, second_response)
@@ -331,7 +331,7 @@ class RPCTest(unittest.TestCase):
request_iterator = iter(requests)
response_future = pool.submit(
multi_callable, request_iterator,
- metadata=((b'test', b'ConcurrentBlockingInvocations'),))
+ metadata=(('test', 'ConcurrentBlockingInvocations'),))
response_futures[index] = response_future
responses = tuple(
response_future.result() for response_future in response_futures)
@@ -350,7 +350,7 @@ class RPCTest(unittest.TestCase):
request_iterator = iter(requests)
response_future = multi_callable.future(
request_iterator,
- metadata=((b'test', b'ConcurrentFutureInvocations'),))
+ metadata=(('test', 'ConcurrentFutureInvocations'),))
response_futures[index] = response_future
responses = tuple(
response_future.result() for response_future in response_futures)
@@ -380,8 +380,8 @@ class RPCTest(unittest.TestCase):
inner_response_future = multi_callable.future(
request,
metadata=(
- (b'test',
- b'WaitingForSomeButNotAllConcurrentFutureInvocations'),))
+ ('test',
+ 'WaitingForSomeButNotAllConcurrentFutureInvocations'),))
outer_response_future = pool.submit(wrap_future(inner_response_future))
response_futures[index] = outer_response_future
@@ -400,7 +400,7 @@ class RPCTest(unittest.TestCase):
response_iterator = multi_callable(
request,
metadata=(
- (b'test', b'ConsumingOneStreamResponseUnaryRequest'),))
+ ('test', 'ConsumingOneStreamResponseUnaryRequest'),))
next(response_iterator)
def testConsumingSomeButNotAllStreamResponsesUnaryRequest(self):
@@ -410,7 +410,7 @@ class RPCTest(unittest.TestCase):
response_iterator = multi_callable(
request,
metadata=(
- (b'test', b'ConsumingSomeButNotAllStreamResponsesUnaryRequest'),))
+ ('test', 'ConsumingSomeButNotAllStreamResponsesUnaryRequest'),))
for _ in range(test_constants.STREAM_LENGTH // 2):
next(response_iterator)
@@ -422,7 +422,7 @@ class RPCTest(unittest.TestCase):
response_iterator = multi_callable(
request_iterator,
metadata=(
- (b'test', b'ConsumingSomeButNotAllStreamResponsesStreamRequest'),))
+ ('test', 'ConsumingSomeButNotAllStreamResponsesStreamRequest'),))
for _ in range(test_constants.STREAM_LENGTH // 2):
next(response_iterator)
@@ -434,7 +434,7 @@ class RPCTest(unittest.TestCase):
response_iterator = multi_callable(
request_iterator,
metadata=(
- (b'test', b'ConsumingTooManyStreamResponsesStreamRequest'),))
+ ('test', 'ConsumingTooManyStreamResponsesStreamRequest'),))
for _ in range(test_constants.STREAM_LENGTH):
next(response_iterator)
for _ in range(test_constants.STREAM_LENGTH):
@@ -453,7 +453,7 @@ class RPCTest(unittest.TestCase):
with self._control.pause():
response_future = multi_callable.future(
request,
- metadata=((b'test', b'CancelledUnaryRequestUnaryResponse'),))
+ metadata=(('test', 'CancelledUnaryRequestUnaryResponse'),))
response_future.cancel()
self.assertTrue(response_future.cancelled())
@@ -468,7 +468,7 @@ class RPCTest(unittest.TestCase):
with self._control.pause():
response_iterator = multi_callable(
request,
- metadata=((b'test', b'CancelledUnaryRequestStreamResponse'),))
+ metadata=(('test', 'CancelledUnaryRequestStreamResponse'),))
self._control.block_until_paused()
response_iterator.cancel()
@@ -488,7 +488,7 @@ class RPCTest(unittest.TestCase):
with self._control.pause():
response_future = multi_callable.future(
request_iterator,
- metadata=((b'test', b'CancelledStreamRequestUnaryResponse'),))
+ metadata=(('test', 'CancelledStreamRequestUnaryResponse'),))
self._control.block_until_paused()
response_future.cancel()
@@ -508,7 +508,7 @@ class RPCTest(unittest.TestCase):
with self._control.pause():
response_iterator = multi_callable(
request_iterator,
- metadata=((b'test', b'CancelledStreamRequestStreamResponse'),))
+ metadata=(('test', 'CancelledStreamRequestStreamResponse'),))
response_iterator.cancel()
with self.assertRaises(grpc.RpcError):
@@ -526,7 +526,7 @@ class RPCTest(unittest.TestCase):
with self.assertRaises(grpc.RpcError) as exception_context:
multi_callable.with_call(
request, timeout=test_constants.SHORT_TIMEOUT,
- metadata=((b'test', b'ExpiredUnaryRequestBlockingUnaryResponse'),))
+ metadata=(('test', 'ExpiredUnaryRequestBlockingUnaryResponse'),))
self.assertIsNotNone(exception_context.exception.initial_metadata())
self.assertIs(
@@ -542,7 +542,7 @@ class RPCTest(unittest.TestCase):
with self._control.pause():
response_future = multi_callable.future(
request, timeout=test_constants.SHORT_TIMEOUT,
- metadata=((b'test', b'ExpiredUnaryRequestFutureUnaryResponse'),))
+ metadata=(('test', 'ExpiredUnaryRequestFutureUnaryResponse'),))
response_future.add_done_callback(callback)
value_passed_to_callback = callback.value()
@@ -567,7 +567,7 @@ class RPCTest(unittest.TestCase):
with self.assertRaises(grpc.RpcError) as exception_context:
response_iterator = multi_callable(
request, timeout=test_constants.SHORT_TIMEOUT,
- metadata=((b'test', b'ExpiredUnaryRequestStreamResponse'),))
+ metadata=(('test', 'ExpiredUnaryRequestStreamResponse'),))
next(response_iterator)
self.assertIs(
@@ -583,7 +583,7 @@ class RPCTest(unittest.TestCase):
with self.assertRaises(grpc.RpcError) as exception_context:
multi_callable(
request_iterator, timeout=test_constants.SHORT_TIMEOUT,
- metadata=((b'test', b'ExpiredStreamRequestBlockingUnaryResponse'),))
+ metadata=(('test', 'ExpiredStreamRequestBlockingUnaryResponse'),))
self.assertIsNotNone(exception_context.exception.initial_metadata())
self.assertIs(
@@ -600,7 +600,7 @@ class RPCTest(unittest.TestCase):
with self._control.pause():
response_future = multi_callable.future(
request_iterator, timeout=test_constants.SHORT_TIMEOUT,
- metadata=((b'test', b'ExpiredStreamRequestFutureUnaryResponse'),))
+ metadata=(('test', 'ExpiredStreamRequestFutureUnaryResponse'),))
response_future.add_done_callback(callback)
value_passed_to_callback = callback.value()
@@ -625,7 +625,7 @@ class RPCTest(unittest.TestCase):
with self.assertRaises(grpc.RpcError) as exception_context:
response_iterator = multi_callable(
request_iterator, timeout=test_constants.SHORT_TIMEOUT,
- metadata=((b'test', b'ExpiredStreamRequestStreamResponse'),))
+ metadata=(('test', 'ExpiredStreamRequestStreamResponse'),))
next(response_iterator)
self.assertIs(
@@ -640,7 +640,7 @@ class RPCTest(unittest.TestCase):
with self.assertRaises(grpc.RpcError) as exception_context:
multi_callable.with_call(
request,
- metadata=((b'test', b'FailedUnaryRequestBlockingUnaryResponse'),))
+ metadata=(('test', 'FailedUnaryRequestBlockingUnaryResponse'),))
self.assertIs(grpc.StatusCode.UNKNOWN, exception_context.exception.code())
@@ -652,7 +652,7 @@ class RPCTest(unittest.TestCase):
with self._control.fail():
response_future = multi_callable.future(
request,
- metadata=((b'test', b'FailedUnaryRequestFutureUnaryResponse'),))
+ metadata=(('test', 'FailedUnaryRequestFutureUnaryResponse'),))
response_future.add_done_callback(callback)
value_passed_to_callback = callback.value()
@@ -672,7 +672,7 @@ class RPCTest(unittest.TestCase):
with self._control.fail():
response_iterator = multi_callable(
request,
- metadata=((b'test', b'FailedUnaryRequestStreamResponse'),))
+ metadata=(('test', 'FailedUnaryRequestStreamResponse'),))
next(response_iterator)
self.assertIs(grpc.StatusCode.UNKNOWN, exception_context.exception.code())
@@ -686,7 +686,7 @@ class RPCTest(unittest.TestCase):
with self.assertRaises(grpc.RpcError) as exception_context:
multi_callable(
request_iterator,
- metadata=((b'test', b'FailedStreamRequestBlockingUnaryResponse'),))
+ metadata=(('test', 'FailedStreamRequestBlockingUnaryResponse'),))
self.assertIs(grpc.StatusCode.UNKNOWN, exception_context.exception.code())
@@ -699,7 +699,7 @@ class RPCTest(unittest.TestCase):
with self._control.fail():
response_future = multi_callable.future(
request_iterator,
- metadata=((b'test', b'FailedStreamRequestFutureUnaryResponse'),))
+ metadata=(('test', 'FailedStreamRequestFutureUnaryResponse'),))
response_future.add_done_callback(callback)
value_passed_to_callback = callback.value()
@@ -720,7 +720,7 @@ class RPCTest(unittest.TestCase):
with self.assertRaises(grpc.RpcError) as exception_context:
response_iterator = multi_callable(
request_iterator,
- metadata=((b'test', b'FailedStreamRequestStreamResponse'),))
+ metadata=(('test', 'FailedStreamRequestStreamResponse'),))
tuple(response_iterator)
self.assertIs(grpc.StatusCode.UNKNOWN, exception_context.exception.code())
@@ -732,7 +732,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _unary_unary_multi_callable(self._channel)
multi_callable.future(
request,
- metadata=((b'test', b'IgnoredUnaryRequestFutureUnaryResponse'),))
+ metadata=(('test', 'IgnoredUnaryRequestFutureUnaryResponse'),))
def testIgnoredUnaryRequestStreamResponse(self):
request = b'\x37\x17'
@@ -740,7 +740,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _unary_stream_multi_callable(self._channel)
multi_callable(
request,
- metadata=((b'test', b'IgnoredUnaryRequestStreamResponse'),))
+ metadata=(('test', 'IgnoredUnaryRequestStreamResponse'),))
def testIgnoredStreamRequestFutureUnaryResponse(self):
requests = tuple(b'\x07\x18' for _ in range(test_constants.STREAM_LENGTH))
@@ -749,7 +749,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _stream_unary_multi_callable(self._channel)
multi_callable.future(
request_iterator,
- metadata=((b'test', b'IgnoredStreamRequestFutureUnaryResponse'),))
+ metadata=(('test', 'IgnoredStreamRequestFutureUnaryResponse'),))
def testIgnoredStreamRequestStreamResponse(self):
requests = tuple(b'\x67\x88' for _ in range(test_constants.STREAM_LENGTH))
@@ -758,7 +758,7 @@ class RPCTest(unittest.TestCase):
multi_callable = _stream_stream_multi_callable(self._channel)
multi_callable(
request_iterator,
- metadata=((b'test', b'IgnoredStreamRequestStreamResponse'),))
+ metadata=(('test', 'IgnoredStreamRequestStreamResponse'),))
if __name__ == '__main__':
diff --git a/src/python/grpcio/tests/unit/beta/_connectivity_channel_test.py b/src/python/grpcio/tests/unit/beta/_connectivity_channel_test.py
index 488f7d7141..5d826a269d 100644
--- a/src/python/grpcio/tests/unit/beta/_connectivity_channel_test.py
+++ b/src/python/grpcio/tests/unit/beta/_connectivity_channel_test.py
@@ -29,162 +29,9 @@
"""Tests of grpc.beta._connectivity_channel."""
-import threading
-import time
import unittest
-from grpc._adapter import _low
-from grpc._adapter import _types
-from grpc.beta import _connectivity_channel
from grpc.beta import interfaces
-from tests.unit.framework.common import test_constants
-
-
-def _drive_completion_queue(completion_queue):
- while True:
- event = completion_queue.next(time.time() + 24 * 60 * 60)
- if event.type == _types.EventType.QUEUE_SHUTDOWN:
- break
-
-
-class _Callback(object):
-
- def __init__(self):
- self._condition = threading.Condition()
- self._connectivities = []
-
- def update(self, connectivity):
- with self._condition:
- self._connectivities.append(connectivity)
- self._condition.notify()
-
- def connectivities(self):
- with self._condition:
- return tuple(self._connectivities)
-
- def block_until_connectivities_satisfy(self, predicate):
- with self._condition:
- while True:
- connectivities = tuple(self._connectivities)
- if predicate(connectivities):
- return connectivities
- else:
- self._condition.wait()
-
-
-class ChannelConnectivityTest(unittest.TestCase):
-
- def test_lonely_channel_connectivity(self):
- low_channel = _low.Channel('localhost:12345', ())
- callback = _Callback()
-
- connectivity_channel = _connectivity_channel.ConnectivityChannel(
- low_channel)
- connectivity_channel.subscribe(callback.update, try_to_connect=False)
- first_connectivities = callback.block_until_connectivities_satisfy(bool)
- connectivity_channel.subscribe(callback.update, try_to_connect=True)
- second_connectivities = callback.block_until_connectivities_satisfy(
- lambda connectivities: 2 <= len(connectivities))
- # Wait for a connection that will never happen.
- time.sleep(test_constants.SHORT_TIMEOUT)
- third_connectivities = callback.connectivities()
- connectivity_channel.unsubscribe(callback.update)
- fourth_connectivities = callback.connectivities()
- connectivity_channel.unsubscribe(callback.update)
- fifth_connectivities = callback.connectivities()
-
- self.assertSequenceEqual(
- (interfaces.ChannelConnectivity.IDLE,), first_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.READY, second_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.READY, third_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.READY, fourth_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.READY, fifth_connectivities)
-
- def test_immediately_connectable_channel_connectivity(self):
- server_completion_queue = _low.CompletionQueue()
- server = _low.Server(server_completion_queue, [])
- port = server.add_http2_port('[::]:0')
- server.start()
- server_completion_queue_thread = threading.Thread(
- target=_drive_completion_queue, args=(server_completion_queue,))
- server_completion_queue_thread.start()
- low_channel = _low.Channel('localhost:%d' % port, ())
- first_callback = _Callback()
- second_callback = _Callback()
-
- connectivity_channel = _connectivity_channel.ConnectivityChannel(
- low_channel)
- connectivity_channel.subscribe(first_callback.update, try_to_connect=False)
- first_connectivities = first_callback.block_until_connectivities_satisfy(
- bool)
- # Wait for a connection that will never happen because try_to_connect=True
- # has not yet been passed.
- time.sleep(test_constants.SHORT_TIMEOUT)
- second_connectivities = first_callback.connectivities()
- connectivity_channel.subscribe(second_callback.update, try_to_connect=True)
- third_connectivities = first_callback.block_until_connectivities_satisfy(
- lambda connectivities: 2 <= len(connectivities))
- fourth_connectivities = second_callback.block_until_connectivities_satisfy(
- bool)
- # Wait for a connection that will happen (or may already have happened).
- first_callback.block_until_connectivities_satisfy(
- lambda connectivities:
- interfaces.ChannelConnectivity.READY in connectivities)
- second_callback.block_until_connectivities_satisfy(
- lambda connectivities:
- interfaces.ChannelConnectivity.READY in connectivities)
- connectivity_channel.unsubscribe(first_callback.update)
- connectivity_channel.unsubscribe(second_callback.update)
-
- server.shutdown()
- server_completion_queue.shutdown()
- server_completion_queue_thread.join()
-
- self.assertSequenceEqual(
- (interfaces.ChannelConnectivity.IDLE,), first_connectivities)
- self.assertSequenceEqual(
- (interfaces.ChannelConnectivity.IDLE,), second_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.TRANSIENT_FAILURE, third_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.FATAL_FAILURE, third_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.TRANSIENT_FAILURE,
- fourth_connectivities)
- self.assertNotIn(
- interfaces.ChannelConnectivity.FATAL_FAILURE, fourth_connectivities)
-
- def test_reachable_then_unreachable_channel_connectivity(self):
- server_completion_queue = _low.CompletionQueue()
- server = _low.Server(server_completion_queue, [])
- port = server.add_http2_port('[::]:0')
- server.start()
- server_completion_queue_thread = threading.Thread(
- target=_drive_completion_queue, args=(server_completion_queue,))
- server_completion_queue_thread.start()
- low_channel = _low.Channel('localhost:%d' % port, ())
- callback = _Callback()
-
- connectivity_channel = _connectivity_channel.ConnectivityChannel(
- low_channel)
- connectivity_channel.subscribe(callback.update, try_to_connect=True)
- callback.block_until_connectivities_satisfy(
- lambda connectivities:
- interfaces.ChannelConnectivity.READY in connectivities)
- # Now take down the server and confirm that channel readiness is repudiated.
- server.shutdown()
- callback.block_until_connectivities_satisfy(
- lambda connectivities:
- connectivities[-1] is not interfaces.ChannelConnectivity.READY)
- connectivity_channel.unsubscribe(callback.update)
-
- server.shutdown()
- server_completion_queue.shutdown()
- server_completion_queue_thread.join()
class ConnectivityStatesTest(unittest.TestCase):
diff --git a/src/python/grpcio/tests/unit/beta/_utilities_test.py b/src/python/grpcio/tests/unit/beta/_utilities_test.py
index 08ce98e751..90fe10c77c 100644
--- a/src/python/grpcio/tests/unit/beta/_utilities_test.py
+++ b/src/python/grpcio/tests/unit/beta/_utilities_test.py
@@ -33,21 +33,12 @@ import threading
import time
import unittest
-from grpc._adapter import _low
-from grpc._adapter import _types
from grpc.beta import implementations
from grpc.beta import utilities
from grpc.framework.foundation import future
from tests.unit.framework.common import test_constants
-def _drive_completion_queue(completion_queue):
- while True:
- event = completion_queue.next(time.time() + 24 * 60 * 60)
- if event.type == _types.EventType.QUEUE_SHUTDOWN:
- break
-
-
class _Callback(object):
def __init__(self):
@@ -87,13 +78,9 @@ class ChannelConnectivityTest(unittest.TestCase):
self.assertFalse(ready_future.running())
def test_immediately_connectable_channel_connectivity(self):
- server_completion_queue = _low.CompletionQueue()
- server = _low.Server(server_completion_queue, [])
- port = server.add_http2_port('[::]:0')
+ server = implementations.server({})
+ port = server.add_insecure_port('[::]:0')
server.start()
- server_completion_queue_thread = threading.Thread(
- target=_drive_completion_queue, args=(server_completion_queue,))
- server_completion_queue_thread.start()
channel = implementations.insecure_channel('localhost', port)
callback = _Callback()
@@ -114,9 +101,7 @@ class ChannelConnectivityTest(unittest.TestCase):
self.assertFalse(ready_future.running())
finally:
ready_future.cancel()
- server.shutdown()
- server_completion_queue.shutdown()
- server_completion_queue_thread.join()
+ server.stop(0)
if __name__ == '__main__':
diff --git a/src/python/grpcio/tests/unit/beta/test_utilities.py b/src/python/grpcio/tests/unit/beta/test_utilities.py
index 8ccad04e05..692da9c97d 100644
--- a/src/python/grpcio/tests/unit/beta/test_utilities.py
+++ b/src/python/grpcio/tests/unit/beta/test_utilities.py
@@ -51,5 +51,5 @@ def not_really_secure_channel(
target = '%s:%d' % (host, port)
channel = grpc.secure_channel(
target, channel_credentials,
- ((b'grpc.ssl_target_name_override', server_host_override,),))
+ (('grpc.ssl_target_name_override', server_host_override,),))
return implementations.Channel(channel)
diff --git a/src/python/grpcio/tests/unit/test_common.py b/src/python/grpcio/tests/unit/test_common.py
index b779f65e7e..c8886bf4ca 100644
--- a/src/python/grpcio/tests/unit/test_common.py
+++ b/src/python/grpcio/tests/unit/test_common.py
@@ -33,10 +33,10 @@ import collections
import six
-INVOCATION_INITIAL_METADATA = ((b'0', b'abc'), (b'1', b'def'), (b'2', b'ghi'),)
-SERVICE_INITIAL_METADATA = ((b'3', b'jkl'), (b'4', b'mno'), (b'5', b'pqr'),)
-SERVICE_TERMINAL_METADATA = ((b'6', b'stu'), (b'7', b'vwx'), (b'8', b'yza'),)
-DETAILS = b'test details'
+INVOCATION_INITIAL_METADATA = (('0', 'abc'), ('1', 'def'), ('2', 'ghi'),)
+SERVICE_INITIAL_METADATA = (('3', 'jkl'), ('4', 'mno'), ('5', 'pqr'),)
+SERVICE_TERMINAL_METADATA = (('6', 'stu'), ('7', 'vwx'), ('8', 'yza'),)
+DETAILS = 'test details'
def metadata_transmitted(original_metadata, transmitted_metadata):
@@ -59,16 +59,10 @@ def metadata_transmitted(original_metadata, transmitted_metadata):
original_metadata after having been transmitted via gRPC.
"""
original = collections.defaultdict(list)
- for key_value_pair in original_metadata:
- key, value = tuple(key_value_pair)
- if not isinstance(key, bytes):
- key = key.encode()
- if not isinstance(value, bytes):
- value = value.encode()
+ for key, value in original_metadata:
original[key].append(value)
transmitted = collections.defaultdict(list)
- for key_value_pair in transmitted_metadata:
- key, value = tuple(key_value_pair)
+ for key, value in transmitted_metadata:
transmitted[key].append(value)
for key, values in six.iteritems(original):
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 4d9707638f..9748cb576b 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -128,6 +128,7 @@ grpc_is_binary_header_type grpc_is_binary_header_import;
grpc_call_error_to_string_type grpc_call_error_to_string_import;
grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import;
grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import;
+grpc_use_signal_type grpc_use_signal_import;
grpc_auth_property_iterator_next_type grpc_auth_property_iterator_next_import;
grpc_auth_context_property_iterator_type grpc_auth_context_property_iterator_import;
grpc_auth_context_peer_identity_type grpc_auth_context_peer_identity_import;
@@ -399,6 +400,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_call_error_to_string_import = (grpc_call_error_to_string_type) GetProcAddress(library, "grpc_call_error_to_string");
grpc_insecure_channel_create_from_fd_import = (grpc_insecure_channel_create_from_fd_type) GetProcAddress(library, "grpc_insecure_channel_create_from_fd");
grpc_server_add_insecure_channel_from_fd_import = (grpc_server_add_insecure_channel_from_fd_type) GetProcAddress(library, "grpc_server_add_insecure_channel_from_fd");
+ grpc_use_signal_import = (grpc_use_signal_type) GetProcAddress(library, "grpc_use_signal");
grpc_auth_property_iterator_next_import = (grpc_auth_property_iterator_next_type) GetProcAddress(library, "grpc_auth_property_iterator_next");
grpc_auth_context_property_iterator_import = (grpc_auth_context_property_iterator_type) GetProcAddress(library, "grpc_auth_context_property_iterator");
grpc_auth_context_peer_identity_import = (grpc_auth_context_peer_identity_type) GetProcAddress(library, "grpc_auth_context_peer_identity");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 072e29c232..13f961495c 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -335,6 +335,9 @@ extern grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_fr
typedef void(*grpc_server_add_insecure_channel_from_fd_type)(grpc_server *server, grpc_completion_queue *cq, int fd);
extern grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import;
#define grpc_server_add_insecure_channel_from_fd grpc_server_add_insecure_channel_from_fd_import
+typedef void(*grpc_use_signal_type)(int signum);
+extern grpc_use_signal_type grpc_use_signal_import;
+#define grpc_use_signal grpc_use_signal_import
typedef const grpc_auth_property *(*grpc_auth_property_iterator_next_type)(grpc_auth_property_iterator *it);
extern grpc_auth_property_iterator_next_type grpc_auth_property_iterator_next_import;
#define grpc_auth_property_iterator_next grpc_auth_property_iterator_next_import
diff --git a/src/ruby/lib/grpc/version.rb b/src/ruby/lib/grpc/version.rb
index 01c8c5ac8f..5e6aaef2eb 100644
--- a/src/ruby/lib/grpc/version.rb
+++ b/src/ruby/lib/grpc/version.rb
@@ -29,5 +29,5 @@
# GRPC contains the General RPC module.
module GRPC
- VERSION = '0.15.0.dev'
+ VERSION = '0.16.0.dev'
end
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index aedeca272d..d60d84996f 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -43,11 +43,11 @@ shared_context 'setup: tags' do
Time.now + 5
end
- def server_allows_client_to_proceed
+ def server_allows_client_to_proceed(metadata = {})
recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
expect(recvd_rpc).to_not eq nil
server_call = recvd_rpc.call
- ops = { CallOps::SEND_INITIAL_METADATA => {} }
+ ops = { CallOps::SEND_INITIAL_METADATA => metadata }
svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline, ops)
expect(svr_batch.send_metadata).to be true
server_call
@@ -135,6 +135,48 @@ shared_examples 'basic GRPC message delivery is OK' do
expect(svr_batch.send_message).to be true
end
+ it 'compressed messages can be sent and received' do
+ call = new_client_call
+ server_call = nil
+ long_request_str = '0' * 2000
+ long_response_str = '1' * 2000
+ md = { 'grpc-internal-encoding-request' => 'gzip' }
+
+ server_thread = Thread.new do
+ server_call = server_allows_client_to_proceed(md)
+ end
+
+ client_ops = {
+ CallOps::SEND_INITIAL_METADATA => md,
+ CallOps::SEND_MESSAGE => long_request_str
+ }
+ batch_result = call.run_batch(@client_queue, @client_tag, deadline,
+ client_ops)
+ expect(batch_result.send_metadata).to be true
+ expect(batch_result.send_message).to be true
+
+ # confirm the server can read the inbound message
+ server_thread.join
+ server_ops = {
+ CallOps::RECV_MESSAGE => nil,
+ CallOps::SEND_MESSAGE => long_response_str
+ }
+ svr_batch = server_call.run_batch(@server_queue, @server_tag, deadline,
+ server_ops)
+ expect(svr_batch.message).to eq(long_request_str)
+ expect(svr_batch.send_message).to be true
+
+ client_ops = {
+ CallOps::SEND_CLOSE_FROM_CLIENT => nil,
+ CallOps::RECV_INITIAL_METADATA => nil,
+ CallOps::RECV_MESSAGE => nil
+ }
+ batch_result = call.run_batch(@client_queue, @client_tag, deadline,
+ client_ops)
+ expect(batch_result.send_close).to be true
+ expect(batch_result.message).to eq long_response_str
+ end
+
it 'servers can ignore a client write and send a status' do
call = new_client_call
server_call = nil
diff --git a/src/ruby/tools/version.rb b/src/ruby/tools/version.rb
index dca7fd7e72..68c1bf369d 100644
--- a/src/ruby/tools/version.rb
+++ b/src/ruby/tools/version.rb
@@ -29,6 +29,6 @@
module GRPC
module Tools
- VERSION = '0.15.0.dev'
+ VERSION = '0.16.0.dev'
end
end