From b6b8de1758be800f170a15d7cf05fffc04619bb8 Mon Sep 17 00:00:00 2001 From: Ian Coolidge Date: Thu, 19 Oct 2017 14:50:26 -0700 Subject: [PATCH 1/8] reland: cpu_linux: Don't spam sched_getcpu failures on qemu __NR_getcpu isn't implemented on qemu, and for some reason sysconf(_SC_NPROCESSORS_ONLN) returns the number of processers of the host system, giving a false indication that there is more than one cpu for the qemu case. Expand the init_num_cpus sequence to also run sched_getcpu once, if GPR_MUSL_LIBC_COMPAT isn't defined. If that call isn't supported, initialize 'ncpus' to 1. Later, in gpr_cpu_current_cpu, use gpr_cpu_num_cores to avoid the system call in cases where we know it isn't supported, or if the ncpus is otherwise 1. --- src/core/lib/support/cpu_linux.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/lib/support/cpu_linux.cc b/src/core/lib/support/cpu_linux.cc index 22806684421..21b1a71dc92 100644 --- a/src/core/lib/support/cpu_linux.cc +++ b/src/core/lib/support/cpu_linux.cc @@ -36,6 +36,13 @@ static int ncpus = 0; static void init_num_cpus() { +#ifndef GPR_MUSL_LIBC_COMPAT + if (sched_getcpu() < 0) { + gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); + ncpus = 1; + return; + } +#endif /* This must be signed. sysconf returns -1 when the number cannot be determined */ ncpus = (int)sysconf(_SC_NPROCESSORS_ONLN); @@ -56,6 +63,9 @@ unsigned gpr_cpu_current_cpu(void) { // sched_getcpu() is undefined on musl return 0; #else + if (gpr_cpu_num_cores() == 1) { + return 0; + } int cpu = sched_getcpu(); if (cpu < 0) { gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); From 4cf5f3e41eeaee39c6396b19f36f9690ad606bd1 Mon Sep 17 00:00:00 2001 From: Vijay Pai Date: Fri, 10 Nov 2017 16:28:29 -0800 Subject: [PATCH 2/8] Restructure plugin builds to avoid building more than needed --- BUILD | 88 ++++++++++++++++--------- bazel/grpc_build_system.bzl | 4 +- src/compiler/protobuf_plugin.h | 1 - src/compiler/python_generator_helpers.h | 2 - 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/BUILD b/BUILD index bcb75af0321..a32aa8d8ddf 100644 --- a/BUILD +++ b/BUILD @@ -311,36 +311,12 @@ grpc_cc_library( grpc_cc_library( name = "grpc_plugin_support", - srcs = [ - "src/compiler/cpp_generator.cc", - "src/compiler/csharp_generator.cc", - "src/compiler/node_generator.cc", - "src/compiler/objective_c_generator.cc", - "src/compiler/php_generator.cc", - "src/compiler/python_generator.cc", - "src/compiler/ruby_generator.cc", - ], hdrs = [ "src/compiler/config.h", - "src/compiler/cpp_generator.h", "src/compiler/cpp_generator_helpers.h", - "src/compiler/csharp_generator.h", - "src/compiler/csharp_generator_helpers.h", "src/compiler/generator_helpers.h", - "src/compiler/node_generator.h", - "src/compiler/node_generator_helpers.h", - "src/compiler/objective_c_generator.h", - "src/compiler/objective_c_generator_helpers.h", - "src/compiler/php_generator.h", - "src/compiler/php_generator_helpers.h", "src/compiler/protobuf_plugin.h", - "src/compiler/python_generator.h", "src/compiler/python_generator_helpers.h", - "src/compiler/python_private_generator.h", - "src/compiler/ruby_generator.h", - "src/compiler/ruby_generator_helpers-inl.h", - "src/compiler/ruby_generator_map-inl.h", - "src/compiler/ruby_generator_string-inl.h", "src/compiler/schema_interface.h", ], external_deps = [ @@ -354,43 +330,93 @@ grpc_cc_library( grpc_proto_plugin( name = "grpc_cpp_plugin", - srcs = ["src/compiler/cpp_plugin.cc"], + srcs = [ + "src/compiler/cpp_generator.cc", + "src/compiler/cpp_plugin.cc", + ], + hdrs = [ + "src/compiler/cpp_generator.h", + ], deps = [":grpc_plugin_support"], ) grpc_proto_plugin( name = "grpc_csharp_plugin", - srcs = ["src/compiler/csharp_plugin.cc"], + srcs = [ + "src/compiler/csharp_generator.cc", + "src/compiler/csharp_plugin.cc", + ], + hdrs = [ + "src/compiler/csharp_generator.h", + "src/compiler/csharp_generator_helpers.h", + ], deps = [":grpc_plugin_support"], ) grpc_proto_plugin( name = "grpc_node_plugin", - srcs = ["src/compiler/node_plugin.cc"], + srcs = [ + "src/compiler/node_generator.cc", + "src/compiler/node_plugin.cc", + ], + hdrs = [ + "src/compiler/node_generator.h", + "src/compiler/node_generator_helpers.h", + ], deps = [":grpc_plugin_support"], ) grpc_proto_plugin( name = "grpc_objective_c_plugin", - srcs = ["src/compiler/objective_c_plugin.cc"], + srcs = [ + "src/compiler/objective_c_generator.cc", + "src/compiler/objective_c_plugin.cc", + ], + hdrs = [ + "src/compiler/objective_c_generator.h", + "src/compiler/objective_c_generator_helpers.h", + ], deps = [":grpc_plugin_support"], ) grpc_proto_plugin( name = "grpc_php_plugin", - srcs = ["src/compiler/php_plugin.cc"], + srcs = [ + "src/compiler/php_generator.cc", + "src/compiler/php_plugin.cc", + ], + hdrs = [ + "src/compiler/php_generator.h", + "src/compiler/php_generator_helpers.h", + ], deps = [":grpc_plugin_support"], ) grpc_proto_plugin( name = "grpc_python_plugin", - srcs = ["src/compiler/python_plugin.cc"], + srcs = [ + "src/compiler/python_generator.cc", + "src/compiler/python_plugin.cc", + ], + hdrs = [ + "src/compiler/python_generator.h", + "src/compiler/python_private_generator.h", + ], deps = [":grpc_plugin_support"], ) grpc_proto_plugin( name = "grpc_ruby_plugin", - srcs = ["src/compiler/ruby_plugin.cc"], + srcs = [ + "src/compiler/ruby_generator.cc", + "src/compiler/ruby_plugin.cc", + ], + hdrs = [ + "src/compiler/ruby_generator.h", + "src/compiler/ruby_generator_helpers-inl.h", + "src/compiler/ruby_generator_map-inl.h", + "src/compiler/ruby_generator_string-inl.h", + ], deps = [":grpc_plugin_support"], ) diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl index 6cfed6b0e72..25d7607e0fe 100644 --- a/bazel/grpc_build_system.bzl +++ b/bazel/grpc_build_system.bzl @@ -45,10 +45,10 @@ def grpc_cc_library(name, srcs = [], public_hdrs = [], hdrs = [], alwayslink = alwayslink, ) -def grpc_proto_plugin(name, srcs = [], deps = []): +def grpc_proto_plugin(name, srcs = [], hdrs = [], deps = []): native.cc_binary( name = name, - srcs = srcs, + srcs = srcs + hdrs, deps = deps, ) diff --git a/src/compiler/protobuf_plugin.h b/src/compiler/protobuf_plugin.h index 1551908156a..0f22dd4f336 100644 --- a/src/compiler/protobuf_plugin.h +++ b/src/compiler/protobuf_plugin.h @@ -22,7 +22,6 @@ #include "src/compiler/config.h" #include "src/compiler/cpp_generator_helpers.h" #include "src/compiler/python_generator_helpers.h" -#include "src/compiler/python_private_generator.h" #include "src/compiler/schema_interface.h" #include diff --git a/src/compiler/python_generator_helpers.h b/src/compiler/python_generator_helpers.h index b1b58befdf4..5ecd192163f 100644 --- a/src/compiler/python_generator_helpers.h +++ b/src/compiler/python_generator_helpers.h @@ -26,8 +26,6 @@ #include "src/compiler/config.h" #include "src/compiler/generator_helpers.h" -#include "src/compiler/python_generator.h" -#include "src/compiler/python_private_generator.h" using grpc::protobuf::Descriptor; using grpc::protobuf::FileDescriptor; From 3f6b10afba43e2855efd2fbeb3f1f4ea6c2a9657 Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Sat, 28 Oct 2017 16:57:29 -0700 Subject: [PATCH 3/8] Use existing read buffer in security handshaker if present --- CMakeLists.txt | 38 +++ Makefile | 42 ++++ build.yaml | 17 ++ .../security/transport/security_handshaker.cc | 45 ++-- .../readahead_handshaker_server_ssl.c | 103 ++++++++ test/core/handshake/server_ssl.c | 200 +-------------- test/core/handshake/server_ssl_common.c | 237 ++++++++++++++++++ test/core/handshake/server_ssl_common.h | 22 ++ .../generated/sources_and_headers.json | 21 +- tools/run_tests/generated/tests.json | 20 ++ 10 files changed, 525 insertions(+), 220 deletions(-) create mode 100644 test/core/handshake/readahead_handshaker_server_ssl.c create mode 100644 test/core/handshake/server_ssl_common.c create mode 100644 test/core/handshake/server_ssl_common.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ac6c9b63d9..b3cbbbaea53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -466,6 +466,9 @@ endif() if(_gRPC_PLATFORM_LINUX) add_dependencies(buildtests_c handshake_server) endif() +if(_gRPC_PLATFORM_LINUX) +add_dependencies(buildtests_c handshake_server_with_readahead_handshaker) +endif() add_dependencies(buildtests_c hpack_parser_test) add_dependencies(buildtests_c hpack_table_test) add_dependencies(buildtests_c http_parser_test) @@ -7395,6 +7398,7 @@ if(_gRPC_PLATFORM_LINUX) add_executable(handshake_server test/core/handshake/server_ssl.c + test/core/handshake/server_ssl_common.c ) @@ -7421,6 +7425,40 @@ target_link_libraries(handshake_server gpr ) +endif() +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX) + +add_executable(handshake_server_with_readahead_handshaker + test/core/handshake/readahead_handshaker_server_ssl.c + test/core/handshake/server_ssl_common.c +) + + +target_include_directories(handshake_server_with_readahead_handshaker + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/abseil-cpp +) + +target_link_libraries(handshake_server_with_readahead_handshaker + ${_gRPC_SSL_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc + gpr_test_util + gpr +) + endif() endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) diff --git a/Makefile b/Makefile index 325c9123d99..7c0ee0d17ea 100644 --- a/Makefile +++ b/Makefile @@ -1023,6 +1023,7 @@ grpc_ssl_credentials_test: $(BINDIR)/$(CONFIG)/grpc_ssl_credentials_test grpc_verify_jwt: $(BINDIR)/$(CONFIG)/grpc_verify_jwt handshake_client: $(BINDIR)/$(CONFIG)/handshake_client handshake_server: $(BINDIR)/$(CONFIG)/handshake_server +handshake_server_with_readahead_handshaker: $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker hpack_parser_fuzzer_test: $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test hpack_parser_test: $(BINDIR)/$(CONFIG)/hpack_parser_test hpack_table_test: $(BINDIR)/$(CONFIG)/hpack_table_test @@ -1417,6 +1418,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/grpc_ssl_credentials_test \ $(BINDIR)/$(CONFIG)/handshake_client \ $(BINDIR)/$(CONFIG)/handshake_server \ + $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker \ $(BINDIR)/$(CONFIG)/hpack_parser_test \ $(BINDIR)/$(CONFIG)/hpack_table_test \ $(BINDIR)/$(CONFIG)/http_parser_test \ @@ -1894,6 +1896,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/handshake_client || ( echo test handshake_client failed ; exit 1 ) $(E) "[RUN] Testing handshake_server" $(Q) $(BINDIR)/$(CONFIG)/handshake_server || ( echo test handshake_server failed ; exit 1 ) + $(E) "[RUN] Testing handshake_server_with_readahead_handshaker" + $(Q) $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker || ( echo test handshake_server_with_readahead_handshaker failed ; exit 1 ) $(E) "[RUN] Testing hpack_parser_test" $(Q) $(BINDIR)/$(CONFIG)/hpack_parser_test || ( echo test hpack_parser_test failed ; exit 1 ) $(E) "[RUN] Testing hpack_table_test" @@ -11221,6 +11225,7 @@ endif HANDSHAKE_SERVER_SRC = \ test/core/handshake/server_ssl.c \ + test/core/handshake/server_ssl_common.c \ HANDSHAKE_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_SERVER_SRC)))) ifeq ($(NO_SECURE),true) @@ -11242,6 +11247,8 @@ endif $(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +$(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl_common.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + deps_handshake_server: $(HANDSHAKE_SERVER_OBJS:.o=.dep) ifneq ($(NO_SECURE),true) @@ -11251,6 +11258,41 @@ endif endif +HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_SRC = \ + test/core/handshake/readahead_handshaker_server_ssl.c \ + test/core/handshake/server_ssl_common.c \ + +HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker: $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker + +endif + +$(OBJDIR)/$(CONFIG)/test/core/handshake/readahead_handshaker_server_ssl.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl_common.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_handshake_server_with_readahead_handshaker: $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS:.o=.dep) +endif +endif + + HPACK_PARSER_FUZZER_TEST_SRC = \ test/core/transport/chttp2/hpack_parser_fuzzer_test.c \ diff --git a/build.yaml b/build.yaml index 592cbb9f8c8..adefb5d6d73 100644 --- a/build.yaml +++ b/build.yaml @@ -2579,6 +2579,23 @@ targets: language: c src: - test/core/handshake/server_ssl.c + - test/core/handshake/server_ssl_common.c + deps: + - grpc_test_util + - grpc + - gpr_test_util + - gpr + exclude_iomgrs: + - uv + platforms: + - linux + secure: true +- name: handshake_server_with_readahead_handshaker + build: test + language: c + src: + - test/core/handshake/readahead_handshaker_server_ssl.c + - test/core/handshake/server_ssl_common.c deps: - grpc_test_util - grpc diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc index 3d196056177..b42f2699ae4 100644 --- a/src/core/lib/security/transport/security_handshaker.cc +++ b/src/core/lib/security/transport/security_handshaker.cc @@ -65,6 +65,25 @@ typedef struct { tsi_handshaker_result *handshaker_result; } security_handshaker; +static size_t move_read_buffer_into_handshake_buffer(grpc_exec_ctx *exec_ctx, + security_handshaker *h) { + size_t bytes_in_read_buffer = h->args->read_buffer->length; + if (h->handshake_buffer_size < bytes_in_read_buffer) { + h->handshake_buffer = + (uint8_t *)gpr_realloc(h->handshake_buffer, bytes_in_read_buffer); + h->handshake_buffer_size = bytes_in_read_buffer; + } + size_t offset = 0; + while (h->args->read_buffer->count > 0) { + grpc_slice next_slice = grpc_slice_buffer_take_first(h->args->read_buffer); + memcpy(h->handshake_buffer + offset, GRPC_SLICE_START_PTR(next_slice), + GRPC_SLICE_LENGTH(next_slice)); + offset += GRPC_SLICE_LENGTH(next_slice); + grpc_slice_unref_internal(exec_ctx, next_slice); + } + return bytes_in_read_buffer; +} + static void security_handshaker_unref(grpc_exec_ctx *exec_ctx, security_handshaker *h) { if (gpr_unref(&h->refs)) { @@ -177,8 +196,6 @@ static void on_peer_checked_inner(grpc_exec_ctx *exec_ctx, } tsi_handshaker_result_destroy(h->handshaker_result); h->handshaker_result = NULL; - // Clear out the read buffer before it gets passed to the transport. - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, h->args->read_buffer); // Add auth context to channel args. grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context); grpc_channel_args *tmp_args = h->args->args; @@ -311,23 +328,8 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, return; } // Copy all slices received. - size_t i; - size_t bytes_received_size = 0; - for (i = 0; i < h->args->read_buffer->count; i++) { - bytes_received_size += GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); - } - if (bytes_received_size > h->handshake_buffer_size) { - h->handshake_buffer = - (uint8_t *)gpr_realloc(h->handshake_buffer, bytes_received_size); - h->handshake_buffer_size = bytes_received_size; - } - size_t offset = 0; - for (i = 0; i < h->args->read_buffer->count; i++) { - size_t slice_size = GPR_SLICE_LENGTH(h->args->read_buffer->slices[i]); - memcpy(h->handshake_buffer + offset, - GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]), slice_size); - offset += slice_size; - } + size_t bytes_received_size = + move_read_buffer_into_handshake_buffer(exec_ctx, h); // Call TSI handshaker. error = do_handshaker_next_locked(exec_ctx, h, h->handshake_buffer, bytes_received_size); @@ -403,7 +405,10 @@ static void security_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, h->args = args; h->on_handshake_done = on_handshake_done; gpr_ref(&h->refs); - grpc_error *error = do_handshaker_next_locked(exec_ctx, h, NULL, 0); + size_t bytes_received_size = + move_read_buffer_into_handshake_buffer(exec_ctx, h); + grpc_error *error = do_handshaker_next_locked( + exec_ctx, h, h->handshake_buffer, bytes_received_size); if (error != GRPC_ERROR_NONE) { security_handshake_failed_locked(exec_ctx, h, error); gpr_mu_unlock(&h->mu); diff --git a/test/core/handshake/readahead_handshaker_server_ssl.c b/test/core/handshake/readahead_handshaker_server_ssl.c new file mode 100644 index 00000000000..f25c35615c6 --- /dev/null +++ b/test/core/handshake/readahead_handshaker_server_ssl.c @@ -0,0 +1,103 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "src/core/lib/iomgr/load_file.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#include "src/core/lib/channel/handshaker_factory.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/security/transport/security_handshaker.h" + +#include "test/core/handshake/server_ssl_common.h" + +/* The purpose of this test is to exercise the case when a + * grpc *security_handshaker* begins its handshake with data already + * in the read buffer of the handshaker arg. This scenario is created by + * adding a fake "readahead" handshaker at the beginning of the server's + * handshaker list, which just reads from the connection and then places + * read bytes into the read buffer of the handshake arg (to be passed down + * to the security_handshaker). This test is meant to protect code relying on + * this functionality that lives outside of this repo. */ + +static void readahead_handshaker_destroy(grpc_exec_ctx *ctx, + grpc_handshaker *handshaker) { + gpr_free(handshaker); +} + +static void readahead_handshaker_shutdown(grpc_exec_ctx *ctx, + grpc_handshaker *handshaker, + grpc_error *error) {} + +static void readahead_handshaker_do_handshake( + grpc_exec_ctx *ctx, grpc_handshaker *handshaker, + grpc_tcp_server_acceptor *acceptor, grpc_closure *on_handshake_done, + grpc_handshaker_args *args) { + grpc_endpoint_read(ctx, args->endpoint, args->read_buffer, on_handshake_done); +} + +const grpc_handshaker_vtable readahead_handshaker_vtable = { + readahead_handshaker_destroy, readahead_handshaker_shutdown, + readahead_handshaker_do_handshake}; + +static grpc_handshaker *readahead_handshaker_create(grpc_exec_ctx *ctx) { + grpc_handshaker *h = (grpc_handshaker *)gpr_zalloc(sizeof(grpc_handshaker)); + grpc_handshaker_init(&readahead_handshaker_vtable, h); + return h; +} + +static void readahead_handshaker_factory_add_handshakers( + grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *hf, + const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) { + grpc_handshake_manager_add(handshake_mgr, + readahead_handshaker_create(exec_ctx)); +} + +static void readahead_handshaker_factory_destroy( + grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) {} + +static const grpc_handshaker_factory_vtable + readahead_handshaker_factory_vtable = { + readahead_handshaker_factory_add_handshakers, + readahead_handshaker_factory_destroy}; + +int main(int argc, char *argv[]) { + grpc_handshaker_factory readahead_handshaker_factory = { + &readahead_handshaker_factory_vtable}; + grpc_init(); + grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_SERVER, + &readahead_handshaker_factory); + const char *full_alpn_list[] = {"grpc-exp", "h2"}; + GPR_ASSERT(server_ssl_test(full_alpn_list, 2, "grpc-exp")); + grpc_shutdown(); + return 0; +} diff --git a/test/core/handshake/server_ssl.c b/test/core/handshake/server_ssl.c index 85a8b4de412..8b960763386 100644 --- a/test/core/handshake/server_ssl.c +++ b/test/core/handshake/server_ssl.c @@ -34,205 +34,7 @@ #include "test/core/util/port.h" #include "test/core/util/test_config.h" -#define SSL_CERT_PATH "src/core/tsi/test_creds/server1.pem" -#define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key" -#define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem" - -// Handshake completed signal to server thread. -static gpr_event client_handshake_complete; - -static int create_socket(int port) { - int s; - struct sockaddr_in addr; - - addr.sin_family = AF_INET; - addr.sin_port = htons((uint16_t)port); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) { - perror("Unable to create socket"); - return -1; - } - - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - perror("Unable to connect"); - return -1; - } - - return s; -} - -// Simple gRPC server. This listens until client_handshake_complete occurs. -static void server_thread(void *arg) { - const int port = *(int *)arg; - - // Load key pair and establish server SSL credentials. - grpc_ssl_pem_key_cert_pair pem_key_cert_pair; - grpc_slice ca_slice, cert_slice, key_slice; - GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(SSL_CA_PATH, 1, &ca_slice))); - GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(SSL_CERT_PATH, 1, &cert_slice))); - GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(SSL_KEY_PATH, 1, &key_slice))); - const char *ca_cert = (const char *)GRPC_SLICE_START_PTR(ca_slice); - pem_key_cert_pair.private_key = (const char *)GRPC_SLICE_START_PTR(key_slice); - pem_key_cert_pair.cert_chain = (const char *)GRPC_SLICE_START_PTR(cert_slice); - grpc_server_credentials *ssl_creds = grpc_ssl_server_credentials_create( - ca_cert, &pem_key_cert_pair, 1, 0, NULL); - - // Start server listening on local port. - char *addr; - gpr_asprintf(&addr, "127.0.0.1:%d", port); - grpc_server *server = grpc_server_create(NULL, NULL); - GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds)); - free(addr); - - grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL); - - grpc_server_register_completion_queue(server, cq, NULL); - grpc_server_start(server); - - // Wait a bounded number of time until client_handshake_complete is set, - // sleeping between polls. - int retries = 10; - while (!gpr_event_get(&client_handshake_complete) && retries-- > 0) { - const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(1); - grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL); - GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT); - } - - gpr_log(GPR_INFO, "Shutting down server"); - grpc_server_shutdown_and_notify(server, cq, NULL); - grpc_completion_queue_shutdown(cq); - - const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(5); - grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL); - GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); - - grpc_server_destroy(server); - grpc_completion_queue_destroy(cq); - grpc_server_credentials_release(ssl_creds); - grpc_slice_unref(cert_slice); - grpc_slice_unref(key_slice); - grpc_slice_unref(ca_slice); -} - -// This test launches a gRPC server on a separate thread and then establishes a -// TLS handshake via a minimal TLS client. The TLS client has configurable (via -// alpn_list) ALPN settings and can probe at the supported ALPN preferences -// using this (via alpn_expected). -static bool server_ssl_test(const char *alpn_list[], unsigned int alpn_list_len, - const char *alpn_expected) { - bool success = true; - - grpc_init(); - int port = grpc_pick_unused_port_or_die(); - gpr_event_init(&client_handshake_complete); - - // Launch the gRPC server thread. - gpr_thd_options thdopt = gpr_thd_options_default(); - gpr_thd_id thdid; - gpr_thd_options_set_joinable(&thdopt); - GPR_ASSERT(gpr_thd_new(&thdid, server_thread, &port, &thdopt)); - - SSL_load_error_strings(); - OpenSSL_add_ssl_algorithms(); - - const SSL_METHOD *method = TLSv1_2_client_method(); - SSL_CTX *ctx = SSL_CTX_new(method); - if (!ctx) { - perror("Unable to create SSL context"); - ERR_print_errors_fp(stderr); - abort(); - } - - // Load key pair. - if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) { - ERR_print_errors_fp(stderr); - abort(); - } - if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) { - ERR_print_errors_fp(stderr); - abort(); - } - - // Set the cipher list to match the one expressed in - // src/core/tsi/ssl_transport_security.c. - const char *cipher_list = - "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-" - "SHA384:ECDHE-RSA-AES256-GCM-SHA384"; - if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) { - ERR_print_errors_fp(stderr); - gpr_log(GPR_ERROR, "Couldn't set server cipher list."); - abort(); - } - - // Configure ALPN list the client will send to the server. This must match the - // wire format, see documentation for SSL_CTX_set_alpn_protos. - unsigned int alpn_protos_len = alpn_list_len; - for (unsigned int i = 0; i < alpn_list_len; ++i) { - alpn_protos_len += (unsigned int)strlen(alpn_list[i]); - } - unsigned char *alpn_protos = gpr_malloc(alpn_protos_len); - unsigned char *p = alpn_protos; - for (unsigned int i = 0; i < alpn_list_len; ++i) { - const uint8_t len = (uint8_t)strlen(alpn_list[i]); - *p++ = len; - memcpy(p, alpn_list[i], len); - p += len; - } - GPR_ASSERT(SSL_CTX_set_alpn_protos(ctx, alpn_protos, alpn_protos_len) == 0); - - // Try and connect to server. We allow a bounded number of retries as we might - // be racing with the server setup on its separate thread. - int retries = 10; - int sock = -1; - while (sock == -1 && retries-- > 0) { - sock = create_socket(port); - if (sock < 0) { - sleep(1); - } - } - GPR_ASSERT(sock > 0); - gpr_log(GPR_INFO, "Connected to server on port %d", port); - - // Establish a SSL* and connect at SSL layer. - SSL *ssl = SSL_new(ctx); - GPR_ASSERT(ssl); - SSL_set_fd(ssl, sock); - if (SSL_connect(ssl) <= 0) { - ERR_print_errors_fp(stderr); - gpr_log(GPR_ERROR, "Handshake failed."); - success = false; - } else { - gpr_log(GPR_INFO, "Handshake successful."); - // Validate ALPN preferred by server matches alpn_expected. - const unsigned char *alpn_selected; - unsigned int alpn_selected_len; - SSL_get0_alpn_selected(ssl, &alpn_selected, &alpn_selected_len); - if (strlen(alpn_expected) != alpn_selected_len || - strncmp((const char *)alpn_selected, alpn_expected, - alpn_selected_len) != 0) { - gpr_log(GPR_ERROR, "Unexpected ALPN protocol preference"); - success = false; - } - } - gpr_event_set(&client_handshake_complete, &client_handshake_complete); - - SSL_free(ssl); - gpr_free(alpn_protos); - SSL_CTX_free(ctx); - EVP_cleanup(); - close(sock); - - gpr_thd_join(thdid); - - grpc_shutdown(); - - return success; -} +#include "test/core/handshake/server_ssl_common.h" int main(int argc, char *argv[]) { // Handshake succeeeds when the client supplies the standard ALPN list. diff --git a/test/core/handshake/server_ssl_common.c b/test/core/handshake/server_ssl_common.c new file mode 100644 index 00000000000..d1911ecfde4 --- /dev/null +++ b/test/core/handshake/server_ssl_common.c @@ -0,0 +1,237 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "test/core/handshake/server_ssl_common.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "src/core/lib/iomgr/load_file.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#define SSL_CERT_PATH "src/core/tsi/test_creds/server1.pem" +#define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key" +#define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem" + +// Handshake completed signal to server thread. +static gpr_event client_handshake_complete; + +static int create_socket(int port) { + int s; + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = htons((uint16_t)port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("Unable to create socket"); + return -1; + } + + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Unable to connect"); + return -1; + } + + return s; +} + +// Simple gRPC server. This listens until client_handshake_complete occurs. +static void server_thread(void *arg) { + const int port = *(int *)arg; + + // Load key pair and establish server SSL credentials. + grpc_ssl_pem_key_cert_pair pem_key_cert_pair; + grpc_slice ca_slice, cert_slice, key_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CA_PATH, 1, &ca_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CERT_PATH, 1, &cert_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_KEY_PATH, 1, &key_slice))); + const char *ca_cert = (const char *)GRPC_SLICE_START_PTR(ca_slice); + pem_key_cert_pair.private_key = (const char *)GRPC_SLICE_START_PTR(key_slice); + pem_key_cert_pair.cert_chain = (const char *)GRPC_SLICE_START_PTR(cert_slice); + grpc_server_credentials *ssl_creds = grpc_ssl_server_credentials_create( + ca_cert, &pem_key_cert_pair, 1, 0, NULL); + + // Start server listening on local port. + char *addr; + gpr_asprintf(&addr, "127.0.0.1:%d", port); + grpc_server *server = grpc_server_create(NULL, NULL); + GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds)); + free(addr); + + grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL); + + grpc_server_register_completion_queue(server, cq, NULL); + grpc_server_start(server); + + // Wait a bounded number of time until client_handshake_complete is set, + // sleeping between polls. + int retries = 10; + while (!gpr_event_get(&client_handshake_complete) && retries-- > 0) { + const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(1); + grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL); + GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT); + } + + gpr_log(GPR_INFO, "Shutting down server"); + grpc_server_shutdown_and_notify(server, cq, NULL); + grpc_completion_queue_shutdown(cq); + + const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(5); + grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, NULL); + GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); + + grpc_server_destroy(server); + grpc_completion_queue_destroy(cq); + grpc_server_credentials_release(ssl_creds); + grpc_slice_unref(cert_slice); + grpc_slice_unref(key_slice); + grpc_slice_unref(ca_slice); +} + +// This test launches a gRPC server on a separate thread and then establishes a +// TLS handshake via a minimal TLS client. The TLS client has configurable (via +// alpn_list) ALPN settings and can probe at the supported ALPN preferences +// using this (via alpn_expected). +bool server_ssl_test(const char *alpn_list[], unsigned int alpn_list_len, + const char *alpn_expected) { + bool success = true; + + grpc_init(); + int port = grpc_pick_unused_port_or_die(); + gpr_event_init(&client_handshake_complete); + + // Launch the gRPC server thread. + gpr_thd_options thdopt = gpr_thd_options_default(); + gpr_thd_id thdid; + gpr_thd_options_set_joinable(&thdopt); + GPR_ASSERT(gpr_thd_new(&thdid, server_thread, &port, &thdopt)); + + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + + const SSL_METHOD *method = TLSv1_2_client_method(); + SSL_CTX *ctx = SSL_CTX_new(method); + if (!ctx) { + perror("Unable to create SSL context"); + ERR_print_errors_fp(stderr); + abort(); + } + + // Load key pair. + if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) { + ERR_print_errors_fp(stderr); + abort(); + } + if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) { + ERR_print_errors_fp(stderr); + abort(); + } + + // Set the cipher list to match the one expressed in + // src/core/tsi/ssl_transport_security.c. + const char *cipher_list = + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-" + "SHA384:ECDHE-RSA-AES256-GCM-SHA384"; + if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) { + ERR_print_errors_fp(stderr); + gpr_log(GPR_ERROR, "Couldn't set server cipher list."); + abort(); + } + + // Configure ALPN list the client will send to the server. This must match the + // wire format, see documentation for SSL_CTX_set_alpn_protos. + unsigned int alpn_protos_len = alpn_list_len; + for (unsigned int i = 0; i < alpn_list_len; ++i) { + alpn_protos_len += (unsigned int)strlen(alpn_list[i]); + } + unsigned char *alpn_protos = gpr_malloc(alpn_protos_len); + unsigned char *p = alpn_protos; + for (unsigned int i = 0; i < alpn_list_len; ++i) { + const uint8_t len = (uint8_t)strlen(alpn_list[i]); + *p++ = len; + memcpy(p, alpn_list[i], len); + p += len; + } + GPR_ASSERT(SSL_CTX_set_alpn_protos(ctx, alpn_protos, alpn_protos_len) == 0); + + // Try and connect to server. We allow a bounded number of retries as we might + // be racing with the server setup on its separate thread. + int retries = 10; + int sock = -1; + while (sock == -1 && retries-- > 0) { + sock = create_socket(port); + if (sock < 0) { + sleep(1); + } + } + GPR_ASSERT(sock > 0); + gpr_log(GPR_INFO, "Connected to server on port %d", port); + + // Establish a SSL* and connect at SSL layer. + SSL *ssl = SSL_new(ctx); + GPR_ASSERT(ssl); + SSL_set_fd(ssl, sock); + if (SSL_connect(ssl) <= 0) { + ERR_print_errors_fp(stderr); + gpr_log(GPR_ERROR, "Handshake failed."); + success = false; + } else { + gpr_log(GPR_INFO, "Handshake successful."); + // Validate ALPN preferred by server matches alpn_expected. + const unsigned char *alpn_selected; + unsigned int alpn_selected_len; + SSL_get0_alpn_selected(ssl, &alpn_selected, &alpn_selected_len); + if (strlen(alpn_expected) != alpn_selected_len || + strncmp((const char *)alpn_selected, alpn_expected, + alpn_selected_len) != 0) { + gpr_log(GPR_ERROR, "Unexpected ALPN protocol preference"); + success = false; + } + } + gpr_event_set(&client_handshake_complete, &client_handshake_complete); + + SSL_free(ssl); + gpr_free(alpn_protos); + SSL_CTX_free(ctx); + EVP_cleanup(); + close(sock); + + gpr_thd_join(thdid); + + grpc_shutdown(); + + return success; +} diff --git a/test/core/handshake/server_ssl_common.h b/test/core/handshake/server_ssl_common.h new file mode 100644 index 00000000000..67b323d82f9 --- /dev/null +++ b/test/core/handshake/server_ssl_common.h @@ -0,0 +1,22 @@ +// +// Created by apolcyn on 10/31/17. +// + +#ifndef GRPC_SERVER_SSL_COMMON_H +#define GRPC_SERVER_SSL_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include "src/core/lib/iomgr/load_file.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +bool server_ssl_test(const char *alpn_list[], unsigned int alpn_list_len, + const char *alpn_expected); + +#endif // GRPC_SERVER_SSL_COMMON_H diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 46f4cb65329..9f5e39e0542 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -1280,7 +1280,26 @@ "language": "c", "name": "handshake_server", "src": [ - "test/core/handshake/server_ssl.c" + "test/core/handshake/server_ssl.c", + "test/core/handshake/server_ssl_common.c" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc_test_util" + ], + "headers": [], + "is_filegroup": false, + "language": "c", + "name": "handshake_server_with_readahead_handshaker", + "src": [ + "test/core/handshake/readahead_handshaker_server_ssl.c", + "test/core/handshake/server_ssl_common.c" ], "third_party": false, "type": "target" diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 3b1b6587f63..7f197b4ba00 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1599,6 +1599,26 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [ + "uv" + ], + "flaky": false, + "gtest": false, + "language": "c", + "name": "handshake_server_with_readahead_handshaker", + "platforms": [ + "linux" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, From f51b7e16ee96cbcd23a85aef982ca7a4bada2727 Mon Sep 17 00:00:00 2001 From: Alexander Polcyn Date: Mon, 13 Nov 2017 21:05:32 -0800 Subject: [PATCH 4/8] Fix missing copyright, fix merge conflict in updated BUILD --- build.yaml | 4 +++ test/core/handshake/BUILD | 31 +++++++++++++++++++ test/core/handshake/server_ssl_common.h | 20 ++++++++++-- .../generated/sources_and_headers.json | 14 ++++++--- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/build.yaml b/build.yaml index 083f5e9d9b7..910b00ebe2b 100644 --- a/build.yaml +++ b/build.yaml @@ -2511,6 +2511,8 @@ targets: - name: handshake_server build: test language: c + headers: + - test/core/handshake/server_ssl_common.h src: - test/core/handshake/server_ssl.cc - test/core/handshake/server_ssl_common.cc @@ -2527,6 +2529,8 @@ targets: - name: handshake_server_with_readahead_handshaker build: test language: c + headers: + - test/core/handshake/server_ssl_common.h src: - test/core/handshake/readahead_handshaker_server_ssl.cc - test/core/handshake/server_ssl_common.cc diff --git a/test/core/handshake/BUILD b/test/core/handshake/BUILD index aea4a27e999..a3276b9343b 100644 --- a/test/core/handshake/BUILD +++ b/test/core/handshake/BUILD @@ -35,6 +35,18 @@ grpc_cc_test( ], ) +grpc_cc_library( + name = "server_ssl_common", + hdrs = ["server_ssl_common.h"], + srcs = ["server_ssl_common.cc"], + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + ], +) + grpc_cc_test( name = "server_ssl", srcs = ["server_ssl.cc"], @@ -45,6 +57,25 @@ grpc_cc_test( "//src/core/tsi/test_creds:server1.pem", ], deps = [ + ":server_ssl_common", + "//:gpr", + "//:grpc", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + ], +) + +grpc_cc_test( + name = "handshake_server_with_readahead_handshaker", + srcs = ["readahead_handshaker_server_ssl.cc"], + language = "C++", + data = [ + "//src/core/tsi/test_creds:ca.pem", + "//src/core/tsi/test_creds:server1.key", + "//src/core/tsi/test_creds:server1.pem", + ], + deps = [ + ":server_ssl_common", "//:gpr", "//:grpc", "//test/core/util:gpr_test_util", diff --git a/test/core/handshake/server_ssl_common.h b/test/core/handshake/server_ssl_common.h index 94ac588f0e9..77865a408f5 100644 --- a/test/core/handshake/server_ssl_common.h +++ b/test/core/handshake/server_ssl_common.h @@ -1,6 +1,20 @@ -// -// Created by apolcyn on 10/31/17. -// +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ #ifndef GRPC_SERVER_SSL_COMMON_H #define GRPC_SERVER_SSL_COMMON_H diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 6d361c571b5..fce01bf8ff3 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -1207,13 +1207,16 @@ "grpc", "grpc_test_util" ], - "headers": [], + "headers": [ + "test/core/handshake/server_ssl_common.h" + ], "is_filegroup": false, "language": "c", "name": "handshake_server", "src": [ "test/core/handshake/server_ssl.cc", - "test/core/handshake/server_ssl_common.cc" + "test/core/handshake/server_ssl_common.cc", + "test/core/handshake/server_ssl_common.h" ], "third_party": false, "type": "target" @@ -1225,13 +1228,16 @@ "grpc", "grpc_test_util" ], - "headers": [], + "headers": [ + "test/core/handshake/server_ssl_common.h" + ], "is_filegroup": false, "language": "c", "name": "handshake_server_with_readahead_handshaker", "src": [ "test/core/handshake/readahead_handshaker_server_ssl.cc", - "test/core/handshake/server_ssl_common.cc" + "test/core/handshake/server_ssl_common.cc", + "test/core/handshake/server_ssl_common.h" ], "third_party": false, "type": "target" From 3a1989b1b99267a994b7baa9de9629955c2c3bbe Mon Sep 17 00:00:00 2001 From: Juanli Shen Date: Tue, 14 Nov 2017 11:38:11 -0800 Subject: [PATCH 5/8] Fix an internal build failure --- test/core/util/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/test/core/util/BUILD b/test/core/util/BUILD index bc6fe9dd64b..24379234351 100644 --- a/test/core/util/BUILD +++ b/test/core/util/BUILD @@ -103,6 +103,7 @@ grpc_cc_library( grpc_cc_library( name = "fuzzer_corpus_test", + testonly = 1, srcs = ["fuzzer_corpus_test.cc"], deps = [ ":gpr_test_util", From 2cb57e5ef4a6e8a06d87e38f09a00c8245d6a6f9 Mon Sep 17 00:00:00 2001 From: Vijay Pai Date: Tue, 14 Nov 2017 11:48:11 -0800 Subject: [PATCH 6/8] Avalanching operations on completion queue should be private, not API --- .../grpc++/impl/codegen/completion_queue.h | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/include/grpc++/impl/codegen/completion_queue.h b/include/grpc++/impl/codegen/completion_queue.h index 11cc588879f..b8a78625782 100644 --- a/include/grpc++/impl/codegen/completion_queue.h +++ b/include/grpc++/impl/codegen/completion_queue.h @@ -65,6 +65,7 @@ class CompletionQueue; class Server; class ServerBuilder; class ServerContext; +class ServerInterface; namespace internal { class CompletionQueueTag; @@ -187,21 +188,6 @@ class CompletionQueue : private GrpcLibraryCodegen { /// owership is performed. grpc_completion_queue* cq() { return cq_; } - /// Manage state of avalanching operations : completion queue tags that - /// trigger other completion queue operations. The underlying core completion - /// queue should not really shutdown until all avalanching operations have - /// been finalized. Note that we maintain the requirement that an avalanche - /// registration must take place before CQ shutdown (which must be maintained - /// elsehwere) - void InitialAvalanching() { - gpr_atm_rel_store(&avalanches_in_flight_, static_cast(1)); - } - void RegisterAvalanching() { - gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_, - static_cast(1)); - } - void CompleteAvalanching(); - protected: /// Private constructor of CompletionQueue only visible to friend classes CompletionQueue(const grpc_completion_queue_attributes& attributes) { @@ -238,6 +224,7 @@ class CompletionQueue : private GrpcLibraryCodegen { friend class ::grpc::internal::UnknownMethodHandler; friend class ::grpc::Server; friend class ::grpc::ServerContext; + friend class ::grpc::ServerInterface; template friend class ::grpc::internal::BlockingUnaryCallImpl; @@ -309,6 +296,21 @@ class CompletionQueue : private GrpcLibraryCodegen { GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok)); } + /// Manage state of avalanching operations : completion queue tags that + /// trigger other completion queue operations. The underlying core completion + /// queue should not really shutdown until all avalanching operations have + /// been finalized. Note that we maintain the requirement that an avalanche + /// registration must take place before CQ shutdown (which must be maintained + /// elsehwere) + void InitialAvalanching() { + gpr_atm_rel_store(&avalanches_in_flight_, static_cast(1)); + } + void RegisterAvalanching() { + gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_, + static_cast(1)); + } + void CompleteAvalanching(); + grpc_completion_queue* cq_; // owned gpr_atm avalanches_in_flight_; From 6b514e64ff5932c85bee0f7c568540b64f220c2c Mon Sep 17 00:00:00 2001 From: Vijay Pai Date: Tue, 14 Nov 2017 12:09:20 -0800 Subject: [PATCH 7/8] Remove unused import --- Makefile | 4 ++-- src/proto/grpc/testing/BUILD | 1 - src/proto/grpc/testing/services.proto | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 1e3418e4109..b98cf7b70ea 100644 --- a/Makefile +++ b/Makefile @@ -2499,12 +2499,12 @@ $(GENDIR)/src/proto/grpc/testing/services.pb.cc: protoc_dep_error $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc: protoc_dep_error else -$(GENDIR)/src/proto/grpc/testing/services.pb.cc: src/proto/grpc/testing/services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc +$(GENDIR)/src/proto/grpc/testing/services.pb.cc: src/proto/grpc/testing/services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(E) "[PROTOC] Generating protobuf CC file from $<" $(Q) mkdir -p `dirname $@` $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --cpp_out=$(GENDIR) $< -$(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc: src/proto/grpc/testing/services.proto $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc +$(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc: src/proto/grpc/testing/services.proto $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $< diff --git a/src/proto/grpc/testing/BUILD b/src/proto/grpc/testing/BUILD index 36d37822621..7c49fe2472a 100644 --- a/src/proto/grpc/testing/BUILD +++ b/src/proto/grpc/testing/BUILD @@ -76,7 +76,6 @@ grpc_proto_library( deps = [ "control_proto", "messages_proto", - "stats_proto", ], ) diff --git a/src/proto/grpc/testing/services.proto b/src/proto/grpc/testing/services.proto index 2e6583d99c5..93c21f42d11 100644 --- a/src/proto/grpc/testing/services.proto +++ b/src/proto/grpc/testing/services.proto @@ -18,7 +18,6 @@ syntax = "proto3"; import "src/proto/grpc/testing/messages.proto"; import "src/proto/grpc/testing/control.proto"; -import "src/proto/grpc/testing/stats.proto"; package grpc.testing; From e5d2139e1ea5d3671c1a5c40e3255340b3987f7b Mon Sep 17 00:00:00 2001 From: Juanli Shen Date: Tue, 14 Nov 2017 16:07:23 -0800 Subject: [PATCH 8/8] Add client channel stress test --- CMakeLists.txt | 49 +++ Makefile | 52 +++ build.yaml | 14 + test/cpp/client/BUILD | 51 +++ test/cpp/client/client_channel_stress_test.cc | 329 ++++++++++++++++++ .../generated/sources_and_headers.json | 23 ++ tools/run_tests/generated/tests.json | 24 ++ 7 files changed, 542 insertions(+) create mode 100644 test/cpp/client/BUILD create mode 100644 test/cpp/client/client_channel_stress_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index c20c97d61a2..048cee763af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -675,6 +675,7 @@ endif() add_dependencies(buildtests_cxx channel_arguments_test) add_dependencies(buildtests_cxx channel_filter_test) add_dependencies(buildtests_cxx cli_call_test) +add_dependencies(buildtests_cxx client_channel_stress_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx client_crash_test) endif() @@ -10028,6 +10029,54 @@ target_link_libraries(cli_call_test ${_gRPC_GFLAGS_LIBRARIES} ) +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) + +add_executable(client_channel_stress_test + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.h + test/cpp/client/client_channel_stress_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +protobuf_generate_grpc_cpp( + src/proto/grpc/lb/v1/load_balancer.proto +) + +target_include_directories(client_channel_stress_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/abseil-cpp + PRIVATE third_party/googletest/googletest/include + PRIVATE third_party/googletest/googletest + PRIVATE third_party/googletest/googlemock/include + PRIVATE third_party/googletest/googlemock + PRIVATE ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(client_channel_stress_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc++_test_util + grpc_test_util + grpc++ + grpc + gpr_test_util + gpr + ${_gRPC_GFLAGS_LIBRARIES} +) + endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) diff --git a/Makefile b/Makefile index ff26247548d..e9b4de96188 100644 --- a/Makefile +++ b/Makefile @@ -1113,6 +1113,7 @@ bm_pollset: $(BINDIR)/$(CONFIG)/bm_pollset channel_arguments_test: $(BINDIR)/$(CONFIG)/channel_arguments_test channel_filter_test: $(BINDIR)/$(CONFIG)/channel_filter_test cli_call_test: $(BINDIR)/$(CONFIG)/cli_call_test +client_channel_stress_test: $(BINDIR)/$(CONFIG)/client_channel_stress_test client_crash_test: $(BINDIR)/$(CONFIG)/client_crash_test client_crash_test_server: $(BINDIR)/$(CONFIG)/client_crash_test_server client_lb_end2end_test: $(BINDIR)/$(CONFIG)/client_lb_end2end_test @@ -1553,6 +1554,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/channel_arguments_test \ $(BINDIR)/$(CONFIG)/channel_filter_test \ $(BINDIR)/$(CONFIG)/cli_call_test \ + $(BINDIR)/$(CONFIG)/client_channel_stress_test \ $(BINDIR)/$(CONFIG)/client_crash_test \ $(BINDIR)/$(CONFIG)/client_crash_test_server \ $(BINDIR)/$(CONFIG)/client_lb_end2end_test \ @@ -1678,6 +1680,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/channel_arguments_test \ $(BINDIR)/$(CONFIG)/channel_filter_test \ $(BINDIR)/$(CONFIG)/cli_call_test \ + $(BINDIR)/$(CONFIG)/client_channel_stress_test \ $(BINDIR)/$(CONFIG)/client_crash_test \ $(BINDIR)/$(CONFIG)/client_crash_test_server \ $(BINDIR)/$(CONFIG)/client_lb_end2end_test \ @@ -2060,6 +2063,8 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/channel_filter_test || ( echo test channel_filter_test failed ; exit 1 ) $(E) "[RUN] Testing cli_call_test" $(Q) $(BINDIR)/$(CONFIG)/cli_call_test || ( echo test cli_call_test failed ; exit 1 ) + $(E) "[RUN] Testing client_channel_stress_test" + $(Q) $(BINDIR)/$(CONFIG)/client_channel_stress_test || ( echo test client_channel_stress_test failed ; exit 1 ) $(E) "[RUN] Testing client_crash_test" $(Q) $(BINDIR)/$(CONFIG)/client_crash_test || ( echo test client_crash_test failed ; exit 1 ) $(E) "[RUN] Testing client_lb_end2end_test" @@ -14326,6 +14331,53 @@ endif endif +CLIENT_CHANNEL_STRESS_TEST_SRC = \ + $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc \ + test/cpp/client/client_channel_stress_test.cc \ + +CLIENT_CHANNEL_STRESS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CLIENT_CHANNEL_STRESS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/client_channel_stress_test: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. + +$(BINDIR)/$(CONFIG)/client_channel_stress_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/client_channel_stress_test: $(PROTOBUF_DEP) $(CLIENT_CHANNEL_STRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(CLIENT_CHANNEL_STRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/client_channel_stress_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/src/proto/grpc/lb/v1/load_balancer.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/test/cpp/client/client_channel_stress_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_client_channel_stress_test: $(CLIENT_CHANNEL_STRESS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CLIENT_CHANNEL_STRESS_TEST_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/client/client_channel_stress_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc + + CLIENT_CRASH_TEST_SRC = \ test/cpp/end2end/client_crash_test.cc \ diff --git a/build.yaml b/build.yaml index 47f26102022..d0a2dc9c67c 100644 --- a/build.yaml +++ b/build.yaml @@ -3835,6 +3835,20 @@ targets: - grpc - gpr_test_util - gpr +- name: client_channel_stress_test + gtest: false + build: test + language: c++ + src: + - src/proto/grpc/lb/v1/load_balancer.proto + - test/cpp/client/client_channel_stress_test.cc + deps: + - grpc++_test_util + - grpc_test_util + - grpc++ + - grpc + - gpr_test_util + - gpr - name: client_crash_test gtest: true cpu_cost: 0.1 diff --git a/test/cpp/client/BUILD b/test/cpp/client/BUILD new file mode 100644 index 00000000000..12825e88c26 --- /dev/null +++ b/test/cpp/client/BUILD @@ -0,0 +1,51 @@ +# Copyright 2017 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +licenses(["notice"]) # Apache v2 + +load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package") + +grpc_package(name = "test/cpp/client") + +grpc_cc_test( + name = "credentials_test", + srcs = ["credentials_test.cc"], + external_deps = [ + "gtest", + ], + deps = [ + "//:gpr", + "//:grpc", + "//:grpc++", + ], +) + +grpc_cc_test( + name = "client_channel_stress_test", + srcs = ["client_channel_stress_test.cc"], + deps = [ + "//:gpr", + "//:grpc", + "//:grpc++", + "//:grpc_resolver_fake", + "//src/proto/grpc/lb/v1:load_balancer_proto", + "//src/proto/grpc/testing:echo_messages_proto", + "//src/proto/grpc/testing:echo_proto", + "//src/proto/grpc/testing/duplicate:echo_duplicate_proto", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + "//test/cpp/end2end:test_service_impl", + "//test/cpp/util:test_util", + ], +) diff --git a/test/cpp/client/client_channel_stress_test.cc b/test/cpp/client/client_channel_stress_test.cc new file mode 100644 index 00000000000..8940f6ff9e8 --- /dev/null +++ b/test/cpp/client/client_channel_stress_test.cc @@ -0,0 +1,329 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" +#include "src/core/lib/iomgr/sockaddr.h" +} + +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" +#include "test/cpp/end2end/test_service_impl.h" + +#include "src/proto/grpc/lb/v1/load_balancer.grpc.pb.h" +#include "src/proto/grpc/testing/echo.grpc.pb.h" + +using grpc::lb::v1::LoadBalanceRequest; +using grpc::lb::v1::LoadBalanceResponse; +using grpc::lb::v1::LoadBalancer; + +namespace grpc { +namespace testing { +namespace { + +const size_t kNumBackends = 10; +const size_t kNumBalancers = 5; +const size_t kNumClientThreads = 100; +const int kResolutionUpdateIntervalMs = 50; +const int kServerlistUpdateIntervalMs = 10; +const int kTestDurationSec = 30; + +using BackendServiceImpl = TestServiceImpl; + +class BalancerServiceImpl : public LoadBalancer::Service { + public: + using Stream = ServerReaderWriter; + + explicit BalancerServiceImpl(const std::vector& all_backend_ports) + : all_backend_ports_(all_backend_ports) {} + + Status BalanceLoad(ServerContext* context, Stream* stream) override { + gpr_log(GPR_INFO, "LB[%p]: Start BalanceLoad.", this); + LoadBalanceRequest request; + stream->Read(&request); + while (!shutdown_) { + stream->Write(BuildRandomResponseForBackends()); + std::this_thread::sleep_for( + std::chrono::milliseconds(kServerlistUpdateIntervalMs)); + } + gpr_log(GPR_INFO, "LB[%p]: Finish BalanceLoad.", this); + return Status::OK; + } + + void Shutdown() { shutdown_ = true; } + + private: + grpc::string Ip4ToPackedString(const char* ip_str) { + struct in_addr ip4; + GPR_ASSERT(inet_pton(AF_INET, ip_str, &ip4) == 1); + return grpc::string(reinterpret_cast(&ip4), sizeof(ip4)); + } + + LoadBalanceResponse BuildRandomResponseForBackends() { + // Generate a random serverlist with varying size (if N = + // all_backend_ports_.size(), num_non_drop_entry is in [0, 2N], + // num_drop_entry is in [0, N]), order, duplicate, and drop rate. + size_t num_non_drop_entry = + std::rand() % (all_backend_ports_.size() * 2 + 1); + size_t num_drop_entry = std::rand() % (all_backend_ports_.size() + 1); + std::vector random_backend_indices; + for (size_t i = 0; i < num_non_drop_entry; ++i) { + random_backend_indices.push_back(std::rand() % all_backend_ports_.size()); + } + for (size_t i = 0; i < num_drop_entry; ++i) { + random_backend_indices.push_back(-1); + } + std::random_shuffle(random_backend_indices.begin(), + random_backend_indices.end()); + // Build the response according to the random list generated above. + LoadBalanceResponse response; + for (int index : random_backend_indices) { + auto* server = response.mutable_server_list()->add_servers(); + if (index < 0) { + server->set_drop(true); + server->set_load_balance_token("load_balancing"); + } else { + server->set_ip_address(Ip4ToPackedString("127.0.0.1")); + server->set_port(all_backend_ports_[index]); + } + } + return response; + } + + std::atomic_bool shutdown_{false}; + const std::vector all_backend_ports_; +}; + +class ClientChannelStressTest { + public: + void Run() { + Start(); + // Keep updating resolution for the test duration. + gpr_log(GPR_INFO, "Start updating resolution."); + const auto wait_duration = + std::chrono::milliseconds(kResolutionUpdateIntervalMs); + std::vector addresses; + auto start_time = std::chrono::steady_clock::now(); + while (true) { + if (std::chrono::duration_cast( + std::chrono::steady_clock::now() - start_time) + .count() > kTestDurationSec) { + break; + } + // Generate a random subset of balancers. + addresses.clear(); + for (const auto& balancer_server : balancer_servers_) { + // Select each address with probability of 0.8. + if (std::rand() % 10 < 8) { + addresses.emplace_back(AddressData{balancer_server.port_, true, ""}); + } + } + std::random_shuffle(addresses.begin(), addresses.end()); + SetNextResolution(addresses); + std::this_thread::sleep_for(wait_duration); + } + gpr_log(GPR_INFO, "Finish updating resolution."); + Shutdown(); + } + + private: + template + struct ServerThread { + explicit ServerThread(const grpc::string& type, + const grpc::string& server_host, T* service) + : type_(type), service_(service) { + std::mutex mu; + // We need to acquire the lock here in order to prevent the notify_one + // by ServerThread::Start from firing before the wait below is hit. + std::unique_lock lock(mu); + port_ = grpc_pick_unused_port_or_die(); + gpr_log(GPR_INFO, "starting %s server on port %d", type_.c_str(), port_); + std::condition_variable cond; + thread_.reset(new std::thread( + std::bind(&ServerThread::Start, this, server_host, &mu, &cond))); + cond.wait(lock); + gpr_log(GPR_INFO, "%s server startup complete", type_.c_str()); + } + + void Start(const grpc::string& server_host, std::mutex* mu, + std::condition_variable* cond) { + // We need to acquire the lock here in order to prevent the notify_one + // below from firing before its corresponding wait is executed. + std::lock_guard lock(*mu); + std::ostringstream server_address; + server_address << server_host << ":" << port_; + ServerBuilder builder; + builder.AddListeningPort(server_address.str(), + InsecureServerCredentials()); + builder.RegisterService(service_); + server_ = builder.BuildAndStart(); + cond->notify_one(); + } + + void Shutdown() { + gpr_log(GPR_INFO, "%s about to shutdown", type_.c_str()); + server_->Shutdown(grpc_timeout_milliseconds_to_deadline(0)); + thread_->join(); + gpr_log(GPR_INFO, "%s shutdown completed", type_.c_str()); + } + + int port_; + grpc::string type_; + std::unique_ptr server_; + T* service_; + std::unique_ptr thread_; + }; + + struct AddressData { + int port; + bool is_balancer; + grpc::string balancer_name; + }; + + void SetNextResolution(const std::vector& address_data) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_lb_addresses* addresses = + grpc_lb_addresses_create(address_data.size(), nullptr); + for (size_t i = 0; i < address_data.size(); ++i) { + char* lb_uri_str; + gpr_asprintf(&lb_uri_str, "ipv4:127.0.0.1:%d", address_data[i].port); + grpc_uri* lb_uri = grpc_uri_parse(&exec_ctx, lb_uri_str, true); + GPR_ASSERT(lb_uri != nullptr); + grpc_lb_addresses_set_address_from_uri( + addresses, i, lb_uri, address_data[i].is_balancer, + address_data[i].balancer_name.c_str(), nullptr); + grpc_uri_destroy(lb_uri); + gpr_free(lb_uri_str); + } + grpc_arg fake_addresses = grpc_lb_addresses_create_channel_arg(addresses); + grpc_channel_args fake_result = {1, &fake_addresses}; + grpc_fake_resolver_response_generator_set_response( + &exec_ctx, response_generator_, &fake_result); + grpc_lb_addresses_destroy(&exec_ctx, addresses); + grpc_exec_ctx_finish(&exec_ctx); + } + + void KeepSendingRequests() { + gpr_log(GPR_INFO, "Start sending requests."); + while (!shutdown_) { + ClientContext context; + context.set_deadline(grpc_timeout_milliseconds_to_deadline(1000)); + EchoRequest request; + request.set_message("test"); + EchoResponse response; + { + std::lock_guard lock(stub_mutex_); + stub_->Echo(&context, request, &response); + } + } + gpr_log(GPR_INFO, "Finish sending requests."); + } + + void CreateStub() { + ChannelArguments args; + response_generator_ = grpc_fake_resolver_response_generator_create(); + args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR, + response_generator_); + std::ostringstream uri; + uri << "fake:///servername_not_used"; + channel_ = + CreateCustomChannel(uri.str(), InsecureChannelCredentials(), args); + stub_ = grpc::testing::EchoTestService::NewStub(channel_); + } + + void Start() { + // Start the backends. + std::vector backend_ports; + for (size_t i = 0; i < kNumBackends; ++i) { + backends_.emplace_back(new BackendServiceImpl()); + backend_servers_.emplace_back(ServerThread( + "backend", server_host_, backends_.back().get())); + backend_ports.push_back(backend_servers_.back().port_); + } + // Start the load balancers. + for (size_t i = 0; i < kNumBalancers; ++i) { + balancers_.emplace_back(new BalancerServiceImpl(backend_ports)); + balancer_servers_.emplace_back(ServerThread( + "balancer", server_host_, balancers_.back().get())); + } + // Start sending RPCs in multiple threads. + CreateStub(); + for (size_t i = 0; i < kNumClientThreads; ++i) { + client_threads_.emplace_back( + std::thread(&ClientChannelStressTest::KeepSendingRequests, this)); + } + } + + void Shutdown() { + shutdown_ = true; + for (size_t i = 0; i < client_threads_.size(); ++i) { + client_threads_[i].join(); + } + for (size_t i = 0; i < balancers_.size(); ++i) { + balancers_[i]->Shutdown(); + balancer_servers_[i].Shutdown(); + } + for (size_t i = 0; i < backends_.size(); ++i) { + backend_servers_[i].Shutdown(); + } + grpc_fake_resolver_response_generator_unref(response_generator_); + } + + std::atomic_bool shutdown_{false}; + const grpc::string server_host_ = "localhost"; + std::shared_ptr channel_; + std::unique_ptr stub_; + std::mutex stub_mutex_; + std::vector> backends_; + std::vector> balancers_; + std::vector> backend_servers_; + std::vector> balancer_servers_; + grpc_fake_resolver_response_generator* response_generator_; + std::vector client_threads_; +}; + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc_init(); + grpc_test_init(argc, argv); + grpc::testing::ClientChannelStressTest test; + test.Run(); + grpc_shutdown(); + return 0; +} diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index b74e49fa2cd..91f2b548c36 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -2886,6 +2886,29 @@ "third_party": false, "type": "target" }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc++", + "grpc++_test_util", + "grpc_test_util" + ], + "headers": [ + "src/proto/grpc/lb/v1/load_balancer.grpc.pb.h", + "src/proto/grpc/lb/v1/load_balancer.pb.h", + "src/proto/grpc/lb/v1/load_balancer_mock.grpc.pb.h" + ], + "is_filegroup": false, + "language": "c++", + "name": "client_channel_stress_test", + "src": [ + "test/cpp/client/client_channel_stress_test.cc" + ], + "third_party": false, + "type": "target" + }, { "deps": [ "gpr", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 5ba06bf1a09..93e50765533 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -3393,6 +3393,30 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c++", + "name": "client_channel_stress_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,