Split interop client and server into components
diff --git a/Makefile b/Makefile
index c50eb8c..b7b14fb 100644
--- a/Makefile
+++ b/Makefile
@@ -1075,7 +1075,7 @@
 
 privatelibs_c:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack_uds.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a $(LIBDIR)/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_thread_stress.a $(LIBDIR)/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_payload_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_large_metadata_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_thread_stress_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read_legacy.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a
 
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libpubsub_client_lib.a $(LIBDIR)/$(CONFIG)/libqps.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libinterop_client_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libpubsub_client_lib.a $(LIBDIR)/$(CONFIG)/libqps.a
 
 buildtests: buildtests_c buildtests_cxx
 
@@ -3717,6 +3717,130 @@
 $(OBJDIR)/$(CONFIG)/src/compiler/ruby_generator.o: 
 
 
+LIBINTEROP_CLIENT_LIB_SRC = \
+    $(GENDIR)/test/cpp/interop/empty.pb.cc $(GENDIR)/test/cpp/interop/empty.grpc.pb.cc \
+    $(GENDIR)/test/cpp/interop/messages.pb.cc $(GENDIR)/test/cpp/interop/messages.grpc.pb.cc \
+    $(GENDIR)/test/cpp/interop/test.pb.cc $(GENDIR)/test/cpp/interop/test.grpc.pb.cc \
+    test/cpp/interop/client_helper.cc \
+    test/cpp/interop/interop_client.cc \
+
+
+LIBINTEROP_CLIENT_LIB_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_CLIENT_LIB_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL with ALPN.
+
+$(LIBDIR)/$(CONFIG)/libinterop_client_lib.a: openssl_dep_error
+
+
+else
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay.
+
+$(LIBDIR)/$(CONFIG)/libinterop_client_lib.a: protobuf_dep_error
+
+
+else
+
+ifneq ($(OPENSSL_DEP),)
+# This is to ensure the embedded OpenSSL is built beforehand, properly
+# installing headers to their final destination on the drive. We need this
+# otherwise parallel compilation will fail if a source is compiled first.
+test/cpp/interop/empty.proto: $(OPENSSL_DEP)
+test/cpp/interop/messages.proto: $(OPENSSL_DEP)
+test/cpp/interop/test.proto: $(OPENSSL_DEP)
+test/cpp/interop/client_helper.cc: $(OPENSSL_DEP)
+test/cpp/interop/interop_client.cc: $(OPENSSL_DEP)
+endif
+
+$(LIBDIR)/$(CONFIG)/libinterop_client_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBINTEROP_CLIENT_LIB_OBJS)
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_client_lib.a
+	$(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libinterop_client_lib.a $(LIBINTEROP_CLIENT_LIB_OBJS)
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib $(LIBDIR)/$(CONFIG)/libinterop_client_lib.a
+endif
+
+
+
+
+endif
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBINTEROP_CLIENT_LIB_OBJS:.o=.dep)
+endif
+endif
+
+
+
+
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/client_helper.o:     $(GENDIR)/test/cpp/interop/empty.pb.cc $(GENDIR)/test/cpp/interop/empty.grpc.pb.cc    $(GENDIR)/test/cpp/interop/messages.pb.cc $(GENDIR)/test/cpp/interop/messages.grpc.pb.cc    $(GENDIR)/test/cpp/interop/test.pb.cc $(GENDIR)/test/cpp/interop/test.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/interop_client.o:     $(GENDIR)/test/cpp/interop/empty.pb.cc $(GENDIR)/test/cpp/interop/empty.grpc.pb.cc    $(GENDIR)/test/cpp/interop/messages.pb.cc $(GENDIR)/test/cpp/interop/messages.grpc.pb.cc    $(GENDIR)/test/cpp/interop/test.pb.cc $(GENDIR)/test/cpp/interop/test.grpc.pb.cc
+
+
+LIBINTEROP_SERVER_LIB_SRC = \
+    test/cpp/interop/server_helper.cc \
+
+
+LIBINTEROP_SERVER_LIB_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_SERVER_LIB_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL with ALPN.
+
+$(LIBDIR)/$(CONFIG)/libinterop_server_lib.a: openssl_dep_error
+
+
+else
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay.
+
+$(LIBDIR)/$(CONFIG)/libinterop_server_lib.a: protobuf_dep_error
+
+
+else
+
+ifneq ($(OPENSSL_DEP),)
+# This is to ensure the embedded OpenSSL is built beforehand, properly
+# installing headers to their final destination on the drive. We need this
+# otherwise parallel compilation will fail if a source is compiled first.
+test/cpp/interop/server_helper.cc: $(OPENSSL_DEP)
+endif
+
+$(LIBDIR)/$(CONFIG)/libinterop_server_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBINTEROP_SERVER_LIB_OBJS)
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a
+	$(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBINTEROP_SERVER_LIB_OBJS)
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a
+endif
+
+
+
+
+endif
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBINTEROP_SERVER_LIB_OBJS:.o=.dep)
+endif
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/server_helper.o: 
+
+
 LIBPUBSUB_CLIENT_LIB_SRC = \
     $(GENDIR)/examples/pubsub/label.pb.cc $(GENDIR)/examples/pubsub/label.grpc.pb.cc \
     $(GENDIR)/examples/pubsub/empty.pb.cc $(GENDIR)/examples/pubsub/empty.grpc.pb.cc \
@@ -8486,9 +8610,6 @@
 
 
 INTEROP_CLIENT_SRC = \
-    $(GENDIR)/test/cpp/interop/empty.pb.cc $(GENDIR)/test/cpp/interop/empty.grpc.pb.cc \
-    $(GENDIR)/test/cpp/interop/messages.pb.cc $(GENDIR)/test/cpp/interop/messages.grpc.pb.cc \
-    $(GENDIR)/test/cpp/interop/test.pb.cc $(GENDIR)/test/cpp/interop/test.grpc.pb.cc \
     test/cpp/interop/client.cc \
 
 INTEROP_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(INTEROP_CLIENT_SRC))))
@@ -8510,19 +8631,16 @@
 
 else
 
-$(BINDIR)/$(CONFIG)/interop_client: $(PROTOBUF_DEP) $(INTEROP_CLIENT_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
+$(BINDIR)/$(CONFIG)/interop_client: $(PROTOBUF_DEP) $(INTEROP_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libinterop_client_lib.a $(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) $(INTEROP_CLIENT_OBJS) $(GTEST_LIB) $(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) -o $(BINDIR)/$(CONFIG)/interop_client
+	$(Q) $(LDXX) $(LDFLAGS) $(INTEROP_CLIENT_OBJS) $(GTEST_LIB) $(LIBDIR)/$(CONFIG)/libinterop_client_lib.a $(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) -o $(BINDIR)/$(CONFIG)/interop_client
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/interop/empty.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/interop/messages.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/interop/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
-$(OBJDIR)/$(CONFIG)/test/cpp/interop/client.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/interop/client.o:  $(LIBDIR)/$(CONFIG)/libinterop_client_lib.a $(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_interop_client: $(INTEROP_CLIENT_OBJS:.o=.dep)
 
@@ -8558,19 +8676,19 @@
 
 else
 
-$(BINDIR)/$(CONFIG)/interop_server: $(PROTOBUF_DEP) $(INTEROP_SERVER_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
+$(BINDIR)/$(CONFIG)/interop_server: $(PROTOBUF_DEP) $(INTEROP_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(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) $(INTEROP_SERVER_OBJS) $(GTEST_LIB) $(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) -o $(BINDIR)/$(CONFIG)/interop_server
+	$(Q) $(LDXX) $(LDFLAGS) $(INTEROP_SERVER_OBJS) $(GTEST_LIB) $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(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) -o $(BINDIR)/$(CONFIG)/interop_server
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/interop/empty.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/interop/messages.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/interop/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
-$(OBJDIR)/$(CONFIG)/test/cpp/interop/server.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/interop/empty.o:  $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(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/interop/messages.o:  $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(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/interop/test.o:  $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(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/interop/server.o:  $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(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_interop_server: $(INTEROP_SERVER_OBJS:.o=.dep)
 
diff --git a/build.json b/build.json
index f8f23e0..bba9810 100644
--- a/build.json
+++ b/build.json
@@ -532,6 +532,36 @@
       "secure": "no"
     },
     {
+      "name": "interop_client_lib",
+      "build": "private",
+      "language": "c++",
+      "src": [
+        "test/cpp/interop/empty.proto",
+        "test/cpp/interop/messages.proto",
+        "test/cpp/interop/test.proto",
+        "test/cpp/interop/client_helper.cc",
+        "test/cpp/interop/interop_client.cc"
+      ],
+      "deps": [
+        "grpc_test_util",
+        "grpc++",
+        "grpc",
+        "gpr_test_util",
+        "gpr"
+      ]
+    },
+    {
+      "name": "interop_server_lib",
+      "build": "private",
+      "language": "c++",
+      "src": [
+        "test/cpp/interop/server_helper.cc"
+      ],
+      "deps": [
+        "grpc++"
+      ]
+    },
+    {
       "name": "pubsub_client_lib",
       "build": "private",
       "language": "c++",
@@ -1862,12 +1892,10 @@
       "run": false,
       "language": "c++",
       "src": [
-        "test/cpp/interop/empty.proto",
-        "test/cpp/interop/messages.proto",
-        "test/cpp/interop/test.proto",
         "test/cpp/interop/client.cc"
       ],
       "deps": [
+        "interop_client_lib",
         "grpc++_test_util",
         "grpc_test_util",
         "grpc++",
@@ -1888,6 +1916,7 @@
         "test/cpp/interop/server.cc"
       ],
       "deps": [
+        "interop_server_lib",
         "grpc++_test_util",
         "grpc_test_util",
         "grpc++",
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index 7c5e1aa..189cdeb 100644
--- a/test/cpp/interop/client.cc
+++ b/test/cpp/interop/client.cc
@@ -31,29 +31,19 @@
  *
  */
 
-#include <chrono>
-#include <fstream>
 #include <memory>
-#include <sstream>
-#include <string>
-#include <thread>
 
 #include <unistd.h>
 
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 #include <gflags/gflags.h>
-#include <grpc++/channel_arguments.h>
 #include <grpc++/channel_interface.h>
 #include <grpc++/client_context.h>
-#include <grpc++/create_channel.h>
-#include <grpc++/credentials.h>
 #include <grpc++/status.h>
 #include <grpc++/stream.h>
-#include "test/cpp/util/create_test_channel.h"
-#include "test/cpp/interop/test.grpc.pb.h"
-#include "test/cpp/interop/empty.grpc.pb.h"
-#include "test/cpp/interop/messages.grpc.pb.h"
+#include "test/cpp/interop/client_helper.h"
+#include "test/cpp/interop/interop_client.h"
 
 DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
 DEFINE_bool(use_prod_roots, false, "True to use SSL roots for google");
@@ -81,21 +71,8 @@
               "Path to service account json key file.");
 DEFINE_string(oauth_scope, "", "Scope for OAuth tokens.");
 
-using grpc::ChannelInterface;
-using grpc::ClientContext;
-using grpc::ComputeEngineCredentials;
-using grpc::CreateTestChannel;
-using grpc::Credentials;
-using grpc::JWTCredentials;
-using grpc::ServiceAccountCredentials;
-using grpc::testing::ResponseParameters;
-using grpc::testing::SimpleRequest;
-using grpc::testing::SimpleResponse;
-using grpc::testing::StreamingInputCallRequest;
-using grpc::testing::StreamingInputCallResponse;
-using grpc::testing::StreamingOutputCallRequest;
-using grpc::testing::StreamingOutputCallResponse;
-using grpc::testing::TestService;
+using grpc::testing::CreateChannelForTestCase;
+using grpc::testing::GetServiceAccountJsonKey;
 
 // In some distros, gflags is in the namespace google, and in some others,
 // in gflags. This hack is enabling us to find both.
@@ -104,362 +81,48 @@
 using namespace google;
 using namespace gflags;
 
-namespace {
-// The same value is defined by the Java client.
-const std::vector<int> request_stream_sizes = {27182, 8, 1828, 45904};
-const std::vector<int> response_stream_sizes = {31415, 9, 2653, 58979};
-const int kNumResponseMessages = 2000;
-const int kResponseMessageSize = 1030;
-const int kReceiveDelayMilliSeconds = 20;
-const int kLargeRequestSize = 314159;
-const int kLargeResponseSize = 271812;
-}  // namespace
-
-grpc::string GetServiceAccountJsonKey() {
-  static grpc::string json_key;
-  if (json_key.empty()) {
-    std::ifstream json_key_file(FLAGS_service_account_key_file);
-    std::stringstream key_stream;
-    key_stream << json_key_file.rdbuf();
-    json_key = key_stream.str();
-  }
-  return json_key;
-}
-
-std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
-    const grpc::string& test_case) {
-  GPR_ASSERT(FLAGS_server_port);
-  const int host_port_buf_size = 1024;
-  char host_port[host_port_buf_size];
-  snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
-           FLAGS_server_port);
-
-  if (test_case == "service_account_creds") {
-    std::unique_ptr<Credentials> creds;
-    GPR_ASSERT(FLAGS_enable_ssl);
-    grpc::string json_key = GetServiceAccountJsonKey();
-    creds = ServiceAccountCredentials(json_key, FLAGS_oauth_scope,
-                                      std::chrono::hours(1));
-    return CreateTestChannel(host_port, FLAGS_server_host_override,
-                             FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
-  } else if (test_case == "compute_engine_creds") {
-    std::unique_ptr<Credentials> creds;
-    GPR_ASSERT(FLAGS_enable_ssl);
-    creds = ComputeEngineCredentials();
-    return CreateTestChannel(host_port, FLAGS_server_host_override,
-                             FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
-  } else if (test_case == "jwt_token_creds") {
-    std::unique_ptr<Credentials> creds;
-    GPR_ASSERT(FLAGS_enable_ssl);
-    grpc::string json_key = GetServiceAccountJsonKey();
-    creds = JWTCredentials(json_key, std::chrono::hours(1));
-    return CreateTestChannel(host_port, FLAGS_server_host_override,
-                             FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
-  } else {
-    return CreateTestChannel(host_port, FLAGS_server_host_override,
-                             FLAGS_enable_ssl, FLAGS_use_prod_roots);
-  }
-}
-
-void AssertOkOrPrintErrorStatus(const grpc::Status& s) {
-  if (s.IsOk()) {
-    return;
-  }
-  gpr_log(GPR_INFO, "Error status code: %d, message: %s", s.code(),
-          s.details().c_str());
-  GPR_ASSERT(0);
-}
-
-void DoEmpty() {
-  gpr_log(GPR_INFO, "Sending an empty rpc...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("empty_unary");
-  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
-
-  grpc::testing::Empty request = grpc::testing::Empty::default_instance();
-  grpc::testing::Empty response = grpc::testing::Empty::default_instance();
-  ClientContext context;
-
-  grpc::Status s = stub->EmptyCall(&context, request, &response);
-  AssertOkOrPrintErrorStatus(s);
-
-  gpr_log(GPR_INFO, "Empty rpc done.");
-}
-
-// Shared code to set large payload, make rpc and check response payload.
-void PerformLargeUnary(std::shared_ptr<ChannelInterface> channel,
-                       SimpleRequest* request, SimpleResponse* response) {
-  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
-
-  ClientContext context;
-  request->set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
-  request->set_response_size(kLargeResponseSize);
-  grpc::string payload(kLargeRequestSize, '\0');
-  request->mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
-
-  grpc::Status s = stub->UnaryCall(&context, *request, response);
-
-  AssertOkOrPrintErrorStatus(s);
-  GPR_ASSERT(response->payload().type() ==
-             grpc::testing::PayloadType::COMPRESSABLE);
-  GPR_ASSERT(response->payload().body() ==
-             grpc::string(kLargeResponseSize, '\0'));
-}
-
-void DoComputeEngineCreds() {
-  gpr_log(GPR_INFO,
-          "Sending a large unary rpc with compute engine credentials ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("compute_engine_creds");
-  SimpleRequest request;
-  SimpleResponse response;
-  request.set_fill_username(true);
-  request.set_fill_oauth_scope(true);
-  PerformLargeUnary(channel, &request, &response);
-  gpr_log(GPR_INFO, "Got username %s", response.username().c_str());
-  gpr_log(GPR_INFO, "Got oauth_scope %s", response.oauth_scope().c_str());
-  GPR_ASSERT(!response.username().empty());
-  GPR_ASSERT(response.username().c_str() == FLAGS_default_service_account);
-  GPR_ASSERT(!response.oauth_scope().empty());
-  const char* oauth_scope_str = response.oauth_scope().c_str();
-  GPR_ASSERT(FLAGS_oauth_scope.find(oauth_scope_str) != grpc::string::npos);
-  gpr_log(GPR_INFO, "Large unary with compute engine creds done.");
-}
-
-void DoServiceAccountCreds() {
-  gpr_log(GPR_INFO,
-          "Sending a large unary rpc with service account credentials ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("service_account_creds");
-  SimpleRequest request;
-  SimpleResponse response;
-  request.set_fill_username(true);
-  request.set_fill_oauth_scope(true);
-  PerformLargeUnary(channel, &request, &response);
-  GPR_ASSERT(!response.username().empty());
-  GPR_ASSERT(!response.oauth_scope().empty());
-  grpc::string json_key = GetServiceAccountJsonKey();
-  GPR_ASSERT(json_key.find(response.username()) != grpc::string::npos);
-  const char* oauth_scope_str = response.oauth_scope().c_str();
-  GPR_ASSERT(FLAGS_oauth_scope.find(oauth_scope_str) != grpc::string::npos);
-  gpr_log(GPR_INFO, "Large unary with service account creds done.");
-}
-
-void DoJwtTokenCreds() {
-  gpr_log(GPR_INFO, "Sending a large unary rpc with JWT token credentials ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("jwt_token_creds");
-  SimpleRequest request;
-  SimpleResponse response;
-  request.set_fill_username(true);
-  PerformLargeUnary(channel, &request, &response);
-  GPR_ASSERT(!response.username().empty());
-  grpc::string json_key = GetServiceAccountJsonKey();
-  GPR_ASSERT(json_key.find(response.username()) != grpc::string::npos);
-  gpr_log(GPR_INFO, "Large unary with JWT token creds done.");
-}
-
-void DoLargeUnary() {
-  gpr_log(GPR_INFO, "Sending a large unary rpc...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("large_unary");
-  SimpleRequest request;
-  SimpleResponse response;
-  PerformLargeUnary(channel, &request, &response);
-  gpr_log(GPR_INFO, "Large unary done.");
-}
-
-void DoRequestStreaming() {
-  gpr_log(GPR_INFO, "Sending request steaming rpc ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("client_streaming");
-  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
-
-  grpc::ClientContext context;
-  StreamingInputCallRequest request;
-  StreamingInputCallResponse response;
-
-  std::unique_ptr<grpc::ClientWriter<StreamingInputCallRequest>> stream(
-      stub->StreamingInputCall(&context, &response));
-
-  int aggregated_payload_size = 0;
-  for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
-    grpc::testing::Payload* payload = request.mutable_payload();
-    payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
-    GPR_ASSERT(stream->Write(request));
-    aggregated_payload_size += request_stream_sizes[i];
-  }
-  stream->WritesDone();
-  grpc::Status s = stream->Finish();
-
-  GPR_ASSERT(response.aggregated_payload_size() == aggregated_payload_size);
-  AssertOkOrPrintErrorStatus(s);
-  gpr_log(GPR_INFO, "Request streaming done.");
-}
-
-void DoResponseStreaming() {
-  gpr_log(GPR_INFO, "Receiving response steaming rpc ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("server_streaming");
-  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
-
-  grpc::ClientContext context;
-  StreamingOutputCallRequest request;
-  for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
-    ResponseParameters* response_parameter = request.add_response_parameters();
-    response_parameter->set_size(response_stream_sizes[i]);
-  }
-  StreamingOutputCallResponse response;
-  std::unique_ptr<grpc::ClientReader<StreamingOutputCallResponse>> stream(
-      stub->StreamingOutputCall(&context, request));
-
-  unsigned int i = 0;
-  while (stream->Read(&response)) {
-    GPR_ASSERT(response.payload().body() ==
-               grpc::string(response_stream_sizes[i], '\0'));
-    ++i;
-  }
-  GPR_ASSERT(response_stream_sizes.size() == i);
-  grpc::Status s = stream->Finish();
-
-  AssertOkOrPrintErrorStatus(s);
-  gpr_log(GPR_INFO, "Response streaming done.");
-}
-
-void DoResponseStreamingWithSlowConsumer() {
-  gpr_log(GPR_INFO, "Receiving response steaming rpc with slow consumer ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("slow_consumer");
-  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
-
-  grpc::ClientContext context;
-  StreamingOutputCallRequest request;
-
-  for (int i = 0; i < kNumResponseMessages; ++i) {
-    ResponseParameters* response_parameter = request.add_response_parameters();
-    response_parameter->set_size(kResponseMessageSize);
-  }
-  StreamingOutputCallResponse response;
-  std::unique_ptr<grpc::ClientReader<StreamingOutputCallResponse>> stream(
-      stub->StreamingOutputCall(&context, request));
-
-  int i = 0;
-  while (stream->Read(&response)) {
-    GPR_ASSERT(response.payload().body() ==
-               grpc::string(kResponseMessageSize, '\0'));
-    gpr_log(GPR_INFO, "received message %d", i);
-    usleep(kReceiveDelayMilliSeconds * 1000);
-    ++i;
-  }
-  GPR_ASSERT(kNumResponseMessages == i);
-  grpc::Status s = stream->Finish();
-
-  AssertOkOrPrintErrorStatus(s);
-  gpr_log(GPR_INFO, "Response streaming done.");
-}
-
-void DoHalfDuplex() {
-  gpr_log(GPR_INFO, "Sending half-duplex streaming rpc ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("half_duplex");
-  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
-
-  grpc::ClientContext context;
-  std::unique_ptr<grpc::ClientReaderWriter<StreamingOutputCallRequest,
-                                           StreamingOutputCallResponse>>
-      stream(stub->HalfDuplexCall(&context));
-
-  StreamingOutputCallRequest request;
-  ResponseParameters* response_parameter = request.add_response_parameters();
-  for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
-    response_parameter->set_size(response_stream_sizes[i]);
-    GPR_ASSERT(stream->Write(request));
-  }
-  stream->WritesDone();
-
-  unsigned int i = 0;
-  StreamingOutputCallResponse response;
-  while (stream->Read(&response)) {
-    GPR_ASSERT(response.payload().has_body());
-    GPR_ASSERT(response.payload().body() ==
-               grpc::string(response_stream_sizes[i], '\0'));
-    ++i;
-  }
-  GPR_ASSERT(response_stream_sizes.size() == i);
-  grpc::Status s = stream->Finish();
-  AssertOkOrPrintErrorStatus(s);
-  gpr_log(GPR_INFO, "Half-duplex streaming rpc done.");
-}
-
-void DoPingPong() {
-  gpr_log(GPR_INFO, "Sending Ping Pong streaming rpc ...");
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelForTestCase("ping_pong");
-  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel));
-
-  grpc::ClientContext context;
-  std::unique_ptr<grpc::ClientReaderWriter<StreamingOutputCallRequest,
-                                           StreamingOutputCallResponse>>
-      stream(stub->FullDuplexCall(&context));
-
-  StreamingOutputCallRequest request;
-  request.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
-  ResponseParameters* response_parameter = request.add_response_parameters();
-  grpc::testing::Payload* payload = request.mutable_payload();
-  StreamingOutputCallResponse response;
-  for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
-    response_parameter->set_size(response_stream_sizes[i]);
-    payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
-    GPR_ASSERT(stream->Write(request));
-    GPR_ASSERT(stream->Read(&response));
-    GPR_ASSERT(response.payload().has_body());
-    GPR_ASSERT(response.payload().body() ==
-               grpc::string(response_stream_sizes[i], '\0'));
-  }
-
-  stream->WritesDone();
-  GPR_ASSERT(!stream->Read(&response));
-  grpc::Status s = stream->Finish();
-  AssertOkOrPrintErrorStatus(s);
-  gpr_log(GPR_INFO, "Ping pong streaming done.");
-}
-
 int main(int argc, char** argv) {
   grpc_init();
 
   ParseCommandLineFlags(&argc, &argv, true);
 
+  grpc::testing::InteropClient client(
+      CreateChannelForTestCase(FLAGS_test_case));
   if (FLAGS_test_case == "empty_unary") {
-    DoEmpty();
+    client.DoEmpty();
   } else if (FLAGS_test_case == "large_unary") {
-    DoLargeUnary();
+    client.DoLargeUnary();
   } else if (FLAGS_test_case == "client_streaming") {
-    DoRequestStreaming();
+    client.DoRequestStreaming();
   } else if (FLAGS_test_case == "server_streaming") {
-    DoResponseStreaming();
+    client.DoResponseStreaming();
   } else if (FLAGS_test_case == "slow_consumer") {
-    DoResponseStreamingWithSlowConsumer();
+    client.DoResponseStreamingWithSlowConsumer();
   } else if (FLAGS_test_case == "half_duplex") {
-    DoHalfDuplex();
+    client.DoHalfDuplex();
   } else if (FLAGS_test_case == "ping_pong") {
-    DoPingPong();
+    client.DoPingPong();
   } else if (FLAGS_test_case == "service_account_creds") {
-    DoServiceAccountCreds();
+    grpc::string json_key = GetServiceAccountJsonKey();
+    client.DoServiceAccountCreds(json_key, FLAGS_oauth_scope);
   } else if (FLAGS_test_case == "compute_engine_creds") {
-    DoComputeEngineCreds();
+    client.DoComputeEngineCreds(FLAGS_default_service_account,
+                                FLAGS_oauth_scope);
   } else if (FLAGS_test_case == "jwt_token_creds") {
-    DoJwtTokenCreds();
+    grpc::string json_key = GetServiceAccountJsonKey();
+    client.DoJwtTokenCreds(json_key);
   } else if (FLAGS_test_case == "all") {
-    DoEmpty();
-    DoLargeUnary();
-    DoRequestStreaming();
-    DoResponseStreaming();
-    DoHalfDuplex();
-    DoPingPong();
+    client.DoEmpty();
+    client.DoLargeUnary();
+    client.DoRequestStreaming();
+    client.DoResponseStreaming();
+    client.DoHalfDuplex();
+    client.DoPingPong();
     // service_account_creds and jwt_token_creds can only run with ssl.
     if (FLAGS_enable_ssl) {
-      DoServiceAccountCreds();
-      DoJwtTokenCreds();
+      grpc::string json_key = GetServiceAccountJsonKey();
+      client.DoServiceAccountCreds(json_key, FLAGS_oauth_scope);
+      client.DoJwtTokenCreds(json_key);
     }
     // compute_engine_creds only runs in GCE.
   } else {
@@ -470,6 +133,7 @@
         "service_account_creds|compute_engine_creds|jwt_token_creds",
         FLAGS_test_case.c_str());
   }
+  client.Reset(nullptr);
 
   grpc_shutdown();
   return 0;
diff --git a/test/cpp/interop/client_helper.cc b/test/cpp/interop/client_helper.cc
new file mode 100644
index 0000000..362e6af
--- /dev/null
+++ b/test/cpp/interop/client_helper.cc
@@ -0,0 +1,119 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/interop/client_helper.h"
+
+#include <fstream>
+#include <memory>
+#include <sstream>
+
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <gflags/gflags.h>
+#include <grpc++/channel_arguments.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/credentials.h>
+#include <grpc++/stream.h>
+#include "test/cpp/util/create_test_channel.h"
+
+DECLARE_bool(enable_ssl);
+DECLARE_bool(use_prod_roots);
+DECLARE_int32(server_port);
+DECLARE_string(server_host);
+DECLARE_string(server_host_override);
+DECLARE_string(test_case);
+DECLARE_string(default_service_account);
+DECLARE_string(service_account_key_file);
+DECLARE_string(oauth_scope);
+
+// In some distros, gflags is in the namespace google, and in some others,
+// in gflags. This hack is enabling us to find both.
+namespace google {}
+namespace gflags {}
+using namespace google;
+using namespace gflags;
+
+namespace grpc {
+namespace testing {
+
+grpc::string GetServiceAccountJsonKey() {
+  static grpc::string json_key;
+  if (json_key.empty()) {
+    std::ifstream json_key_file(FLAGS_service_account_key_file);
+    std::stringstream key_stream;
+    key_stream << json_key_file.rdbuf();
+    json_key = key_stream.str();
+  }
+  return json_key;
+}
+
+std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
+    const grpc::string& test_case) {
+  GPR_ASSERT(FLAGS_server_port);
+  const int host_port_buf_size = 1024;
+  char host_port[host_port_buf_size];
+  snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
+           FLAGS_server_port);
+
+  if (test_case == "service_account_creds") {
+    std::unique_ptr<Credentials> creds;
+    GPR_ASSERT(FLAGS_enable_ssl);
+    grpc::string json_key = GetServiceAccountJsonKey();
+    creds = ServiceAccountCredentials(json_key, FLAGS_oauth_scope,
+                                      std::chrono::hours(1));
+    return CreateTestChannel(host_port, FLAGS_server_host_override,
+                             FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
+  } else if (test_case == "compute_engine_creds") {
+    std::unique_ptr<Credentials> creds;
+    GPR_ASSERT(FLAGS_enable_ssl);
+    creds = ComputeEngineCredentials();
+    return CreateTestChannel(host_port, FLAGS_server_host_override,
+                             FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
+  } else if (test_case == "jwt_token_creds") {
+    std::unique_ptr<Credentials> creds;
+    GPR_ASSERT(FLAGS_enable_ssl);
+    grpc::string json_key = GetServiceAccountJsonKey();
+    creds = JWTCredentials(json_key, std::chrono::hours(1));
+    return CreateTestChannel(host_port, FLAGS_server_host_override,
+                             FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
+  } else {
+    return CreateTestChannel(host_port, FLAGS_server_host_override,
+                             FLAGS_enable_ssl, FLAGS_use_prod_roots);
+  }
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/interop/client_helper.h b/test/cpp/interop/client_helper.h
new file mode 100644
index 0000000..897f974
--- /dev/null
+++ b/test/cpp/interop/client_helper.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H
+#define GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H
+
+#include <memory>
+
+#include <grpc++/config.h>
+#include <grpc++/channel_interface.h>
+
+namespace grpc {
+namespace testing {
+
+grpc::string GetServiceAccountJsonKey();
+
+std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
+    const grpc::string& test_case);
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_INTEROP_CLIENT_HELPER_H
diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc
new file mode 100644
index 0000000..fd9c2e0
--- /dev/null
+++ b/test/cpp/interop/interop_client.cc
@@ -0,0 +1,311 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/interop/interop_client.h"
+
+#include <memory>
+
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/client_context.h>
+#include <grpc++/status.h>
+#include <grpc++/stream.h>
+#include "test/cpp/interop/test.grpc.pb.h"
+#include "test/cpp/interop/empty.grpc.pb.h"
+#include "test/cpp/interop/messages.grpc.pb.h"
+
+namespace grpc {
+namespace testing {
+
+namespace {
+// The same value is defined by the Java client.
+const std::vector<int> request_stream_sizes = {27182, 8, 1828, 45904};
+const std::vector<int> response_stream_sizes = {31415, 9, 2653, 58979};
+const int kNumResponseMessages = 2000;
+const int kResponseMessageSize = 1030;
+const int kReceiveDelayMilliSeconds = 20;
+const int kLargeRequestSize = 314159;
+const int kLargeResponseSize = 271812;
+}  // namespace
+
+InteropClient::InteropClient(std::shared_ptr<ChannelInterface> channel)
+    : channel_(channel) {}
+
+void InteropClient::AssertOkOrPrintErrorStatus(const Status& s) {
+  if (s.IsOk()) {
+    return;
+  }
+  gpr_log(GPR_INFO, "Error status code: %d, message: %s", s.code(),
+          s.details().c_str());
+  GPR_ASSERT(0);
+}
+
+void InteropClient::DoEmpty() {
+  gpr_log(GPR_INFO, "Sending an empty rpc...");
+  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
+
+  Empty request = Empty::default_instance();
+  Empty response = Empty::default_instance();
+  ClientContext context;
+
+  Status s = stub->EmptyCall(&context, request, &response);
+  AssertOkOrPrintErrorStatus(s);
+
+  gpr_log(GPR_INFO, "Empty rpc done.");
+}
+
+// Shared code to set large payload, make rpc and check response payload.
+void InteropClient::PerformLargeUnary(SimpleRequest* request,
+                                      SimpleResponse* response) {
+  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
+
+  ClientContext context;
+  request->set_response_type(PayloadType::COMPRESSABLE);
+  request->set_response_size(kLargeResponseSize);
+  grpc::string payload(kLargeRequestSize, '\0');
+  request->mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+
+  Status s = stub->UnaryCall(&context, *request, response);
+
+  AssertOkOrPrintErrorStatus(s);
+  GPR_ASSERT(response->payload().type() == PayloadType::COMPRESSABLE);
+  GPR_ASSERT(response->payload().body() ==
+             grpc::string(kLargeResponseSize, '\0'));
+}
+
+void InteropClient::DoComputeEngineCreds(
+    const grpc::string& default_service_account,
+    const grpc::string& oauth_scope) {
+  gpr_log(GPR_INFO,
+          "Sending a large unary rpc with compute engine credentials ...");
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_fill_username(true);
+  request.set_fill_oauth_scope(true);
+  PerformLargeUnary(&request, &response);
+  gpr_log(GPR_INFO, "Got username %s", response.username().c_str());
+  gpr_log(GPR_INFO, "Got oauth_scope %s", response.oauth_scope().c_str());
+  GPR_ASSERT(!response.username().empty());
+  GPR_ASSERT(response.username().c_str() == default_service_account);
+  GPR_ASSERT(!response.oauth_scope().empty());
+  const char* oauth_scope_str = response.oauth_scope().c_str();
+  GPR_ASSERT(oauth_scope.find(oauth_scope_str) != grpc::string::npos);
+  gpr_log(GPR_INFO, "Large unary with compute engine creds done.");
+}
+
+void InteropClient::DoServiceAccountCreds(const grpc::string& username,
+                                          const grpc::string& oauth_scope) {
+  gpr_log(GPR_INFO,
+          "Sending a large unary rpc with service account credentials ...");
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_fill_username(true);
+  request.set_fill_oauth_scope(true);
+  PerformLargeUnary(&request, &response);
+  GPR_ASSERT(!response.username().empty());
+  GPR_ASSERT(!response.oauth_scope().empty());
+  GPR_ASSERT(username.find(response.username()) != grpc::string::npos);
+  const char* oauth_scope_str = response.oauth_scope().c_str();
+  GPR_ASSERT(oauth_scope.find(oauth_scope_str) != grpc::string::npos);
+  gpr_log(GPR_INFO, "Large unary with service account creds done.");
+}
+
+void InteropClient::DoJwtTokenCreds(const grpc::string& username) {
+  gpr_log(GPR_INFO, "Sending a large unary rpc with JWT token credentials ...");
+  SimpleRequest request;
+  SimpleResponse response;
+  request.set_fill_username(true);
+  PerformLargeUnary(&request, &response);
+  GPR_ASSERT(!response.username().empty());
+  GPR_ASSERT(username.find(response.username()) != grpc::string::npos);
+  gpr_log(GPR_INFO, "Large unary with JWT token creds done.");
+}
+
+void InteropClient::DoLargeUnary() {
+  gpr_log(GPR_INFO, "Sending a large unary rpc...");
+  SimpleRequest request;
+  SimpleResponse response;
+  PerformLargeUnary(&request, &response);
+  gpr_log(GPR_INFO, "Large unary done.");
+}
+
+void InteropClient::DoRequestStreaming() {
+  gpr_log(GPR_INFO, "Sending request steaming rpc ...");
+  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
+
+  ClientContext context;
+  StreamingInputCallRequest request;
+  StreamingInputCallResponse response;
+
+  std::unique_ptr<ClientWriter<StreamingInputCallRequest>> stream(
+      stub->StreamingInputCall(&context, &response));
+
+  int aggregated_payload_size = 0;
+  for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
+    Payload* payload = request.mutable_payload();
+    payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
+    GPR_ASSERT(stream->Write(request));
+    aggregated_payload_size += request_stream_sizes[i];
+  }
+  stream->WritesDone();
+  Status s = stream->Finish();
+
+  GPR_ASSERT(response.aggregated_payload_size() == aggregated_payload_size);
+  AssertOkOrPrintErrorStatus(s);
+  gpr_log(GPR_INFO, "Request streaming done.");
+}
+
+void InteropClient::DoResponseStreaming() {
+  gpr_log(GPR_INFO, "Receiving response steaming rpc ...");
+  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
+
+  ClientContext context;
+  StreamingOutputCallRequest request;
+  for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
+    ResponseParameters* response_parameter = request.add_response_parameters();
+    response_parameter->set_size(response_stream_sizes[i]);
+  }
+  StreamingOutputCallResponse response;
+  std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream(
+      stub->StreamingOutputCall(&context, request));
+
+  unsigned int i = 0;
+  while (stream->Read(&response)) {
+    GPR_ASSERT(response.payload().body() ==
+               grpc::string(response_stream_sizes[i], '\0'));
+    ++i;
+  }
+  GPR_ASSERT(response_stream_sizes.size() == i);
+  Status s = stream->Finish();
+
+  AssertOkOrPrintErrorStatus(s);
+  gpr_log(GPR_INFO, "Response streaming done.");
+}
+
+void InteropClient::DoResponseStreamingWithSlowConsumer() {
+  gpr_log(GPR_INFO, "Receiving response steaming rpc with slow consumer ...");
+  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
+
+  ClientContext context;
+  StreamingOutputCallRequest request;
+
+  for (int i = 0; i < kNumResponseMessages; ++i) {
+    ResponseParameters* response_parameter = request.add_response_parameters();
+    response_parameter->set_size(kResponseMessageSize);
+  }
+  StreamingOutputCallResponse response;
+  std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream(
+      stub->StreamingOutputCall(&context, request));
+
+  int i = 0;
+  while (stream->Read(&response)) {
+    GPR_ASSERT(response.payload().body() ==
+               grpc::string(kResponseMessageSize, '\0'));
+    gpr_log(GPR_INFO, "received message %d", i);
+    usleep(kReceiveDelayMilliSeconds * 1000);
+    ++i;
+  }
+  GPR_ASSERT(kNumResponseMessages == i);
+  Status s = stream->Finish();
+
+  AssertOkOrPrintErrorStatus(s);
+  gpr_log(GPR_INFO, "Response streaming done.");
+}
+
+void InteropClient::DoHalfDuplex() {
+  gpr_log(GPR_INFO, "Sending half-duplex streaming rpc ...");
+  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
+
+  ClientContext context;
+  std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest,
+                                     StreamingOutputCallResponse>>
+      stream(stub->HalfDuplexCall(&context));
+
+  StreamingOutputCallRequest request;
+  ResponseParameters* response_parameter = request.add_response_parameters();
+  for (unsigned int i = 0; i < response_stream_sizes.size(); ++i) {
+    response_parameter->set_size(response_stream_sizes[i]);
+    GPR_ASSERT(stream->Write(request));
+  }
+  stream->WritesDone();
+
+  unsigned int i = 0;
+  StreamingOutputCallResponse response;
+  while (stream->Read(&response)) {
+    GPR_ASSERT(response.payload().has_body());
+    GPR_ASSERT(response.payload().body() ==
+               grpc::string(response_stream_sizes[i], '\0'));
+    ++i;
+  }
+  GPR_ASSERT(response_stream_sizes.size() == i);
+  Status s = stream->Finish();
+  AssertOkOrPrintErrorStatus(s);
+  gpr_log(GPR_INFO, "Half-duplex streaming rpc done.");
+}
+
+void InteropClient::DoPingPong() {
+  gpr_log(GPR_INFO, "Sending Ping Pong streaming rpc ...");
+  std::unique_ptr<TestService::Stub> stub(TestService::NewStub(channel_));
+
+  ClientContext context;
+  std::unique_ptr<ClientReaderWriter<StreamingOutputCallRequest,
+                                     StreamingOutputCallResponse>>
+      stream(stub->FullDuplexCall(&context));
+
+  StreamingOutputCallRequest request;
+  request.set_response_type(PayloadType::COMPRESSABLE);
+  ResponseParameters* response_parameter = request.add_response_parameters();
+  Payload* payload = request.mutable_payload();
+  StreamingOutputCallResponse response;
+  for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) {
+    response_parameter->set_size(response_stream_sizes[i]);
+    payload->set_body(grpc::string(request_stream_sizes[i], '\0'));
+    GPR_ASSERT(stream->Write(request));
+    GPR_ASSERT(stream->Read(&response));
+    GPR_ASSERT(response.payload().has_body());
+    GPR_ASSERT(response.payload().body() ==
+               grpc::string(response_stream_sizes[i], '\0'));
+  }
+
+  stream->WritesDone();
+  GPR_ASSERT(!stream->Read(&response));
+  Status s = stream->Finish();
+  AssertOkOrPrintErrorStatus(s);
+  gpr_log(GPR_INFO, "Ping pong streaming done.");
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/interop/interop_client.h b/test/cpp/interop/interop_client.h
new file mode 100644
index 0000000..b0ab320
--- /dev/null
+++ b/test/cpp/interop/interop_client.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_TEST_CPP_INTEROP_INTEROP_CLIENT_H
+#define GRPC_TEST_CPP_INTEROP_INTEROP_CLIENT_H
+#include <memory>
+
+#include <grpc/grpc.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/status.h>
+#include "test/cpp/interop/messages.grpc.pb.h"
+
+namespace grpc {
+namespace testing {
+
+class InteropClient {
+ public:
+  explicit InteropClient(std::shared_ptr<ChannelInterface> channel);
+  ~InteropClient() {}
+
+  void Reset(std::shared_ptr<ChannelInterface> channel) { channel_ = channel; }
+
+  void DoEmpty();
+  void DoLargeUnary();
+  void DoPingPong();
+  void DoHalfDuplex();
+  void DoRequestStreaming();
+  void DoResponseStreaming();
+  void DoResponseStreamingWithSlowConsumer();
+  // Auth tests.
+  // username is a string containing the user email
+  void DoJwtTokenCreds(const grpc::string& username);
+  void DoComputeEngineCreds(const grpc::string& default_service_account,
+                            const grpc::string& oauth_scope);
+  // username is a string containing the user email
+  void DoServiceAccountCreds(const grpc::string& username,
+                             const grpc::string& oauth_scope);
+
+ private:
+  void PerformLargeUnary(SimpleRequest* request, SimpleResponse* response);
+  void AssertOkOrPrintErrorStatus(const Status& s);
+
+  std::shared_ptr<ChannelInterface> channel_;
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_INTEROP_INTEROP_CLIENT_H
diff --git a/test/cpp/interop/server.cc b/test/cpp/interop/server.cc
index 87bf489..d87493b 100644
--- a/test/cpp/interop/server.cc
+++ b/test/cpp/interop/server.cc
@@ -41,7 +41,6 @@
 #include <gflags/gflags.h>
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
-#include "test/core/end2end/data/ssl_test_data.h"
 #include <grpc++/config.h>
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
@@ -52,6 +51,7 @@
 #include "test/cpp/interop/test.grpc.pb.h"
 #include "test/cpp/interop/empty.grpc.pb.h"
 #include "test/cpp/interop/messages.grpc.pb.h"
+#include "test/cpp/interop/server_helper.h"
 
 DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
 DEFINE_int32(port, 0, "Server port.");
@@ -211,15 +211,8 @@
 
   ServerBuilder builder;
   builder.RegisterService(&service);
-  std::shared_ptr<ServerCredentials> creds = grpc::InsecureServerCredentials();
-  if (FLAGS_enable_ssl) {
-    SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
-							test_server1_cert};
-    SslServerCredentialsOptions ssl_opts;
-    ssl_opts.pem_root_certs = "";
-    ssl_opts.pem_key_cert_pairs.push_back(pkcp);
-    creds = grpc::SslServerCredentials(ssl_opts);
-  }
+  std::shared_ptr<ServerCredentials> creds =
+      grpc::testing::CreateInteropServerCredentials();
   builder.AddListeningPort(server_address.str(), creds);
   std::unique_ptr<Server> server(builder.BuildAndStart());
   gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());
diff --git a/test/cpp/interop/server_helper.cc b/test/cpp/interop/server_helper.cc
new file mode 100644
index 0000000..56597c8
--- /dev/null
+++ b/test/cpp/interop/server_helper.cc
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/interop/server_helper.h"
+
+#include <memory>
+
+#include <gflags/gflags.h>
+#include "test/core/end2end/data/ssl_test_data.h"
+#include <grpc++/config.h>
+#include <grpc++/server_credentials.h>
+
+DECLARE_bool(enable_ssl);
+
+// In some distros, gflags is in the namespace google, and in some others,
+// in gflags. This hack is enabling us to find both.
+namespace google {}
+namespace gflags {}
+using namespace google;
+using namespace gflags;
+
+namespace grpc {
+namespace testing {
+
+std::shared_ptr<ServerCredentials> CreateInteropServerCredentials() {
+  if (FLAGS_enable_ssl) {
+    SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
+                                                        test_server1_cert};
+    SslServerCredentialsOptions ssl_opts;
+    ssl_opts.pem_root_certs = "";
+    ssl_opts.pem_key_cert_pairs.push_back(pkcp);
+    return SslServerCredentials(ssl_opts);
+  } else {
+    return InsecureServerCredentials();
+  }
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/interop/server_helper.h b/test/cpp/interop/server_helper.h
new file mode 100644
index 0000000..f98e67b
--- /dev/null
+++ b/test/cpp/interop/server_helper.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_TEST_CPP_INTEROP_SERVER_HELPER_H
+#define GRPC_TEST_CPP_INTEROP_SERVER_HELPER_H
+
+#include <memory>
+
+#include <grpc++/server_credentials.h>
+
+namespace grpc {
+namespace testing {
+
+std::shared_ptr<ServerCredentials> CreateInteropServerCredentials();
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_INTEROP_SERVER_HELPER_H