Merge branch 'master' of https://github.com/grpc/grpc into tracing++
diff --git a/BUILD b/BUILD
index 6a51449..6e8191a 100644
--- a/BUILD
+++ b/BUILD
@@ -316,36 +316,12 @@
 
 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 = [
@@ -359,43 +335,93 @@
 
 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/CMakeLists.txt b/CMakeLists.txt
index 4daff95..7eb0eb7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -462,6 +462,9 @@
 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)
@@ -7236,6 +7239,7 @@
 
 add_executable(handshake_server
   test/core/handshake/server_ssl.cc
+  test/core/handshake/server_ssl_common.cc
 )
 
 
@@ -7265,6 +7269,40 @@
 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.cc
+  test/core/handshake/server_ssl_common.cc
+)
+
+
+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)
 
 add_executable(hpack_parser_test
   test/core/transport/chttp2/hpack_parser_test.cc
diff --git a/Makefile b/Makefile
index 1e3418e..69a6000 100644
--- a/Makefile
+++ b/Makefile
@@ -1019,6 +1019,7 @@
 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
@@ -1408,6 +1409,7 @@
   $(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 \
@@ -1876,6 +1878,8 @@
 	$(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"
@@ -11040,6 +11044,7 @@
 
 HANDSHAKE_SERVER_SRC = \
     test/core/handshake/server_ssl.cc \
+    test/core/handshake/server_ssl_common.cc \
 
 HANDSHAKE_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_SERVER_SRC))))
 ifeq ($(NO_SECURE),true)
@@ -11061,6 +11066,8 @@
 
 $(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)
@@ -11070,6 +11077,41 @@
 endif
 
 
+HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_SRC = \
+    test/core/handshake/readahead_handshaker_server_ssl.cc \
+    test/core/handshake/server_ssl_common.cc \
+
+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.cc \
 
diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl
index b35ca73..8a8ec9b 100644
--- a/bazel/grpc_build_system.bzl
+++ b/bazel/grpc_build_system.bzl
@@ -49,10 +49,10 @@
     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/build.yaml b/build.yaml
index cd6486e..910b00e 100644
--- a/build.yaml
+++ b/build.yaml
@@ -2511,8 +2511,29 @@
 - 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
+  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
+  headers:
+  - test/core/handshake/server_ssl_common.h
+  src:
+  - test/core/handshake/readahead_handshaker_server_ssl.cc
+  - test/core/handshake/server_ssl_common.cc
   deps:
   - grpc_test_util
   - grpc
diff --git a/src/compiler/protobuf_plugin.h b/src/compiler/protobuf_plugin.h
index 1551908..0f22dd4 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 <vector>
diff --git a/src/compiler/python_generator_helpers.h b/src/compiler/python_generator_helpers.h
index b1b58be..5ecd192 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;
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
index f863bb9..e64ba53 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
@@ -1751,7 +1751,7 @@
   grpc_lb_addresses_destroy(exec_ctx, glb_policy->fallback_backend_addresses);
   glb_policy->fallback_backend_addresses =
       extract_backend_addresses_locked(exec_ctx, addresses);
-  if (glb_policy->lb_fallback_timeout_ms > 0 &&
+  if (glb_policy->started_picking && glb_policy->lb_fallback_timeout_ms > 0 &&
       !glb_policy->fallback_timer_active) {
     rr_handover_locked(exec_ctx, glb_policy);
   }
diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc
index c540445..7067b70 100644
--- a/src/core/lib/security/transport/security_handshaker.cc
+++ b/src/core/lib/security/transport/security_handshaker.cc
@@ -65,6 +65,25 @@
   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 @@
   }
   tsi_handshaker_result_destroy(h->handshaker_result);
   h->handshaker_result = nullptr;
-  // 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;
@@ -312,23 +329,8 @@
     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);
@@ -405,7 +407,10 @@
   h->args = args;
   h->on_handshake_done = on_handshake_done;
   gpr_ref(&h->refs);
-  grpc_error* error = do_handshaker_next_locked(exec_ctx, h, nullptr, 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/src/ruby/lib/grpc/generic/interceptors.rb b/src/ruby/lib/grpc/generic/interceptors.rb
index 73faec4..24482f3 100644
--- a/src/ruby/lib/grpc/generic/interceptors.rb
+++ b/src/ruby/lib/grpc/generic/interceptors.rb
@@ -41,7 +41,7 @@
     # @param [Method] method
     # @param [Hash] metadata
     #
-    def request_response(request:, call:, method:, metadata:)
+    def request_response(request: nil, call: nil, method: nil, metadata: nil)
       GRPC.logger.debug "Intercepting request response method #{method}" \
         " for request #{request} with call #{call} and metadata: #{metadata}"
       yield
@@ -55,7 +55,7 @@
     # @param [Method] method
     # @param [Hash] metadata
     #
-    def client_streamer(requests:, call:, method:, metadata:)
+    def client_streamer(requests: nil, call: nil, method: nil, metadata: nil)
       GRPC.logger.debug "Intercepting client streamer method #{method}" \
        " for requests #{requests} with call #{call} and metadata: #{metadata}"
       yield
@@ -69,7 +69,7 @@
     # @param [Method] method
     # @param [Hash] metadata
     #
-    def server_streamer(request:, call:, method:, metadata:)
+    def server_streamer(request: nil, call: nil, method: nil, metadata: nil)
       GRPC.logger.debug "Intercepting server streamer method #{method}" \
         " for request #{request} with call #{call} and metadata: #{metadata}"
       yield
@@ -83,7 +83,7 @@
     # @param [Method] method
     # @param [Hash] metadata
     #
-    def bidi_streamer(requests:, call:, method:, metadata:)
+    def bidi_streamer(requests: nil, call: nil, method: nil, metadata: nil)
       GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
         " for requests #{requests} with call #{call} and metadata: #{metadata}"
       yield
@@ -102,7 +102,7 @@
     # @param [GRPC::ActiveCall::SingleReqView] call
     # @param [Method] method
     #
-    def request_response(request:, call:, method:)
+    def request_response(request: nil, call: nil, method: nil)
       GRPC.logger.debug "Intercepting request response method #{method}" \
         " for request #{request} with call #{call}"
       yield
@@ -114,7 +114,7 @@
     # @param [GRPC::ActiveCall::MultiReqView] call
     # @param [Method] method
     #
-    def client_streamer(call:, method:)
+    def client_streamer(call: nil, method: nil)
       GRPC.logger.debug "Intercepting client streamer method #{method}" \
         " with call #{call}"
       yield
@@ -127,7 +127,7 @@
     # @param [GRPC::ActiveCall::SingleReqView] call
     # @param [Method] method
     #
-    def server_streamer(request:, call:, method:)
+    def server_streamer(request: nil, call: nil, method: nil)
       GRPC.logger.debug "Intercepting server streamer method #{method}" \
         " for request #{request} with call #{call}"
       yield
@@ -140,7 +140,7 @@
     # @param [GRPC::ActiveCall::MultiReqView] call
     # @param [Method] method
     #
-    def bidi_streamer(requests:, call:, method:)
+    def bidi_streamer(requests: nil, call: nil, method: nil)
       GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
         " for requests #{requests} with call #{call}"
       yield
diff --git a/test/core/debug/stats_test.cc b/test/core/debug/stats_test.cc
index c4b0d50..5fae61f 100644
--- a/test/core/debug/stats_test.cc
+++ b/test/core/debug/stats_test.cc
@@ -109,11 +109,12 @@
     }
   };
   std::vector<int> test_values;
-  for (int j = -1000;
-       j < grpc_stats_histo_bucket_boundaries
-                   [kHistogram][grpc_stats_histo_buckets[kHistogram] - 1] +
-               1000;
-       j++) {
+  // largest bucket boundary for current histogram type.
+  int max_bucket_boundary =
+      grpc_stats_histo_bucket_boundaries[kHistogram]
+                                        [grpc_stats_histo_buckets[kHistogram] -
+                                         1];
+  for (int j = -1000; j < max_bucket_boundary + 1000;) {
     int expected_bucket = FindExpectedBucket(kHistogram, j);
     if (cur_bucket != expected_bucket) {
       threads.emplace_back(
@@ -122,6 +123,14 @@
       test_values.clear();
     }
     test_values.push_back(j);
+    if (j < max_bucket_boundary &&
+        FindExpectedBucket(kHistogram, j + 1000) == expected_bucket &&
+        FindExpectedBucket(kHistogram, j - 1000) == expected_bucket) {
+      // if we are far from bucket boundary, skip values to speed-up the tests
+      j += 500;
+    } else {
+      j++;
+    }
   }
   run(test_values, cur_bucket);
   for (auto& t : threads) {
diff --git a/test/core/handshake/BUILD b/test/core/handshake/BUILD
index aea4a27..a3276b9 100644
--- a/test/core/handshake/BUILD
+++ b/test/core/handshake/BUILD
@@ -35,6 +35,18 @@
     ],
 )
 
+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 @@
         "//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/readahead_handshaker_server_ssl.cc b/test/core/handshake/readahead_handshaker_server_ssl.cc
new file mode 100644
index 0000000..2810082
--- /dev/null
+++ b/test/core/handshake/readahead_handshaker_server_ssl.cc
@@ -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 <arpa/inet.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#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.cc b/test/core/handshake/server_ssl.cc
index 69c4fdc..736d3e5 100644
--- a/test/core/handshake/server_ssl.cc
+++ b/test/core/handshake/server_ssl.cc
@@ -34,206 +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, nullptr);
-
-  // Start server listening on local port.
-  char* addr;
-  gpr_asprintf(&addr, "127.0.0.1:%d", port);
-  grpc_server* server = grpc_server_create(nullptr, nullptr);
-  GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds));
-  free(addr);
-
-  grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
-
-  grpc_server_register_completion_queue(server, cq, nullptr);
-  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, nullptr);
-    GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
-  }
-
-  gpr_log(GPR_INFO, "Shutting down server");
-  grpc_server_shutdown_and_notify(server, cq, nullptr);
-  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, nullptr);
-  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 =
-      static_cast<unsigned char*>(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.cc b/test/core/handshake/server_ssl_common.cc
new file mode 100644
index 0000000..599b281
--- /dev/null
+++ b/test/core/handshake/server_ssl_common.cc
@@ -0,0 +1,238 @@
+/*
+ *
+ * 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 <arpa/inet.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#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, nullptr);
+
+  // Start server listening on local port.
+  char* addr;
+  gpr_asprintf(&addr, "127.0.0.1:%d", port);
+  grpc_server* server = grpc_server_create(nullptr, nullptr);
+  GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds));
+  free(addr);
+
+  grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
+
+  grpc_server_register_completion_queue(server, cq, nullptr);
+  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, nullptr);
+    GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT);
+  }
+
+  gpr_log(GPR_INFO, "Shutting down server");
+  grpc_server_shutdown_and_notify(server, cq, nullptr);
+  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, nullptr);
+  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 =
+      static_cast<unsigned char*>(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 0000000..77865a4
--- /dev/null
+++ b/test/core/handshake/server_ssl_common.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * 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
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#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/test/core/util/BUILD b/test/core/util/BUILD
index bc6fe9d..2437923 100644
--- a/test/core/util/BUILD
+++ b/test/core/util/BUILD
@@ -103,6 +103,7 @@
 
 grpc_cc_library(
     name = "fuzzer_corpus_test",
+    testonly = 1,
     srcs = ["fuzzer_corpus_test.cc"],
     deps = [
         ":gpr_test_util",
diff --git a/tools/interop_matrix/client_matrix.py b/tools/interop_matrix/client_matrix.py
index 6bd7ff6..4345031 100644
--- a/tools/interop_matrix/client_matrix.py
+++ b/tools/interop_matrix/client_matrix.py
@@ -99,7 +99,7 @@
    'csharp': [
         #'v1.0.1',
         #'v1.1.4',  Fail to build.
-        #'v1.2.5',  Fail to run test with csharp image.
+        'v1.2.5',
         'v1.3.9',
         'v1.4.2',
         'v1.6.6',
diff --git a/tools/interop_matrix/testcases/csharp__v1.2.5 b/tools/interop_matrix/testcases/csharp__v1.2.5
new file mode 100644
index 0000000..19da788
--- /dev/null
+++ b/tools/interop_matrix/testcases/csharp__v1.2.5
@@ -0,0 +1,20 @@
+#!/bin/bash
+echo "Testing ${docker_image:=grpc_interop_csharp:a95229ca-d387-4127-ad48-69a7464e23b8}"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response"
+docker run -i --rm=true -w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug --net=host $docker_image bash -c "mono Grpc.IntegrationTesting.Client.exe --server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server"
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 6cd9a04..fce01bf 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -1207,12 +1207,37 @@
       "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.cc", 
+      "test/core/handshake/server_ssl_common.cc", 
+      "test/core/handshake/server_ssl_common.h"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "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.h"
     ], 
     "third_party": false, 
     "type": "target"
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 5df5a74..5d7d3b3 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -1507,6 +1507,26 @@
     "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, 
+    "ci_platforms": [
       "linux", 
       "mac", 
       "posix",