diff --git a/.travis.yml b/.travis.yml
index de320b59a3ae43020889d62362e2a9a0ff6c18cb..e43a89e453a7fd0831c8aed07ffbce96dfd726e2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,9 +4,11 @@ before_install:
   - sudo add-apt-repository ppa:h-rayflood/llvm -y
   - sudo apt-get update -qq
   - sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv clang-3.5
+  - sudo pip install cpp-coveralls
 env:
   global:
     - RUBY_VERSION=2.1
+    - COVERALLS_PARALLEL=true
   matrix:
     - CONFIG=dbg TEST=c
     - CONFIG=dbg TEST=c++
@@ -15,9 +17,15 @@ env:
     - CONFIG=opt TEST=node
     - CONFIG=opt TEST=ruby
     - CONFIG=opt TEST=python
+    - CONFIG=gcov TEST=c
+    - CONFIG=gcov TEST=c++
 script:
   - rvm use $RUBY_VERSION
   - gem install bundler
   - ./tools/run_tests/run_tests.py -l $TEST -t -j 16 -c $CONFIG -s 4.0
+after_success:
+  - if [ "$CONFIG" = "gcov" ] ; then coveralls --exclude third_party --exclude gens -b. --gcov-options '\-p' ; fi
 notifications:
-  email: false
\ No newline at end of file
+  email: false
+  webhooks:
+    - https://coveralls.io/webhook?repo_token=54IxAHPjJNdQJzJAhPU0MFpCtg7KvcydQ
diff --git a/Makefile b/Makefile
index ace93ea84e2d5b9304c8acef8f406902dee45166..8c6fc3220da66fb292cdb9327de95c3616c97e2b 100644
--- a/Makefile
+++ b/Makefile
@@ -544,12 +544,11 @@ interop_test: $(BINDIR)/$(CONFIG)/interop_test
 pubsub_client: $(BINDIR)/$(CONFIG)/pubsub_client
 pubsub_publisher_test: $(BINDIR)/$(CONFIG)/pubsub_publisher_test
 pubsub_subscriber_test: $(BINDIR)/$(CONFIG)/pubsub_subscriber_test
-qps_client: $(BINDIR)/$(CONFIG)/qps_client
-qps_client_async: $(BINDIR)/$(CONFIG)/qps_client_async
-qps_server: $(BINDIR)/$(CONFIG)/qps_server
-qps_server_async: $(BINDIR)/$(CONFIG)/qps_server_async
+qps_driver: $(BINDIR)/$(CONFIG)/qps_driver
+qps_worker: $(BINDIR)/$(CONFIG)/qps_worker
 status_test: $(BINDIR)/$(CONFIG)/status_test
 thread_pool_test: $(BINDIR)/$(CONFIG)/thread_pool_test
+chttp2_fake_security_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_bad_hostname_test
 chttp2_fake_security_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test
 chttp2_fake_security_cancel_after_accept_and_writes_closed_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test
 chttp2_fake_security_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test
@@ -598,6 +597,7 @@ chttp2_fake_security_simple_delayed_request_legacy_test: $(BINDIR)/$(CONFIG)/cht
 chttp2_fake_security_simple_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_legacy_test
 chttp2_fake_security_thread_stress_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_legacy_test
 chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test
+chttp2_fullstack_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_test
 chttp2_fullstack_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test
 chttp2_fullstack_cancel_after_accept_and_writes_closed_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test
 chttp2_fullstack_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test
@@ -646,6 +646,7 @@ chttp2_fullstack_simple_delayed_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_
 chttp2_fullstack_simple_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_legacy_test
 chttp2_fullstack_thread_stress_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_legacy_test
 chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test
+chttp2_fullstack_uds_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_bad_hostname_test
 chttp2_fullstack_uds_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_test
 chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_test
 chttp2_fullstack_uds_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_test
@@ -694,6 +695,7 @@ chttp2_fullstack_uds_simple_delayed_request_legacy_test: $(BINDIR)/$(CONFIG)/cht
 chttp2_fullstack_uds_simple_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_legacy_test
 chttp2_fullstack_uds_thread_stress_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_legacy_test
 chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test
+chttp2_simple_ssl_fullstack_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_bad_hostname_test
 chttp2_simple_ssl_fullstack_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test
 chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test
 chttp2_simple_ssl_fullstack_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test
@@ -742,6 +744,7 @@ chttp2_simple_ssl_fullstack_simple_delayed_request_legacy_test: $(BINDIR)/$(CONF
 chttp2_simple_ssl_fullstack_simple_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_legacy_test
 chttp2_simple_ssl_fullstack_thread_stress_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_legacy_test
 chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test
+chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test
 chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test
 chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test
 chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test
@@ -790,6 +793,7 @@ chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_legacy_test: $(BI
 chttp2_simple_ssl_with_oauth2_fullstack_simple_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_legacy_test
 chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test
 chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test
+chttp2_socket_pair_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_test
 chttp2_socket_pair_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test
 chttp2_socket_pair_cancel_after_accept_and_writes_closed_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test
 chttp2_socket_pair_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test
@@ -838,6 +842,7 @@ chttp2_socket_pair_simple_delayed_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp
 chttp2_socket_pair_simple_request_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_legacy_test
 chttp2_socket_pair_thread_stress_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_legacy_test
 chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test
+chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test
 chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test
 chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test
 chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test
@@ -937,13 +942,13 @@ static: static_c static_cxx
 
 static_c:  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a
 
-static_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++.a
+static_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a
 
 shared: shared_c shared_cxx
 
 shared_c:  $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT)
 
-shared_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT)
+shared_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT)
 
 shared_csharp: shared_c  $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT)
 grpc_csharp_ext: shared_csharp
@@ -952,15 +957,15 @@ plugins: $(PROTOC_PLUGINS)
 
 privatelibs: privatelibs_c privatelibs_cxx
 
-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_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_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
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libpubsub_client_lib.a $(LIBDIR)/$(CONFIG)/libqps.a
 
 buildtests: buildtests_c buildtests_cxx
 
-buildtests_c: privatelibs_c $(BINDIR)/$(CONFIG)/alarm_heap_test $(BINDIR)/$(CONFIG)/alarm_list_test $(BINDIR)/$(CONFIG)/alarm_test $(BINDIR)/$(CONFIG)/alpn_test $(BINDIR)/$(CONFIG)/bin_encoder_test $(BINDIR)/$(CONFIG)/census_hash_table_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_test $(BINDIR)/$(CONFIG)/census_statistics_performance_test $(BINDIR)/$(CONFIG)/census_statistics_quick_test $(BINDIR)/$(CONFIG)/census_statistics_small_log_test $(BINDIR)/$(CONFIG)/census_stub_test $(BINDIR)/$(CONFIG)/census_window_stats_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/chttp2_transport_end2end_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/echo_client $(BINDIR)/$(CONFIG)/echo_server $(BINDIR)/$(CONFIG)/echo_test $(BINDIR)/$(CONFIG)/fd_posix_test $(BINDIR)/$(CONFIG)/fling_client $(BINDIR)/$(CONFIG)/fling_server $(BINDIR)/$(CONFIG)/fling_stream_test $(BINDIR)/$(CONFIG)/fling_test $(BINDIR)/$(CONFIG)/gpr_cancellable_test $(BINDIR)/$(CONFIG)/gpr_cmdline_test $(BINDIR)/$(CONFIG)/gpr_env_test $(BINDIR)/$(CONFIG)/gpr_file_test $(BINDIR)/$(CONFIG)/gpr_histogram_test $(BINDIR)/$(CONFIG)/gpr_host_port_test $(BINDIR)/$(CONFIG)/gpr_log_test $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test $(BINDIR)/$(CONFIG)/gpr_slice_test $(BINDIR)/$(CONFIG)/gpr_string_test $(BINDIR)/$(CONFIG)/gpr_sync_test $(BINDIR)/$(CONFIG)/gpr_thd_test $(BINDIR)/$(CONFIG)/gpr_time_test $(BINDIR)/$(CONFIG)/gpr_useful_test $(BINDIR)/$(CONFIG)/grpc_base64_test $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test $(BINDIR)/$(CONFIG)/grpc_channel_stack_test $(BINDIR)/$(CONFIG)/grpc_completion_queue_test $(BINDIR)/$(CONFIG)/grpc_credentials_test $(BINDIR)/$(CONFIG)/grpc_json_token_test $(BINDIR)/$(CONFIG)/grpc_stream_op_test $(BINDIR)/$(CONFIG)/hpack_parser_test $(BINDIR)/$(CONFIG)/hpack_table_test $(BINDIR)/$(CONFIG)/httpcli_format_request_test $(BINDIR)/$(CONFIG)/httpcli_parser_test $(BINDIR)/$(CONFIG)/httpcli_test $(BINDIR)/$(CONFIG)/json_rewrite $(BINDIR)/$(CONFIG)/json_rewrite_test $(BINDIR)/$(CONFIG)/json_test $(BINDIR)/$(CONFIG)/lame_client_test $(BINDIR)/$(CONFIG)/message_compress_test $(BINDIR)/$(CONFIG)/metadata_buffer_test $(BINDIR)/$(CONFIG)/multi_init_test $(BINDIR)/$(CONFIG)/murmur_hash_test $(BINDIR)/$(CONFIG)/no_server_test $(BINDIR)/$(CONFIG)/poll_kick_posix_test $(BINDIR)/$(CONFIG)/resolve_address_test $(BINDIR)/$(CONFIG)/secure_endpoint_test $(BINDIR)/$(CONFIG)/sockaddr_utils_test $(BINDIR)/$(CONFIG)/tcp_client_posix_test $(BINDIR)/$(CONFIG)/tcp_posix_test $(BINDIR)/$(CONFIG)/tcp_server_posix_test $(BINDIR)/$(CONFIG)/time_averaged_stats_test $(BINDIR)/$(CONFIG)/time_test $(BINDIR)/$(CONFIG)/timeout_encoding_test $(BINDIR)/$(CONFIG)/transport_metadata_test $(BINDIR)/$(CONFIG)/transport_security_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_legacy_test
+buildtests_c: privatelibs_c $(BINDIR)/$(CONFIG)/alarm_heap_test $(BINDIR)/$(CONFIG)/alarm_list_test $(BINDIR)/$(CONFIG)/alarm_test $(BINDIR)/$(CONFIG)/alpn_test $(BINDIR)/$(CONFIG)/bin_encoder_test $(BINDIR)/$(CONFIG)/census_hash_table_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test $(BINDIR)/$(CONFIG)/census_statistics_multiple_writers_test $(BINDIR)/$(CONFIG)/census_statistics_performance_test $(BINDIR)/$(CONFIG)/census_statistics_quick_test $(BINDIR)/$(CONFIG)/census_statistics_small_log_test $(BINDIR)/$(CONFIG)/census_stub_test $(BINDIR)/$(CONFIG)/census_window_stats_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/chttp2_transport_end2end_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/echo_client $(BINDIR)/$(CONFIG)/echo_server $(BINDIR)/$(CONFIG)/echo_test $(BINDIR)/$(CONFIG)/fd_posix_test $(BINDIR)/$(CONFIG)/fling_client $(BINDIR)/$(CONFIG)/fling_server $(BINDIR)/$(CONFIG)/fling_stream_test $(BINDIR)/$(CONFIG)/fling_test $(BINDIR)/$(CONFIG)/gpr_cancellable_test $(BINDIR)/$(CONFIG)/gpr_cmdline_test $(BINDIR)/$(CONFIG)/gpr_env_test $(BINDIR)/$(CONFIG)/gpr_file_test $(BINDIR)/$(CONFIG)/gpr_histogram_test $(BINDIR)/$(CONFIG)/gpr_host_port_test $(BINDIR)/$(CONFIG)/gpr_log_test $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test $(BINDIR)/$(CONFIG)/gpr_slice_test $(BINDIR)/$(CONFIG)/gpr_string_test $(BINDIR)/$(CONFIG)/gpr_sync_test $(BINDIR)/$(CONFIG)/gpr_thd_test $(BINDIR)/$(CONFIG)/gpr_time_test $(BINDIR)/$(CONFIG)/gpr_useful_test $(BINDIR)/$(CONFIG)/grpc_base64_test $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test $(BINDIR)/$(CONFIG)/grpc_channel_stack_test $(BINDIR)/$(CONFIG)/grpc_completion_queue_test $(BINDIR)/$(CONFIG)/grpc_credentials_test $(BINDIR)/$(CONFIG)/grpc_json_token_test $(BINDIR)/$(CONFIG)/grpc_stream_op_test $(BINDIR)/$(CONFIG)/hpack_parser_test $(BINDIR)/$(CONFIG)/hpack_table_test $(BINDIR)/$(CONFIG)/httpcli_format_request_test $(BINDIR)/$(CONFIG)/httpcli_parser_test $(BINDIR)/$(CONFIG)/httpcli_test $(BINDIR)/$(CONFIG)/json_rewrite $(BINDIR)/$(CONFIG)/json_rewrite_test $(BINDIR)/$(CONFIG)/json_test $(BINDIR)/$(CONFIG)/lame_client_test $(BINDIR)/$(CONFIG)/message_compress_test $(BINDIR)/$(CONFIG)/metadata_buffer_test $(BINDIR)/$(CONFIG)/multi_init_test $(BINDIR)/$(CONFIG)/murmur_hash_test $(BINDIR)/$(CONFIG)/no_server_test $(BINDIR)/$(CONFIG)/poll_kick_posix_test $(BINDIR)/$(CONFIG)/resolve_address_test $(BINDIR)/$(CONFIG)/secure_endpoint_test $(BINDIR)/$(CONFIG)/sockaddr_utils_test $(BINDIR)/$(CONFIG)/tcp_client_posix_test $(BINDIR)/$(CONFIG)/tcp_posix_test $(BINDIR)/$(CONFIG)/tcp_server_posix_test $(BINDIR)/$(CONFIG)/time_averaged_stats_test $(BINDIR)/$(CONFIG)/time_test $(BINDIR)/$(CONFIG)/timeout_encoding_test $(BINDIR)/$(CONFIG)/transport_metadata_test $(BINDIR)/$(CONFIG)/transport_security_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_empty_batch_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_large_metadata_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_with_payload_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_legacy_test $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_legacy_test
 
-buildtests_cxx: privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/credentials_test $(BINDIR)/$(CONFIG)/end2end_test $(BINDIR)/$(CONFIG)/interop_client $(BINDIR)/$(CONFIG)/interop_server $(BINDIR)/$(CONFIG)/interop_test $(BINDIR)/$(CONFIG)/pubsub_client $(BINDIR)/$(CONFIG)/pubsub_publisher_test $(BINDIR)/$(CONFIG)/pubsub_subscriber_test $(BINDIR)/$(CONFIG)/qps_client $(BINDIR)/$(CONFIG)/qps_client_async $(BINDIR)/$(CONFIG)/qps_server $(BINDIR)/$(CONFIG)/qps_server_async $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/thread_pool_test
+buildtests_cxx: privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/credentials_test $(BINDIR)/$(CONFIG)/end2end_test $(BINDIR)/$(CONFIG)/interop_client $(BINDIR)/$(CONFIG)/interop_server $(BINDIR)/$(CONFIG)/interop_test $(BINDIR)/$(CONFIG)/pubsub_client $(BINDIR)/$(CONFIG)/pubsub_publisher_test $(BINDIR)/$(CONFIG)/pubsub_subscriber_test $(BINDIR)/$(CONFIG)/qps_driver $(BINDIR)/$(CONFIG)/qps_worker $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/thread_pool_test
 
 test: test_c test_cxx
 
@@ -1099,6 +1104,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/transport_metadata_test || ( echo test transport_metadata_test failed ; exit 1 )
 	$(E) "[RUN]     Testing transport_security_test"
 	$(Q) $(BINDIR)/$(CONFIG)/transport_security_test || ( echo test transport_security_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_fake_security_bad_hostname_test"
+	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fake_security_bad_hostname_test || ( echo test chttp2_fake_security_bad_hostname_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fake_security_cancel_after_accept_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test || ( echo test chttp2_fake_security_cancel_after_accept_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fake_security_cancel_after_accept_and_writes_closed_test"
@@ -1195,6 +1202,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fake_security_thread_stress_legacy_test || ( echo test chttp2_fake_security_thread_stress_legacy_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test || ( echo test chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_fullstack_bad_hostname_test"
+	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_test || ( echo test chttp2_fullstack_bad_hostname_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fullstack_cancel_after_accept_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test || ( echo test chttp2_fullstack_cancel_after_accept_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fullstack_cancel_after_accept_and_writes_closed_test"
@@ -1291,6 +1300,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_thread_stress_legacy_test || ( echo test chttp2_fullstack_thread_stress_legacy_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test || ( echo test chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_fullstack_uds_bad_hostname_test"
+	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_bad_hostname_test || ( echo test chttp2_fullstack_uds_bad_hostname_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fullstack_uds_cancel_after_accept_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_cancel_after_accept_test || ( echo test chttp2_fullstack_uds_cancel_after_accept_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fullstack_uds_cancel_after_accept_and_writes_closed_test"
@@ -1387,6 +1398,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_thread_stress_legacy_test || ( echo test chttp2_fullstack_uds_thread_stress_legacy_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test || ( echo test chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_simple_ssl_fullstack_bad_hostname_test"
+	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_bad_hostname_test || ( echo test chttp2_simple_ssl_fullstack_bad_hostname_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_simple_ssl_fullstack_cancel_after_accept_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test || ( echo test chttp2_simple_ssl_fullstack_cancel_after_accept_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test"
@@ -1483,6 +1496,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_legacy_test || ( echo test chttp2_simple_ssl_fullstack_thread_stress_legacy_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test || ( echo test chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test"
+	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test || ( echo test chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test || ( echo test chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test"
@@ -1579,6 +1594,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test || ( echo test chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_legacy_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test || ( echo test chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_bad_hostname_test"
+	$(Q) $(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_test || ( echo test chttp2_socket_pair_bad_hostname_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_socket_pair_cancel_after_accept_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test || ( echo test chttp2_socket_pair_cancel_after_accept_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_socket_pair_cancel_after_accept_and_writes_closed_test"
@@ -1675,6 +1692,8 @@ test_c: buildtests_c
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_socket_pair_thread_stress_legacy_test || ( echo test chttp2_socket_pair_thread_stress_legacy_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test || ( echo test chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test"
+	$(Q) $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test"
@@ -1825,6 +1844,8 @@ strip-static_cxx: static_cxx
 ifeq ($(CONFIG),opt)
 	$(E) "[STRIP]   Stripping libgrpc++.a"
 	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++.a
+	$(E) "[STRIP]   Stripping libgrpc++_unsecure.a"
+	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a
 endif
 
 strip-shared_c: shared_c
@@ -1841,6 +1862,8 @@ strip-shared_cxx: shared_cxx
 ifeq ($(CONFIG),opt)
 	$(E) "[STRIP]   Stripping libgrpc++.so"
 	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT)
+	$(E) "[STRIP]   Stripping libgrpc++_unsecure.so"
+	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT)
 endif
 
 strip-shared_csharp: shared_csharp
@@ -2000,6 +2023,9 @@ install-static_cxx: static_cxx strip-static_cxx
 	$(E) "[INSTALL] Installing libgrpc++.a"
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(prefix)/lib/libgrpc++.a
+	$(E) "[INSTALL] Installing libgrpc++_unsecure.a"
+	$(Q) $(INSTALL) -d $(prefix)/lib
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(prefix)/lib/libgrpc++_unsecure.a
 
 
 
@@ -2064,6 +2090,19 @@ ifneq ($(SYSTEM),Darwin)
 	$(Q) ln -sf libgrpc++.$(SHARED_EXT) $(prefix)/lib/libgrpc++.so
 endif
 endif
+ifeq ($(SYSTEM),MINGW32)
+	$(E) "[INSTALL] Installing grpc++_unsecure.$(SHARED_EXT)"
+	$(Q) $(INSTALL) -d $(prefix)/lib
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT) $(prefix)/lib/grpc++_unsecure.$(SHARED_EXT)
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure-imp.a $(prefix)/lib/libgrpc++_unsecure-imp.a
+else
+	$(E) "[INSTALL] Installing libgrpc++_unsecure.$(SHARED_EXT)"
+	$(Q) $(INSTALL) -d $(prefix)/lib
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc++_unsecure.$(SHARED_EXT)
+ifneq ($(SYSTEM),Darwin)
+	$(Q) ln -sf libgrpc++_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc++_unsecure.so
+endif
+endif
 ifneq ($(SYSTEM),MINGW32)
 ifneq ($(SYSTEM),Darwin)
 	$(Q) ldconfig || true
@@ -2331,7 +2370,6 @@ LIBGRPC_SRC = \
     src/core/security/server_secure_chttp2.c \
     src/core/surface/init_secure.c \
     src/core/surface/secure_channel_create.c \
-    src/core/surface/secure_server_create.c \
     src/core/tsi/fake_transport_security.c \
     src/core/tsi/ssl_transport_security.c \
     src/core/tsi/transport_security.c \
@@ -2477,7 +2515,6 @@ src/core/security/security_context.c: $(OPENSSL_DEP)
 src/core/security/server_secure_chttp2.c: $(OPENSSL_DEP)
 src/core/surface/init_secure.c: $(OPENSSL_DEP)
 src/core/surface/secure_channel_create.c: $(OPENSSL_DEP)
-src/core/surface/secure_server_create.c: $(OPENSSL_DEP)
 src/core/tsi/fake_transport_security.c: $(OPENSSL_DEP)
 src/core/tsi/ssl_transport_security.c: $(OPENSSL_DEP)
 src/core/tsi/transport_security.c: $(OPENSSL_DEP)
@@ -2583,13 +2620,13 @@ $(LIBDIR)/$(CONFIG)/libgrpc.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_OBJS)
 	$(Q) mkdir -p `dirname $@`
 	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc.a
 	$(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBGRPC_OBJS)
-	$(Q) rm -rf tmp-merge
-	$(Q) mkdir tmp-merge
-	$(Q) ( cd tmp-merge ; $(AR) x ../$(LIBDIR)/$(CONFIG)/libgrpc.a )
-	$(Q) for l in $(OPENSSL_MERGE_LIBS) ; do ( cd tmp-merge ; ar x ../$${l} ) ; done
-	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc.a tmp-merge/__.SYMDEF*
-	$(Q) ar rcs $(LIBDIR)/$(CONFIG)/libgrpc.a tmp-merge/*
-	$(Q) rm -rf tmp-merge
+	$(Q) rm -rf tmp-merge-grpc
+	$(Q) mkdir tmp-merge-grpc
+	$(Q) ( cd tmp-merge-grpc ; $(AR) x ../$(LIBDIR)/$(CONFIG)/libgrpc.a )
+	$(Q) for l in $(OPENSSL_MERGE_LIBS) ; do ( cd tmp-merge-grpc ; ar x ../$${l} ) ; done
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc.a tmp-merge-grpc/__.SYMDEF*
+	$(Q) ar rcs $(LIBDIR)/$(CONFIG)/libgrpc.a tmp-merge-grpc/*
+	$(Q) rm -rf tmp-merge-grpc
 ifeq ($(SYSTEM),Darwin)
 	$(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc.a
 endif
@@ -2640,7 +2677,6 @@ $(OBJDIR)/$(CONFIG)/src/core/security/security_context.o:
 $(OBJDIR)/$(CONFIG)/src/core/security/server_secure_chttp2.o: 
 $(OBJDIR)/$(CONFIG)/src/core/surface/init_secure.o: 
 $(OBJDIR)/$(CONFIG)/src/core/surface/secure_channel_create.o: 
-$(OBJDIR)/$(CONFIG)/src/core/surface/secure_server_create.o: 
 $(OBJDIR)/$(CONFIG)/src/core/tsi/fake_transport_security.o: 
 $(OBJDIR)/$(CONFIG)/src/core/tsi/ssl_transport_security.o: 
 $(OBJDIR)/$(CONFIG)/src/core/tsi/transport_security.o: 
@@ -3055,17 +3091,21 @@ $(OBJDIR)/$(CONFIG)/src/core/transport/transport.o:
 
 
 LIBGRPC++_SRC = \
+    src/cpp/client/secure_credentials.cc \
+    src/cpp/server/secure_server_credentials.cc \
     src/cpp/client/channel.cc \
     src/cpp/client/channel_arguments.cc \
     src/cpp/client/client_context.cc \
     src/cpp/client/client_unary_call.cc \
     src/cpp/client/create_channel.cc \
     src/cpp/client/credentials.cc \
+    src/cpp/client/insecure_credentials.cc \
     src/cpp/client/internal_stub.cc \
     src/cpp/common/call.cc \
     src/cpp/common/completion_queue.cc \
     src/cpp/common/rpc_method.cc \
     src/cpp/proto/proto_utils.cc \
+    src/cpp/server/insecure_server_credentials.cc \
     src/cpp/server/server.cc \
     src/cpp/server/server_builder.cc \
     src/cpp/server/server_context.cc \
@@ -3132,17 +3172,21 @@ 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.
+src/cpp/client/secure_credentials.cc: $(OPENSSL_DEP)
+src/cpp/server/secure_server_credentials.cc: $(OPENSSL_DEP)
 src/cpp/client/channel.cc: $(OPENSSL_DEP)
 src/cpp/client/channel_arguments.cc: $(OPENSSL_DEP)
 src/cpp/client/client_context.cc: $(OPENSSL_DEP)
 src/cpp/client/client_unary_call.cc: $(OPENSSL_DEP)
 src/cpp/client/create_channel.cc: $(OPENSSL_DEP)
 src/cpp/client/credentials.cc: $(OPENSSL_DEP)
+src/cpp/client/insecure_credentials.cc: $(OPENSSL_DEP)
 src/cpp/client/internal_stub.cc: $(OPENSSL_DEP)
 src/cpp/common/call.cc: $(OPENSSL_DEP)
 src/cpp/common/completion_queue.cc: $(OPENSSL_DEP)
 src/cpp/common/rpc_method.cc: $(OPENSSL_DEP)
 src/cpp/proto/proto_utils.cc: $(OPENSSL_DEP)
+src/cpp/server/insecure_server_credentials.cc: $(OPENSSL_DEP)
 src/cpp/server/server.cc: $(OPENSSL_DEP)
 src/cpp/server/server_builder.cc: $(OPENSSL_DEP)
 src/cpp/server/server_context.cc: $(OPENSSL_DEP)
@@ -3167,15 +3211,15 @@ ifeq ($(SYSTEM),MINGW32)
 $(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT): $(LIBGRPC++_OBJS)  $(ZLIB_DEP)$(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT)$(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++-imp.a -o $(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc-imp
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++-imp.a -o $(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc-imp
 else
 $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT): $(LIBGRPC++_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
 ifeq ($(SYSTEM),Darwin)
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc
 else
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc++.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc++.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc
 	$(Q) ln -sf libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++.so.0
 	$(Q) ln -sf libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++.so
 endif
@@ -3191,17 +3235,21 @@ ifneq ($(NO_DEPS),true)
 endif
 endif
 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/secure_credentials.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/secure_server_credentials.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/client/channel.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/client/channel_arguments.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/client/client_context.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/client/client_unary_call.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/client/create_channel.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/client/credentials.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/insecure_credentials.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/client/internal_stub.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/common/call.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/common/completion_queue.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/common/rpc_method.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/proto/proto_utils.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/insecure_server_credentials.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/server/server.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/server/server_builder.o: 
 $(OBJDIR)/$(CONFIG)/src/cpp/server/server_context.o: 
@@ -3276,6 +3324,125 @@ endif
 $(OBJDIR)/$(CONFIG)/test/cpp/util/create_test_channel.o:     $(GENDIR)/test/cpp/util/messages.pb.cc    $(GENDIR)/test/cpp/util/echo.pb.cc    $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc
 
 
+LIBGRPC++_UNSECURE_SRC = \
+    src/cpp/client/channel.cc \
+    src/cpp/client/channel_arguments.cc \
+    src/cpp/client/client_context.cc \
+    src/cpp/client/client_unary_call.cc \
+    src/cpp/client/create_channel.cc \
+    src/cpp/client/credentials.cc \
+    src/cpp/client/insecure_credentials.cc \
+    src/cpp/client/internal_stub.cc \
+    src/cpp/common/call.cc \
+    src/cpp/common/completion_queue.cc \
+    src/cpp/common/rpc_method.cc \
+    src/cpp/proto/proto_utils.cc \
+    src/cpp/server/insecure_server_credentials.cc \
+    src/cpp/server/server.cc \
+    src/cpp/server/server_builder.cc \
+    src/cpp/server/server_context.cc \
+    src/cpp/server/server_credentials.cc \
+    src/cpp/server/thread_pool.cc \
+    src/cpp/util/status.cc \
+    src/cpp/util/time.cc \
+
+PUBLIC_HEADERS_CXX += \
+    include/grpc++/async_unary_call.h \
+    include/grpc++/channel_arguments.h \
+    include/grpc++/channel_interface.h \
+    include/grpc++/client_context.h \
+    include/grpc++/completion_queue.h \
+    include/grpc++/config.h \
+    include/grpc++/create_channel.h \
+    include/grpc++/credentials.h \
+    include/grpc++/impl/call.h \
+    include/grpc++/impl/client_unary_call.h \
+    include/grpc++/impl/internal_stub.h \
+    include/grpc++/impl/rpc_method.h \
+    include/grpc++/impl/rpc_service_method.h \
+    include/grpc++/impl/service_type.h \
+    include/grpc++/server.h \
+    include/grpc++/server_builder.h \
+    include/grpc++/server_context.h \
+    include/grpc++/server_credentials.h \
+    include/grpc++/status.h \
+    include/grpc++/status_code_enum.h \
+    include/grpc++/stream.h \
+    include/grpc++/thread_pool_interface.h \
+
+LIBGRPC++_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_UNSECURE_SRC))))
+
+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)/libgrpc++_unsecure.a: protobuf_dep_error
+
+ifeq ($(SYSTEM),MINGW32)
+$(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT): protobuf_dep_error
+else
+$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT): protobuf_dep_error
+endif
+
+else
+
+$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBGRPC++_UNSECURE_OBJS)
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a
+	$(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBGRPC++_UNSECURE_OBJS)
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a
+endif
+
+
+
+ifeq ($(SYSTEM),MINGW32)
+$(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP)$(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT)$(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT)
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_unsecure.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure-imp.a -o $(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc_unsecure-imp
+else
+$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT)
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+ifeq ($(SYSTEM),Darwin)
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure
+else
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc++_unsecure.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure
+	$(Q) ln -sf libgrpc++_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.so.0
+	$(Q) ln -sf libgrpc++_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.so
+endif
+endif
+
+endif
+
+ifneq ($(NO_DEPS),true)
+-include $(LIBGRPC++_UNSECURE_OBJS:.o=.dep)
+endif
+
+$(OBJDIR)/$(CONFIG)/src/cpp/client/channel.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/channel_arguments.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/client_context.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/client_unary_call.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/create_channel.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/credentials.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/insecure_credentials.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/client/internal_stub.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/common/call.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/common/completion_queue.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/common/rpc_method.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/proto/proto_utils.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/insecure_server_credentials.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/server.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/server_builder.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/server_context.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/server_credentials.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/server/thread_pool.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/util/status.o: 
+$(OBJDIR)/$(CONFIG)/src/cpp/util/time.o: 
+
+
 LIBPUBSUB_CLIENT_LIB_SRC = \
     $(GENDIR)/examples/pubsub/label.pb.cc \
     $(GENDIR)/examples/pubsub/empty.pb.cc \
@@ -3344,6 +3511,68 @@ $(OBJDIR)/$(CONFIG)/examples/pubsub/publisher.o:     $(GENDIR)/examples/pubsub/l
 $(OBJDIR)/$(CONFIG)/examples/pubsub/subscriber.o:     $(GENDIR)/examples/pubsub/label.pb.cc    $(GENDIR)/examples/pubsub/empty.pb.cc    $(GENDIR)/examples/pubsub/pubsub.pb.cc
 
 
+LIBQPS_SRC = \
+    $(GENDIR)/test/cpp/qps/qpstest.pb.cc \
+    test/cpp/qps/driver.cc \
+    test/cpp/qps/timer.cc \
+
+
+LIBQPS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBQPS_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL with ALPN.
+
+$(LIBDIR)/$(CONFIG)/libqps.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)/libqps.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/qps/qpstest.proto: $(OPENSSL_DEP)
+test/cpp/qps/driver.cc: $(OPENSSL_DEP)
+test/cpp/qps/timer.cc: $(OPENSSL_DEP)
+endif
+
+$(LIBDIR)/$(CONFIG)/libqps.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBQPS_OBJS)
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libqps.a
+	$(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libqps.a $(LIBQPS_OBJS)
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib $(LIBDIR)/$(CONFIG)/libqps.a
+endif
+
+
+
+
+endif
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBQPS_OBJS:.o=.dep)
+endif
+endif
+
+
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o:     $(GENDIR)/test/cpp/qps/qpstest.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o:     $(GENDIR)/test/cpp/qps/qpstest.pb.cc
+
+
 LIBGRPC_CSHARP_EXT_SRC = \
     src/csharp/ext/grpc_csharp_ext.c \
 
@@ -3387,15 +3616,15 @@ ifeq ($(SYSTEM),MINGW32)
 $(LIBDIR)/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT): $(LIBGRPC_CSHARP_EXT_OBJS)  $(ZLIB_DEP)$(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT)$(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_csharp_ext.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext-imp.a -o $(LIBDIR)/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) -lgpr-imp -lgrpc-imp
+	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_csharp_ext.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext-imp.a -o $(LIBDIR)/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) -lgpr-imp -lgrpc-imp
 else
 $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT): $(LIBGRPC_CSHARP_EXT_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(OPENSSL_DEP)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
 ifeq ($(SYSTEM),Darwin)
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) -lgpr -lgrpc
+	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) -lgpr -lgrpc
 else
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_csharp_ext.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS) -lgpr -lgrpc
+	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_csharp_ext.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) -lgpr -lgrpc
 	$(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.so.0
 	$(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.so
 endif
@@ -3734,6 +3963,31 @@ endif
 $(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.o: 
 
 
+LIBEND2END_TEST_BAD_HOSTNAME_SRC = \
+    test/core/end2end/tests/bad_hostname.c \
+
+
+LIBEND2END_TEST_BAD_HOSTNAME_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_BAD_HOSTNAME_SRC))))
+
+$(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a: $(ZLIB_DEP) $(LIBEND2END_TEST_BAD_HOSTNAME_OBJS)
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a
+	$(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBEND2END_TEST_BAD_HOSTNAME_OBJS)
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a
+endif
+
+
+
+
+ifneq ($(NO_DEPS),true)
+-include $(LIBEND2END_TEST_BAD_HOSTNAME_OBJS:.o=.dep)
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/end2end/tests/bad_hostname.o: 
+
+
 LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_SRC = \
     test/core/end2end/tests/cancel_after_accept.c \
 
@@ -7925,16 +8179,16 @@ $(BINDIR)/$(CONFIG)/pubsub_client: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/pubsub_client: $(PROTOBUF_DEP) $(PUBSUB_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libpubsub_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
+$(BINDIR)/$(CONFIG)/pubsub_client: $(PROTOBUF_DEP) $(PUBSUB_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libpubsub_client_lib.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) $(PUBSUB_CLIENT_OBJS) $(GTEST_LIB) $(LIBDIR)/$(CONFIG)/libpubsub_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)/pubsub_client
+	$(Q) $(LDXX) $(LDFLAGS) $(PUBSUB_CLIENT_OBJS) $(GTEST_LIB) $(LIBDIR)/$(CONFIG)/libpubsub_client_lib.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)/pubsub_client
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/examples/pubsub/main.o:  $(LIBDIR)/$(CONFIG)/libpubsub_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
+$(OBJDIR)/$(CONFIG)/examples/pubsub/main.o:  $(LIBDIR)/$(CONFIG)/libpubsub_client_lib.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_pubsub_client: $(PUBSUB_CLIENT_OBJS:.o=.dep)
 
@@ -8029,17 +8283,16 @@ endif
 endif
 
 
-QPS_CLIENT_SRC = \
-    $(GENDIR)/test/cpp/qps/qpstest.pb.cc \
-    test/cpp/qps/client.cc \
+QPS_DRIVER_SRC = \
+    test/cpp/qps/qps_driver.cc \
 
-QPS_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_CLIENT_SRC))))
+QPS_DRIVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_DRIVER_SRC))))
 
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL with ALPN.
 
-$(BINDIR)/$(CONFIG)/qps_client: openssl_dep_error
+$(BINDIR)/$(CONFIG)/qps_driver: openssl_dep_error
 
 else
 
@@ -8048,86 +8301,44 @@ ifeq ($(NO_PROTOBUF),true)
 
 # You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
 
-$(BINDIR)/$(CONFIG)/qps_client: protobuf_dep_error
+$(BINDIR)/$(CONFIG)/qps_driver: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/qps_client: $(PROTOBUF_DEP) $(QPS_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)/qps_driver: $(PROTOBUF_DEP) $(QPS_DRIVER_OBJS) $(LIBDIR)/$(CONFIG)/libqps.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) $(QPS_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)/qps_client
+	$(Q) $(LDXX) $(LDFLAGS) $(QPS_DRIVER_OBJS) $(GTEST_LIB) $(LIBDIR)/$(CONFIG)/libqps.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)/qps_driver
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qpstest.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/qps/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/qps/qps_driver.o:  $(LIBDIR)/$(CONFIG)/libqps.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_qps_client: $(QPS_CLIENT_OBJS:.o=.dep)
+deps_qps_driver: $(QPS_DRIVER_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(QPS_CLIENT_OBJS:.o=.dep)
+-include $(QPS_DRIVER_OBJS:.o=.dep)
 endif
 endif
 
 
-QPS_CLIENT_ASYNC_SRC = \
-    $(GENDIR)/test/cpp/qps/qpstest.pb.cc \
+QPS_WORKER_SRC = \
     test/cpp/qps/client_async.cc \
+    test/cpp/qps/client_sync.cc \
+    test/cpp/qps/server_async.cc \
+    test/cpp/qps/server_sync.cc \
+    test/cpp/qps/worker.cc \
 
-QPS_CLIENT_ASYNC_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_CLIENT_ASYNC_SRC))))
-
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL with ALPN.
-
-$(BINDIR)/$(CONFIG)/qps_client_async: openssl_dep_error
-
-else
-
-
-ifeq ($(NO_PROTOBUF),true)
-
-# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
-
-$(BINDIR)/$(CONFIG)/qps_client_async: protobuf_dep_error
-
-else
-
-$(BINDIR)/$(CONFIG)/qps_client_async: $(PROTOBUF_DEP) $(QPS_CLIENT_ASYNC_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(QPS_CLIENT_ASYNC_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)/qps_client_async
-
-endif
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qpstest.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/qps/client_async.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_qps_client_async: $(QPS_CLIENT_ASYNC_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(QPS_CLIENT_ASYNC_OBJS:.o=.dep)
-endif
-endif
-
-
-QPS_SERVER_SRC = \
-    $(GENDIR)/test/cpp/qps/qpstest.pb.cc \
-    test/cpp/qps/server.cc \
-
-QPS_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_SERVER_SRC))))
+QPS_WORKER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_WORKER_SRC))))
 
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL with ALPN.
 
-$(BINDIR)/$(CONFIG)/qps_server: openssl_dep_error
+$(BINDIR)/$(CONFIG)/qps_worker: openssl_dep_error
 
 else
 
@@ -8136,42 +8347,44 @@ ifeq ($(NO_PROTOBUF),true)
 
 # You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
 
-$(BINDIR)/$(CONFIG)/qps_server: protobuf_dep_error
+$(BINDIR)/$(CONFIG)/qps_worker: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/qps_server: $(PROTOBUF_DEP) $(QPS_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)/qps_worker: $(PROTOBUF_DEP) $(QPS_WORKER_OBJS) $(LIBDIR)/$(CONFIG)/libqps.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) $(QPS_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)/qps_server
+	$(Q) $(LDXX) $(LDFLAGS) $(QPS_WORKER_OBJS) $(GTEST_LIB) $(LIBDIR)/$(CONFIG)/libqps.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)/qps_worker
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qpstest.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/qps/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/qps/client_async.o:  $(LIBDIR)/$(CONFIG)/libqps.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/qps/client_sync.o:  $(LIBDIR)/$(CONFIG)/libqps.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/qps/server_async.o:  $(LIBDIR)/$(CONFIG)/libqps.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/qps/server_sync.o:  $(LIBDIR)/$(CONFIG)/libqps.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/qps/worker.o:  $(LIBDIR)/$(CONFIG)/libqps.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_qps_server: $(QPS_SERVER_OBJS:.o=.dep)
+deps_qps_worker: $(QPS_WORKER_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(QPS_SERVER_OBJS:.o=.dep)
+-include $(QPS_WORKER_OBJS:.o=.dep)
 endif
 endif
 
 
-QPS_SERVER_ASYNC_SRC = \
-    $(GENDIR)/test/cpp/qps/qpstest.pb.cc \
-    test/cpp/qps/server_async.cc \
+STATUS_TEST_SRC = \
+    test/cpp/util/status_test.cc \
 
-QPS_SERVER_ASYNC_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_SERVER_ASYNC_SRC))))
+STATUS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STATUS_TEST_SRC))))
 
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL with ALPN.
 
-$(BINDIR)/$(CONFIG)/qps_server_async: openssl_dep_error
+$(BINDIR)/$(CONFIG)/status_test: openssl_dep_error
 
 else
 
@@ -8180,41 +8393,40 @@ ifeq ($(NO_PROTOBUF),true)
 
 # You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
 
-$(BINDIR)/$(CONFIG)/qps_server_async: protobuf_dep_error
+$(BINDIR)/$(CONFIG)/status_test: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/qps_server_async: $(PROTOBUF_DEP) $(QPS_SERVER_ASYNC_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)/status_test: $(PROTOBUF_DEP) $(STATUS_TEST_OBJS) $(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) $(QPS_SERVER_ASYNC_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)/qps_server_async
+	$(Q) $(LDXX) $(LDFLAGS) $(STATUS_TEST_OBJS) $(GTEST_LIB) $(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)/status_test
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qpstest.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/qps/server_async.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/util/status_test.o:  $(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_qps_server_async: $(QPS_SERVER_ASYNC_OBJS:.o=.dep)
+deps_status_test: $(STATUS_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(QPS_SERVER_ASYNC_OBJS:.o=.dep)
+-include $(STATUS_TEST_OBJS:.o=.dep)
 endif
 endif
 
 
-STATUS_TEST_SRC = \
-    test/cpp/util/status_test.cc \
+THREAD_POOL_TEST_SRC = \
+    test/cpp/server/thread_pool_test.cc \
 
-STATUS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STATUS_TEST_SRC))))
+THREAD_POOL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(THREAD_POOL_TEST_SRC))))
 
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL with ALPN.
 
-$(BINDIR)/$(CONFIG)/status_test: openssl_dep_error
+$(BINDIR)/$(CONFIG)/thread_pool_test: openssl_dep_error
 
 else
 
@@ -8223,68 +8435,55 @@ ifeq ($(NO_PROTOBUF),true)
 
 # You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
 
-$(BINDIR)/$(CONFIG)/status_test: protobuf_dep_error
+$(BINDIR)/$(CONFIG)/thread_pool_test: protobuf_dep_error
 
 else
 
-$(BINDIR)/$(CONFIG)/status_test: $(PROTOBUF_DEP) $(STATUS_TEST_OBJS) $(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)/thread_pool_test: $(PROTOBUF_DEP) $(THREAD_POOL_TEST_OBJS) $(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) $(STATUS_TEST_OBJS) $(GTEST_LIB) $(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)/status_test
+	$(Q) $(LDXX) $(LDFLAGS) $(THREAD_POOL_TEST_OBJS) $(GTEST_LIB) $(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)/thread_pool_test
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/util/status_test.o:  $(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/server/thread_pool_test.o:  $(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_status_test: $(STATUS_TEST_OBJS:.o=.dep)
+deps_thread_pool_test: $(THREAD_POOL_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(STATUS_TEST_OBJS:.o=.dep)
+-include $(THREAD_POOL_TEST_OBJS:.o=.dep)
 endif
 endif
 
 
-THREAD_POOL_TEST_SRC = \
-    test/cpp/server/thread_pool_test.cc \
+CHTTP2_FAKE_SECURITY_BAD_HOSTNAME_TEST_SRC = \
 
-THREAD_POOL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(THREAD_POOL_TEST_SRC))))
+CHTTP2_FAKE_SECURITY_BAD_HOSTNAME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_FAKE_SECURITY_BAD_HOSTNAME_TEST_SRC))))
 
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL with ALPN.
 
-$(BINDIR)/$(CONFIG)/thread_pool_test: openssl_dep_error
+$(BINDIR)/$(CONFIG)/chttp2_fake_security_bad_hostname_test: openssl_dep_error
 
 else
 
-
-ifeq ($(NO_PROTOBUF),true)
-
-# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
-
-$(BINDIR)/$(CONFIG)/thread_pool_test: protobuf_dep_error
-
-else
-
-$(BINDIR)/$(CONFIG)/thread_pool_test: $(PROTOBUF_DEP) $(THREAD_POOL_TEST_OBJS) $(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)/chttp2_fake_security_bad_hostname_test: $(CHTTP2_FAKE_SECURITY_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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) $(LDXX) $(LDFLAGS) $(THREAD_POOL_TEST_OBJS) $(GTEST_LIB) $(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)/thread_pool_test
-
-endif
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_FAKE_SECURITY_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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)/chttp2_fake_security_bad_hostname_test
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/server/thread_pool_test.o:  $(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_thread_pool_test: $(THREAD_POOL_TEST_OBJS:.o=.dep)
+deps_chttp2_fake_security_bad_hostname_test: $(CHTTP2_FAKE_SECURITY_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(THREAD_POOL_TEST_OBJS:.o=.dep)
+-include $(CHTTP2_FAKE_SECURITY_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
 endif
 endif
 
@@ -9681,6 +9880,35 @@ endif
 endif
 
 
+CHTTP2_FULLSTACK_BAD_HOSTNAME_TEST_SRC = \
+
+CHTTP2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_FULLSTACK_BAD_HOSTNAME_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+$(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_test: openssl_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/chttp2_fullstack_bad_hostname_test: $(CHTTP2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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) $(CHTTP2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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)/chttp2_fullstack_bad_hostname_test
+
+endif
+
+
+deps_chttp2_fullstack_bad_hostname_test: $(CHTTP2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHTTP2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHTTP2_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_SRC = \
 
 CHTTP2_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_SRC))))
@@ -11073,6 +11301,35 @@ endif
 endif
 
 
+CHTTP2_FULLSTACK_UDS_BAD_HOSTNAME_TEST_SRC = \
+
+CHTTP2_FULLSTACK_UDS_BAD_HOSTNAME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_FULLSTACK_UDS_BAD_HOSTNAME_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+$(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_bad_hostname_test: openssl_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/chttp2_fullstack_uds_bad_hostname_test: $(CHTTP2_FULLSTACK_UDS_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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) $(CHTTP2_FULLSTACK_UDS_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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)/chttp2_fullstack_uds_bad_hostname_test
+
+endif
+
+
+deps_chttp2_fullstack_uds_bad_hostname_test: $(CHTTP2_FULLSTACK_UDS_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHTTP2_FULLSTACK_UDS_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHTTP2_FULLSTACK_UDS_CANCEL_AFTER_ACCEPT_TEST_SRC = \
 
 CHTTP2_FULLSTACK_UDS_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_FULLSTACK_UDS_CANCEL_AFTER_ACCEPT_TEST_SRC))))
@@ -12465,6 +12722,35 @@ endif
 endif
 
 
+CHTTP2_SIMPLE_SSL_FULLSTACK_BAD_HOSTNAME_TEST_SRC = \
+
+CHTTP2_SIMPLE_SSL_FULLSTACK_BAD_HOSTNAME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SIMPLE_SSL_FULLSTACK_BAD_HOSTNAME_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+$(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_bad_hostname_test: openssl_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/chttp2_simple_ssl_fullstack_bad_hostname_test: $(CHTTP2_SIMPLE_SSL_FULLSTACK_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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) $(CHTTP2_SIMPLE_SSL_FULLSTACK_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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)/chttp2_simple_ssl_fullstack_bad_hostname_test
+
+endif
+
+
+deps_chttp2_simple_ssl_fullstack_bad_hostname_test: $(CHTTP2_SIMPLE_SSL_FULLSTACK_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHTTP2_SIMPLE_SSL_FULLSTACK_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHTTP2_SIMPLE_SSL_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_SRC = \
 
 CHTTP2_SIMPLE_SSL_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SIMPLE_SSL_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_SRC))))
@@ -13857,6 +14143,35 @@ endif
 endif
 
 
+CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_BAD_HOSTNAME_TEST_SRC = \
+
+CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_BAD_HOSTNAME_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+$(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test: openssl_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test: $(CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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) $(CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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)/chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test
+
+endif
+
+
+deps_chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test: $(CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_SRC = \
 
 CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_CANCEL_AFTER_ACCEPT_TEST_SRC))))
@@ -15249,6 +15564,35 @@ endif
 endif
 
 
+CHTTP2_SOCKET_PAIR_BAD_HOSTNAME_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_BAD_HOSTNAME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_BAD_HOSTNAME_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+$(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_test: openssl_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/chttp2_socket_pair_bad_hostname_test: $(CHTTP2_SOCKET_PAIR_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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) $(CHTTP2_SOCKET_PAIR_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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)/chttp2_socket_pair_bad_hostname_test
+
+endif
+
+
+deps_chttp2_socket_pair_bad_hostname_test: $(CHTTP2_SOCKET_PAIR_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHTTP2_SOCKET_PAIR_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHTTP2_SOCKET_PAIR_CANCEL_AFTER_ACCEPT_TEST_SRC = \
 
 CHTTP2_SOCKET_PAIR_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_CANCEL_AFTER_ACCEPT_TEST_SRC))))
@@ -16641,6 +16985,35 @@ endif
 endif
 
 
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_BAD_HOSTNAME_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_BAD_HOSTNAME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_BAD_HOSTNAME_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+$(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test: openssl_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_BAD_HOSTNAME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(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)/chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test
+
+endif
+
+
+deps_chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_BAD_HOSTNAME_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_SRC = \
 
 CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_SRC))))
diff --git a/README.md b/README.md
index 8fa9fa4956e36f809fdc8741ef87e208d23c2093..fa60b83d1620c70a49c340ee9517b8b2b2f76caa 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[![Build Status](https://travis-ci.org/grpc/grpc.svg?branch=master)](https://travis-ci.org/grpc/grpc)
+
 [gRPC - An RPC library and framework](http://github.com/grpc/grpc)
 ===================================
 
diff --git a/build.json b/build.json
index cfba96180f095f0663de96a32b9ff6da22d0ad90..c9500ebe1bd781a9b3249d3a284e27a34172e491 100644
--- a/build.json
+++ b/build.json
@@ -9,6 +9,61 @@
     }
   },
   "filegroups": [
+    {
+      "name": "grpc++_base",
+      "public_headers": [
+        "include/grpc++/async_unary_call.h",
+        "include/grpc++/channel_arguments.h",
+        "include/grpc++/channel_interface.h",
+        "include/grpc++/client_context.h",
+        "include/grpc++/completion_queue.h",
+        "include/grpc++/config.h",
+        "include/grpc++/create_channel.h",
+        "include/grpc++/credentials.h",
+        "include/grpc++/impl/call.h",
+        "include/grpc++/impl/client_unary_call.h",
+        "include/grpc++/impl/internal_stub.h",
+        "include/grpc++/impl/rpc_method.h",
+        "include/grpc++/impl/rpc_service_method.h",
+        "include/grpc++/impl/service_type.h",
+        "include/grpc++/server.h",
+        "include/grpc++/server_builder.h",
+        "include/grpc++/server_context.h",
+        "include/grpc++/server_credentials.h",
+        "include/grpc++/status.h",
+        "include/grpc++/status_code_enum.h",
+        "include/grpc++/stream.h",
+        "include/grpc++/thread_pool_interface.h"
+      ],
+      "headers": [
+        "src/cpp/client/channel.h",
+        "src/cpp/proto/proto_utils.h",
+        "src/cpp/server/thread_pool.h",
+        "src/cpp/util/time.h"
+      ],
+      "src": [
+        "src/cpp/client/channel.cc",
+        "src/cpp/client/channel_arguments.cc",
+        "src/cpp/client/client_context.cc",
+        "src/cpp/client/client_unary_call.cc",
+        "src/cpp/client/create_channel.cc",
+        "src/cpp/client/credentials.cc",
+        "src/cpp/client/insecure_credentials.cc",
+        "src/cpp/client/internal_stub.cc",
+        "src/cpp/common/call.cc",
+        "src/cpp/common/completion_queue.cc",
+        "src/cpp/common/rpc_method.cc",
+        "src/cpp/proto/proto_utils.cc",
+        "src/cpp/server/insecure_server_credentials.cc",
+        "src/cpp/server/server.cc",
+        "src/cpp/server/server_builder.cc",
+        "src/cpp/server/server_context.cc",
+        "src/cpp/server/server_credentials.cc",
+        "src/cpp/server/thread_pool.cc",
+        "src/cpp/util/status.cc",
+        "src/cpp/util/time.cc"
+      ]
+    },
     {
       "name": "grpc_base",
       "public_headers": [
@@ -80,7 +135,6 @@
         "src/core/surface/completion_queue.h",
         "src/core/surface/event_string.h",
         "src/core/surface/init.h",
-        "src/core/surface/lame_client.h",
         "src/core/surface/server.h",
         "src/core/surface/surface_trace.h",
         "src/core/transport/chttp2/bin_encoder.h",
@@ -276,7 +330,7 @@
         "src/core/support/time_posix.c",
         "src/core/support/time_win32.c"
       ],
-      "secure": false,
+      "secure": "no",
       "vs_project_guid": "{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}"
     },
     {
@@ -333,7 +387,6 @@
         "src/core/security/server_secure_chttp2.c",
         "src/core/surface/init_secure.c",
         "src/core/surface/secure_channel_create.c",
-        "src/core/surface/secure_server_create.c",
         "src/core/tsi/fake_transport_security.c",
         "src/core/tsi/ssl_transport_security.c",
         "src/core/tsi/transport_security.c"
@@ -345,7 +398,7 @@
       "filegroups": [
         "grpc_base"
       ],
-      "secure": true,
+      "secure": "yes",
       "vs_project_guid": "{29D16885-7228-4C31-81ED-5F9187C7F2A9}"
     },
     {
@@ -385,68 +438,26 @@
       "filegroups": [
         "grpc_base"
       ],
-      "secure": false,
+      "secure": "no",
       "vs_project_guid": "{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}"
     },
     {
       "name": "grpc++",
       "build": "all",
       "language": "c++",
-      "public_headers": [
-        "include/grpc++/async_unary_call.h",
-        "include/grpc++/channel_arguments.h",
-        "include/grpc++/channel_interface.h",
-        "include/grpc++/client_context.h",
-        "include/grpc++/completion_queue.h",
-        "include/grpc++/config.h",
-        "include/grpc++/create_channel.h",
-        "include/grpc++/credentials.h",
-        "include/grpc++/impl/call.h",
-        "include/grpc++/impl/client_unary_call.h",
-        "include/grpc++/impl/internal_stub.h",
-        "include/grpc++/impl/rpc_method.h",
-        "include/grpc++/impl/rpc_service_method.h",
-        "include/grpc++/impl/service_type.h",
-        "include/grpc++/server.h",
-        "include/grpc++/server_builder.h",
-        "include/grpc++/server_context.h",
-        "include/grpc++/server_credentials.h",
-        "include/grpc++/status.h",
-        "include/grpc++/status_code_enum.h",
-        "include/grpc++/stream.h",
-        "include/grpc++/thread_pool_interface.h"
-      ],
-      "headers": [
-        "src/cpp/client/channel.h",
-        "src/cpp/proto/proto_utils.h",
-        "src/cpp/server/thread_pool.h",
-        "src/cpp/util/time.h"
-      ],
       "src": [
-        "src/cpp/client/channel.cc",
-        "src/cpp/client/channel_arguments.cc",
-        "src/cpp/client/client_context.cc",
-        "src/cpp/client/client_unary_call.cc",
-        "src/cpp/client/create_channel.cc",
-        "src/cpp/client/credentials.cc",
-        "src/cpp/client/internal_stub.cc",
-        "src/cpp/common/call.cc",
-        "src/cpp/common/completion_queue.cc",
-        "src/cpp/common/rpc_method.cc",
-        "src/cpp/proto/proto_utils.cc",
-        "src/cpp/server/server.cc",
-        "src/cpp/server/server_builder.cc",
-        "src/cpp/server/server_context.cc",
-        "src/cpp/server/server_credentials.cc",
-        "src/cpp/server/thread_pool.cc",
-        "src/cpp/util/status.cc",
-        "src/cpp/util/time.cc"
+        "src/cpp/client/secure_credentials.cc",
+        "src/cpp/server/secure_server_credentials.cc"
       ],
       "deps": [
         "gpr",
         "grpc"
       ],
-      "secure": true,
+      "baselib": true,
+      "filegroups": [
+        "grpc++_base"
+      ],
+      "secure": "check",
       "vs_project_guid": "{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}"
     },
     {
@@ -460,6 +471,20 @@
         "test/cpp/util/create_test_channel.cc"
       ]
     },
+    {
+      "name": "grpc++_unsecure",
+      "build": "all",
+      "language": "c++",
+      "deps": [
+        "gpr",
+        "grpc_unsecure"
+      ],
+      "baselib": true,
+      "filegroups": [
+        "grpc++_base"
+      ],
+      "secure": "no"
+    },
     {
       "name": "pubsub_client_lib",
       "build": "private",
@@ -477,6 +502,20 @@
         "gpr"
       ]
     },
+    {
+      "name": "qps",
+      "build": "private",
+      "language": "c++",
+      "headers": [
+        "test/cpp/qps/driver.h",
+        "test/cpp/qps/timer.h"
+      ],
+      "src": [
+        "test/cpp/qps/qpstest.proto",
+        "test/cpp/qps/driver.cc",
+        "test/cpp/qps/timer.cc"
+      ]
+    },
     {
       "name": "grpc_csharp_ext",
       "build": "all",
@@ -1672,7 +1711,7 @@
         "src/compiler/cpp_plugin.cc"
       ],
       "deps": [],
-      "secure": false
+      "secure": "no"
     },
     {
       "name": "grpc_python_plugin",
@@ -1686,7 +1725,7 @@
         "src/compiler/python_plugin.cc"
       ],
       "deps": [],
-      "secure": false
+      "secure": "no"
     },
     {
       "name": "grpc_ruby_plugin",
@@ -1697,7 +1736,7 @@
         "src/compiler/ruby_plugin.cc"
       ],
       "deps": [],
-      "secure": false
+      "secure": "no"
     },
     {
       "name": "interop_client",
@@ -1763,7 +1802,6 @@
       ],
       "deps": [
         "pubsub_client_lib",
-        "grpc++_test_util",
         "grpc_test_util",
         "grpc++",
         "grpc",
@@ -1806,15 +1844,15 @@
       ]
     },
     {
-      "name": "qps_client",
+      "name": "qps_driver",
       "build": "test",
       "run": false,
       "language": "c++",
       "src": [
-        "test/cpp/qps/qpstest.proto",
-        "test/cpp/qps/client.cc"
+        "test/cpp/qps/qps_driver.cc"
       ],
       "deps": [
+        "qps",
         "grpc++_test_util",
         "grpc_test_util",
         "grpc++",
@@ -1824,51 +1862,23 @@
       ]
     },
     {
-      "name": "qps_client_async",
+      "name": "qps_worker",
       "build": "test",
       "run": false,
       "language": "c++",
-      "src": [
-        "test/cpp/qps/qpstest.proto",
-        "test/cpp/qps/client_async.cc"
-      ],
-      "deps": [
-        "grpc++_test_util",
-        "grpc_test_util",
-        "grpc++",
-        "grpc",
-        "gpr_test_util",
-        "gpr"
-      ]
-    },
-    {
-      "name": "qps_server",
-      "build": "test",
-      "run": false,
-      "language": "c++",
-      "src": [
-        "test/cpp/qps/qpstest.proto",
-        "test/cpp/qps/server.cc"
+      "headers": [
+        "test/cpp/qps/client.h",
+        "test/cpp/qps/server.h"
       ],
-      "deps": [
-        "grpc++_test_util",
-        "grpc_test_util",
-        "grpc++",
-        "grpc",
-        "gpr_test_util",
-        "gpr"
-      ]
-    },
-    {
-      "name": "qps_server_async",
-      "build": "test",
-      "run": false,
-      "language": "c++",
       "src": [
-        "test/cpp/qps/qpstest.proto",
-        "test/cpp/qps/server_async.cc"
+        "test/cpp/qps/client_async.cc",
+        "test/cpp/qps/client_sync.cc",
+        "test/cpp/qps/server_async.cc",
+        "test/cpp/qps/server_sync.cc",
+        "test/cpp/qps/worker.cc"
       ],
       "deps": [
+        "qps",
         "grpc++_test_util",
         "grpc_test_util",
         "grpc++",
diff --git a/examples/pubsub/main.cc b/examples/pubsub/main.cc
index 39fb8aea15c8cf5355bf841c5cdba410aede88ff..6f7737e2476ffa04088321e39210bcf7a486182e 100644
--- a/examples/pubsub/main.cc
+++ b/examples/pubsub/main.cc
@@ -41,6 +41,7 @@
 #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>
@@ -48,17 +49,11 @@
 
 #include "examples/pubsub/publisher.h"
 #include "examples/pubsub/subscriber.h"
-#include "test/cpp/util/create_test_channel.h"
 
 DEFINE_int32(server_port, 443, "Server port.");
 DEFINE_string(server_host,
               "pubsub-staging.googleapis.com", "Server host to connect to");
 DEFINE_string(project_id, "", "GCE project id such as stoked-keyword-656");
-DEFINE_string(service_account_key_file, "",
-              "Path to service account json key file.");
-DEFINE_string(oauth_scope,
-              "https://www.googleapis.com/auth/cloud-platform",
-              "Scope for OAuth tokens.");
 
 // In some distros, gflags is in the namespace google, and in some others,
 // in gflags. This hack is enabling us to find both.
@@ -75,17 +70,6 @@ const char kMessageData[] = "Test Data";
 
 }  // namespace
 
-grpc::string GetServiceAccountJsonKey() {
-  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;
-}
-
 int main(int argc, char** argv) {
   grpc_init();
   ParseCommandLineFlags(&argc, &argv, true);
@@ -93,23 +77,11 @@ int main(int argc, char** argv) {
 
   std::ostringstream ss;
 
-  std::unique_ptr<grpc::Credentials> creds;
-  if (FLAGS_service_account_key_file != "") {
-    grpc::string json_key = GetServiceAccountJsonKey();
-    creds = grpc::CredentialsFactory::ServiceAccountCredentials(
-        json_key, FLAGS_oauth_scope, std::chrono::hours(1));
-  } else {
-    creds = grpc::CredentialsFactory::ComputeEngineCredentials();
-  }
-
   ss << FLAGS_server_host << ":" << FLAGS_server_port;
-  std::shared_ptr<grpc::ChannelInterface> channel(
-      grpc::CreateTestChannel(
-          ss.str(),
-          FLAGS_server_host,
-          true,                // enable SSL
-          true,                // use prod roots
-          creds));
+
+  std::unique_ptr<grpc::Credentials> creds = grpc::GoogleDefaultCredentials();
+  std::shared_ptr<grpc::ChannelInterface> channel =
+      grpc::CreateChannel(ss.str(), creds, grpc::ChannelArguments());
 
   grpc::examples::pubsub::Publisher publisher(channel);
   grpc::examples::pubsub::Subscriber subscriber(channel);
@@ -129,6 +101,7 @@ int main(int argc, char** argv) {
       subscription_name, &subscription_topic).IsOk()) {
     subscriber.DeleteSubscription(subscription_name);
   }
+
   if (publisher.GetTopic(topic).IsOk()) publisher.DeleteTopic(topic);
 
   grpc::Status s = publisher.CreateTopic(topic);
diff --git a/examples/pubsub/publisher_test.cc b/examples/pubsub/publisher_test.cc
index c988b4802ebacc3fc658a2c348607f7fafb33640..f9b6bb3418d21fcfcc7db4a5af343bb25ff540dd 100644
--- a/examples/pubsub/publisher_test.cc
+++ b/examples/pubsub/publisher_test.cc
@@ -40,6 +40,7 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc++/server_credentials.h>
 #include <grpc++/status.h>
 #include <gtest/gtest.h>
 
@@ -106,12 +107,11 @@ class PublisherTest : public ::testing::Test {
     int port = grpc_pick_unused_port_or_die();
     server_address_ << "localhost:" << port;
     ServerBuilder builder;
-    builder.AddPort(server_address_.str());
+    builder.AddPort(server_address_.str(), grpc::InsecureServerCredentials());
     builder.RegisterService(&service_);
     server_ = builder.BuildAndStart();
 
-    channel_ =
-        CreateChannelDeprecated(server_address_.str(), ChannelArguments());
+    channel_ = CreateChannel(server_address_.str(), grpc::InsecureCredentials(), ChannelArguments());
 
     publisher_.reset(new grpc::examples::pubsub::Publisher(channel_));
   }
diff --git a/examples/pubsub/subscriber_test.cc b/examples/pubsub/subscriber_test.cc
index 4ff125f4b3ae9d0dd068f39d8e72a7c898455602..2d606336adde9177fd040256bff9909c2dddb36a 100644
--- a/examples/pubsub/subscriber_test.cc
+++ b/examples/pubsub/subscriber_test.cc
@@ -40,6 +40,7 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc++/server_credentials.h>
 #include <grpc++/status.h>
 #include <gtest/gtest.h>
 
@@ -104,12 +105,11 @@ class SubscriberTest : public ::testing::Test {
     int port = grpc_pick_unused_port_or_die();
     server_address_ << "localhost:" << port;
     ServerBuilder builder;
-    builder.AddPort(server_address_.str());
+    builder.AddPort(server_address_.str(), grpc::InsecureServerCredentials());
     builder.RegisterService(&service_);
     server_ = builder.BuildAndStart();
 
-    channel_ =
-        CreateChannelDeprecated(server_address_.str(), ChannelArguments());
+    channel_ = CreateChannel(server_address_.str(), grpc::InsecureCredentials(), ChannelArguments());
 
     subscriber_.reset(new grpc::examples::pubsub::Subscriber(channel_));
   }
diff --git a/include/grpc++/async_unary_call.h b/include/grpc++/async_unary_call.h
index 71b7d3ff85831da36a1222cf89d118f2def628e5..658941bb6d47ed78b5e4d8f8508367bb19490aea 100644
--- a/include/grpc++/async_unary_call.h
+++ b/include/grpc++/async_unary_call.h
@@ -49,7 +49,7 @@ class ClientAsyncResponseReader GRPC_FINAL {
  public:
   ClientAsyncResponseReader(ChannelInterface* channel, CompletionQueue* cq,
                     const RpcMethod& method, ClientContext* context,
-                    const google::protobuf::Message& request, void* tag)
+                    const grpc::protobuf::Message& request, void* tag)
       : context_(context),
         call_(channel->CreateCall(method, context, cq)) {
     init_buf_.Reset(tag);
@@ -77,7 +77,6 @@ class ClientAsyncResponseReader GRPC_FINAL {
     call_.PerformOps(&finish_buf_);
   }
 
-
  private:
   ClientContext* context_;
   Call call_;
@@ -93,7 +92,7 @@ class ServerAsyncResponseWriter GRPC_FINAL
   explicit ServerAsyncResponseWriter(ServerContext* ctx)
       : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
 
-  void SendInitialMetadata(void* tag) {
+  void SendInitialMetadata(void* tag) GRPC_OVERRIDE {
     GPR_ASSERT(!ctx_->sent_initial_metadata_);
 
     meta_buf_.Reset(tag);
diff --git a/include/grpc++/channel_arguments.h b/include/grpc++/channel_arguments.h
index ad96ef14ae3ed6832be58adbdc7c7782418726ec..8d338c654ec5ae171e914ce36ddba04db561031d 100644
--- a/include/grpc++/channel_arguments.h
+++ b/include/grpc++/channel_arguments.h
@@ -62,8 +62,11 @@ class ChannelArguments {
   void SetInt(const grpc::string& key, int value);
   void SetString(const grpc::string& key, const grpc::string& value);
 
+  // Populates given channel_args with args_, does not take ownership.
+  void SetChannelArgs(grpc_channel_args* channel_args) const;
+
  private:
-  friend class Channel;
+  friend class SecureCredentials;
   friend class testing::ChannelArgumentsTest;
 
   // TODO(yangg) implement copy and assign
@@ -73,9 +76,6 @@ class ChannelArguments {
   // Returns empty string when it is not set.
   grpc::string GetSslTargetNameOverride() const;
 
-  // Populates given channel_args with args_, does not take ownership.
-  void SetChannelArgs(grpc_channel_args* channel_args) const;
-
   std::vector<grpc_arg> args_;
   std::list<grpc::string> strings_;
 };
diff --git a/include/grpc++/channel_interface.h b/include/grpc++/channel_interface.h
index 77d1363618424736fd2edf00add1d9c8e4a94159..51260aed3d708f1e1cb2d9bac07f142ada4df13d 100644
--- a/include/grpc++/channel_interface.h
+++ b/include/grpc++/channel_interface.h
@@ -37,12 +37,6 @@
 #include <grpc++/status.h>
 #include <grpc++/impl/call.h>
 
-namespace google {
-namespace protobuf {
-class Message;
-}  // namespace protobuf
-}  // namespace google
-
 struct grpc_call;
 
 namespace grpc {
diff --git a/include/grpc++/client_context.h b/include/grpc++/client_context.h
index 87e5e9ad6c47c2a1bef39b4b46aa4de7a535a6d5..c55d7c2d5860fb2c602066549cff1354e7c4a8f4 100644
--- a/include/grpc++/client_context.h
+++ b/include/grpc++/client_context.h
@@ -47,12 +47,6 @@ using std::chrono::system_clock;
 struct grpc_call;
 struct grpc_completion_queue;
 
-namespace google {
-namespace protobuf {
-class Message;
-}  // namespace protobuf
-}  // namespace google
-
 namespace grpc {
 
 class CallOpBuffer;
diff --git a/include/grpc++/completion_queue.h b/include/grpc++/completion_queue.h
index 0ca12604038932792d746a17e61d66eecff21bd5..f741e3c36bbce44e3aa29c29f25e1962b694096d 100644
--- a/include/grpc++/completion_queue.h
+++ b/include/grpc++/completion_queue.h
@@ -106,8 +106,8 @@ class CompletionQueue {
   friend Status BlockingUnaryCall(ChannelInterface *channel,
                                   const RpcMethod &method,
                                   ClientContext *context,
-                                  const google::protobuf::Message &request,
-                                  google::protobuf::Message *result);
+                                  const grpc::protobuf::Message &request,
+                                  grpc::protobuf::Message *result);
 
   // Wraps grpc_completion_queue_pluck.
   // Cannot be mixed with calls to Next().
diff --git a/include/grpc++/config.h b/include/grpc++/config.h
index cfa8d3be9f11317c009aade97e2a6834f17b1554..327ed7a549a4dbf1c1d988a62fe015c5a4f21e1c 100644
--- a/include/grpc++/config.h
+++ b/include/grpc++/config.h
@@ -34,8 +34,6 @@
 #ifndef GRPCXX_CONFIG_H
 #define GRPCXX_CONFIG_H
 
-#include <string>
-
 #ifdef GRPC_OLD_CXX
 #define GRPC_FINAL
 #define GRPC_OVERRIDE
@@ -44,9 +42,23 @@
 #define GRPC_OVERRIDE override
 #endif
 
+#ifndef GRPC_CUSTOM_STRING
+#include <string>
+#define GRPC_CUSTOM_STRING std::string
+#endif
+
+#ifndef GRPC_CUSTOM_MESSAGE
+#include <google/protobuf/message.h>
+#define GRPC_CUSTOM_MESSAGE ::google::protobuf::Message
+#endif
+
 namespace grpc {
 
-typedef std::string string;
+typedef GRPC_CUSTOM_STRING string;
+
+namespace protobuf {
+typedef GRPC_CUSTOM_MESSAGE Message;
+}  // namespace protobuf
 
 }  // namespace grpc
 
diff --git a/include/grpc++/create_channel.h b/include/grpc++/create_channel.h
index 3f13188365cd126ee36868331263c4751e93a124..da375b97db457f1edf225e7934da734efb96b1ff 100644
--- a/include/grpc++/create_channel.h
+++ b/include/grpc++/create_channel.h
@@ -43,11 +43,6 @@ namespace grpc {
 class ChannelArguments;
 class ChannelInterface;
 
-// Deprecation warning: This function will soon be deleted
-// (See pull request #711)
-std::shared_ptr<ChannelInterface> CreateChannelDeprecated(
-    const grpc::string& target, const ChannelArguments& args);
-
 // If creds does not hold an object or is invalid, a lame channel is returned.
 std::shared_ptr<ChannelInterface> CreateChannel(
     const grpc::string& target, const std::unique_ptr<Credentials>& creds,
diff --git a/include/grpc++/credentials.h b/include/grpc++/credentials.h
index 12c1a2fc98a6eaa1d0bc3ca62e5b0eb7142d19e5..c677cc3e0a3edd8bc01f3d0805ece638e5213760 100644
--- a/include/grpc++/credentials.h
+++ b/include/grpc++/credentials.h
@@ -39,29 +39,29 @@
 
 #include <grpc++/config.h>
 
-struct grpc_credentials;
-
 namespace grpc {
+class ChannelArguments;
+class ChannelInterface;
+class SecureCredentials;
 
-// grpc_credentials wrapper class. Typical use in C++ applications is limited
-// to creating an instance using CredentialsFactory, and passing it down
-// during channel construction.
-
-class Credentials GRPC_FINAL {
+class Credentials {
  public:
-  ~Credentials();
+  virtual ~Credentials();
 
-  // TODO(abhikumar): Specify a plugin API here to be implemented by
-  // credentials that do not have a corresponding implementation in C.
+ protected:
+  friend std::unique_ptr<Credentials> CompositeCredentials(
+    const std::unique_ptr<Credentials>& creds1,
+    const std::unique_ptr<Credentials>& creds2);
 
- private:
-  explicit Credentials(grpc_credentials*);
-  grpc_credentials* GetRawCreds();
+  virtual SecureCredentials* AsSecureCredentials() = 0;
 
-  friend class Channel;
-  friend class CredentialsFactory;
+ private:
+  friend std::shared_ptr<ChannelInterface> CreateChannel(
+      const grpc::string& target, const std::unique_ptr<Credentials>& creds,
+      const ChannelArguments& args);
 
-  grpc_credentials* creds_;
+  virtual std::shared_ptr<ChannelInterface> CreateChannel(
+      const grpc::string& target, const ChannelArguments& args) = 0;
 };
 
 // Options used to build SslCredentials
@@ -79,57 +79,44 @@ struct SslCredentialsOptions {
   grpc::string pem_cert_chain;
 };
 
-// Factory for building different types of Credentials
-// The methods may return empty unique_ptr when credentials cannot be created.
+// Factories for building different types of Credentials
+// The functions may return empty unique_ptr when credentials cannot be created.
 // If a Credentials pointer is returned, it can still be invalid when used to
 // create a channel. A lame channel will be created then and all rpcs will
 // fail on it.
-class CredentialsFactory {
- public:
-  // Builds google credentials with reasonable defaults.
-  // WARNING: Do NOT use this credentials to connect to a non-google service as
-  // this could result in an oauth2 token leak.
-  static std::unique_ptr<Credentials> GoogleDefaultCredentials();
-
-  // Builds SSL Credentials given SSL specific options
-  static std::unique_ptr<Credentials> SslCredentials(
-      const SslCredentialsOptions& options);
-
-  // Builds credentials for use when running in GCE
-  // WARNING: Do NOT use this credentials to connect to a non-google service as
-  // this could result in an oauth2 token leak.
-  static std::unique_ptr<Credentials> ComputeEngineCredentials();
-
-  // Builds service account credentials.
-  // WARNING: Do NOT use this credentials to connect to a non-google service as
-  // this could result in an oauth2 token leak.
-  // json_key is the JSON key string containing the client's private key.
-  // scope is a space-delimited list of the requested permissions.
-  // token_lifetime is the lifetime of each token acquired through this service
-  // account credentials. It should be positive and should not exceed
-  // grpc_max_auth_token_lifetime or will be cropped to this value.
-  static std::unique_ptr<Credentials> ServiceAccountCredentials(
-      const grpc::string& json_key, const grpc::string& scope,
-      std::chrono::seconds token_lifetime);
-
-  // Builds JWT credentials.
-  // json_key is the JSON key string containing the client's private key.
-  // token_lifetime is the lifetime of each Json Web Token (JWT) created with
-  // this credentials.  It should not exceed grpc_max_auth_token_lifetime or
-  // will be cropped to this value.
-  static std::unique_ptr<Credentials> JWTCredentials(
-      const grpc::string& json_key, std::chrono::seconds token_lifetime);
-
-  // Builds IAM credentials.
-  static std::unique_ptr<Credentials> IAMCredentials(
-      const grpc::string& authorization_token,
-      const grpc::string& authority_selector);
-
-  // Combines two credentials objects into a composite credentials
-  static std::unique_ptr<Credentials> CompositeCredentials(
-      const std::unique_ptr<Credentials>& creds1,
-      const std::unique_ptr<Credentials>& creds2);
-};
+
+// Builds credentials with reasonable defaults.
+std::unique_ptr<Credentials> GoogleDefaultCredentials();
+
+// Builds SSL Credentials given SSL specific options
+std::unique_ptr<Credentials> SslCredentials(
+    const SslCredentialsOptions& options);
+
+// Builds credentials for use when running in GCE
+std::unique_ptr<Credentials> ComputeEngineCredentials();
+
+// Builds service account credentials.
+// json_key is the JSON key string containing the client's private key.
+// scope is a space-delimited list of the requested permissions.
+// token_lifetime is the lifetime of each token acquired through this service
+// account credentials. It should be positive and should not exceed
+// grpc_max_auth_token_lifetime or will be cropped to this value.
+std::unique_ptr<Credentials> ServiceAccountCredentials(
+    const grpc::string& json_key, const grpc::string& scope,
+    std::chrono::seconds token_lifetime);
+
+// Builds IAM credentials.
+std::unique_ptr<Credentials> IAMCredentials(
+    const grpc::string& authorization_token,
+    const grpc::string& authority_selector);
+
+// Combines two credentials objects into a composite credentials
+std::unique_ptr<Credentials> CompositeCredentials(
+    const std::unique_ptr<Credentials>& creds1,
+    const std::unique_ptr<Credentials>& creds2);
+
+// Credentials for an unencrypted, unauthenticated channel
+std::unique_ptr<Credentials> InsecureCredentials();
 
 }  // namespace grpc
 
diff --git a/include/grpc++/impl/call.h b/include/grpc++/impl/call.h
index 3e199e3eaefdb70f3f42ecc9c81df6a2708b75e5..5de56629735f617ff8442f1529bfa853d299a79a 100644
--- a/include/grpc++/impl/call.h
+++ b/include/grpc++/impl/call.h
@@ -42,12 +42,6 @@
 #include <memory>
 #include <map>
 
-namespace google {
-namespace protobuf {
-class Message;
-}  // namespace protobuf
-}  // namespace google
-
 struct grpc_call;
 struct grpc_op;
 
@@ -67,8 +61,8 @@ class CallOpBuffer : public CompletionQueueTag {
       std::multimap<grpc::string, grpc::string> *metadata);
   void AddSendInitialMetadata(ClientContext *ctx);
   void AddRecvInitialMetadata(ClientContext *ctx);
-  void AddSendMessage(const google::protobuf::Message &message);
-  void AddRecvMessage(google::protobuf::Message *message);
+  void AddSendMessage(const grpc::protobuf::Message &message);
+  void AddRecvMessage(grpc::protobuf::Message *message);
   void AddClientSendClose();
   void AddClientRecvStatus(ClientContext *ctx, Status *status);
   void AddServerSendStatus(std::multimap<grpc::string, grpc::string> *metadata,
@@ -95,10 +89,10 @@ class CallOpBuffer : public CompletionQueueTag {
   std::multimap<grpc::string, grpc::string> *recv_initial_metadata_;
   grpc_metadata_array recv_initial_metadata_arr_;
   // Send message
-  const google::protobuf::Message *send_message_;
+  const grpc::protobuf::Message *send_message_;
   grpc_byte_buffer *send_message_buf_;
   // Recv message
-  google::protobuf::Message *recv_message_;
+  grpc::protobuf::Message *recv_message_;
   grpc_byte_buffer *recv_message_buf_;
   // Client send close
   bool client_send_close_;
diff --git a/include/grpc++/impl/client_unary_call.h b/include/grpc++/impl/client_unary_call.h
index d8703264e60327c65abf2331c099f3d3f58a8e7c..fd9715da504f9d29bbccd29ed0fde2cfbfb64ee4 100644
--- a/include/grpc++/impl/client_unary_call.h
+++ b/include/grpc++/impl/client_unary_call.h
@@ -34,11 +34,7 @@
 #ifndef GRPCXX_IMPL_CLIENT_UNARY_CALL_H
 #define GRPCXX_IMPL_CLIENT_UNARY_CALL_H
 
-namespace google {
-namespace protobuf {
-class Message;
-}  // namespace protobuf
-}  // namespace google
+#include <grpc++/config.h>
 
 namespace grpc {
 
@@ -51,8 +47,8 @@ class Status;
 // Wrapper that performs a blocking unary call
 Status BlockingUnaryCall(ChannelInterface *channel, const RpcMethod &method,
                          ClientContext *context,
-                         const google::protobuf::Message &request,
-                         google::protobuf::Message *result);
+                         const grpc::protobuf::Message &request,
+                         grpc::protobuf::Message *result);
 
 }  // namespace grpc
 
diff --git a/include/grpc++/impl/rpc_method.h b/include/grpc++/impl/rpc_method.h
index ab407f5c4682e66d1c0408d956a5cd326507c676..e8909ac1846516ba0e7093bdc16a526c8b9666fd 100644
--- a/include/grpc++/impl/rpc_method.h
+++ b/include/grpc++/impl/rpc_method.h
@@ -34,12 +34,6 @@
 #ifndef GRPCXX_IMPL_RPC_METHOD_H
 #define GRPCXX_IMPL_RPC_METHOD_H
 
-namespace google {
-namespace protobuf {
-class Message;
-}  // namespace protobuf
-}  // namespace google
-
 namespace grpc {
 
 class RpcMethod {
diff --git a/include/grpc++/impl/rpc_service_method.h b/include/grpc++/impl/rpc_service_method.h
index ff94c7e6c001260352379c12adc2245e04423e2f..097667827a133a75eaadb402df58211284155222 100644
--- a/include/grpc++/impl/rpc_service_method.h
+++ b/include/grpc++/impl/rpc_service_method.h
@@ -39,10 +39,10 @@
 #include <memory>
 #include <vector>
 
+#include <grpc++/config.h>
 #include <grpc++/impl/rpc_method.h>
 #include <grpc++/status.h>
 #include <grpc++/stream.h>
-#include <google/protobuf/message.h>
 
 namespace grpc {
 class ServerContext;
@@ -56,13 +56,13 @@ class MethodHandler {
   virtual ~MethodHandler() {}
   struct HandlerParameter {
     HandlerParameter(Call* c, ServerContext* context,
-                     const google::protobuf::Message* req,
-                     google::protobuf::Message* resp)
+                     const grpc::protobuf::Message* req,
+                     grpc::protobuf::Message* resp)
         : call(c), server_context(context), request(req), response(resp) {}
     Call* call;
     ServerContext* server_context;
-    const google::protobuf::Message* request;
-    google::protobuf::Message* response;
+    const grpc::protobuf::Message* request;
+    grpc::protobuf::Message* response;
   };
   virtual Status RunHandler(const HandlerParameter& param) = 0;
 };
@@ -165,8 +165,8 @@ class RpcServiceMethod : public RpcMethod {
   // Takes ownership of the handler and two prototype objects.
   RpcServiceMethod(const char* name, RpcMethod::RpcType type,
                    MethodHandler* handler,
-                   google::protobuf::Message* request_prototype,
-                   google::protobuf::Message* response_prototype)
+                   grpc::protobuf::Message* request_prototype,
+                   grpc::protobuf::Message* response_prototype)
       : RpcMethod(name, type),
         handler_(handler),
         request_prototype_(request_prototype),
@@ -174,17 +174,17 @@ class RpcServiceMethod : public RpcMethod {
 
   MethodHandler* handler() { return handler_.get(); }
 
-  google::protobuf::Message* AllocateRequestProto() {
+  grpc::protobuf::Message* AllocateRequestProto() {
     return request_prototype_->New();
   }
-  google::protobuf::Message* AllocateResponseProto() {
+  grpc::protobuf::Message* AllocateResponseProto() {
     return response_prototype_->New();
   }
 
  private:
   std::unique_ptr<MethodHandler> handler_;
-  std::unique_ptr<google::protobuf::Message> request_prototype_;
-  std::unique_ptr<google::protobuf::Message> response_prototype_;
+  std::unique_ptr<grpc::protobuf::Message> request_prototype_;
+  std::unique_ptr<grpc::protobuf::Message> response_prototype_;
 };
 
 // This class contains all the method information for an rpc service. It is
diff --git a/include/grpc++/impl/service_type.h b/include/grpc++/impl/service_type.h
index 7481d64d6af58168a73d91d1f47a39b722498ea8..7cd3ddad6b7a9f594805869938c2ec5160b6c4fa 100644
--- a/include/grpc++/impl/service_type.h
+++ b/include/grpc++/impl/service_type.h
@@ -34,11 +34,7 @@
 #ifndef GRPCXX_IMPL_SERVICE_TYPE_H
 #define GRPCXX_IMPL_SERVICE_TYPE_H
 
-namespace google {
-namespace protobuf {
-class Message;
-}  // namespace protobuf
-}  // namespace google
+#include <grpc++/config.h>
 
 namespace grpc {
 
@@ -72,7 +68,7 @@ class AsynchronousService {
    public:
     virtual void RequestAsyncCall(void* registered_method,
                                   ServerContext* context,
-                                  ::google::protobuf::Message* request,
+                                  ::grpc::protobuf::Message* request,
                                   ServerAsyncStreamingInterface* stream,
                                   CompletionQueue* cq, void* tag) = 0;
   };
@@ -91,7 +87,7 @@ class AsynchronousService {
 
  protected:
   void RequestAsyncUnary(int index, ServerContext* context,
-                         ::google::protobuf::Message* request,
+                         grpc::protobuf::Message* request,
                          ServerAsyncStreamingInterface* stream,
                          CompletionQueue* cq, void* tag) {
     dispatch_impl_->RequestAsyncCall(request_args_[index], context, request,
@@ -104,7 +100,7 @@ class AsynchronousService {
                                      stream, cq, tag);
   }
   void RequestServerStreaming(int index, ServerContext* context,
-                              ::google::protobuf::Message* request,
+                              grpc::protobuf::Message* request,
                               ServerAsyncStreamingInterface* stream,
                               CompletionQueue* cq, void* tag) {
     dispatch_impl_->RequestAsyncCall(request_args_[index], context, request,
diff --git a/include/grpc++/server.h b/include/grpc++/server.h
index 3282b82d04eade3549a72928073dcb9d477a74b5..43c8432caf5fda06f98c338ee757acc23483f544 100644
--- a/include/grpc++/server.h
+++ b/include/grpc++/server.h
@@ -47,12 +47,6 @@
 
 struct grpc_server;
 
-namespace google {
-namespace protobuf {
-class Message;
-}  // namespace protobuf
-}  // namespace google
-
 namespace grpc {
 class AsynchronousService;
 class RpcService;
@@ -81,15 +75,14 @@ class Server GRPC_FINAL : private CallHook,
   class AsyncRequest;
 
   // ServerBuilder use only
-  Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
-         ServerCredentials* creds);
-  Server();
+  Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned);
+  Server() = delete;
   // Register a service. This call does not take ownership of the service.
   // The service must exist for the lifetime of the Server instance.
   bool RegisterService(RpcService* service);
   bool RegisterAsyncService(AsynchronousService* service);
   // Add a listening port. Can be called multiple times.
-  int AddPort(const grpc::string& addr);
+  int AddPort(const grpc::string& addr, ServerCredentials* creds);
   // Start the server.
   bool Start();
 
@@ -101,9 +94,9 @@ class Server GRPC_FINAL : private CallHook,
 
   // DispatchImpl
   void RequestAsyncCall(void* registered_method, ServerContext* context,
-                        ::google::protobuf::Message* request,
+                        grpc::protobuf::Message* request,
                         ServerAsyncStreamingInterface* stream,
-                        CompletionQueue* cq, void* tag);
+                        CompletionQueue* cq, void* tag) GRPC_OVERRIDE;
 
   // Completion queue.
   CompletionQueue cq_;
@@ -119,13 +112,11 @@ class Server GRPC_FINAL : private CallHook,
   std::list<SyncRequest> sync_methods_;
 
   // Pointer to the c grpc server.
-  grpc_server* server_;
+  grpc_server* const server_;
 
   ThreadPoolInterface* thread_pool_;
   // Whether the thread pool is created and owned by the server.
   bool thread_pool_owned_;
-  // Whether the server is created with credentials.
-  bool secure_;
 };
 
 }  // namespace grpc
diff --git a/include/grpc++/server_builder.h b/include/grpc++/server_builder.h
index 5566002dc22c5127adf19117ef82a7c369442bb7..a3270775637ce064bf052dadb3e978c68aaa93be 100644
--- a/include/grpc++/server_builder.h
+++ b/include/grpc++/server_builder.h
@@ -65,11 +65,9 @@ class ServerBuilder {
   void RegisterAsyncService(AsynchronousService* service);
 
   // Add a listening port. Can be called multiple times.
-  void AddPort(const grpc::string& addr);
-
-  // Set a ServerCredentials. Can only be called once.
-  // TODO(yangg) move this to be part of AddPort
-  void SetCredentials(const std::shared_ptr<ServerCredentials>& creds);
+  void AddPort(const grpc::string& addr,
+               std::shared_ptr<ServerCredentials> creds,
+               int* selected_port = nullptr);
 
   // Set the thread pool used for running appliation rpc handlers.
   // Does not take ownership.
@@ -79,9 +77,15 @@ class ServerBuilder {
   std::unique_ptr<Server> BuildAndStart();
 
  private:
+  struct Port {
+    grpc::string addr;
+    std::shared_ptr<ServerCredentials> creds;
+    int* selected_port;
+  };
+
   std::vector<RpcService*> services_;
   std::vector<AsynchronousService*> async_services_;
-  std::vector<grpc::string> ports_;
+  std::vector<Port> ports_;
   std::shared_ptr<ServerCredentials> creds_;
   ThreadPoolInterface* thread_pool_;
 };
diff --git a/include/grpc++/server_context.h b/include/grpc++/server_context.h
index 9387f4a7e267a6bc6cf2765b7ae4b48943ff9b4a..a986fff46bf1a28787f00c6d22f2f60ff65f5f98 100644
--- a/include/grpc++/server_context.h
+++ b/include/grpc++/server_context.h
@@ -37,7 +37,7 @@
 #include <chrono>
 #include <map>
 
-#include "config.h"
+#include <grpc++/config.h>
 
 struct gpr_timespec;
 struct grpc_metadata;
diff --git a/include/grpc++/server_credentials.h b/include/grpc++/server_credentials.h
index 45cd279e0b03413757bdd8be739ba5c1d00e1684..83ae9fd1eb0fd0c773580d8f56f638f97c3f4bf1 100644
--- a/include/grpc++/server_credentials.h
+++ b/include/grpc++/server_credentials.h
@@ -39,24 +39,21 @@
 
 #include <grpc++/config.h>
 
-struct grpc_server_credentials;
+struct grpc_server;
 
 namespace grpc {
+class Server;
 
 // grpc_server_credentials wrapper class.
-class ServerCredentials GRPC_FINAL {
+class ServerCredentials {
  public:
-  ~ServerCredentials();
+  virtual ~ServerCredentials();
 
  private:
-  explicit ServerCredentials(grpc_server_credentials* c_creds);
+  friend class ::grpc::Server;
 
-  grpc_server_credentials* GetRawCreds();
-
-  friend class ServerCredentialsFactory;
-  friend class Server;
-
-  grpc_server_credentials* creds_;
+  virtual int AddPortToServer(const grpc::string& addr,
+                              grpc_server* server) = 0;
 };
 
 // Options to create ServerCredentials with SSL
@@ -69,13 +66,11 @@ struct SslServerCredentialsOptions {
   std::vector<PemKeyCertPair> pem_key_cert_pairs;
 };
 
-// Factory for building different types of ServerCredentials
-class ServerCredentialsFactory {
- public:
-  // Builds SSL ServerCredentials given SSL specific options
-  static std::shared_ptr<ServerCredentials> SslCredentials(
-      const SslServerCredentialsOptions& options);
-};
+// Builds SSL ServerCredentials given SSL specific options
+std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const SslServerCredentialsOptions& options);
+
+std::shared_ptr<ServerCredentials> InsecureServerCredentials();
 
 }  // namespace grpc
 
diff --git a/include/grpc++/stream.h b/include/grpc++/stream.h
index d95a379757e4d684789930a9f4b68ebf7faaea73..7625bcc38d314896e4eaaf4cd729bf7ec2259f07 100644
--- a/include/grpc++/stream.h
+++ b/include/grpc++/stream.h
@@ -88,7 +88,7 @@ class ClientReader GRPC_FINAL : public ClientStreamingInterface,
  public:
   // Blocking create a stream and write the first request out.
   ClientReader(ChannelInterface* channel, const RpcMethod& method,
-               ClientContext* context, const google::protobuf::Message& request)
+               ClientContext* context, const grpc::protobuf::Message& request)
       : context_(context), call_(channel->CreateCall(method, context, &cq_)) {
     CallOpBuffer buf;
     buf.AddSendInitialMetadata(&context->send_initial_metadata_);
@@ -142,7 +142,7 @@ class ClientWriter GRPC_FINAL : public ClientStreamingInterface,
  public:
   // Blocking create a stream.
   ClientWriter(ChannelInterface* channel, const RpcMethod& method,
-               ClientContext* context, google::protobuf::Message* response)
+               ClientContext* context, grpc::protobuf::Message* response)
       : context_(context),
         response_(response),
         call_(channel->CreateCall(method, context, &cq_)) {
@@ -179,7 +179,7 @@ class ClientWriter GRPC_FINAL : public ClientStreamingInterface,
 
  private:
   ClientContext* context_;
-  google::protobuf::Message* const response_;
+  grpc::protobuf::Message* const response_;
   CompletionQueue cq_;
   Call call_;
 };
@@ -386,7 +386,7 @@ class ClientAsyncReader GRPC_FINAL : public ClientAsyncStreamingInterface,
   // Create a stream and write the first request out.
   ClientAsyncReader(ChannelInterface* channel, CompletionQueue* cq,
                     const RpcMethod& method, ClientContext* context,
-                    const google::protobuf::Message& request, void* tag)
+                    const grpc::protobuf::Message& request, void* tag)
       : context_(context), call_(channel->CreateCall(method, context, cq)) {
     init_buf_.Reset(tag);
     init_buf_.AddSendInitialMetadata(&context->send_initial_metadata_);
@@ -436,7 +436,7 @@ class ClientAsyncWriter GRPC_FINAL : public ClientAsyncStreamingInterface,
  public:
   ClientAsyncWriter(ChannelInterface* channel, CompletionQueue* cq,
                     const RpcMethod& method, ClientContext* context,
-                    google::protobuf::Message* response, void* tag)
+                    grpc::protobuf::Message* response, void* tag)
       : context_(context),
         response_(response),
         call_(channel->CreateCall(method, context, cq)) {
@@ -477,7 +477,7 @@ class ClientAsyncWriter GRPC_FINAL : public ClientAsyncStreamingInterface,
 
  private:
   ClientContext* context_;
-  google::protobuf::Message* const response_;
+  grpc::protobuf::Message* const response_;
   Call call_;
   CallOpBuffer init_buf_;
   CallOpBuffer meta_buf_;
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index bb1653101f4e529917f385dedbc6c5cad7822c90..e401da873bb034b82f4ed9ab152c2884c3481318 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -436,6 +436,9 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
 grpc_channel *grpc_channel_create(const char *target,
                                   const grpc_channel_args *args);
 
+/* Create a lame client: this client fails every operation attempted on it. */
+grpc_channel *grpc_lame_client_channel_create(void);
+
 /* Close and destroy a grpc channel */
 void grpc_channel_destroy(grpc_channel *channel);
 
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 577f03e85fee2a03817e813722769dc5cf9cb4af..ab2cc08489ddd7629648dc6071874b2a3c220b5d 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -169,17 +169,12 @@ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
 
 /* --- Secure server creation. --- */
 
-/* Creates a secure server using the passed-in server credentials. */
-grpc_server *grpc_secure_server_create(grpc_server_credentials *creds,
-                                       grpc_completion_queue *cq,
-                                       const grpc_channel_args *args);
-
 /* Add a HTTP2 over an encrypted link over tcp listener.
    Server must have been created with grpc_secure_server_create.
    Returns bound port number on success, 0 on failure.
    REQUIRES: server not started */
-int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr);
-
+int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
+                                      grpc_server_credentials *creds);
 
 #ifdef __cplusplus
 }
diff --git a/include/grpc/support/histogram.h b/include/grpc/support/histogram.h
index 31f7fedfd5dd072305a76496731f273afece36d6..64d08f0bf1f9aa63b558f7fb2be751d18df308f4 100644
--- a/include/grpc/support/histogram.h
+++ b/include/grpc/support/histogram.h
@@ -34,6 +34,9 @@
 #ifndef GRPC_SUPPORT_HISTOGRAM_H
 #define GRPC_SUPPORT_HISTOGRAM_H
 
+#include <grpc/support/port_platform.h>
+#include <stddef.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -59,6 +62,13 @@ double gpr_histogram_count(gpr_histogram *histogram);
 double gpr_histogram_sum(gpr_histogram *histogram);
 double gpr_histogram_sum_of_squares(gpr_histogram *histogram);
 
+const gpr_uint32 *gpr_histogram_get_contents(gpr_histogram *histogram,
+                                             size_t *count);
+void gpr_histogram_merge_contents(gpr_histogram *histogram,
+                                  const gpr_uint32 *data, size_t data_count,
+                                  double min_seen, double max_seen, double sum,
+                                  double sum_of_squares, double count);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/grpc/support/slice_buffer.h b/include/grpc/support/slice_buffer.h
index 56f71ef2349c711507c9f92b1b2f3a7c55ca6d0e..c7e5dbc64708449c0a3ef0c17ab2d8562b1c957e 100644
--- a/include/grpc/support/slice_buffer.h
+++ b/include/grpc/support/slice_buffer.h
@@ -74,6 +74,8 @@ void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *slices, size_t n);
 /* add a very small (less than 8 bytes) amount of data to the end of a slice
    buffer: returns a pointer into which to add the data */
 gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned len);
+/* pop the last buffer, but don't unref it */
+void gpr_slice_buffer_pop(gpr_slice_buffer *sb);
 /* clear a slice buffer, unref all elements */
 void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb);
 
diff --git a/src/compiler/python_plugin.cc b/src/compiler/python_plugin.cc
index 0dd2c5b834416488c6ddbec0e4782b83b055e7a9..825f15a5c787ba775d7d36380f914f0aa4564816 100644
--- a/src/compiler/python_plugin.cc
+++ b/src/compiler/python_plugin.cc
@@ -36,6 +36,7 @@
 #include <cstring>
 #include <memory>
 #include <string>
+#include <tuple>
 
 #include "src/compiler/python_generator.h"
 #include <google/protobuf/compiler/code_generator.h>
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index f0a8453fd77a1609519e3fa17709a9c0ead990df..0bb722e2b12c44748d089f68e66cd200c522f2f0 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -66,7 +66,7 @@ static void backup_poller(void *p) {
     gpr_timespec next_poll = gpr_time_add(last_poll, delta);
     grpc_pollset_work(&g_backup_pollset, gpr_time_add(gpr_now(), gpr_time_from_seconds(1)));
     gpr_mu_unlock(&g_backup_pollset.mu);
-    /*gpr_sleep_until(next_poll);*/
+    gpr_sleep_until(next_poll);
     gpr_mu_lock(&g_backup_pollset.mu);
     last_poll = next_poll;
   }
@@ -267,7 +267,6 @@ static void unary_poll_do_promote(void *args, int success) {
    * and we don't have any mechanism to unbecome multipoller. */
   pollset->in_flight_cbs--;
   if (pollset->shutting_down) {
-    gpr_log(GPR_INFO, "Shutting down");
     /* We don't care about this pollset anymore. */
     if (pollset->in_flight_cbs == 0) {
       do_shutdown_cb = 1;
@@ -275,7 +274,6 @@ static void unary_poll_do_promote(void *args, int success) {
   } else if (grpc_fd_is_orphaned(fd)) {
     /* Don't try to add it to anything, we'll drop our ref on it below */
   } else if (pollset->vtable != original_vtable) {
-    gpr_log(GPR_INFO, "Not original vtable");
     pollset->vtable->add_fd(pollset, fd);
   } else if (fd != pollset->data.ptr) {
     grpc_fd *fds[2];
diff --git a/src/core/iomgr/resolve_address_posix.c b/src/core/iomgr/resolve_address_posix.c
index 989b968ae27267a88887a21c21bda31046401009..9a9283c93c386cd7319d599774cbf722a02201b4 100644
--- a/src/core/iomgr/resolve_address_posix.c
+++ b/src/core/iomgr/resolve_address_posix.c
@@ -101,6 +101,21 @@ grpc_resolved_addresses *grpc_blocking_resolve_address(
   hints.ai_flags = AI_PASSIVE;     /* for wildcard IP address */
 
   s = getaddrinfo(host, port, &hints, &result);
+  if (s != 0) {
+    /* Retry if well-known service name is recognized */
+    char *svc[][2] = {
+      {"http", "80"},
+      {"https", "443"}
+    };
+    int i;
+    for (i = 0; i < (int)(sizeof(svc) / sizeof(svc[0])); i++) {
+      if (strcmp(port, svc[i][0]) == 0) {
+        s = getaddrinfo(host, svc[i][1], &hints, &result);
+        break;
+      }
+    }
+  }
+
   if (s != 0) {
     gpr_log(GPR_ERROR, "getaddrinfo: %s", gai_strerror(s));
     goto done;
diff --git a/src/core/security/auth.c b/src/core/security/auth.c
index 9b67d59cb8e6c282c31fb0a14d58a60d8b49077d..5fc6d2717f7dfe0f4d68ef474fa7b4334464a61c 100644
--- a/src/core/security/auth.c
+++ b/src/core/security/auth.c
@@ -59,6 +59,7 @@ typedef struct {
   grpc_mdstr *authority_string;
   grpc_mdstr *path_string;
   grpc_mdstr *error_msg_key;
+  grpc_mdstr *status_key;
 } channel_data;
 
 static void do_nothing(void *ignored, grpc_op_error error) {}
@@ -66,17 +67,25 @@ static void do_nothing(void *ignored, grpc_op_error error) {}
 static void bubbleup_error(grpc_call_element *elem, const char *error_msg) {
   grpc_call_op finish_op;
   channel_data *channeld = elem->channel_data;
+  char status[GPR_LTOA_MIN_BUFSIZE];
 
   gpr_log(GPR_ERROR, "%s", error_msg);
   finish_op.type = GRPC_RECV_METADATA;
   finish_op.dir = GRPC_CALL_UP;
   finish_op.flags = 0;
   finish_op.data.metadata = grpc_mdelem_from_metadata_strings(
-      channeld->md_ctx, channeld->error_msg_key,
+      channeld->md_ctx, grpc_mdstr_ref(channeld->error_msg_key),
       grpc_mdstr_from_string(channeld->md_ctx, error_msg));
   finish_op.done_cb = do_nothing;
   finish_op.user_data = NULL;
   grpc_call_next_op(elem, &finish_op);
+
+  gpr_ltoa(GRPC_STATUS_UNAUTHENTICATED, status);
+  finish_op.data.metadata = grpc_mdelem_from_metadata_strings(
+      channeld->md_ctx, grpc_mdstr_ref(channeld->status_key),
+      grpc_mdstr_from_string(channeld->md_ctx, status));
+  grpc_call_next_op(elem, &finish_op);
+
   grpc_call_element_send_cancel(elem);
 }
 
@@ -151,6 +160,7 @@ static void on_host_checked(void *user_data, grpc_security_status status) {
                  grpc_mdstr_as_c_string(calld->host));
     bubbleup_error(elem, error_msg);
     gpr_free(error_msg);
+    calld->op.done_cb(calld->op.user_data, GRPC_OP_ERROR);
   }
 }
 
@@ -193,6 +203,7 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
                          call_host);
             bubbleup_error(elem, error_msg);
             gpr_free(error_msg);
+            op->done_cb(op->user_data, GRPC_OP_ERROR);
           }
           break;
         }
@@ -265,6 +276,7 @@ static void init_channel_elem(grpc_channel_element *elem,
   channeld->path_string = grpc_mdstr_from_string(channeld->md_ctx, ":path");
   channeld->error_msg_key =
       grpc_mdstr_from_string(channeld->md_ctx, "grpc-message");
+  channeld->status_key = grpc_mdstr_from_string(channeld->md_ctx, "grpc-status");
 }
 
 /* Destructor for channel data */
@@ -279,6 +291,9 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
   if (channeld->error_msg_key != NULL) {
     grpc_mdstr_unref(channeld->error_msg_key);
   }
+  if (channeld->status_key != NULL) {
+    grpc_mdstr_unref(channeld->status_key);
+  }
   if (channeld->path_string != NULL) {
     grpc_mdstr_unref(channeld->path_string);
   }
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
index 7e72b238c80ed28747f53816aab8c3cf404bf1fd..3ad1e7edd7617b7274ac19041ac694b64e704547 100644
--- a/src/core/security/credentials.c
+++ b/src/core/security/credentials.c
@@ -348,7 +348,7 @@ static void jwt_get_request_metadata(grpc_credentials *creds,
   {
     gpr_mu_lock(&c->cache_mu);
     if (c->cached.service_url != NULL &&
-        !strcmp(c->cached.service_url, service_url) &&
+        strcmp(c->cached.service_url, service_url) == 0 &&
         c->cached.jwt_md != NULL &&
         (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, gpr_now()),
                       refresh_threshold) > 0)) {
@@ -957,7 +957,7 @@ static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
   grpc_credentials *creds = *creds_addr;
   result.creds_array = creds_addr;
   result.num_creds = 1;
-  if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
+  if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) {
     result = *grpc_composite_credentials_get_credentials(creds);
   }
   return result;
@@ -995,7 +995,7 @@ const grpc_credentials_array *grpc_composite_credentials_get_credentials(
     grpc_credentials *creds) {
   const grpc_composite_credentials *c =
       (const grpc_composite_credentials *)creds;
-  GPR_ASSERT(!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
+  GPR_ASSERT(strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0);
   return &c->inner;
 }
 
@@ -1003,14 +1003,14 @@ grpc_credentials *grpc_credentials_contains_type(
     grpc_credentials *creds, const char *type,
     grpc_credentials **composite_creds) {
   size_t i;
-  if (!strcmp(creds->type, type)) {
+  if (strcmp(creds->type, type) == 0) {
     if (composite_creds != NULL) *composite_creds = NULL;
     return creds;
-  } else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
+  } else if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) {
     const grpc_credentials_array *inner_creds_array =
         grpc_composite_credentials_get_credentials(creds);
     for (i = 0; i < inner_creds_array->num_creds; i++) {
-      if (!strcmp(type, inner_creds_array->creds_array[i]->type)) {
+      if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) {
         if (composite_creds != NULL) *composite_creds = creds;
         return inner_creds_array->creds_array[i];
       }
diff --git a/src/core/security/factories.c b/src/core/security/factories.c
index c9701b9080db47e6e5a8bf2e9daa9310f49318fd..02267d55457c5f0563dd5b2a3275721a761352b6 100644
--- a/src/core/security/factories.c
+++ b/src/core/security/factories.c
@@ -33,9 +33,9 @@
 
 #include <string.h>
 
+#include <grpc/grpc.h>
 #include "src/core/security/credentials.h"
 #include "src/core/security/security_context.h"
-#include "src/core/surface/lame_client.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
@@ -50,31 +50,3 @@ grpc_channel *grpc_secure_channel_create(grpc_credentials *creds,
   return grpc_secure_channel_create_with_factories(
       factories, GPR_ARRAY_SIZE(factories), creds, target, args);
 }
-
-grpc_server *grpc_secure_server_create(grpc_server_credentials *creds,
-                                       grpc_completion_queue *cq,
-                                       const grpc_channel_args *args) {
-  grpc_security_status status = GRPC_SECURITY_ERROR;
-  grpc_security_context *ctx = NULL;
-  grpc_server *server = NULL;
-  if (creds == NULL) return NULL; /* TODO(ctiller): Return lame server. */
-
-  if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
-    status = grpc_ssl_server_security_context_create(
-        grpc_ssl_server_credentials_get_config(creds), &ctx);
-  } else if (!strcmp(creds->type,
-                     GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY)) {
-    ctx = grpc_fake_server_security_context_create();
-    status = GRPC_SECURITY_OK;
-  }
-
-  if (status != GRPC_SECURITY_OK) {
-    gpr_log(GPR_ERROR,
-            "Unable to create secure server with credentials of type %s.",
-            creds->type);
-    return NULL; /* TODO(ctiller): Return lame server. */
-  }
-  server = grpc_secure_server_create_internal(cq, args, ctx);
-  grpc_security_context_unref(ctx);
-  return server;
-}
diff --git a/src/core/security/google_default_credentials.c b/src/core/security/google_default_credentials.c
index dc0e453b8781851f31b022f6873efb95ed460400..bdc907e7b33ada137d4af315799ac63db8f669ac 100644
--- a/src/core/security/google_default_credentials.c
+++ b/src/core/security/google_default_credentials.c
@@ -75,8 +75,8 @@ static void on_compute_engine_detection_http_response(
     size_t i;
     for (i = 0; i < response->hdr_count; i++) {
       grpc_httpcli_header *header = &response->hdrs[i];
-      if (!strcmp(header->key, "Metadata-Flavor") &&
-          !strcmp(header->value, "Google")) {
+      if (strcmp(header->key, "Metadata-Flavor") == 0 &&
+          strcmp(header->value, "Google") == 0) {
         detector->success = 1;
         break;
       }
diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c
index 26d57036a6d58d66b968faf499960fdf0f14ac0a..40b612b2065e11213f82a33834407a6a452376b9 100644
--- a/src/core/security/json_token.c
+++ b/src/core/security/json_token.c
@@ -257,7 +257,7 @@ static char *dot_concat_and_free_strings(char *str1, char *str2) {
 }
 
 const EVP_MD *openssl_digest_from_algorithm(const char *algorithm) {
-  if (!strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM)) {
+  if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) {
     return EVP_sha256();
   } else {
     gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm);
diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c
index 0dc37fa73c0e33d9cec0458cf8e4013b647763f4..e180cad52b26ea74dda96aae4ba3cdd546e72936 100644
--- a/src/core/security/security_context.c
+++ b/src/core/security/security_context.c
@@ -42,7 +42,6 @@
 #include "src/core/support/env.h"
 #include "src/core/support/file.h"
 #include "src/core/support/string.h"
-#include "src/core/surface/lame_client.h"
 #include "src/core/transport/chttp2/alpn.h"
 
 #include <grpc/support/alloc.h>
@@ -422,7 +421,7 @@ static grpc_security_status ssl_channel_check_call_host(
   /* If the target name was overridden, then the original target_name was
      'checked' transitively during the previous peer check at the end of the
      handshake. */
-  if (c->overridden_target_name != NULL && !strcmp(host, c->target_name)) {
+  if (c->overridden_target_name != NULL && strcmp(host, c->target_name) == 0) {
     return GRPC_SECURITY_OK;
   } else {
     return GRPC_SECURITY_ERROR;
@@ -611,7 +610,7 @@ grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds,
 
   for (i = 0; args && i < args->num_args; i++) {
     grpc_arg *arg = &args->args[i];
-    if (!strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) &&
+    if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
         arg->type == GRPC_ARG_STRING) {
       overridden_target_name = arg->value.string;
       break;
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index c88f0726bb7a8d17efd05f2022f58a0766b0408a..c155b80b7e6aa0b10f6f001daf73100a4dca0325 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -33,8 +33,11 @@
 
 #include <grpc/grpc.h>
 
+#include <string.h>
+
 #include "src/core/channel/http_filter.h"
 #include "src/core/channel/http_server_filter.h"
+#include "src/core/iomgr/endpoint.h"
 #include "src/core/iomgr/resolve_address.h"
 #include "src/core/iomgr/tcp_server.h"
 #include "src/core/security/security_context.h"
@@ -43,8 +46,29 @@
 #include "src/core/transport/chttp2_transport.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/sync.h>
 #include <grpc/support/useful.h>
 
+typedef struct grpc_server_secure_state {
+  grpc_server *server;
+  grpc_tcp_server *tcp;
+  grpc_security_context *ctx;
+  int is_shutdown;
+  gpr_mu mu;
+  gpr_refcount refcount;
+} grpc_server_secure_state;
+
+static void state_ref(grpc_server_secure_state *state) {
+  gpr_ref(&state->refcount);
+}
+
+static void state_unref(grpc_server_secure_state *state) {
+  if (gpr_unref(&state->refcount)) {
+    grpc_security_context_unref(state->ctx);
+    gpr_free(state);
+  }
+}
+
 static grpc_transport_setup_result setup_transport(void *server,
                                                    grpc_transport *transport,
                                                    grpc_mdctx *mdctx) {
@@ -54,49 +78,84 @@ static grpc_transport_setup_result setup_transport(void *server,
                                      GPR_ARRAY_SIZE(extra_filters), mdctx);
 }
 
-static void on_secure_transport_setup_done(void *server,
+static void on_secure_transport_setup_done(void *statep,
                                            grpc_security_status status,
                                            grpc_endpoint *secure_endpoint) {
+  grpc_server_secure_state *state = statep;
   if (status == GRPC_SECURITY_OK) {
-    grpc_create_chttp2_transport(
-        setup_transport, server, grpc_server_get_channel_args(server),
-        secure_endpoint, NULL, 0, grpc_mdctx_create(), 0);
+    gpr_mu_lock(&state->mu);
+    if (!state->is_shutdown) {
+      grpc_create_chttp2_transport(
+          setup_transport, state->server,
+          grpc_server_get_channel_args(state->server),
+          secure_endpoint, NULL, 0, grpc_mdctx_create(), 0);
+    } else {
+      /* We need to consume this here, because the server may already have gone
+       * away. */
+      grpc_endpoint_destroy(secure_endpoint);
+    }
+    gpr_mu_unlock(&state->mu);
   } else {
     gpr_log(GPR_ERROR, "Secure transport failed with error %d", status);
   }
+  state_unref(state);
 }
 
-static void on_accept(void *server, grpc_endpoint *tcp) {
-  const grpc_channel_args *args = grpc_server_get_channel_args(server);
-  grpc_security_context *ctx = grpc_find_security_context_in_args(args);
-  GPR_ASSERT(ctx);
-  grpc_setup_secure_transport(ctx, tcp, on_secure_transport_setup_done, server);
+static void on_accept(void *statep, grpc_endpoint *tcp) {
+  grpc_server_secure_state *state = statep;
+  state_ref(state);
+  grpc_setup_secure_transport(state->ctx, tcp, on_secure_transport_setup_done, state);
 }
 
-/* Note: the following code is the same with server_chttp2.c */
-
 /* Server callback: start listening on our ports */
-static void start(grpc_server *server, void *tcpp, grpc_pollset **pollsets,
+static void start(grpc_server *server, void *statep, grpc_pollset **pollsets,
                   size_t pollset_count) {
-  grpc_tcp_server *tcp = tcpp;
-  grpc_tcp_server_start(tcp, pollsets, pollset_count, on_accept, server);
+  grpc_server_secure_state *state = statep;
+  grpc_tcp_server_start(state->tcp, pollsets, pollset_count, on_accept, state);
 }
 
 /* Server callback: destroy the tcp listener (so we don't generate further
    callbacks) */
-static void destroy(grpc_server *server, void *tcpp) {
-  grpc_tcp_server *tcp = tcpp;
-  grpc_tcp_server_destroy(tcp);
+static void destroy(grpc_server *server, void *statep) {
+  grpc_server_secure_state *state = statep;
+  gpr_mu_lock(&state->mu);
+  state->is_shutdown = 1;
+  grpc_tcp_server_destroy(state->tcp);
+  gpr_mu_unlock(&state->mu);
+  state_unref(state);
 }
 
-int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
+int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, grpc_server_credentials *creds) {
   grpc_resolved_addresses *resolved = NULL;
   grpc_tcp_server *tcp = NULL;
+  grpc_server_secure_state *state = NULL;
   size_t i;
   unsigned count = 0;
   int port_num = -1;
   int port_temp;
+  grpc_security_status status = GRPC_SECURITY_ERROR;
+  grpc_security_context *ctx = NULL;
 
+  /* create security context */
+  if (creds == NULL) goto error;
+
+  if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL) == 0) {
+    status = grpc_ssl_server_security_context_create(
+        grpc_ssl_server_credentials_get_config(creds), &ctx);
+  } else if (strcmp(creds->type,
+                    GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) == 0) {
+    ctx = grpc_fake_server_security_context_create();
+    status = GRPC_SECURITY_OK;
+  }
+
+  if (status != GRPC_SECURITY_OK) {
+    gpr_log(GPR_ERROR,
+            "Unable to create secure server with credentials of type %s.",
+            creds->type);
+    goto error;
+  }
+
+  /* resolve address */
   resolved = grpc_blocking_resolve_address(addr, "https");
   if (!resolved) {
     goto error;
@@ -132,18 +191,32 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
   }
   grpc_resolved_addresses_destroy(resolved);
 
+  state = gpr_malloc(sizeof(*state));
+  state->server = server;
+  state->tcp = tcp;
+  state->ctx = ctx;
+  state->is_shutdown = 0;
+  gpr_mu_init(&state->mu);
+  gpr_ref_init(&state->refcount, 1);
+
   /* Register with the server only upon success */
-  grpc_server_add_listener(server, tcp, start, destroy);
+  grpc_server_add_listener(server, state, start, destroy);
 
   return port_num;
 
 /* Error path: cleanup and return */
 error:
+  if (ctx) {
+    grpc_security_context_unref(ctx);
+  }
   if (resolved) {
     grpc_resolved_addresses_destroy(resolved);
   }
   if (tcp) {
     grpc_tcp_server_destroy(tcp);
   }
+  if (state) {
+    gpr_free(state);
+  }
   return 0;
 }
diff --git a/src/core/support/histogram.c b/src/core/support/histogram.c
index eacb77082f971fcbf75ebd552fb67efaf7734c34..ed344b43e8d0f9fc4a6c0eb9d98e2df5042f4e3b 100644
--- a/src/core/support/histogram.c
+++ b/src/core/support/histogram.c
@@ -126,25 +126,35 @@ void gpr_histogram_add(gpr_histogram *h, double x) {
 }
 
 int gpr_histogram_merge(gpr_histogram *dst, gpr_histogram *src) {
-  size_t i;
   if ((dst->num_buckets != src->num_buckets) ||
       (dst->multiplier != src->multiplier)) {
     /* Fail because these histograms don't match */
     return 0;
   }
-  dst->sum += src->sum;
-  dst->sum_of_squares += src->sum_of_squares;
-  dst->count += src->count;
-  if (src->min_seen < dst->min_seen) {
-    dst->min_seen = src->min_seen;
+  gpr_histogram_merge_contents(dst, src->buckets, src->num_buckets,
+                               src->min_seen, src->max_seen, src->sum,
+                               src->sum_of_squares, src->count);
+  return 1;
+}
+
+void gpr_histogram_merge_contents(gpr_histogram *dst, const gpr_uint32 *data,
+                                  size_t data_count, double min_seen,
+                                  double max_seen, double sum,
+                                  double sum_of_squares, double count) {
+  size_t i;
+  GPR_ASSERT(dst->num_buckets == data_count);
+  dst->sum += sum;
+  dst->sum_of_squares += sum_of_squares;
+  dst->count += count;
+  if (min_seen < dst->min_seen) {
+    dst->min_seen = min_seen;
   }
-  if (src->max_seen > dst->max_seen) {
-    dst->max_seen = src->max_seen;
+  if (max_seen > dst->max_seen) {
+    dst->max_seen = max_seen;
   }
   for (i = 0; i < dst->num_buckets; i++) {
-    dst->buckets[i] += src->buckets[i];
+    dst->buckets[i] += data[i];
   }
-  return 1;
 }
 
 static double threshold_for_count_below(gpr_histogram *h, double count_below) {
@@ -222,3 +232,8 @@ double gpr_histogram_sum(gpr_histogram *h) { return h->sum; }
 double gpr_histogram_sum_of_squares(gpr_histogram *h) {
   return h->sum_of_squares;
 }
+
+const gpr_uint32 *gpr_histogram_get_contents(gpr_histogram *h, size_t *size) {
+  *size = h->num_buckets;
+  return h->buckets;
+}
diff --git a/src/core/support/slice_buffer.c b/src/core/support/slice_buffer.c
index 6cd51f925c3b46b083dddc19ad4d8a3b360b14e5..b280e4bd020014b26f08de866fa75a2ddb460566 100644
--- a/src/core/support/slice_buffer.c
+++ b/src/core/support/slice_buffer.c
@@ -143,6 +143,13 @@ void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *s, size_t n) {
   }
 }
 
+void gpr_slice_buffer_pop(gpr_slice_buffer *sb) {
+  if (sb->count != 0) {
+    size_t count = --sb->count;
+    sb->length -= GPR_SLICE_LENGTH(sb->slices[count]);
+  }
+}
+
 void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb) {
   size_t i;
 
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 7cf3c0e4fd6c4032d48e02ebcd8b457a32f423dc..cfce943794072f8ffa04e9af4880a1f8ebe856f8 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -140,6 +140,8 @@ struct grpc_call {
   gpr_uint8 have_alarm;
   /* are we currently performing a send operation */
   gpr_uint8 sending;
+  /* are we currently completing requests */
+  gpr_uint8 completing;
   /* pairs with completed_requests */
   gpr_uint8 num_completed_requests;
   /* flag that we need to request more data */
@@ -357,7 +359,7 @@ static void lock(grpc_call *call) { gpr_mu_lock(&call->mu); }
 static void unlock(grpc_call *call) {
   send_action sa = SEND_NOTHING;
   completed_request completed_requests[GRPC_IOREQ_OP_COUNT];
-  int num_completed_requests = call->num_completed_requests;
+  int completing_requests = 0;
   int need_more_data =
       call->need_more_data &&
       (call->write_state >= WRITE_STATE_STARTED || !call->is_client);
@@ -367,10 +369,13 @@ static void unlock(grpc_call *call) {
     call->need_more_data = 0;
   }
 
-  if (num_completed_requests != 0) {
+  if (!call->completing && call->num_completed_requests != 0) {
+    completing_requests = call->num_completed_requests;
     memcpy(completed_requests, call->completed_requests,
            sizeof(completed_requests));
     call->num_completed_requests = 0;
+    call->completing = 1;
+    grpc_call_internal_ref(call);
   }
 
   if (!call->sending) {
@@ -391,9 +396,15 @@ static void unlock(grpc_call *call) {
     enact_send_action(call, sa);
   }
 
-  for (i = 0; i < num_completed_requests; i++) {
-    completed_requests[i].on_complete(call, completed_requests[i].status,
-                                      completed_requests[i].user_data);
+  if (completing_requests > 0) {
+    for (i = 0; i < completing_requests; i++) {
+      completed_requests[i].on_complete(call, completed_requests[i].status,
+                                        completed_requests[i].user_data);
+    }
+    lock(call);
+    call->completing = 0;
+    unlock(call);
+    grpc_call_internal_unref(call, 0);
   }
 }
 
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index e38734c6a4921fb4e7e63a6d247262b13637e1c9..e764a3b9af0a82666137eecbf22a83eef2f66edb 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -39,6 +39,7 @@
 #include "src/core/iomgr/iomgr.h"
 #include "src/core/surface/call.h"
 #include "src/core/surface/client.h"
+#include "src/core/surface/init.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
@@ -63,6 +64,7 @@ grpc_channel *grpc_channel_create_from_filters(
   size_t size =
       sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
   grpc_channel *channel = gpr_malloc(size);
+  GPR_ASSERT(grpc_is_initialized() && "call grpc_init()");
   channel->is_client = is_client;
   /* decremented by grpc_channel_destroy, and grpc_client_channel_closed if is_client */
   gpr_ref_init(&channel->refs, 1 + is_client);
diff --git a/src/core/surface/init.c b/src/core/surface/init.c
index 4db66fb66eb05b485a428f35113df0a775664d95..e48c4202e5831004453791d7004a0756879048de 100644
--- a/src/core/surface/init.c
+++ b/src/core/surface/init.c
@@ -40,17 +40,17 @@
 #include "src/core/surface/surface_trace.h"
 #include "src/core/transport/chttp2_transport.h"
 
-static gpr_once g_init = GPR_ONCE_INIT;
+static gpr_once g_basic_init = GPR_ONCE_INIT;
 static gpr_mu g_init_mu;
 static int g_initializations;
 
-static void do_init(void) {
+static void do_basic_init(void) {
   gpr_mu_init(&g_init_mu);
   g_initializations = 0;
 }
 
 void grpc_init(void) {
-  gpr_once_init(&g_init, do_init);
+  gpr_once_init(&g_basic_init, do_basic_init);
 
   gpr_mu_lock(&g_init_mu);
   if (++g_initializations == 1) {
@@ -73,3 +73,13 @@ void grpc_shutdown(void) {
   }
   gpr_mu_unlock(&g_init_mu);
 }
+
+int grpc_is_initialized(void) {
+  int r;
+  gpr_once_init(&g_basic_init, do_basic_init);
+  gpr_mu_lock(&g_init_mu);
+  r = g_initializations > 0;
+  gpr_mu_unlock(&g_init_mu);
+  return r;
+}
+
diff --git a/src/core/surface/init.h b/src/core/surface/init.h
index ab40bedf8754918fd965d52e5dfa4c2bcc1872c7..416874020d7c79b090b2e66487d443b15febd93a 100644
--- a/src/core/surface/init.h
+++ b/src/core/surface/init.h
@@ -35,5 +35,6 @@
 #define GRPC_INTERNAL_CORE_SURFACE_INIT_H
 
 void grpc_security_pre_init(void);
+int grpc_is_initialized(void);
 
 #endif  /* GRPC_INTERNAL_CORE_SURFACE_INIT_H */
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index 57f6ddf0f7ff6bca8b82e02946dd7cbb8a3e1bb9..b40c48381f4296da467ce53df4cd9ac9b8420258 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/surface/lame_client.h"
+#include <grpc/grpc.h>
 
 #include <string.h>
 
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index c99a1b4cc9c9c72c3c144607890f6a143b69ba39..424734c54ce5615fcbe700bb22bd834803004d82 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -44,6 +44,7 @@
 #include "src/core/surface/call.h"
 #include "src/core/surface/channel.h"
 #include "src/core/surface/completion_queue.h"
+#include "src/core/surface/init.h"
 #include "src/core/transport/metadata.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
@@ -612,6 +613,9 @@ grpc_server *grpc_server_create_from_filters(grpc_completion_queue *cq,
   int census_enabled = grpc_channel_args_is_census_enabled(args);
 
   grpc_server *server = gpr_malloc(sizeof(grpc_server));
+
+  GPR_ASSERT(grpc_is_initialized() && "call grpc_init()");
+
   memset(server, 0, sizeof(grpc_server));
   if (cq) addcq(server, cq);
 
diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c
index fd702593b89dfdcef593f5061003ef21320070ce..27434b39e2d21695a09a2500e10bcdaf65c0f0fd 100644
--- a/src/core/surface/server_chttp2.c
+++ b/src/core/surface/server_chttp2.c
@@ -53,6 +53,13 @@ static grpc_transport_setup_result setup_transport(void *server,
 }
 
 static void new_transport(void *server, grpc_endpoint *tcp) {
+  /*
+   * Beware that the call to grpc_create_chttp2_transport() has to happen before
+   * grpc_tcp_server_destroy(). This is fine here, but similar code
+   * asynchronously doing a handshake instead of calling grpc_tcp_server_start()
+   * (as in server_secure_chttp2.c) needs to add synchronization to avoid this
+   * case.
+   */
   grpc_create_chttp2_transport(setup_transport, server,
                                grpc_server_get_channel_args(server), tcp, NULL,
                                0, grpc_mdctx_create(), 0);
diff --git a/src/core/tsi/fake_transport_security.c b/src/core/tsi/fake_transport_security.c
index f58f04ea20b8ebe17b92c43d4c8970bdd997b024..9ce1ddb95e2676353caced21e8eeb6c6dc991228 100644
--- a/src/core/tsi/fake_transport_security.c
+++ b/src/core/tsi/fake_transport_security.c
@@ -102,8 +102,8 @@ static tsi_result tsi_fake_handshake_message_from_string(
     const char* msg_string, tsi_fake_handshake_message* msg) {
   int i;
   for (i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) {
-    if (!strncmp(msg_string, tsi_fake_handshake_message_strings[i],
-                 strlen(tsi_fake_handshake_message_strings[i]))) {
+    if (strncmp(msg_string, tsi_fake_handshake_message_strings[i],
+                strlen(tsi_fake_handshake_message_strings[i])) == 0) {
       *msg = i;
       return TSI_OK;
     }
diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c
index dc43f7e270cf52ccc6d803a986f14311f6d583e4..33645ca8b866193c5f0f82c9654eb794eae989c1 100644
--- a/src/core/tsi/ssl_transport_security.c
+++ b/src/core/tsi/ssl_transport_security.c
@@ -1083,7 +1083,8 @@ static int does_entry_match_name(const char* entry, size_t entry_length,
     if (entry_length == 0) return 0;
   }
 
-  if ((name_length == entry_length) && !strncmp(name, entry, entry_length)) {
+  if ((name_length == entry_length) &&
+      strncmp(name, entry, entry_length) == 0) {
     return 1; /* Perfect match. */
   }
   if (entry[0] != '*') return 0;
@@ -1110,7 +1111,7 @@ static int does_entry_match_name(const char* entry, size_t entry_length,
     name_subdomain_length--;
   }
   return ((entry_length > 0) && (name_subdomain_length == entry_length) &&
-          !strncmp(entry, name_subdomain, entry_length));
+          strncmp(entry, name_subdomain, entry_length) == 0);
 }
 
 static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap,
diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c
index c8c74c5de5998739bd6b20501ba17e9d72f1f577..f4ab9d2bc6d12feab4e4b5554e2bc0aa6605ea58 100644
--- a/src/core/tsi/transport_security.c
+++ b/src/core/tsi/transport_security.c
@@ -208,7 +208,7 @@ const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* self,
       return property;
     }
     if (name != NULL && property->name != NULL &&
-        !strcmp(property->name, name)) {
+        strcmp(property->name, name) == 0) {
       return property;
     }
   }
diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc
index ca69d66cbbfcf541e7e0bdfde03b77052c74d929..5380d3a232c3739ef634e4922311faa18ee8e204 100644
--- a/src/cpp/client/channel.cc
+++ b/src/cpp/client/channel.cc
@@ -50,47 +50,26 @@
 #include <grpc++/impl/call.h>
 #include <grpc++/impl/rpc_method.h>
 #include <grpc++/status.h>
-#include <google/protobuf/message.h>
 
 namespace grpc {
 
-Channel::Channel(const grpc::string &target, const ChannelArguments &args)
-    : target_(target) {
-  grpc_channel_args channel_args;
-  args.SetChannelArgs(&channel_args);
-  c_channel_ = grpc_channel_create(
-      target_.c_str(), channel_args.num_args > 0 ? &channel_args : nullptr);
-}
-
-Channel::Channel(const grpc::string &target,
-                 const std::unique_ptr<Credentials> &creds,
-                 const ChannelArguments &args)
-    : target_(args.GetSslTargetNameOverride().empty()
-                  ? target
-                  : args.GetSslTargetNameOverride()) {
-  grpc_channel_args channel_args;
-  args.SetChannelArgs(&channel_args);
-  grpc_credentials *c_creds = creds ? creds->GetRawCreds() : nullptr;
-  c_channel_ = grpc_secure_channel_create(
-      c_creds, target.c_str(),
-      channel_args.num_args > 0 ? &channel_args : nullptr);
-}
+Channel::Channel(const grpc::string& target, grpc_channel* channel)
+    : target_(target), c_channel_(channel) {}
 
 Channel::~Channel() { grpc_channel_destroy(c_channel_); }
 
-Call Channel::CreateCall(const RpcMethod &method, ClientContext *context,
-                         CompletionQueue *cq) {
-  auto c_call =
-      grpc_channel_create_call(
-          c_channel_, cq->cq(), method.name(),
-          context->authority().empty() ? target_.c_str()
-                                       : context->authority().c_str(),
-          context->RawDeadline());
+Call Channel::CreateCall(const RpcMethod& method, ClientContext* context,
+                         CompletionQueue* cq) {
+  auto c_call = grpc_channel_create_call(c_channel_, cq->cq(), method.name(),
+                                         context->authority().empty()
+                                             ? target_.c_str()
+                                             : context->authority().c_str(),
+                                         context->RawDeadline());
   context->set_call(c_call);
   return Call(c_call, this, cq);
 }
 
-void Channel::PerformOpsOnCall(CallOpBuffer *buf, Call *call) {
+void Channel::PerformOpsOnCall(CallOpBuffer* buf, Call* call) {
   static const size_t MAX_OPS = 8;
   size_t nops = MAX_OPS;
   grpc_op ops[MAX_OPS];
diff --git a/src/cpp/client/channel.h b/src/cpp/client/channel.h
index 63c6e2bde6d4fc9c0cb286ad37fe526c86dd74d2..a1de3817e6947773dbdff3d9c534f094d2132420 100644
--- a/src/cpp/client/channel.h
+++ b/src/cpp/client/channel.h
@@ -51,10 +51,7 @@ class StreamContextInterface;
 
 class Channel GRPC_FINAL : public ChannelInterface {
  public:
-  Channel(const grpc::string &target, const ChannelArguments &args);
-  Channel(const grpc::string &target, const std::unique_ptr<Credentials> &creds,
-          const ChannelArguments &args);
-
+  Channel(const grpc::string &target, grpc_channel *c_channel);
   ~Channel() GRPC_OVERRIDE;
 
   virtual Call CreateCall(const RpcMethod &method, ClientContext *context,
@@ -63,7 +60,7 @@ class Channel GRPC_FINAL : public ChannelInterface {
 
  private:
   const grpc::string target_;
-  grpc_channel *c_channel_;  // owned
+  grpc_channel *const c_channel_;  // owned
 };
 
 }  // namespace grpc
diff --git a/src/cpp/client/client_unary_call.cc b/src/cpp/client/client_unary_call.cc
index 684b3cbadb49a6c33fa029fd296e1dac2b91b3cf..5c179de9d8feffeba5834811321aed6df71c4cdd 100644
--- a/src/cpp/client/client_unary_call.cc
+++ b/src/cpp/client/client_unary_call.cc
@@ -44,8 +44,8 @@ namespace grpc {
 // Wrapper that performs a blocking unary call
 Status BlockingUnaryCall(ChannelInterface *channel, const RpcMethod &method,
                          ClientContext *context,
-                         const google::protobuf::Message &request,
-                         google::protobuf::Message *result) {
+                         const grpc::protobuf::Message &request,
+                         grpc::protobuf::Message *result) {
   CompletionQueue cq;
   Call call(channel->CreateCall(method, context, &cq));
   CallOpBuffer buf;
diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc
index 583e072799bfd4a379772de76b07555f9c3d2397..57d215d0f333a8a91c1bfe4755fe45eb17cc9098 100644
--- a/src/cpp/client/create_channel.cc
+++ b/src/cpp/client/create_channel.cc
@@ -40,14 +40,10 @@
 namespace grpc {
 class ChannelArguments;
 
-std::shared_ptr<ChannelInterface> CreateChannelDeprecated(
-    const grpc::string &target, const ChannelArguments &args) {
-  return std::shared_ptr<ChannelInterface>(new Channel(target, args));
-}
-
 std::shared_ptr<ChannelInterface> CreateChannel(
     const grpc::string &target, const std::unique_ptr<Credentials> &creds,
     const ChannelArguments &args) {
-  return std::shared_ptr<ChannelInterface>(new Channel(target, creds, args));
+  return creds ? creds->CreateChannel(target, args) : 
+  	std::shared_ptr<ChannelInterface>(new Channel(target, grpc_lame_client_channel_create()));
 }
 }  // namespace grpc
diff --git a/src/cpp/client/credentials.cc b/src/cpp/client/credentials.cc
index a140f551e0dffc918223cf4deb3b8fd2ee9f9238..e8062849887c0582f713b8fca79f1a6ab6923383 100644
--- a/src/cpp/client/credentials.cc
+++ b/src/cpp/client/credentials.cc
@@ -31,97 +31,10 @@
  *
  */
 
-#include <string>
-
-#include <grpc/grpc_security.h>
-#include <grpc/support/log.h>
-
 #include <grpc++/credentials.h>
 
 namespace grpc {
 
-Credentials::Credentials(grpc_credentials *c_creds) : creds_(c_creds) {}
-
-Credentials::~Credentials() { grpc_credentials_release(creds_); }
-grpc_credentials *Credentials::GetRawCreds() { return creds_; }
-
-std::unique_ptr<Credentials> CredentialsFactory::GoogleDefaultCredentials() {
-  grpc_credentials *c_creds = grpc_google_default_credentials_create();
-  std::unique_ptr<Credentials> cpp_creds(new Credentials(c_creds));
-  return cpp_creds;
-}
-
-// Builds SSL Credentials given SSL specific options
-std::unique_ptr<Credentials> CredentialsFactory::SslCredentials(
-    const SslCredentialsOptions &options) {
-  grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
-      options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
-
-  grpc_credentials *c_creds = grpc_ssl_credentials_create(
-      options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
-      options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair);
-  std::unique_ptr<Credentials> cpp_creds(
-      c_creds == nullptr ? nullptr : new Credentials(c_creds));
-  return cpp_creds;
-}
-
-// Builds credentials for use when running in GCE
-std::unique_ptr<Credentials> CredentialsFactory::ComputeEngineCredentials() {
-  grpc_credentials *c_creds = grpc_compute_engine_credentials_create();
-  std::unique_ptr<Credentials> cpp_creds(
-      c_creds == nullptr ? nullptr : new Credentials(c_creds));
-  return cpp_creds;
-}
-
-// Builds service account credentials.
-std::unique_ptr<Credentials> CredentialsFactory::ServiceAccountCredentials(
-    const grpc::string &json_key, const grpc::string &scope,
-    std::chrono::seconds token_lifetime) {
-  gpr_timespec lifetime = gpr_time_from_seconds(
-      token_lifetime.count() > 0 ? token_lifetime.count() : 0);
-  grpc_credentials *c_creds = grpc_service_account_credentials_create(
-      json_key.c_str(), scope.c_str(), lifetime);
-  std::unique_ptr<Credentials> cpp_creds(
-      c_creds == nullptr ? nullptr : new Credentials(c_creds));
-  return cpp_creds;
-}
-
-// Builds JWT credentials.
-std::unique_ptr<Credentials> CredentialsFactory::JWTCredentials(
-    const grpc::string &json_key, std::chrono::seconds token_lifetime) {
-  gpr_timespec lifetime = gpr_time_from_seconds(
-      token_lifetime.count() > 0 ? token_lifetime.count() : 0);
-  grpc_credentials *c_creds =
-      grpc_jwt_credentials_create(json_key.c_str(), lifetime);
-  std::unique_ptr<Credentials> cpp_creds(
-      c_creds == nullptr ? nullptr : new Credentials(c_creds));
-  return cpp_creds;
-}
-
-// Builds IAM credentials.
-std::unique_ptr<Credentials> CredentialsFactory::IAMCredentials(
-    const grpc::string &authorization_token,
-    const grpc::string &authority_selector) {
-  grpc_credentials *c_creds = grpc_iam_credentials_create(
-      authorization_token.c_str(), authority_selector.c_str());
-  std::unique_ptr<Credentials> cpp_creds(
-      c_creds == nullptr ? nullptr : new Credentials(c_creds));
-  return cpp_creds;
-}
-
-// Combines two credentials objects into a composite credentials.
-std::unique_ptr<Credentials> CredentialsFactory::CompositeCredentials(
-    const std::unique_ptr<Credentials> &creds1,
-    const std::unique_ptr<Credentials> &creds2) {
-  // Note that we are not saving unique_ptrs to the two credentials
-  // passed in here. This is OK because the underlying C objects (i.e.,
-  // creds1 and creds2) into grpc_composite_credentials_create will see their
-  // refcounts incremented.
-  grpc_credentials *c_creds = grpc_composite_credentials_create(
-      creds1->GetRawCreds(), creds2->GetRawCreds());
-  std::unique_ptr<Credentials> cpp_creds(
-      c_creds == nullptr ? nullptr : new Credentials(c_creds));
-  return cpp_creds;
-}
+Credentials::~Credentials() {}
 
 }  // namespace grpc
diff --git a/src/core/surface/secure_server_create.c b/src/cpp/client/insecure_credentials.cc
similarity index 66%
rename from src/core/surface/secure_server_create.c
rename to src/cpp/client/insecure_credentials.cc
index 1d5b9279977cbe2bd40da3082905b564d5ef9eaa..2dcfe69591aef588056d20a5e172210bed97d808 100644
--- a/src/core/surface/secure_server_create.c
+++ b/src/cpp/client/insecure_credentials.cc
@@ -31,27 +31,35 @@
  *
  */
 
-#include <grpc/grpc.h>
+#include <string>
 
-#include "src/core/channel/channel_args.h"
-#include "src/core/security/security_context.h"
-#include "src/core/surface/completion_queue.h"
-#include "src/core/surface/server.h"
+#include <grpc/grpc.h>
 #include <grpc/support/log.h>
 
-grpc_server *grpc_secure_server_create_internal(
-    grpc_completion_queue *cq, const grpc_channel_args *args,
-    grpc_security_context *context) {
-  grpc_arg context_arg;
-  grpc_channel_args *args_copy;
-  grpc_server *server;
-  if (grpc_find_security_context_in_args(args) != NULL) {
-    gpr_log(GPR_ERROR, "Cannot set security context in channel args.");
+#include <grpc++/channel_arguments.h>
+#include <grpc++/config.h>
+#include <grpc++/credentials.h>
+#include "src/cpp/client/channel.h"
+
+namespace grpc {
+
+namespace {
+class InsecureCredentialsImpl GRPC_FINAL : public Credentials {
+ public:
+  std::shared_ptr<grpc::ChannelInterface> CreateChannel(
+      const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE {
+    grpc_channel_args channel_args;
+    args.SetChannelArgs(&channel_args);
+    return std::shared_ptr<ChannelInterface>(new Channel(
+        target, grpc_channel_create(target.c_str(), &channel_args)));
   }
 
-  context_arg = grpc_security_context_to_arg(context);
-  args_copy = grpc_channel_args_copy_and_add(args, &context_arg);
-  server = grpc_server_create_from_filters(cq, NULL, 0, args_copy);
-  grpc_channel_args_destroy(args_copy);
-  return server;
+  SecureCredentials* AsSecureCredentials() { return nullptr; }
+};
+}  // namespace
+
+std::unique_ptr<Credentials> InsecureCredentials() {
+  return std::unique_ptr<Credentials>(new InsecureCredentialsImpl());
 }
+
+}  // namespace grpc
diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc
new file mode 100644
index 0000000000000000000000000000000000000000..47f645c1b6366d9698470a5f79ce974df588648a
--- /dev/null
+++ b/src/cpp/client/secure_credentials.cc
@@ -0,0 +1,132 @@
+/*
+ *
+ * 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 <string>
+
+#include <grpc/grpc_security.h>
+#include <grpc/support/log.h>
+
+#include <grpc++/channel_arguments.h>
+#include <grpc++/config.h>
+#include <grpc++/credentials.h>
+#include "src/cpp/client/channel.h"
+
+namespace grpc {
+
+class SecureCredentials GRPC_FINAL : public Credentials {
+ public:
+  explicit SecureCredentials(grpc_credentials* c_creds) : c_creds_(c_creds) {}
+  ~SecureCredentials() GRPC_OVERRIDE { grpc_credentials_release(c_creds_); }
+  grpc_credentials* GetRawCreds() { return c_creds_; }
+
+  std::shared_ptr<grpc::ChannelInterface> CreateChannel(
+      const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE {
+    grpc_channel_args channel_args;
+    args.SetChannelArgs(&channel_args);
+    return std::shared_ptr<ChannelInterface>(new Channel(
+        args.GetSslTargetNameOverride().empty()
+            ? target : args.GetSslTargetNameOverride(),
+        grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args)));
+  }
+
+  SecureCredentials* AsSecureCredentials() { return this; }
+
+ private:
+  grpc_credentials* const c_creds_;
+};
+
+namespace {
+std::unique_ptr<Credentials> WrapCredentials(grpc_credentials* creds) {
+  return creds == nullptr
+             ? nullptr
+             : std::unique_ptr<Credentials>(new SecureCredentials(creds));
+}
+}  // namespace
+
+std::unique_ptr<Credentials> GoogleDefaultCredentials() {
+  return WrapCredentials(grpc_google_default_credentials_create());
+}
+
+// Builds SSL Credentials given SSL specific options
+std::unique_ptr<Credentials> SslCredentials(
+    const SslCredentialsOptions& options) {
+  grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
+      options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
+
+  grpc_credentials* c_creds = grpc_ssl_credentials_create(
+      options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
+      options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair);
+  return WrapCredentials(c_creds);
+}
+
+// Builds credentials for use when running in GCE
+std::unique_ptr<Credentials> ComputeEngineCredentials() {
+  return WrapCredentials(grpc_compute_engine_credentials_create());
+}
+
+// Builds service account credentials.
+std::unique_ptr<Credentials> ServiceAccountCredentials(
+    const grpc::string& json_key, const grpc::string& scope,
+    std::chrono::seconds token_lifetime) {
+  gpr_timespec lifetime = gpr_time_from_seconds(
+      token_lifetime.count() > 0 ? token_lifetime.count() : 0);
+  return WrapCredentials(grpc_service_account_credentials_create(
+      json_key.c_str(), scope.c_str(), lifetime));
+}
+
+// Builds IAM credentials.
+std::unique_ptr<Credentials> IAMCredentials(
+    const grpc::string& authorization_token,
+    const grpc::string& authority_selector) {
+  return WrapCredentials(grpc_iam_credentials_create(
+      authorization_token.c_str(), authority_selector.c_str()));
+}
+
+// Combines two credentials objects into a composite credentials.
+std::unique_ptr<Credentials> CompositeCredentials(
+    const std::unique_ptr<Credentials>& creds1,
+    const std::unique_ptr<Credentials>& creds2) {
+  // Note that we are not saving unique_ptrs to the two credentials
+  // passed in here. This is OK because the underlying C objects (i.e.,
+  // creds1 and creds2) into grpc_composite_credentials_create will see their
+  // refcounts incremented.
+  SecureCredentials* s1 = creds1->AsSecureCredentials();
+  SecureCredentials* s2 = creds2->AsSecureCredentials();
+  if (s1 && s2) {
+    return WrapCredentials(grpc_composite_credentials_create(
+        s1->GetRawCreds(), s2->GetRawCreds()));
+  }
+  return nullptr;
+}
+
+}  // namespace grpc
diff --git a/src/cpp/common/call.cc b/src/cpp/common/call.cc
index f3a691114d2453a1a325277dedc04efbd28ac613..6ce1e8a7d580eb30eab36afce5e08af53737e854 100644
--- a/src/cpp/common/call.cc
+++ b/src/cpp/common/call.cc
@@ -31,7 +31,6 @@
  *
  */
 
-#include <google/protobuf/message.h>
 #include <grpc/support/alloc.h>
 #include <grpc++/impl/call.h>
 #include <grpc++/client_context.h>
@@ -163,11 +162,11 @@ void CallOpBuffer::AddSendInitialMetadata(ClientContext* ctx) {
   AddSendInitialMetadata(&ctx->send_initial_metadata_);
 }
 
-void CallOpBuffer::AddSendMessage(const google::protobuf::Message& message) {
+void CallOpBuffer::AddSendMessage(const grpc::protobuf::Message& message) {
   send_message_ = &message;
 }
 
-void CallOpBuffer::AddRecvMessage(google::protobuf::Message* message) {
+void CallOpBuffer::AddRecvMessage(grpc::protobuf::Message* message) {
   recv_message_ = message;
   recv_message_->Clear();
 }
diff --git a/src/cpp/proto/proto_utils.cc b/src/cpp/proto/proto_utils.cc
index 69a6bb080e0abed0b637ab443118955528bf9dbb..e4e51bfebf5e78d048cf6ce447b105307dbc48b1 100644
--- a/src/cpp/proto/proto_utils.cc
+++ b/src/cpp/proto/proto_utils.cc
@@ -35,39 +35,135 @@
 #include <grpc++/config.h>
 
 #include <grpc/grpc.h>
+#include <grpc/byte_buffer.h>
 #include <grpc/support/slice.h>
-#include <google/protobuf/message.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/port_platform.h>
+#include <google/protobuf/io/zero_copy_stream.h>
 
-namespace grpc {
+const int kMaxBufferLength = 8192;
 
-bool SerializeProto(const google::protobuf::Message &msg,
-                    grpc_byte_buffer **bp) {
-  grpc::string msg_str;
-  bool success = msg.SerializeToString(&msg_str);
-  if (success) {
-    gpr_slice slice =
-        gpr_slice_from_copied_buffer(msg_str.data(), msg_str.length());
-    *bp = grpc_byte_buffer_create(&slice, 1);
-    gpr_slice_unref(slice);
+class GrpcBufferWriter GRPC_FINAL
+    : public ::google::protobuf::io::ZeroCopyOutputStream {
+ public:
+  explicit GrpcBufferWriter(grpc_byte_buffer **bp,
+                            int block_size = kMaxBufferLength)
+      : block_size_(block_size), byte_count_(0), have_backup_(false) {
+    *bp = grpc_byte_buffer_create(NULL, 0);
+    slice_buffer_ = &(*bp)->data.slice_buffer;
+  }
+
+  ~GrpcBufferWriter() GRPC_OVERRIDE {
+    if (have_backup_) {
+      gpr_slice_unref(backup_slice_);
+    }
+  }
+
+  bool Next(void **data, int *size) GRPC_OVERRIDE {
+    if (have_backup_) {
+      slice_ = backup_slice_;
+      have_backup_ = false;
+    } else {
+      slice_ = gpr_slice_malloc(block_size_);
+    }
+    *data = GPR_SLICE_START_PTR(slice_);
+    byte_count_ += *size = GPR_SLICE_LENGTH(slice_);
+    gpr_slice_buffer_add(slice_buffer_, slice_);
+    return true;
+  }
+
+  void BackUp(int count) GRPC_OVERRIDE {
+    gpr_slice_buffer_pop(slice_buffer_);
+    if (count == block_size_) {
+      backup_slice_ = slice_;
+    } else {
+      backup_slice_ =
+          gpr_slice_split_tail(&slice_, GPR_SLICE_LENGTH(slice_) - count);
+      gpr_slice_buffer_add(slice_buffer_, slice_);
+    }
+    have_backup_ = true;
+    byte_count_ -= count;
+  }
+
+  gpr_int64 ByteCount() const GRPC_OVERRIDE { return byte_count_; }
+
+ private:
+  const int block_size_;
+  gpr_int64 byte_count_;
+  gpr_slice_buffer *slice_buffer_;
+  bool have_backup_;
+  gpr_slice backup_slice_;
+  gpr_slice slice_;
+};
+
+class GrpcBufferReader GRPC_FINAL
+    : public ::google::protobuf::io::ZeroCopyInputStream {
+ public:
+  explicit GrpcBufferReader(grpc_byte_buffer *buffer)
+      : byte_count_(0), backup_count_(0) {
+    reader_ = grpc_byte_buffer_reader_create(buffer);
+  }
+  ~GrpcBufferReader() GRPC_OVERRIDE {
+    grpc_byte_buffer_reader_destroy(reader_);
   }
-  return success;
-}
 
-bool DeserializeProto(grpc_byte_buffer *buffer,
-                      google::protobuf::Message *msg) {
-  grpc::string msg_string;
-  grpc_byte_buffer_reader *reader = grpc_byte_buffer_reader_create(buffer);
-  gpr_slice slice;
-  while (grpc_byte_buffer_reader_next(reader, &slice)) {
-    const char *data = reinterpret_cast<const char *>(
-        slice.refcount ? slice.data.refcounted.bytes
-                       : slice.data.inlined.bytes);
-    msg_string.append(data, slice.refcount ? slice.data.refcounted.length
-                                           : slice.data.inlined.length);
-    gpr_slice_unref(slice);
+  bool Next(const void **data, int *size) GRPC_OVERRIDE {
+    if (backup_count_ > 0) {
+      *data = GPR_SLICE_START_PTR(slice_) + GPR_SLICE_LENGTH(slice_) -
+              backup_count_;
+      *size = backup_count_;
+      backup_count_ = 0;
+      return true;
+    }
+    if (!grpc_byte_buffer_reader_next(reader_, &slice_)) {
+      return false;
+    }
+    gpr_slice_unref(slice_);
+    *data = GPR_SLICE_START_PTR(slice_);
+    byte_count_ += *size = GPR_SLICE_LENGTH(slice_);
+    return true;
   }
-  grpc_byte_buffer_reader_destroy(reader);
-  return msg->ParseFromString(msg_string);
+
+  void BackUp(int count) GRPC_OVERRIDE {
+    backup_count_ = count;
+  }
+
+  bool Skip(int count) GRPC_OVERRIDE {
+    const void *data;
+    int size;
+    while (Next(&data, &size)) {
+      if (size >= count) {
+        BackUp(size - count);
+        return true;
+      }
+      // size < count;
+      count -= size;
+    }
+    // error or we have too large count;
+    return false;
+  }
+
+  gpr_int64 ByteCount() const GRPC_OVERRIDE {
+    return byte_count_ - backup_count_;
+  }
+
+ private:
+  gpr_int64 byte_count_;
+  gpr_int64 backup_count_;
+  grpc_byte_buffer_reader *reader_;
+  gpr_slice slice_;
+};
+
+namespace grpc {
+
+bool SerializeProto(const grpc::protobuf::Message &msg, grpc_byte_buffer **bp) {
+  GrpcBufferWriter writer(bp);
+  return msg.SerializeToZeroCopyStream(&writer);
+}
+
+bool DeserializeProto(grpc_byte_buffer *buffer, grpc::protobuf::Message *msg) {
+  GrpcBufferReader reader(buffer);
+  return msg->ParseFromZeroCopyStream(&reader);
 }
 
 }  // namespace grpc
diff --git a/src/cpp/proto/proto_utils.h b/src/cpp/proto/proto_utils.h
index a0af4d6465d6e0cac71008f3dbf63eb89b82b26b..7a1b1f8b7cb289776c681d72b6a1feca1234de39 100644
--- a/src/cpp/proto/proto_utils.h
+++ b/src/cpp/proto/proto_utils.h
@@ -34,23 +34,20 @@
 #ifndef GRPC_INTERNAL_CPP_PROTO_PROTO_UTILS_H
 #define GRPC_INTERNAL_CPP_PROTO_PROTO_UTILS_H
 
+#include <grpc++/config.h>
+
 struct grpc_byte_buffer;
-namespace google {
-namespace protobuf {
-class Message;
-}
-}
 
 namespace grpc {
 
 // Serialize the msg into a buffer created inside the function. The caller
 // should destroy the returned buffer when done with it. If serialization fails,
 // false is returned and buffer is left unchanged.
-bool SerializeProto(const google::protobuf::Message &msg,
+bool SerializeProto(const grpc::protobuf::Message &msg,
                     grpc_byte_buffer **buffer);
 
 // The caller keeps ownership of buffer and msg.
-bool DeserializeProto(grpc_byte_buffer *buffer, google::protobuf::Message *msg);
+bool DeserializeProto(grpc_byte_buffer *buffer, grpc::protobuf::Message *msg);
 
 }  // namespace grpc
 
diff --git a/src/cpp/server/async_server_context.cc b/src/cpp/server/async_server_context.cc
index 5f8c2ba10f4cd5511d0a3216c2d920370f48622f..f21efcfb19ac2c0b82986ce88bfcf9127b093c76 100644
--- a/src/cpp/server/async_server_context.cc
+++ b/src/cpp/server/async_server_context.cc
@@ -36,7 +36,7 @@
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 #include "src/cpp/proto/proto_utils.h"
-#include <google/protobuf/message.h>
+#include <grpc++/config.h>
 #include <grpc++/status.h>
 
 namespace grpc {
@@ -58,14 +58,14 @@ void AsyncServerContext::Accept(grpc_completion_queue *cq) {
                  call_, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
 }
 
-bool AsyncServerContext::StartRead(google::protobuf::Message *request) {
+bool AsyncServerContext::StartRead(grpc::protobuf::Message *request) {
   GPR_ASSERT(request);
   request_ = request;
   grpc_call_error err = grpc_call_start_read_old(call_, this);
   return err == GRPC_CALL_OK;
 }
 
-bool AsyncServerContext::StartWrite(const google::protobuf::Message &response,
+bool AsyncServerContext::StartWrite(const grpc::protobuf::Message &response,
                                     int flags) {
   grpc_byte_buffer *buffer = nullptr;
   if (!SerializeProto(response, &buffer)) {
diff --git a/src/cpp/server/insecure_server_credentials.cc b/src/cpp/server/insecure_server_credentials.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f5e4732f73050bcf9db1b78655db2ed7a6c68cc4
--- /dev/null
+++ b/src/cpp/server/insecure_server_credentials.cc
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/grpc_security.h>
+#include <grpc++/server_credentials.h>
+
+namespace grpc {
+namespace {
+class InsecureServerCredentialsImpl GRPC_FINAL : public ServerCredentials {
+ public:
+  int AddPortToServer(const grpc::string& addr,
+                      grpc_server* server) GRPC_OVERRIDE {
+    return grpc_server_add_http2_port(server, addr.c_str());
+  }
+};
+}  // namespace
+
+std::shared_ptr<ServerCredentials> InsecureServerCredentials() {
+  return std::shared_ptr<ServerCredentials>(new InsecureServerCredentialsImpl());
+}
+
+}  // namespace grpc
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ff356385034e7a4a10a43b8134e151abf115ad1d
--- /dev/null
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/grpc_security.h>
+
+#include <grpc++/server_credentials.h>
+
+namespace grpc {
+
+namespace {
+class SecureServerCredentials GRPC_FINAL : public ServerCredentials {
+ public:
+  explicit SecureServerCredentials(grpc_server_credentials* creds) : creds_(creds) {}
+  ~SecureServerCredentials() GRPC_OVERRIDE {
+    grpc_server_credentials_release(creds_);
+  }
+
+  int AddPortToServer(const grpc::string& addr,
+                      grpc_server* server) GRPC_OVERRIDE {
+    return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
+  }
+
+ private:
+  grpc_server_credentials* const creds_;
+};
+}  // namespace
+
+std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const SslServerCredentialsOptions &options) {
+  std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
+  for (const auto &key_cert_pair : options.pem_key_cert_pairs) {
+    pem_key_cert_pairs.push_back(
+        {key_cert_pair.private_key.c_str(), key_cert_pair.cert_chain.c_str()});
+  }
+  grpc_server_credentials *c_creds = grpc_ssl_server_credentials_create(
+      options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
+      &pem_key_cert_pairs[0], pem_key_cert_pairs.size());
+  return std::shared_ptr<ServerCredentials>(new SecureServerCredentials(c_creds));
+}
+
+}  // namespace grpc
diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc
index 97bf0f1a6ea8ce9a15a6fb901770a6b197fa6fa7..e69032a657d27f4c200c69bf422ae50f692aac47 100644
--- a/src/cpp/server/server.cc
+++ b/src/cpp/server/server.cc
@@ -117,8 +117,8 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
     }
 
     void Run() {
-      std::unique_ptr<google::protobuf::Message> req;
-      std::unique_ptr<google::protobuf::Message> res;
+      std::unique_ptr<grpc::protobuf::Message> req;
+      std::unique_ptr<grpc::protobuf::Message> res;
       if (has_request_payload_) {
         req.reset(method_->AllocateRequestProto());
         if (!DeserializeProto(request_payload_, req.get())) {
@@ -170,26 +170,13 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
   grpc_completion_queue* cq_;
 };
 
-Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
-               ServerCredentials* creds)
+Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned)
     : started_(false),
       shutdown_(false),
       num_running_cb_(0),
+      server_(grpc_server_create(cq_.cq(), nullptr)),
       thread_pool_(thread_pool),
-      thread_pool_owned_(thread_pool_owned),
-      secure_(creds != nullptr) {
-  if (creds) {
-    server_ =
-        grpc_secure_server_create(creds->GetRawCreds(), cq_.cq(), nullptr);
-  } else {
-    server_ = grpc_server_create(cq_.cq(), nullptr);
-  }
-}
-
-Server::Server() {
-  // Should not be called.
-  GPR_ASSERT(false);
-}
+      thread_pool_owned_(thread_pool_owned) {}
 
 Server::~Server() {
   std::unique_lock<std::mutex> lock(mu_);
@@ -239,13 +226,9 @@ bool Server::RegisterAsyncService(AsynchronousService* service) {
   return true;
 }
 
-int Server::AddPort(const grpc::string& addr) {
+int Server::AddPort(const grpc::string& addr, ServerCredentials* creds) {
   GPR_ASSERT(!started_);
-  if (secure_) {
-    return grpc_server_add_secure_http2_port(server_, addr.c_str());
-  } else {
-    return grpc_server_add_http2_port(server_, addr.c_str());
-  }
+  return creds->AddPortToServer(addr, server_);
 }
 
 bool Server::Start() {
@@ -298,7 +281,7 @@ void Server::PerformOpsOnCall(CallOpBuffer* buf, Call* call) {
 class Server::AsyncRequest GRPC_FINAL : public CompletionQueueTag {
  public:
   AsyncRequest(Server* server, void* registered_method, ServerContext* ctx,
-               ::google::protobuf::Message* request,
+               grpc::protobuf::Message* request,
                ServerAsyncStreamingInterface* stream, CompletionQueue* cq,
                void* tag)
       : tag_(tag),
@@ -324,6 +307,7 @@ class Server::AsyncRequest GRPC_FINAL : public CompletionQueueTag {
 
   bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
     *tag = tag_;
+    bool orig_status = *status;
     if (*status && request_) {
       if (payload_) {
         *status = DeserializeProto(payload_, request_);
@@ -343,7 +327,9 @@ class Server::AsyncRequest GRPC_FINAL : public CompletionQueueTag {
     }
     ctx_->call_ = call_;
     Call call(call_, server_, cq_);
-    ctx_->BeginCompletionOp(&call);
+    if (orig_status && call_) {
+      ctx_->BeginCompletionOp(&call);
+    }
     // just the pointers inside call are copied here
     stream_->BindCall(&call);
     delete this;
@@ -352,7 +338,7 @@ class Server::AsyncRequest GRPC_FINAL : public CompletionQueueTag {
 
  private:
   void* const tag_;
-  ::google::protobuf::Message* const request_;
+  grpc::protobuf::Message* const request_;
   ServerAsyncStreamingInterface* const stream_;
   CompletionQueue* const cq_;
   ServerContext* const ctx_;
@@ -364,7 +350,7 @@ class Server::AsyncRequest GRPC_FINAL : public CompletionQueueTag {
 };
 
 void Server::RequestAsyncCall(void* registered_method, ServerContext* context,
-                              ::google::protobuf::Message* request,
+                              grpc::protobuf::Message* request,
                               ServerAsyncStreamingInterface* stream,
                               CompletionQueue* cq, void* tag) {
   new AsyncRequest(this, registered_method, context, request, stream, cq, tag);
diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc
index ae60f3d8b6743b0fdaec55b144aad75321d748e8..5de592334d76c66ddeaf5dc759e8869486ac1e63 100644
--- a/src/cpp/server/server_builder.cc
+++ b/src/cpp/server/server_builder.cc
@@ -51,14 +51,10 @@ void ServerBuilder::RegisterAsyncService(AsynchronousService* service) {
   async_services_.push_back(service);
 }
 
-void ServerBuilder::AddPort(const grpc::string& addr) {
-  ports_.push_back(addr);
-}
-
-void ServerBuilder::SetCredentials(
-    const std::shared_ptr<ServerCredentials>& creds) {
-  GPR_ASSERT(!creds_);
-  creds_ = creds;
+void ServerBuilder::AddPort(const grpc::string& addr,
+                            std::shared_ptr<ServerCredentials> creds,
+                            int* selected_port) {
+  ports_.push_back(Port{addr, creds, selected_port});
 }
 
 void ServerBuilder::SetThreadPool(ThreadPoolInterface* thread_pool) {
@@ -71,14 +67,13 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
     gpr_log(GPR_ERROR, "Mixing async and sync services is unsupported for now");
     return nullptr;
   }
-  if (!thread_pool_ && services_.size()) {
+  if (!thread_pool_ && !services_.empty()) {
     int cores = gpr_cpu_num_cores();
     if (!cores) cores = 4;
     thread_pool_ = new ThreadPool(cores);
     thread_pool_owned = true;
   }
-  std::unique_ptr<Server> server(
-      new Server(thread_pool_, thread_pool_owned, creds_.get()));
+  std::unique_ptr<Server> server(new Server(thread_pool_, thread_pool_owned));
   for (auto* service : services_) {
     if (!server->RegisterService(service)) {
       return nullptr;
@@ -90,8 +85,10 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
     }
   }
   for (auto& port : ports_) {
-    if (!server->AddPort(port)) {
-      return nullptr;
+    int r = server->AddPort(port.addr, port.creds.get());
+    if (!r) return nullptr;
+    if (port.selected_port != nullptr) {
+      *port.selected_port = r;
     }
   }
   if (!server->Start()) {
diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc
index 69ad000ccc69f2d433910c52dc421466b6e193f4..6bdb465baaa5cd4e3b808f3ea3724c84eb6f7922 100644
--- a/src/cpp/server/server_credentials.cc
+++ b/src/cpp/server/server_credentials.cc
@@ -37,26 +37,6 @@
 
 namespace grpc {
 
-ServerCredentials::ServerCredentials(grpc_server_credentials *c_creds)
-    : creds_(c_creds) {}
-
-ServerCredentials::~ServerCredentials() {
-  grpc_server_credentials_release(creds_);
-}
-
-grpc_server_credentials *ServerCredentials::GetRawCreds() { return creds_; }
-
-std::shared_ptr<ServerCredentials> ServerCredentialsFactory::SslCredentials(
-    const SslServerCredentialsOptions &options) {
-  std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
-  for (const auto &key_cert_pair : options.pem_key_cert_pairs) {
-    pem_key_cert_pairs.push_back(
-        {key_cert_pair.private_key.c_str(), key_cert_pair.cert_chain.c_str()});
-  }
-  grpc_server_credentials *c_creds = grpc_ssl_server_credentials_create(
-      options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
-      &pem_key_cert_pairs[0], pem_key_cert_pairs.size());
-  return std::shared_ptr<ServerCredentials>(new ServerCredentials(c_creds));
-}
+ServerCredentials::~ServerCredentials() {}
 
 }  // namespace grpc
diff --git a/src/csharp/.gitignore b/src/csharp/.gitignore
index d35ff63f6ef4756198b8e7fe344fa50fa03788ee..dbaf60de0cf6dfbeb27fa4948a42a70982b10ef6 100644
--- a/src/csharp/.gitignore
+++ b/src/csharp/.gitignore
@@ -1,4 +1,6 @@
 *.userprefs
+StyleCop.Cache
 test-results
 packages
 Grpc.v12.suo
+TestResult.xml
diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
index 282d521ba3798004837e5c593d268c1103cd39f0..9db08d2f02f7ba5042a694cdd30ef065eedf1c9a 100644
--- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
+++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
@@ -127,8 +127,6 @@ namespace Grpc.Core.Tests
         [Test]
         public void NopPInvokeBenchmark()
         {
-            CompletionCallbackDelegate handler = Handler;
-
             BenchmarkUtil.RunBenchmark(
                 1000000, 100000000,
                 () => {
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index 942651cf3935a3cf3a567573e5c87aa5c35fd64a..83d965debf12a097874e97f026195f91e5605a01 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,9 +27,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
-
 using System;
 using System.Runtime.InteropServices;
 using System.Threading;
@@ -39,18 +36,32 @@ using Grpc.Core.Internal;
 
 namespace Grpc.Core
 {
-	public class Channel : IDisposable
-	{
+    public class Channel : IDisposable
+    {
         readonly ChannelSafeHandle handle;
         readonly String target;
 
-        // TODO: add way how to create grpc_secure_channel....
-		// TODO: add support for channel args...
-		public Channel(string target)
-		{
-            this.handle = ChannelSafeHandle.Create(target, IntPtr.Zero);
-			this.target = target;
-		}
+        /// <summary>
+        /// Creates a channel.
+        /// </summary>
+        public Channel(string target, Credentials credentials = null, ChannelArgs channelArgs = null)
+        {
+            using (ChannelArgsSafeHandle nativeChannelArgs = CreateNativeChannelArgs(channelArgs))
+            {
+                if (credentials != null)
+                {
+                    using (CredentialsSafeHandle nativeCredentials = credentials.ToNativeCredentials())
+                    {
+                        this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs);
+                    }
+                }
+                else
+                {
+                    this.handle = ChannelSafeHandle.Create(target, nativeChannelArgs);
+                }
+            }
+            this.target = GetOverridenTarget(target, channelArgs);
+        }
 
         internal ChannelSafeHandle Handle
         {
@@ -81,5 +92,23 @@ namespace Grpc.Core
                 handle.Dispose();
             }
         }
-	}
+
+        private static string GetOverridenTarget(string target, ChannelArgs args)
+        {
+            if (args != null && !string.IsNullOrEmpty(args.GetSslTargetNameOverride()))
+            {
+                return args.GetSslTargetNameOverride();
+            }
+            return target;
+        }
+
+        private static ChannelArgsSafeHandle CreateNativeChannelArgs(ChannelArgs args)
+        {
+            if (args == null)
+            {
+                return ChannelArgsSafeHandle.CreateNull();
+            }
+            return args.ToNativeChannelArgs();
+        }
+    }
 }
diff --git a/src/csharp/Grpc.Core/ChannelArgs.cs b/src/csharp/Grpc.Core/ChannelArgs.cs
new file mode 100644
index 0000000000000000000000000000000000000000..298b6edf2066f78e031ea6ca990a10bd47961364
--- /dev/null
+++ b/src/csharp/Grpc.Core/ChannelArgs.cs
@@ -0,0 +1,112 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core
+{
+    // TODO: should we be using the builder pattern?
+    public class ChannelArgs
+    {
+        public const string SslTargetNameOverrideKey = "grpc.ssl_target_name_override";
+
+        public class Builder
+        {
+            Dictionary<string,string> stringArgs = new Dictionary<string,string>();
+            // TODO: AddInteger not supported yet.
+            public Builder AddString(string key, string value)
+            {
+                stringArgs.Add(key, value);
+                return this;
+            }
+
+            public ChannelArgs Build()
+            {
+                return new ChannelArgs(stringArgs);
+            }
+        }
+
+        Dictionary<string,string> stringArgs;
+
+        private ChannelArgs(Dictionary<string, string> stringArgs)
+        {
+            // TODO: use immutable dict?
+            this.stringArgs = new Dictionary<string, string>(stringArgs);
+        }
+
+        public string GetSslTargetNameOverride()
+        {
+            string result;
+            if (stringArgs.TryGetValue(SslTargetNameOverrideKey, out result))
+            {
+                return result;
+            }
+            return null;
+        }
+
+        public static Builder NewBuilder()
+        {
+            return new Builder();
+        }
+
+        /// <summary>
+        /// Creates native object for the channel arguments.
+        /// </summary>
+        /// <returns>The native channel arguments.</returns>
+        internal ChannelArgsSafeHandle ToNativeChannelArgs()
+        {
+            ChannelArgsSafeHandle nativeArgs = null;
+            try
+            {
+                nativeArgs = ChannelArgsSafeHandle.Create(stringArgs.Count);
+                int i = 0;
+                foreach (var entry in stringArgs)
+                {
+                    nativeArgs.SetString(i, entry.Key, entry.Value);
+                    i++;
+                }
+                return nativeArgs;
+            }
+            catch (Exception)
+            {
+                if (nativeArgs != null)
+                {
+                    nativeArgs.Dispose();
+                }
+                throw;
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Credentials.cs b/src/csharp/Grpc.Core/Credentials.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5116c277f72a6880abb56a142b5876c1e16c9914
--- /dev/null
+++ b/src/csharp/Grpc.Core/Credentials.cs
@@ -0,0 +1,77 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core
+{
+    public abstract class Credentials
+    {
+        /// <summary>
+        /// Creates native object for the credentials.
+        /// </summary>
+        /// <returns>The native credentials.</returns>
+        internal abstract CredentialsSafeHandle ToNativeCredentials();
+    }
+
+    /// <summary>
+    /// Client-side SSL credentials.
+    /// </summary>
+    public class SslCredentials : Credentials
+    {
+        string pemRootCerts;
+
+        public SslCredentials(string pemRootCerts)
+        {
+            this.pemRootCerts = pemRootCerts;
+        }
+
+        /// <summary>
+        /// PEM encoding of the server root certificates.
+        /// </summary>
+        public string RootCerts
+        {
+            get
+            {
+                return this.pemRootCerts;
+            }
+        }
+
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            return CredentialsSafeHandle.CreateSslCredentials(pemRootCerts);
+        }
+    }
+}
+
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 05d40d45a6e4c6614cbd728d55cf86caaa5d13b6..78b6cdde59bc11f7052a290d3d15c04e8141b310 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -51,7 +51,6 @@
     <Compile Include="Internal\SafeHandleZeroIsInvalid.cs" />
     <Compile Include="Internal\Timespec.cs" />
     <Compile Include="Internal\GrpcThreadPool.cs" />
-    <Compile Include="Internal\AsyncCall.cs" />
     <Compile Include="Internal\ServerSafeHandle.cs" />
     <Compile Include="Method.cs" />
     <Compile Include="ServerCalls.cs" />
@@ -65,6 +64,16 @@
     <Compile Include="Internal\BatchContextSafeHandleNotOwned.cs" />
     <Compile Include="Utils\BenchmarkUtil.cs" />
     <Compile Include="Utils\ExceptionHelper.cs" />
+    <Compile Include="Internal\CredentialsSafeHandle.cs" />
+    <Compile Include="Credentials.cs" />
+    <Compile Include="Internal\ChannelArgsSafeHandle.cs" />
+    <Compile Include="ChannelArgs.cs" />
+    <Compile Include="Internal\AsyncCompletion.cs" />
+    <Compile Include="Internal\AsyncCallBase.cs" />
+    <Compile Include="Internal\AsyncCallServer.cs" />
+    <Compile Include="OperationFailedException.cs" />
+    <Compile Include="Internal\AsyncCall.cs" />
+    <Compile Include="Utils\Preconditions.cs" />
   </ItemGroup>
   <Choose>
     <!-- Under older versions of Monodevelop, Choose is not supported and is just
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index 6f37b059f75dcc22b1d85b0dbf85ef7d95d68f38..5ae036298b00c16f032dc6832f1ba7bf91b76d7f 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -43,84 +43,47 @@ using Grpc.Core.Utils;
 namespace Grpc.Core.Internal
 {
     /// <summary>
-    /// Handles native call lifecycle and provides convenience methods.
+    /// Handles client side native call lifecycle.
     /// </summary>
-    internal class AsyncCall<TWrite, TRead>
+    internal class AsyncCall<TRequest, TResponse> : AsyncCallBase<TRequest, TResponse>
     {
-        readonly Func<TWrite, byte[]> serializer;
-        readonly Func<byte[], TRead> deserializer;
-
         readonly CompletionCallbackDelegate unaryResponseHandler;
         readonly CompletionCallbackDelegate finishedHandler;
-        readonly CompletionCallbackDelegate writeFinishedHandler;
-        readonly CompletionCallbackDelegate readFinishedHandler;
-        readonly CompletionCallbackDelegate halfclosedHandler;
-        readonly CompletionCallbackDelegate finishedServersideHandler;
-
-        object myLock = new object();
-        GCHandle gchandle;
-        CallSafeHandle call;
-        bool disposed;
-
-        bool server;
-
-        bool started;
-        bool errorOccured;
-        bool cancelRequested;
-        bool readingDone;
-        bool halfcloseRequested;
-        bool halfclosed;
-        bool finished;
-
-        // Completion of a pending write if not null.
-        TaskCompletionSource<object> writeTcs;
-
-        // Completion of a pending read if not null.
-        TaskCompletionSource<TRead> readTcs;
-
-        // Completion of a pending halfclose if not null.
-        TaskCompletionSource<object> halfcloseTcs;
 
         // Completion of a pending unary response if not null.
-        TaskCompletionSource<TRead> unaryResponseTcs;
+        TaskCompletionSource<TResponse> unaryResponseTcs;
 
-        // Set after status is received on client. Only used for server streaming and duplex streaming calls.
+        // Set after status is received. Only used for streaming response calls.
         Nullable<Status> finishedStatus;
-        TaskCompletionSource<object> finishedServersideTcs = new TaskCompletionSource<object>();
 
-        // For streaming, the reads will be delivered to this observer.
-        IObserver<TRead> readObserver;
+        bool readObserverCompleted;  // True if readObserver has already been completed.
 
-        public AsyncCall(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
+        public AsyncCall(Func<TRequest, byte[]> serializer, Func<byte[], TResponse> deserializer) : base(serializer, deserializer)
         {
-            this.serializer = serializer;
-            this.deserializer = deserializer;
-            this.unaryResponseHandler = HandleUnaryResponse;
-            this.finishedHandler = HandleFinished;
-            this.writeFinishedHandler = HandleWriteFinished;
-            this.readFinishedHandler = HandleReadFinished;
-            this.halfclosedHandler = HandleHalfclosed;
-            this.finishedServersideHandler = HandleFinishedServerside;
+            this.unaryResponseHandler = CreateBatchCompletionCallback(HandleUnaryResponse);
+            this.finishedHandler = CreateBatchCompletionCallback(HandleFinished);
         }
 
         public void Initialize(Channel channel, CompletionQueueSafeHandle cq, String methodName)
         {
-            InitializeInternal(CallSafeHandle.Create(channel.Handle, cq, methodName, channel.Target, Timespec.InfFuture), false);
+            var call = CallSafeHandle.Create(channel.Handle, cq, methodName, channel.Target, Timespec.InfFuture);
+            InitializeInternal(call);
         }
 
-        public void InitializeServer(CallSafeHandle call)
-        {
-            InitializeInternal(call, true);
-        }
-
-        public TRead UnaryCall(Channel channel, String methodName, TWrite msg)
+        // TODO: this method is not Async, so it shouldn't be in AsyncCall class, but 
+        // it is reusing fair amount of code in this class, so we are leaving it here.
+        // TODO: for other calls, you need to call Initialize, this methods calls initialize 
+        // on its own, so there's a usage inconsistency.
+        /// <summary>
+        /// Blocking unary request - unary response call.
+        /// </summary>
+        public TResponse UnaryCall(Channel channel, String methodName, TRequest msg)
         {
             using(CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create())
             {
-                // TODO: handle serialization error...
-                byte[] payload = serializer(msg);
+                byte[] payload = UnsafeSerialize(msg);
 
-                unaryResponseTcs = new TaskCompletionSource<TRead>();
+                unaryResponseTcs = new TaskCompletionSource<TResponse>();
 
                 lock (myLock)
                 {
@@ -143,508 +106,200 @@ namespace Grpc.Core.Internal
             }
         }
 
-        public Task<TRead> UnaryCallAsync(TWrite msg)
+        /// <summary>
+        /// Starts a unary request - unary response call.
+        /// </summary>
+        public Task<TResponse> UnaryCallAsync(TRequest msg)
         {
             lock (myLock)
             {
+                Preconditions.CheckNotNull(call);
+
                 started = true;
                 halfcloseRequested = true;
                 readingDone = true;
 
-                // TODO: handle serialization error...
-                byte[] payload = serializer(msg);
+                byte[] payload = UnsafeSerialize(msg);
 
-                unaryResponseTcs = new TaskCompletionSource<TRead>();
+                unaryResponseTcs = new TaskCompletionSource<TResponse>();
                 call.StartUnary(payload, unaryResponseHandler);
 
                 return unaryResponseTcs.Task;
             }
         }
 
-        public Task<TRead> ClientStreamingCallAsync()
+        /// <summary>
+        /// Starts a streamed request - unary response call.
+        /// Use StartSendMessage and StartSendCloseFromClient to stream requests.
+        /// </summary>
+        public Task<TResponse> ClientStreamingCallAsync()
         {
             lock (myLock)
             {
+                Preconditions.CheckNotNull(call);
+
                 started = true;
                 readingDone = true;
 
-                unaryResponseTcs = new TaskCompletionSource<TRead>();
+                unaryResponseTcs = new TaskCompletionSource<TResponse>();
                 call.StartClientStreaming(unaryResponseHandler);
 
                 return unaryResponseTcs.Task;
             }
         }
 
-        public void StartServerStreamingCall(TWrite msg, IObserver<TRead> readObserver)
+        /// <summary>
+        /// Starts a unary request - streamed response call.
+        /// </summary>
+        public void StartServerStreamingCall(TRequest msg, IObserver<TResponse> readObserver)
         {
             lock (myLock)
             {
+                Preconditions.CheckNotNull(call);
+
                 started = true;
                 halfcloseRequested = true;
                 halfclosed = true;  // halfclose not confirmed yet, but it will be once finishedHandler is called.
         
                 this.readObserver = readObserver;
 
-                // TODO: handle serialization error...
-                byte[] payload = serializer(msg);
+                byte[] payload = UnsafeSerialize(msg);
         
                 call.StartServerStreaming(payload, finishedHandler);
 
-                ReceiveMessageAsync();
+                StartReceiveMessage();
             }
         }
 
-        public void StartDuplexStreamingCall(IObserver<TRead> readObserver)
+        /// <summary>
+        /// Starts a streaming request - streaming response call.
+        /// Use StartSendMessage and StartSendCloseFromClient to stream requests.
+        /// </summary>
+        public void StartDuplexStreamingCall(IObserver<TResponse> readObserver)
         {
             lock (myLock)
             {
+                Preconditions.CheckNotNull(call);
+
                 started = true;
 
                 this.readObserver = readObserver;
 
                 call.StartDuplexStreaming(finishedHandler);
 
-                ReceiveMessageAsync();
+                StartReceiveMessage();
             }
         }
 
-        public Task ServerSideUnaryRequestCallAsync()
-        {
-            lock (myLock)
-            {
-                started = true;
-                call.StartServerSide(finishedServersideHandler);
-                return finishedServersideTcs.Task;
-            }
-        }
-
-        public Task ServerSideStreamingRequestCallAsync(IObserver<TRead> readObserver)
-        {
-            lock (myLock)
-            {
-                started = true;
-                call.StartServerSide(finishedServersideHandler);
-               
-                if (this.readObserver != null)
-                {
-                    throw new InvalidOperationException("Already registered an observer.");
-                }
-                this.readObserver = readObserver;
-                ReceiveMessageAsync();
-
-                return finishedServersideTcs.Task;
-            }
-        }
-
-        public Task SendMessageAsync(TWrite msg)
+        /// <summary>
+        /// Sends a streaming request. Only one pending send action is allowed at any given time.
+        /// completionDelegate is called when the operation finishes.
+        /// </summary>
+        public void StartSendMessage(TRequest msg, AsyncCompletionDelegate completionDelegate)
         {
-            lock (myLock)
-            {
-                CheckNotDisposed();
-                CheckStarted();
-                CheckNoError();
-
-                if (halfcloseRequested)
-                {
-                    throw new InvalidOperationException("Already halfclosed.");
-                }
-
-                if (writeTcs != null)
-                {
-                    throw new InvalidOperationException("Only one write can be pending at a time");
-                }
-
-                // TODO: wrap serialization...
-                byte[] payload = serializer(msg);
-
-                call.StartSendMessage(payload, writeFinishedHandler);
-                writeTcs = new TaskCompletionSource<object>();
-                return writeTcs.Task;
-            }
+            StartSendMessageInternal(msg, completionDelegate);
         }
 
-        public Task SendCloseFromClientAsync()
+        /// <summary>
+        /// Sends halfclose, indicating client is done with streaming requests.
+        /// Only one pending send action is allowed at any given time.
+        /// completionDelegate is called when the operation finishes.
+        /// </summary>
+        public void StartSendCloseFromClient(AsyncCompletionDelegate completionDelegate)
         {
             lock (myLock)
             {
-                CheckNotDisposed();
-                CheckStarted();
-                CheckNoError();
-
-                if (halfcloseRequested)
-                {
-                    throw new InvalidOperationException("Already halfclosed.");
-                }
+                Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
+                CheckSendingAllowed();
 
                 call.StartSendCloseFromClient(halfclosedHandler);
 
                 halfcloseRequested = true;
-                halfcloseTcs = new TaskCompletionSource<object>();
-                return halfcloseTcs.Task;
-            }
-        }
-
-        public Task SendStatusFromServerAsync(Status status)
-        {
-            lock (myLock)
-            {
-                CheckNotDisposed();
-                CheckStarted();
-                CheckNoError();
-
-                if (halfcloseRequested)
-                {
-                    throw new InvalidOperationException("Already halfclosed.");
-                }
-
-                call.StartSendStatusFromServer(status, halfclosedHandler);
-                halfcloseRequested = true;
-                halfcloseTcs = new TaskCompletionSource<object>();
-                return halfcloseTcs.Task;
+                sendCompletionDelegate = completionDelegate;
             }
         }
 
-        public Task<TRead> ReceiveMessageAsync()
+        /// <summary>
+        /// On client-side, we only fire readObserver.OnCompleted once all messages have been read 
+        /// and status has been received.
+        /// </summary>
+        protected override void CompleteReadObserver()
         {
-            lock (myLock)
+            if (readingDone && finishedStatus.HasValue)
             {
-                CheckNotDisposed();
-                CheckStarted();
-                CheckNoError();
-
-                if (readingDone)
-                {
-                    throw new InvalidOperationException("Already read the last message.");
-                }
-
-                if (readTcs != null)
+                bool shouldComplete;
+                lock (myLock)
                 {
-                    throw new InvalidOperationException("Only one read can be pending at a time");
+                    shouldComplete = !readObserverCompleted;
+                    readObserverCompleted = true;
                 }
 
-                call.StartReceiveMessage(readFinishedHandler);
-
-                readTcs = new TaskCompletionSource<TRead>();
-                return readTcs.Task;
-            }
-        }
-
-        public void Cancel()
-        {
-            lock (myLock)
-            {
-                CheckNotDisposed();
-                CheckStarted();
-                cancelRequested = true;
-            }
-            // grpc_call_cancel is threadsafe
-            call.Cancel();
-        }
-
-        public void CancelWithStatus(Status status)
-        {
-            lock (myLock)
-            {
-                CheckNotDisposed();
-                CheckStarted();
-                cancelRequested = true;
-            }
-            // grpc_call_cancel_with_status is threadsafe
-            call.CancelWithStatus(status);
-        }
-
-        private void InitializeInternal(CallSafeHandle call, bool server)
-        {
-            lock (myLock)
-            {
-                // Make sure this object and the delegated held by it will not be garbage collected
-                // before we release this handle.
-                gchandle = GCHandle.Alloc(this);
-                this.call = call;
-                this.server = server;
-            }
-        }
-
-        private void CheckStarted()
-        {
-            if (!started)
-            {
-                throw new InvalidOperationException("Call not started");
-            }
-        }
-
-        private void CheckNotDisposed()
-        {
-            if (disposed)
-            {
-                throw new InvalidOperationException("Call has already been disposed.");
-            }
-        }
-
-        private void CheckNoError()
-        {
-            if (errorOccured)
-            {
-                throw new InvalidOperationException("Error occured when processing call.");
-            }
-        }
-
-        private bool ReleaseResourcesIfPossible()
-        {
-            if (!disposed && call != null)
-            {
-                if (halfclosed && readingDone && finished)
+                if (shouldComplete)
                 {
-                    ReleaseResources();
-                    return true;
+                    var status = finishedStatus.Value;
+                    if (status.StatusCode != StatusCode.OK)
+                    {
+                        FireReadObserverOnError(new RpcException(status));
+                    }
+                    else
+                    {
+                        FireReadObserverOnCompleted();
+                    }
                 }
             }
-            return false;
-        }
-
-        private void ReleaseResources()
-        {
-            if (call != null) {
-                call.Dispose();
-            }
-            gchandle.Free();
-            disposed = true;
-        }
-
-        private void CompleteStreamObserver(Status status)
-        {
-            if (status.StatusCode != StatusCode.OK)
-            {
-                // TODO: wrap to handle exceptions;
-                readObserver.OnError(new RpcException(status));
-            } else {
-                // TODO: wrap to handle exceptions;
-                readObserver.OnCompleted();
-            }
         }
 
         /// <summary>
         /// Handler for unary response completion.
         /// </summary>
-        private void HandleUnaryResponse(GRPCOpError error, IntPtr batchContextPtr)
+        private void HandleUnaryResponse(bool wasError, BatchContextSafeHandleNotOwned ctx)
         {
-            try
+            lock(myLock)
             {
-                TaskCompletionSource<TRead> tcs;
-                lock(myLock)
-                {
-                    finished = true;
-                    halfclosed = true;
-                    tcs = unaryResponseTcs;
-
-                    ReleaseResourcesIfPossible();
-                }
-
-                var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr);
+                finished = true;
+                halfclosed = true;
 
-                if (error != GRPCOpError.GRPC_OP_OK)
-                {
-                    tcs.SetException(new RpcException(
-                        new Status(StatusCode.Internal, "Internal error occured.")
-                    ));
-                    return;
-                }
-
-                var status = ctx.GetReceivedStatus();
-                if (status.StatusCode != StatusCode.OK)
-                {
-                    tcs.SetException(new RpcException(status));
-                    return;
-                }
-
-                // TODO: handle deserialize error...
-                var msg = deserializer(ctx.GetReceivedMessage());
-                tcs.SetResult(msg);
-            } 
-            catch(Exception e)
-            {
-                Console.WriteLine("Caught exception in a native handler: " + e);
+                ReleaseResourcesIfPossible();
             }
-        }
-
-        private void HandleWriteFinished(GRPCOpError error, IntPtr batchContextPtr)
-        {
-            try
-            {
-                TaskCompletionSource<object> oldTcs = null;
-                lock (myLock)
-                {
-                    oldTcs = writeTcs;
-                    writeTcs = null;
-                }
-
-                if (errorOccured)
-                {
-                    // TODO: use the right type of exception...
-                    oldTcs.SetException(new Exception("Write failed"));
-                }
-                else
-                {
-                    // TODO: where does the continuation run?
-                    oldTcs.SetResult(null);
-                }
 
-            }
-            catch(Exception e)
+            if (wasError)
             {
-                Console.WriteLine("Caught exception in a native handler: " + e);
+                unaryResponseTcs.SetException(new RpcException(
+                    new Status(StatusCode.Internal, "Internal error occured.")
+                ));
+                return;
             }
-        }
-
-        private void HandleHalfclosed(GRPCOpError error, IntPtr batchContextPtr)
-        {
-            try
-            {
-                lock (myLock)
-                {
-                    halfclosed = true;
 
-                    ReleaseResourcesIfPossible();
-                }
-
-                if (error != GRPCOpError.GRPC_OP_OK)
-                {
-                    halfcloseTcs.SetException(new Exception("Halfclose failed"));
-
-                }
-                else
-                {
-                    halfcloseTcs.SetResult(null);
-                }
-            }
-            catch(Exception e)
+            var status = ctx.GetReceivedStatus();
+            if (status.StatusCode != StatusCode.OK)
             {
-                Console.WriteLine("Caught exception in a native handler: " + e);
+                unaryResponseTcs.SetException(new RpcException(status));
+                return;
             }
-        }
-
-        private void HandleReadFinished(GRPCOpError error, IntPtr batchContextPtr)
-        {
-            try
-            {
-                var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr);
-                var payload = ctx.GetReceivedMessage();
-
-                TaskCompletionSource<TRead> oldTcs = null;
-                IObserver<TRead> observer = null;
-
-                Nullable<Status> status = null;
-
-                lock (myLock)
-                {
-                    oldTcs = readTcs;
-                    readTcs = null;
-                    if (payload == null)
-                    {
-                        readingDone = true;
-                    }
-                    observer = readObserver;
-                    status = finishedStatus;
-
-                    ReleaseResourcesIfPossible();
-                }
-
-                // TODO: wrap deserialization...
-                TRead msg = payload != null ? deserializer(payload) : default(TRead);
 
-                oldTcs.SetResult(msg);
+            // TODO: handle deserialization error
+            TResponse msg;
+            TryDeserialize(ctx.GetReceivedMessage(), out msg);
 
-                // TODO: make sure we deliver reads in the right order.
-
-                if (observer != null)
-                {
-                    if (payload != null)
-                    {
-                        // TODO: wrap to handle exceptions
-                        observer.OnNext(msg);
-
-                        // start a new read
-                        ReceiveMessageAsync();
-                    }
-                    else
-                    {
-                        if (!server)
-                        {
-                            if (status.HasValue)
-                            {
-                                CompleteStreamObserver(status.Value);
-                            }
-                        } 
-                        else 
-                        {
-                            // TODO: wrap to handle exceptions..
-                            observer.OnCompleted();
-                        }
-                        // TODO: completeStreamObserver serverside...
-                    }
-               }
-            }
-            catch(Exception e)
-            {
-                Console.WriteLine("Caught exception in a native handler: " + e);
-            }
+            unaryResponseTcs.SetResult(msg);
         }
 
-        private void HandleFinished(GRPCOpError error, IntPtr batchContextPtr)
+        /// <summary>
+        /// Handles receive status completion for calls with streaming response.
+        /// </summary>
+        private void HandleFinished(bool wasError, BatchContextSafeHandleNotOwned ctx)
         {
-            try
-            {
-                var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr);
-                var status = ctx.GetReceivedStatus();
-
-                bool wasReadingDone;
-
-                lock (myLock)
-                {
-                    finished = true;
-                    finishedStatus = status;
-
-                    wasReadingDone = readingDone;
-
-                    ReleaseResourcesIfPossible();
-                }
-
-                if (wasReadingDone) {
-                    CompleteStreamObserver(status);
-                }
-
-            }
-            catch(Exception e)
-            {
-                Console.WriteLine("Caught exception in a native handler: " + e);
-            }
-        }
+            var status = ctx.GetReceivedStatus();
 
-        private void HandleFinishedServerside(GRPCOpError error, IntPtr batchContextPtr)
-        {
-            try
+            lock (myLock)
             {
-                var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr);
-
-                lock(myLock)
-                {
-                    finished = true;
-
-                    // TODO: because of the way server calls are implemented, we need to set
-                    // reading done to true here. Should be fixed in the future.
-                    readingDone = true;
-
-                    ReleaseResourcesIfPossible();
-                }
-                // TODO: handle error ...
-
-                finishedServersideTcs.SetResult(null);
+                finished = true;
+                finishedStatus = status;
 
+                ReleaseResourcesIfPossible();
             }
-            catch(Exception e)
-            {
-                Console.WriteLine("Caught exception in a native handler: " + e);
-            }
+
+            CompleteReadObserver();
         }
     }
 }
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..44d66b394ca6929bc1fea07c0bf85e0de5e0fcf7
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
@@ -0,0 +1,407 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+// 
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Base for handling both client side and server side calls.
+    /// Handles native call lifecycle and provides convenience methods.
+    /// </summary>
+    internal abstract class AsyncCallBase<TWrite, TRead>
+    {
+        readonly Func<TWrite, byte[]> serializer;
+        readonly Func<byte[], TRead> deserializer;
+
+        protected readonly CompletionCallbackDelegate sendFinishedHandler;
+        protected readonly CompletionCallbackDelegate readFinishedHandler;
+        protected readonly CompletionCallbackDelegate halfclosedHandler;
+
+        protected readonly object myLock = new object();
+
+        protected GCHandle gchandle;
+        protected CallSafeHandle call;
+        protected bool disposed;
+
+        protected bool started;
+        protected bool errorOccured;
+        protected bool cancelRequested;
+
+        protected AsyncCompletionDelegate sendCompletionDelegate;  // Completion of a pending send or sendclose if not null.
+        protected bool readPending;  // True if there is a read in progress.
+        protected bool readingDone;
+        protected bool halfcloseRequested;
+        protected bool halfclosed;
+        protected bool finished;  // True if close has been received from the peer.
+
+        // Streaming reads will be delivered to this observer. For a call that only does unary read it may remain null.
+        protected IObserver<TRead> readObserver;
+
+        public AsyncCallBase(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
+        {
+            this.serializer = Preconditions.CheckNotNull(serializer);
+            this.deserializer = Preconditions.CheckNotNull(deserializer);
+  
+            this.sendFinishedHandler = CreateBatchCompletionCallback(HandleSendFinished);
+            this.readFinishedHandler = CreateBatchCompletionCallback(HandleReadFinished);
+            this.halfclosedHandler = CreateBatchCompletionCallback(HandleHalfclosed);
+        }
+
+        /// <summary>
+        /// Requests cancelling the call.
+        /// </summary>
+        public void Cancel()
+        {
+            lock (myLock)
+            {
+                Preconditions.CheckState(started);
+                cancelRequested = true;
+
+                if (!disposed)
+                {
+                    call.Cancel();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Requests cancelling the call with given status.
+        /// </summary>
+        public void CancelWithStatus(Status status)
+        {
+            lock (myLock)
+            {
+                Preconditions.CheckState(started);
+                cancelRequested = true;
+
+                if (!disposed)
+                {
+                    call.CancelWithStatus(status);
+                }
+            }
+        }
+
+        protected void InitializeInternal(CallSafeHandle call)
+        {
+            lock (myLock)
+            {
+                // Make sure this object and the delegated held by it will not be garbage collected
+                // before we release this handle.
+                gchandle = GCHandle.Alloc(this);
+                this.call = call;
+            }
+        }
+
+        /// <summary>
+        /// Initiates sending a message. Only once send operation can be active at a time.
+        /// completionDelegate is invoked upon completion.
+        /// </summary>
+        protected void StartSendMessageInternal(TWrite msg, AsyncCompletionDelegate completionDelegate)
+        {
+            byte[] payload = UnsafeSerialize(msg);
+
+            lock (myLock)
+            {
+                Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
+                CheckSendingAllowed();
+
+                call.StartSendMessage(payload, sendFinishedHandler);
+                sendCompletionDelegate = completionDelegate;
+            }
+        }
+
+        /// <summary>
+        /// Requests receiving a next message.
+        /// </summary>
+        protected void StartReceiveMessage()
+        {
+            lock (myLock)
+            {
+                Preconditions.CheckState(started);
+                Preconditions.CheckState(!disposed);
+                Preconditions.CheckState(!errorOccured);
+
+                Preconditions.CheckState(!readingDone);
+                Preconditions.CheckState(!readPending);
+
+                call.StartReceiveMessage(readFinishedHandler);
+                readPending = true;
+            }
+        }
+
+        /// <summary>
+        /// Default behavior just completes the read observer, but more sofisticated behavior might be required
+        /// by subclasses.
+        /// </summary>
+        protected virtual void CompleteReadObserver()
+        {
+            FireReadObserverOnCompleted();
+        }
+
+        /// <summary>
+        /// If there are no more pending actions and no new actions can be started, releases
+        /// the underlying native resources.
+        /// </summary>
+        protected bool ReleaseResourcesIfPossible()
+        {
+            if (!disposed && call != null)
+            {
+                if (halfclosed && readingDone && finished)
+                {
+                    ReleaseResources();
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void ReleaseResources()
+        {
+            if (call != null)
+            {
+                call.Dispose();
+            }
+            gchandle.Free();
+            disposed = true;
+        }
+
+        protected void CheckSendingAllowed()
+        {
+            Preconditions.CheckState(started);
+            Preconditions.CheckState(!disposed);
+            Preconditions.CheckState(!errorOccured);
+
+            Preconditions.CheckState(!halfcloseRequested, "Already halfclosed.");
+            Preconditions.CheckState(sendCompletionDelegate == null, "Only one write can be pending at a time");
+        }
+
+        protected byte[] UnsafeSerialize(TWrite msg)
+        {
+            return serializer(msg);
+        }
+
+        protected bool TrySerialize(TWrite msg, out byte[] payload)
+        {
+            try
+            {
+                payload = serializer(msg);
+                return true;
+            }
+            catch(Exception)
+            {
+                Console.WriteLine("Exception occured while trying to serialize message");
+                payload = null;
+                return false;
+            }
+        }
+
+        protected bool TryDeserialize(byte[] payload, out TRead msg)
+        {
+            try
+            {
+                msg = deserializer(payload);
+                return true;
+            } 
+            catch(Exception)
+            {
+                Console.WriteLine("Exception occured while trying to deserialize message");
+                msg = default(TRead);
+                return false;
+            }
+        }
+
+        protected void FireReadObserverOnNext(TRead value)
+        {
+            try
+            {
+                readObserver.OnNext(value);
+            }
+            catch(Exception e)
+            {
+                Console.WriteLine("Exception occured while invoking readObserver.OnNext: " + e);
+            }
+        }
+
+        protected void FireReadObserverOnCompleted()
+        {
+            try
+            {
+                readObserver.OnCompleted();
+            }
+            catch(Exception e)
+            {
+                Console.WriteLine("Exception occured while invoking readObserver.OnCompleted: " + e);
+            }
+        }
+
+        protected void FireReadObserverOnError(Exception error)
+        {
+            try
+            {
+                readObserver.OnError(error);
+            }
+            catch(Exception e)
+            {
+                Console.WriteLine("Exception occured while invoking readObserver.OnError: " + e);
+            }
+        }
+
+        protected void FireCompletion(AsyncCompletionDelegate completionDelegate, Exception error)
+        {
+            try
+            {
+                completionDelegate(error);
+            }
+            catch(Exception e)
+            {
+                Console.WriteLine("Exception occured while invoking completion delegate: " + e);
+            }
+        }
+
+        /// <summary>
+        /// Creates completion callback delegate that wraps the batch completion handler in a try catch block to
+        /// prevent propagating exceptions accross managed/unmanaged boundary.
+        /// </summary>
+        protected CompletionCallbackDelegate CreateBatchCompletionCallback(Action<bool, BatchContextSafeHandleNotOwned> handler)
+        {
+            return new CompletionCallbackDelegate( (error, batchContextPtr) => {
+                try
+                {
+                    var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr);
+                    bool wasError = (error != GRPCOpError.GRPC_OP_OK);
+                    handler(wasError, ctx);
+                }
+                catch(Exception e)
+                {
+                    Console.WriteLine("Caught exception in a native handler: " + e);
+                }
+            });
+        }
+
+        /// <summary>
+        /// Handles send completion.
+        /// </summary>
+        private void HandleSendFinished(bool wasError, BatchContextSafeHandleNotOwned ctx)
+        {
+            AsyncCompletionDelegate origCompletionDelegate = null;
+            lock (myLock)
+            {
+                origCompletionDelegate = sendCompletionDelegate;
+                sendCompletionDelegate = null;
+
+                ReleaseResourcesIfPossible();
+            }
+
+            if (wasError)
+            {
+                FireCompletion(origCompletionDelegate, new OperationFailedException("Send failed"));
+            }
+            else
+            {
+                FireCompletion(origCompletionDelegate, null);
+            }
+        }
+
+        /// <summary>
+        /// Handles halfclose completion.
+        /// </summary>
+        private void HandleHalfclosed(bool wasError, BatchContextSafeHandleNotOwned ctx)
+        {
+            AsyncCompletionDelegate origCompletionDelegate = null;
+            lock (myLock)
+            {
+                halfclosed = true;
+                origCompletionDelegate = sendCompletionDelegate;
+                sendCompletionDelegate = null;
+
+                ReleaseResourcesIfPossible();
+            }
+
+            if (wasError)
+            {
+                FireCompletion(origCompletionDelegate, new OperationFailedException("Halfclose failed"));
+            }
+            else
+            {
+                FireCompletion(origCompletionDelegate, null);
+            }
+           
+        }
+
+        /// <summary>
+        /// Handles streaming read completion.
+        /// </summary>
+        private void HandleReadFinished(bool wasError, BatchContextSafeHandleNotOwned ctx)
+        {
+            var payload = ctx.GetReceivedMessage();
+
+            lock (myLock)
+            {
+                readPending = false;
+                if (payload == null)
+                {
+                    readingDone = true;
+                }
+
+                ReleaseResourcesIfPossible();
+            }
+
+            // TODO: handle the case when error occured...
+
+            if (payload != null)
+            {
+                // TODO: handle deserialization error
+                TRead msg;
+                TryDeserialize(payload, out msg);
+
+                FireReadObserverOnNext(msg);
+
+                // Start a new read. The current one has already been delivered,
+                // so correct ordering of reads is assured.
+                StartReceiveMessage();  
+            }
+            else
+            {
+                CompleteReadObserver();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d3a2be553fc6ac21cc1f476c5d7f16d9692be0d3
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
@@ -0,0 +1,125 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+// 
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Handles server side native call lifecycle.
+    /// </summary>
+    internal class AsyncCallServer<TRequest, TResponse> : AsyncCallBase<TResponse, TRequest>
+    {
+        readonly CompletionCallbackDelegate finishedServersideHandler;
+        readonly TaskCompletionSource<object> finishedServersideTcs = new TaskCompletionSource<object>();
+
+        public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer) : base(serializer, deserializer)
+        {
+            this.finishedServersideHandler = CreateBatchCompletionCallback(HandleFinishedServerside);
+        }
+
+        public void Initialize(CallSafeHandle call)
+        {
+            InitializeInternal(call);
+        }
+
+        /// <summary>
+        /// Starts a server side call. Currently, all server side calls are implemented as duplex 
+        /// streaming call and they are adapted to the appropriate streaming arity.
+        /// </summary>
+        public Task ServerSideCallAsync(IObserver<TRequest> readObserver)
+        {
+            lock (myLock)
+            {
+                Preconditions.CheckNotNull(call);
+
+                started = true;
+                this.readObserver = readObserver;
+
+                call.StartServerSide(finishedServersideHandler);
+                StartReceiveMessage();
+                return finishedServersideTcs.Task;
+            }
+        }
+
+        /// <summary>
+        /// Sends a streaming response. Only one pending send action is allowed at any given time.
+        /// completionDelegate is called when the operation finishes.
+        /// </summary>
+        public void StartSendMessage(TResponse msg, AsyncCompletionDelegate completionDelegate)
+        {
+            StartSendMessageInternal(msg, completionDelegate);
+        }
+
+        /// <summary>
+        /// Sends call result status, also indicating server is done with streaming responses.
+        /// Only one pending send action is allowed at any given time.
+        /// completionDelegate is called when the operation finishes.
+        /// </summary>
+        public void StartSendStatusFromServer(Status status, AsyncCompletionDelegate completionDelegate)
+        {
+            lock (myLock)
+            {
+                Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
+                CheckSendingAllowed();
+
+                call.StartSendStatusFromServer(status, halfclosedHandler);
+                halfcloseRequested = true;
+                sendCompletionDelegate = completionDelegate;
+            }
+        }
+
+        /// <summary>
+        /// Handles the server side close completion.
+        /// </summary>
+        private void HandleFinishedServerside(bool wasError, BatchContextSafeHandleNotOwned ctx)
+        {
+            lock (myLock)
+            {
+                finished = true;
+
+                ReleaseResourcesIfPossible();
+            }
+            // TODO: handle error ...
+
+            finishedServersideTcs.SetResult(null);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs b/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b78bb497fa71efdc0a01340f50bc990a8c454ecd
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs
@@ -0,0 +1,95 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+// 
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// If error != null, there's been an error or operation has been cancelled.
+    /// </summary>
+    internal delegate void AsyncCompletionDelegate(Exception error);
+
+    /// <summary>
+    /// Helper for transforming AsyncCompletionDelegate into full-fledged Task.
+    /// </summary>
+    internal class AsyncCompletionTaskSource
+    {
+        readonly TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
+        readonly AsyncCompletionDelegate completionDelegate;
+
+        public AsyncCompletionTaskSource()
+        {
+            completionDelegate = new AsyncCompletionDelegate(HandleCompletion);
+        }
+
+        public Task Task
+        {
+            get
+            {
+                return tcs.Task;
+            }
+        }
+
+        public AsyncCompletionDelegate CompletionDelegate
+        {
+            get
+            {
+                return completionDelegate;
+            }
+        }
+
+        private void HandleCompletion(Exception error)
+        {
+            if (error == null)
+            {
+                tcs.SetResult(null);
+                return;
+            }
+            if (error is OperationCanceledException)
+            {
+                tcs.SetCanceled();
+                return;
+            }
+            tcs.SetException(error);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index 1c0bc98f062db57fd27bca50e8ade5d966e32047..61566b54072bbb8ab11f9bde2240d4b3760bd0a9 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 // 
@@ -30,7 +29,6 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #endregion
-
 using System;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
@@ -38,14 +36,12 @@ using Grpc.Core;
 
 namespace Grpc.Core.Internal
 {
-    //TODO: rename the delegate
-    internal delegate void CompletionCallbackDelegate(GRPCOpError error, IntPtr batchContextPtr);
-
+    internal delegate void CompletionCallbackDelegate(GRPCOpError error,IntPtr batchContextPtr);
     /// <summary>
     /// grpc_call from <grpc/grpc.h>
     /// </summary>
-	internal class CallSafeHandle : SafeHandleZeroIsInvalid
-	{
+    internal class CallSafeHandle : SafeHandleZeroIsInvalid
+    {
         const UInt32 GRPC_WRITE_BUFFER_HINT = 1;
 
         [DllImport("grpc_csharp_ext.dll")]
@@ -59,22 +55,22 @@ namespace Grpc.Core.Internal
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_start_unary(CallSafeHandle call,
-                                                                        [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
-                                                                        byte[] send_buffer, UIntPtr send_buffer_len);
+                                                               [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
+                                                               byte[] send_buffer, UIntPtr send_buffer_len);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern void grpcsharp_call_blocking_unary(CallSafeHandle call, CompletionQueueSafeHandle dedicatedCq,
-                                                               [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
-                                                               byte[] send_buffer, UIntPtr send_buffer_len);
+                                                         [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
+                                                         byte[] send_buffer, UIntPtr send_buffer_len);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call,
-                                                                      [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
+                                                                          [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call,
-                                                                      [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
-                                                                      byte[] send_buffer, UIntPtr send_buffer_len);
+                                                                          [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
+                                                                          byte[] send_buffer, UIntPtr send_buffer_len);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call,
@@ -82,28 +78,27 @@ namespace Grpc.Core.Internal
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call,
-                                                                      [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
-                                                                      byte[] send_buffer, UIntPtr send_buffer_len);
+                                                                [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
+                                                                byte[] send_buffer, UIntPtr send_buffer_len);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_send_close_from_client(CallSafeHandle call,
-                                                                             [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
+                                                                          [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback, StatusCode statusCode, string statusMessage);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_recv_message(CallSafeHandle call,
-                                                               [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
+                                                                [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern GRPCCallError grpcsharp_call_start_serverside(CallSafeHandle call,
-                                                                [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
+                                                                    [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern void grpcsharp_call_destroy(IntPtr call);
 
-
         private CallSafeHandle()
         {
         }
@@ -115,12 +110,12 @@ namespace Grpc.Core.Internal
 
         public void StartUnary(byte[] payload, CompletionCallbackDelegate callback)
         {
-            AssertCallOk(grpcsharp_call_start_unary(this, callback, payload, new UIntPtr((ulong) payload.Length)));
+            AssertCallOk(grpcsharp_call_start_unary(this, callback, payload, new UIntPtr((ulong)payload.Length)));
         }
 
         public void BlockingUnary(CompletionQueueSafeHandle dedicatedCq, byte[] payload, CompletionCallbackDelegate callback)
         {
-            grpcsharp_call_blocking_unary(this, dedicatedCq, callback, payload, new UIntPtr((ulong) payload.Length));
+            grpcsharp_call_blocking_unary(this, dedicatedCq, callback, payload, new UIntPtr((ulong)payload.Length));
         }
 
         public void StartClientStreaming(CompletionCallbackDelegate callback)
@@ -130,7 +125,7 @@ namespace Grpc.Core.Internal
 
         public void StartServerStreaming(byte[] payload, CompletionCallbackDelegate callback)
         {
-            AssertCallOk(grpcsharp_call_start_server_streaming(this, callback, payload, new UIntPtr((ulong) payload.Length)));
+            AssertCallOk(grpcsharp_call_start_server_streaming(this, callback, payload, new UIntPtr((ulong)payload.Length)));
         }
 
         public void StartDuplexStreaming(CompletionCallbackDelegate callback)
@@ -140,7 +135,7 @@ namespace Grpc.Core.Internal
 
         public void StartSendMessage(byte[] payload, CompletionCallbackDelegate callback)
         {
-            AssertCallOk(grpcsharp_call_send_message(this, callback, payload, new UIntPtr((ulong) payload.Length)));
+            AssertCallOk(grpcsharp_call_send_message(this, callback, payload, new UIntPtr((ulong)payload.Length)));
         }
 
         public void StartSendCloseFromClient(CompletionCallbackDelegate callback)
@@ -173,19 +168,20 @@ namespace Grpc.Core.Internal
             AssertCallOk(grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail));
         }
 
-		protected override bool ReleaseHandle()
-		{
+        protected override bool ReleaseHandle()
+        {
             grpcsharp_call_destroy(handle);
-			return true;
-		}
+            return true;
+        }
 
         private static void AssertCallOk(GRPCCallError callError)
         {
             Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
         }
 
-        private static UInt32 GetFlags(bool buffered) {
+        private static UInt32 GetFlags(bool buffered)
+        {
             return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;
         }
-	}
+    }
 }
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ca3c21d84ce83261ca76f8cc941d5d5c05730e71
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
@@ -0,0 +1,77 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+using System;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// grpc_channel_args from <grpc/grpc.h>
+    /// </summary>
+    internal class ChannelArgsSafeHandle : SafeHandleZeroIsInvalid
+    {
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern ChannelArgsSafeHandle grpcsharp_channel_args_create(UIntPtr numArgs);
+
+        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+        static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value);
+
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern void grpcsharp_channel_args_destroy(IntPtr args);
+
+        private ChannelArgsSafeHandle()
+        {
+        }
+
+        public static ChannelArgsSafeHandle CreateNull()
+        {
+            return new ChannelArgsSafeHandle();
+        }
+
+        public static ChannelArgsSafeHandle Create(int size)
+        {
+            return grpcsharp_channel_args_create(new UIntPtr((uint)size));
+        }
+
+        public void SetString(int index, string key, string value)
+        {
+            grpcsharp_channel_args_set_string(this, new UIntPtr((uint)index), key, value);
+        }
+
+        protected override bool ReleaseHandle()
+        {
+            grpcsharp_channel_args_destroy(handle);
+            return true;
+        }
+    }
+}
+
diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
index f15ead35724d94ee4bc6b11b71543d7afa46bf5a..f046f4c6d0d4b0ee32b869ee2d09aea6d1c32aeb 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,9 +27,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
-
 using System;
 using System.Runtime.InteropServices;
 using System.Threading;
@@ -41,27 +38,35 @@ namespace Grpc.Core.Internal
     /// <summary>
     /// grpc_channel from <grpc/grpc.h>
     /// </summary>
-	internal class ChannelSafeHandle : SafeHandleZeroIsInvalid
-	{
+    internal class ChannelSafeHandle : SafeHandleZeroIsInvalid
+    {
         [DllImport("grpc_csharp_ext.dll")]
-        static extern ChannelSafeHandle grpcsharp_channel_create(string target, IntPtr channelArgs);
+        static extern ChannelSafeHandle grpcsharp_channel_create(string target, ChannelArgsSafeHandle channelArgs);
 
-		[DllImport("grpc_csharp_ext.dll")]
-		static extern void grpcsharp_channel_destroy(IntPtr channel);
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern ChannelSafeHandle grpcsharp_secure_channel_create(CredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs);
+
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern void grpcsharp_channel_destroy(IntPtr channel);
 
         private ChannelSafeHandle()
         {
         }
 
-        public static ChannelSafeHandle Create(string target, IntPtr channelArgs)
+        public static ChannelSafeHandle Create(string target, ChannelArgsSafeHandle channelArgs)
         {
             return grpcsharp_channel_create(target, channelArgs);
         }
 
-		protected override bool ReleaseHandle()
-		{
-			grpcsharp_channel_destroy(handle);
-			return true;
-		}
-	}
+        public static ChannelSafeHandle CreateSecure(CredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs)
+        {
+            return grpcsharp_secure_channel_create(credentials, target, channelArgs);
+        }
+
+        protected override bool ReleaseHandle()
+        {
+            grpcsharp_channel_destroy(handle);
+            return true;
+        }
+    }
 }
diff --git a/src/csharp/Grpc.Core/Internal/ClientStreamingInputObserver.cs b/src/csharp/Grpc.Core/Internal/ClientStreamingInputObserver.cs
index fb59e86e2d7f8e0d017abc4b26910ac8cc2fcb65..286c54f2c47811f458d4b1543b06007e7b836378 100644
--- a/src/csharp/Grpc.Core/Internal/ClientStreamingInputObserver.cs
+++ b/src/csharp/Grpc.Core/Internal/ClientStreamingInputObserver.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 // 
@@ -28,40 +27,40 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
-
 using System;
 using Grpc.Core.Internal;
 
 namespace Grpc.Core.Internal
 {
     internal class ClientStreamingInputObserver<TWrite, TRead> : IObserver<TWrite>
-	{
+    {
         readonly AsyncCall<TWrite, TRead> call;
 
         public ClientStreamingInputObserver(AsyncCall<TWrite, TRead> call)
-		{
+        {
             this.call = call;
-		}
-
-		public void OnCompleted()
-		{
+        }
 
+        public void OnCompleted()
+        {
+            var taskSource = new AsyncCompletionTaskSource();
+            call.StartSendCloseFromClient(taskSource.CompletionDelegate);
             // TODO: how bad is the Wait here?
-            call.SendCloseFromClientAsync().Wait();
-		}
+            taskSource.Task.Wait();
+        }
 
-		public void OnError(Exception error)
-		{
-			throw new InvalidOperationException("This should never be called.");
-		}
+        public void OnError(Exception error)
+        {
+            throw new InvalidOperationException("This should never be called.");
+        }
 
-		public void OnNext(TWrite value)
-		{
+        public void OnNext(TWrite value)
+        {
+            var taskSource = new AsyncCompletionTaskSource();
+            call.StartSendMessage(value, taskSource.CompletionDelegate);
             // TODO: how bad is the Wait here?
-            call.SendMessageAsync(value).Wait();
-		}
-	}
+            taskSource.Task.Wait();
+        }
+    }
 }
-
diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
index 3f01fdbfd05c70d4ac130681d6af1ab91a6530c7..6bff923c55229166189b497d666b797436dc5d3b 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,9 +27,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
-
 using System;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
@@ -40,8 +37,8 @@ namespace Grpc.Core.Internal
     /// <summary>
     /// grpc_completion_queue from <grpc/grpc.h>
     /// </summary>
-	internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
-	{
+    internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
+    {
         [DllImport("grpc_csharp_ext.dll")]
         static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create();
 
@@ -73,11 +70,11 @@ namespace Grpc.Core.Internal
             grpcsharp_completion_queue_shutdown(this);
         }
 
-		protected override bool ReleaseHandle()
+        protected override bool ReleaseHandle()
         {
             grpcsharp_completion_queue_destroy(handle);
-			return true;
-		}
-	}
+            return true;
+        }
+    }
 }
 
diff --git a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f361199068eb5da2bb917f732b13bd94ba4a18c0
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
@@ -0,0 +1,64 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// grpc_credentials from <grpc/grpc_security.h>
+    /// </summary>
+    internal class CredentialsSafeHandle : SafeHandleZeroIsInvalid
+    {
+        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+        static extern CredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
+
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern void grpcsharp_credentials_release(IntPtr credentials);
+
+        private CredentialsSafeHandle()
+        {
+        }
+
+        public static CredentialsSafeHandle CreateSslCredentials(string pemRootCerts)
+        {
+            return grpcsharp_ssl_credentials_create(pemRootCerts, null, null);
+        }
+
+        protected override bool ReleaseHandle()
+        {
+            grpcsharp_credentials_release(handle);
+            return true;
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs b/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs
index 08d99214754bd9316a75f5a252fc0861694bbe7a..9873dc9c71077b0d8650199e288e743f8f348896 100644
--- a/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,9 +27,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
-
 using System;
 using Grpc.Core.Internal;
 
@@ -40,32 +37,36 @@ namespace Grpc.Core.Internal
     /// Observer that writes all arriving messages to a call abstraction (in blocking fashion)
     /// and then halfcloses the call. Used for server-side call handling.
     /// </summary>
-    internal class ServerStreamingOutputObserver<TWrite, TRead> : IObserver<TWrite>
-	{
-        readonly AsyncCall<TWrite, TRead> call;
+    internal class ServerStreamingOutputObserver<TRequest, TResponse> : IObserver<TResponse>
+    {
+        readonly AsyncCallServer<TRequest, TResponse> call;
 
-        public ServerStreamingOutputObserver(AsyncCall<TWrite, TRead> call)
-		{
+        public ServerStreamingOutputObserver(AsyncCallServer<TRequest, TResponse> call)
+        {
             this.call = call;
-		}
+        }
 
-		public void OnCompleted()
-		{
+        public void OnCompleted()
+        {
+            var taskSource = new AsyncCompletionTaskSource();
+            call.StartSendStatusFromServer(new Status(StatusCode.OK, ""), taskSource.CompletionDelegate);
             // TODO: how bad is the Wait here?
-            call.SendStatusFromServerAsync(new Status(StatusCode.OK, "")).Wait();
-		}
+            taskSource.Task.Wait();
+        }
 
-		public void OnError(Exception error)
-		{
+        public void OnError(Exception error)
+        {
             // TODO: implement this...
-			throw new InvalidOperationException("This should never be called.");
-		}
+            throw new InvalidOperationException("This should never be called.");
+        }
 
-		public void OnNext(TWrite value)
-		{
+        public void OnNext(TResponse value)
+        {
+            var taskSource = new AsyncCompletionTaskSource();
+            call.StartSendMessage(value, taskSource.CompletionDelegate);
             // TODO: how bad is the Wait here?
-            call.SendMessageAsync(value).Wait();
-		}
-	}
+            taskSource.Task.Wait();
+        }
+    }
 }
 
diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs
index b191ecde94cc6e3e96cf966de8fe6813ab5b2628..e6efd66f13b9eb717d573f4469927d56c749d9f7 100644
--- a/src/csharp/Grpc.Core/Internal/Timespec.cs
+++ b/src/csharp/Grpc.Core/Internal/Timespec.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,21 +27,19 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
-
 using System;
 using System.Runtime.InteropServices;
 using System.Threading;
 
 namespace Grpc.Core.Internal
 {
-	/// <summary>
-	/// gpr_timespec from grpc/support/time.h
-	/// </summary>
-	[StructLayout(LayoutKind.Sequential)]
-	internal struct Timespec
-	{
+    /// <summary>
+    /// gpr_timespec from grpc/support/time.h
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct Timespec
+    {
         const int nanosPerSecond = 1000 * 1000 * 1000;
         const int nanosPerTick = 100;
 
@@ -54,23 +51,22 @@ namespace Grpc.Core.Internal
 
         [DllImport("grpc_csharp_ext.dll")]
         static extern int gprsharp_sizeof_timespec();
-
         // TODO: revisit this.
-		// NOTE: on linux 64bit  sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8
+        // NOTE: on linux 64bit  sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8
         // so IntPtr seems to have the right size to work on both.
-		public System.IntPtr tv_sec;
-		public System.IntPtr tv_nsec;
+        public System.IntPtr tv_sec;
+        public System.IntPtr tv_nsec;
 
-		/// <summary>
-		/// Timespec a long time in the future.
-		/// </summary>
-		public static Timespec InfFuture
-		{
-			get
-			{
+        /// <summary>
+        /// Timespec a long time in the future.
+        /// </summary>
+        public static Timespec InfFuture
+        {
+            get
+            {
                 return gprsharp_inf_future();
-			}
-		}
+            }
+        }
 
         public static Timespec Now
         {
@@ -92,7 +88,8 @@ namespace Grpc.Core.Internal
         /// Creates a GPR deadline from current instant and given timeout.
         /// </summary>
         /// <returns>The from timeout.</returns>
-        public static Timespec DeadlineFromTimeout(TimeSpan timeout) {
+        public static Timespec DeadlineFromTimeout(TimeSpan timeout)
+        {
             if (timeout == Timeout.InfiniteTimeSpan)
             {
                 return Timespec.InfFuture;
@@ -100,7 +97,8 @@ namespace Grpc.Core.Internal
             return Timespec.Now.Add(timeout);
         }
 
-        public Timespec Add(TimeSpan timeSpan) {
+        public Timespec Add(TimeSpan timeSpan)
+        {
             long nanos = tv_nsec.ToInt64() + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick;
             long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0;
 
@@ -109,6 +107,6 @@ namespace Grpc.Core.Internal
             result.tv_sec = new IntPtr(tv_sec.ToInt64() + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec);
             return result;
         }
-	}
+    }
 }
 
diff --git a/src/csharp/Grpc.Core/OperationFailedException.cs b/src/csharp/Grpc.Core/OperationFailedException.cs
new file mode 100644
index 0000000000000000000000000000000000000000..34a8c95a85c3790f49582bdcd0e1d627e837a4c7
--- /dev/null
+++ b/src/csharp/Grpc.Core/OperationFailedException.cs
@@ -0,0 +1,48 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+
+namespace Grpc.Core
+{
+    /// <summary>
+    /// Thrown when gRPC operation fails.
+    /// </summary>
+    public class OperationFailedException : Exception
+    {
+        public OperationFailedException(string message) : base(message)
+        {
+        }
+    }
+}
+
diff --git a/src/csharp/Grpc.Core/ServerCallHandler.cs b/src/csharp/Grpc.Core/ServerCallHandler.cs
index 289f97aecee2777f6a7718bed807d1cacb3278c4..3eb8422f575e3b4f745e03f13856c254d76de10b 100644
--- a/src/csharp/Grpc.Core/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/ServerCallHandler.cs
@@ -32,7 +32,9 @@
 #endregion
 
 using System;
+using System.Linq;
 using Grpc.Core.Internal;
+using Grpc.Core.Utils;
 
 namespace Grpc.Core
 {
@@ -54,17 +56,17 @@ namespace Grpc.Core
 
         public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
         {
-            var asyncCall = new AsyncCall<TResponse, TRequest>(
+            var asyncCall = new AsyncCallServer<TRequest, TResponse>(
                 method.ResponseMarshaller.Serializer,
                 method.RequestMarshaller.Deserializer);
 
-            asyncCall.InitializeServer(call);
+            asyncCall.Initialize(call);
            
-            var finishedTask = asyncCall.ServerSideUnaryRequestCallAsync();
+            var requestObserver = new RecordingObserver<TRequest>();
+            var finishedTask = asyncCall.ServerSideCallAsync(requestObserver);
 
-            var request = asyncCall.ReceiveMessageAsync().Result;
-
-            var responseObserver = new ServerStreamingOutputObserver<TResponse, TRequest>(asyncCall);
+            var request = requestObserver.ToList().Result.Single();
+            var responseObserver = new ServerStreamingOutputObserver<TRequest, TResponse>(asyncCall);
             handler(request, responseObserver);
 
             finishedTask.Wait();
@@ -85,15 +87,15 @@ namespace Grpc.Core
 
         public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
         {
-            var asyncCall = new AsyncCall<TResponse, TRequest>(
+            var asyncCall = new AsyncCallServer<TRequest, TResponse>(
                 method.ResponseMarshaller.Serializer,
                 method.RequestMarshaller.Deserializer);
 
-            asyncCall.InitializeServer(call);
+            asyncCall.Initialize(call);
 
-            var responseObserver = new ServerStreamingOutputObserver<TResponse, TRequest>(asyncCall);
+            var responseObserver = new ServerStreamingOutputObserver<TRequest,TResponse>(asyncCall);
             var requestObserver = handler(responseObserver);
-            var finishedTask = asyncCall.ServerSideStreamingRequestCallAsync(requestObserver);
+            var finishedTask = asyncCall.ServerSideCallAsync(requestObserver);
             finishedTask.Wait();
         }
     }
@@ -103,17 +105,15 @@ namespace Grpc.Core
         public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
         {
             // We don't care about the payload type here.
-            AsyncCall<byte[], byte[]> asyncCall = new AsyncCall<byte[], byte[]>(
+            var asyncCall = new AsyncCallServer<byte[], byte[]>(
                 (payload) => payload, (payload) => payload);
 
+            asyncCall.Initialize(call);
 
-            asyncCall.InitializeServer(call);
-
-            var finishedTask = asyncCall.ServerSideStreamingRequestCallAsync(new NullObserver<byte[]>());
+            var finishedTask = asyncCall.ServerSideCallAsync(new NullObserver<byte[]>());
 
-            // TODO: this makes the call finish before all reads can be done which causes trouble
-            // in AsyncCall.HandleReadFinished callback. Revisit this.
-            asyncCall.SendStatusFromServerAsync(new Status(StatusCode.Unimplemented, "No such method.")).Wait();
+            // TODO: check result of the completion status.
+            asyncCall.StartSendStatusFromServer(new Status(StatusCode.Unimplemented, "No such method."), new AsyncCompletionDelegate((error) => {}));
 
             finishedTask.Wait();
         }
diff --git a/src/csharp/Grpc.Core/Status.cs b/src/csharp/Grpc.Core/Status.cs
index 5ea1df7b481c2bec149fb0550f04b152a427b336..080bbdc2f5b3544b5917126837490732ae1a3f9f 100644
--- a/src/csharp/Grpc.Core/Status.cs
+++ b/src/csharp/Grpc.Core/Status.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,7 +27,6 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
 
 using System;
@@ -36,34 +34,40 @@ using System.Runtime.InteropServices;
 
 namespace Grpc.Core
 {
-	/// <summary>
-	/// Represents RPC result.
-	/// </summary>
-	public struct Status
-	{
-		readonly StatusCode statusCode;
-		readonly string detail;
+    /// <summary>
+    /// Represents RPC result.
+    /// </summary>
+    public struct Status
+    {
+        readonly StatusCode statusCode;
+        readonly string detail;
 
-		public Status(StatusCode statusCode, string detail)
-		{
-			this.statusCode = statusCode;
-			this.detail = detail;
-		}
+        public Status(StatusCode statusCode, string detail)
+        {
+            this.statusCode = statusCode;
+            this.detail = detail;
+        }
 
-		public StatusCode StatusCode
-		{
-			get
-			{
-				return statusCode;
-			}
-		}
+        /// <summary>
+        /// Gets the gRPC status code. OK indicates success, all other values indicate an error.
+        /// </summary>
+        public StatusCode StatusCode
+        {
+            get
+            {
+                return statusCode;
+            }
+        }
 
-		public string Detail
-		{
-			get
-			{
-				return detail;
-			}
-		}
-	}
+        /// <summary>
+        /// Gets the detail.
+        /// </summary>
+        public string Detail
+        {
+            get
+            {
+                return detail;
+            }
+        }
+    }
 }
diff --git a/src/csharp/Grpc.Core/Utils/Preconditions.cs b/src/csharp/Grpc.Core/Utils/Preconditions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b17ce4211783701b6b4ab8d6e9fb84057fe0fc75
--- /dev/null
+++ b/src/csharp/Grpc.Core/Utils/Preconditions.cs
@@ -0,0 +1,113 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+
+namespace Grpc.Core.Utils
+{
+    public static class Preconditions
+    {
+        /// <summary>
+        /// Throws ArgumentException if condition is false.
+        /// </summary>
+        public static void CheckArgument(bool condition)
+        {
+            if (!condition)
+            {
+                throw new ArgumentException();
+            }
+        }
+
+        /// <summary>
+        /// Throws ArgumentException with given message if condition is false.
+        /// </summary>
+        public static void CheckArgument(bool condition, string errorMessage)
+        {
+            if (!condition)
+            {
+                throw new ArgumentException(errorMessage);
+            }
+        }
+
+        /// <summary>
+        /// Throws NullReferenceException if reference is null.
+        /// </summary>
+        public static T CheckNotNull<T> (T reference)
+        {
+            if (reference == null)
+            {
+                throw new NullReferenceException();
+            }
+            return reference;
+        }
+
+        /// <summary>
+        /// Throws NullReferenceException with given message if reference is null.
+        /// </summary>
+        public static T CheckNotNull<T> (T reference, string errorMessage)
+        {
+            if (reference == null)
+            {
+                throw new NullReferenceException(errorMessage);
+            }
+            return reference;
+        }
+
+        /// <summary>
+        /// Throws InvalidOperationException if condition is false.
+        /// </summary>
+        public static void CheckState(bool condition)
+        {
+            if (!condition)
+            {
+                throw new InvalidOperationException();
+            }
+        }
+
+        /// <summary>
+        /// Throws InvalidOperationException with given message if condition is false.
+        /// </summary>
+        public static void CheckState(bool condition, string errorMessage)
+        {
+            if (!condition)
+            {
+                throw new InvalidOperationException(errorMessage);
+            }
+        }
+    }
+}
+
diff --git a/src/csharp/Grpc.Examples.MathClient/MathClient.cs b/src/csharp/Grpc.Examples.MathClient/MathClient.cs
index 95a4678bb8f63e623bd0c3a3c78688922fe7e07a..f5956bd33e008cdafe2e0116d76640645e793570 100644
--- a/src/csharp/Grpc.Examples.MathClient/MathClient.cs
+++ b/src/csharp/Grpc.Examples.MathClient/MathClient.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,9 +27,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
-
 using System;
 using System.Runtime.InteropServices;
 using System.Threading;
@@ -38,25 +35,25 @@ using Grpc.Core;
 
 namespace math
 {
-	class MathClient
+    class MathClient
     {
-		public static void Main (string[] args)
-		{
+        public static void Main(string[] args)
+        {
             GrpcEnvironment.Initialize();
 
-			using (Channel channel = new Channel("127.0.0.1:23456"))
-			{
-				MathGrpc.IMathServiceClient stub = new MathGrpc.MathServiceClientStub(channel);
-				MathExamples.DivExample(stub);
+            using (Channel channel = new Channel("127.0.0.1:23456"))
+            {
+                MathGrpc.IMathServiceClient stub = new MathGrpc.MathServiceClientStub(channel);
+                MathExamples.DivExample(stub);
 
                 MathExamples.FibExample(stub);
 
-				MathExamples.SumExample(stub);
+                MathExamples.SumExample(stub);
 
-				MathExamples.DivManyExample(stub);
-			}
+                MathExamples.DivManyExample(stub);
+            }
 
             GrpcEnvironment.Shutdown();
-		}
-	}
+        }
+    }
 }
diff --git a/src/csharp/Grpc.Examples/MathExamples.cs b/src/csharp/Grpc.Examples/MathExamples.cs
index 97c91b1b1b586248b03e4d94200e7a9629627fef..134270f6f76c8e20c367d132c9e5bffe009df3db 100644
--- a/src/csharp/Grpc.Examples/MathExamples.cs
+++ b/src/csharp/Grpc.Examples/MathExamples.cs
@@ -1,5 +1,4 @@
 #region Copyright notice and license
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,7 +27,6 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 #endregion
 
 using System;
@@ -39,59 +37,63 @@ using Grpc.Core.Utils;
 
 namespace math
 {
-	public static class MathExamples
-	{
-		public static void DivExample(MathGrpc.IMathServiceClient stub)
-		{
-			DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build());
-			Console.WriteLine("Div Result: " + result);
-		}
-
-		public static void DivAsyncExample(MathGrpc.IMathServiceClient stub)
-		{
-			Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
-			DivReply result = call.Result;
-			Console.WriteLine(result);
-		}
-
-		public static void DivAsyncWithCancellationExample(MathGrpc.IMathServiceClient stub)
-		{
-			Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
-			DivReply result = call.Result;
-			Console.WriteLine(result);
-		}
-
-		public static void FibExample(MathGrpc.IMathServiceClient stub)
-		{
+    public static class MathExamples
+    {
+        public static void DivExample(MathGrpc.IMathServiceClient stub)
+        {
+            DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build());
+            Console.WriteLine("Div Result: " + result);
+        }
+
+        public static void DivAsyncExample(MathGrpc.IMathServiceClient stub)
+        {
+            Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
+            DivReply result = call.Result;
+            Console.WriteLine(result);
+        }
+
+        public static void DivAsyncWithCancellationExample(MathGrpc.IMathServiceClient stub)
+        {
+            Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
+            DivReply result = call.Result;
+            Console.WriteLine(result);
+        }
+
+        public static void FibExample(MathGrpc.IMathServiceClient stub)
+        {
             var recorder = new RecordingObserver<Num>();
             stub.Fib(new FibArgs.Builder { Limit = 5 }.Build(), recorder);
 
-			List<Num> numbers = recorder.ToList().Result;
+            List<Num> numbers = recorder.ToList().Result;
             Console.WriteLine("Fib Result: " + string.Join("|", recorder.ToList().Result));
-		}
+        }
 
-		public static void SumExample(MathGrpc.IMathServiceClient stub)
-		{
-			List<Num> numbers = new List<Num>{new Num.Builder { Num_ = 1 }.Build(),
-				new Num.Builder { Num_ = 2 }.Build(),
-				new Num.Builder { Num_ = 3 }.Build()};
+        public static void SumExample(MathGrpc.IMathServiceClient stub)
+        {
+            List<Num> numbers = new List<Num>
+            {new Num.Builder { Num_ = 1 }.Build(),
+                new Num.Builder { Num_ = 2 }.Build(),
+                new Num.Builder { Num_ = 3 }.Build()
+            };
 
             var res = stub.Sum();
-            foreach (var num in numbers) {
+            foreach (var num in numbers)
+            {
                 res.Inputs.OnNext(num);
             }
             res.Inputs.OnCompleted();
 
-			Console.WriteLine("Sum Result: " + res.Task.Result);
-		}
+            Console.WriteLine("Sum Result: " + res.Task.Result);
+        }
 
-		public static void DivManyExample(MathGrpc.IMathServiceClient stub)
-		{
-			List<DivArgs> divArgsList = new List<DivArgs>{
-				new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(),
-				new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(),
-				new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
-			};
+        public static void DivManyExample(MathGrpc.IMathServiceClient stub)
+        {
+            List<DivArgs> divArgsList = new List<DivArgs>
+            {
+                new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(),
+                new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(),
+                new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
+            };
 
             var recorder = new RecordingObserver<DivReply>();
 
@@ -102,30 +104,30 @@ namespace math
             }
             inputs.OnCompleted();
 
-			Console.WriteLine("DivMany Result: " + string.Join("|", recorder.ToList().Result));
-		}
+            Console.WriteLine("DivMany Result: " + string.Join("|", recorder.ToList().Result));
+        }
 
-		public static void DependendRequestsExample(MathGrpc.IMathServiceClient stub)
-		{
-			var numberList = new List<Num>
-			{ new Num.Builder{ Num_ = 1 }.Build(),
-				new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build()
-			};
+        public static void DependendRequestsExample(MathGrpc.IMathServiceClient stub)
+        {
+            var numberList = new List<Num>
+            { new Num.Builder{ Num_ = 1 }.Build(),
+                new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build()
+            };
 
-			numberList.ToObservable();
+            numberList.ToObservable();
 
-			//IObserver<Num> numbers;
-			//Task<Num> call = stub.Sum(out numbers);
-			//foreach (var num in numberList)
-			//{
-			//	numbers.OnNext(num);
-			//}
-			//numbers.OnCompleted();
+            //IObserver<Num> numbers;
+            //Task<Num> call = stub.Sum(out numbers);
+            //foreach (var num in numberList)
+            //{
+            //  numbers.OnNext(num);
+            //}
+            //numbers.OnCompleted();
 
-			//Num sum = call.Result;
+            //Num sum = call.Result;
 
-			//DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build());
-		}
-	}
+            //DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build());
+        }
+    }
 }
 
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index 6d6aaf57473b547f5beb72fdb95bd795ed619b7a..8f7a17efcb187be00ac1163e0185dc46b6b7066f 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -62,8 +62,21 @@
     <None Include="proto\test.proto" />
     <None Include="proto\empty.proto" />
     <None Include="proto\messages.proto" />
+    <None Include="data\README">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="data\ca.pem">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="data\server1.key">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="data\server1.pem">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="proto\" />
+    <Folder Include="data\" />
   </ItemGroup>
 </Project>
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index a7a3c63e03296a3890a3f49c5994c1932879e2a1..30301f165baddff97bafac6a351798453b770e84 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -34,6 +34,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.IO;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using Google.ProtocolBuffers;
@@ -49,10 +50,10 @@ namespace Grpc.IntegrationTesting
         private class ClientOptions
         {
             public bool help;
-            public string serverHost;
-            public string serverHostOverride;
+            public string serverHost= "127.0.0.1";
+            public string serverHostOverride = "foo.test.google.fr";
             public int? serverPort;
-            public string testCase;
+            public string testCase = "large_unary";
             public bool useTls;
             public bool useTestCa;
         }
@@ -98,10 +99,32 @@ namespace Grpc.IntegrationTesting
             GrpcEnvironment.Initialize();
 
             string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort);
-            using (Channel channel = new Channel(addr))
+
+            Credentials credentials = null;
+            if (options.useTls)
             {
-                TestServiceGrpc.ITestServiceClient client = new TestServiceGrpc.TestServiceClientStub(channel);
+                string caPath = "data/ca.pem";  // Default testing CA
+                if (!options.useTestCa)
+                {
+                    caPath = Environment.GetEnvironmentVariable("SSL_CERT_FILE");
+                    if (string.IsNullOrEmpty(caPath))
+                    {
+                        throw new ArgumentException("CA path environment variable is not set.");
+                    }
+                }
+                credentials = new SslCredentials(File.ReadAllText(caPath));
+            }
 
+            ChannelArgs channelArgs = null;
+            if (!string.IsNullOrEmpty(options.serverHostOverride))
+            {
+                channelArgs = ChannelArgs.NewBuilder()
+                    .AddString(ChannelArgs.SslTargetNameOverrideKey, options.serverHostOverride).Build();
+            }
+
+            using (Channel channel = new Channel(addr, credentials, channelArgs))
+            {
+                TestServiceGrpc.ITestServiceClient client = new TestServiceGrpc.TestServiceClientStub(channel);
                 RunTestCase(options.testCase, client);
             }
 
diff --git a/src/csharp/Grpc.IntegrationTesting/data/README b/src/csharp/Grpc.IntegrationTesting/data/README
new file mode 100644
index 0000000000000000000000000000000000000000..888d95b9004f9d2aa72ac62d86233fa2e0a8ba8f
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting/data/README
@@ -0,0 +1 @@
+CONFIRMEDTESTKEY
diff --git a/src/csharp/Grpc.IntegrationTesting/data/ca.pem b/src/csharp/Grpc.IntegrationTesting/data/ca.pem
new file mode 100644
index 0000000000000000000000000000000000000000..6c8511a73c68ac6afa6f0cb0b25c05ca8318e2a8
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting/data/ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
+Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
+BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
+g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
+Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
+sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
+oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
+Dfcog5wrJytaQ6UA0wE=
+-----END CERTIFICATE-----
diff --git a/src/csharp/Grpc.IntegrationTesting/data/server1.key b/src/csharp/Grpc.IntegrationTesting/data/server1.key
new file mode 100644
index 0000000000000000000000000000000000000000..143a5b87658d5c3519de970f37efd1c32a98223d
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting/data/server1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
+M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
+3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
+AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
+V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
+tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
+dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
+K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
+81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
+DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
+aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
+ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
+XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
+F98XJ7tIFfJq
+-----END PRIVATE KEY-----
diff --git a/src/csharp/Grpc.IntegrationTesting/data/server1.pem b/src/csharp/Grpc.IntegrationTesting/data/server1.pem
new file mode 100644
index 0000000000000000000000000000000000000000..8e582e571f643ca3fb6042a786c709a53f5afcf3
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting/data/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 8f5a414187273b34250d1a8ad423e2efda6bee3d..e24438704cfb0fc5085318e323ee622027bbbfbd 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -39,6 +39,7 @@
 #include <grpc/support/slice.h>
 #include <grpc/support/thd.h>
 #include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
 
 #include <string.h>
 
@@ -266,6 +267,45 @@ grpcsharp_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq,
   return grpc_channel_create_call(channel, cq, method, host, deadline);
 }
 
+/* Channel args */
+
+GPR_EXPORT grpc_channel_args *GPR_CALLTYPE
+grpcsharp_channel_args_create(size_t num_args) {
+  grpc_channel_args *args =
+      (grpc_channel_args *)gpr_malloc(sizeof(grpc_channel_args));
+  memset(args, 0, sizeof(grpc_channel_args));
+
+  args->num_args = num_args;
+  args->args = (grpc_arg *)gpr_malloc(sizeof(grpc_arg) * num_args);
+  memset(args->args, 0, sizeof(grpc_arg) * num_args);
+  return args;
+}
+
+GPR_EXPORT void GPR_CALLTYPE
+grpcsharp_channel_args_set_string(grpc_channel_args *args, size_t index,
+                                  const char *key, const char *value) {
+  GPR_ASSERT(args);
+  GPR_ASSERT(index < args->num_args);
+  args->args[index].type = GRPC_ARG_STRING;
+  args->args[index].key = gpr_strdup(key);
+  args->args[index].value.string = gpr_strdup(value);
+}
+
+GPR_EXPORT void GPR_CALLTYPE
+grpcsharp_channel_args_destroy(grpc_channel_args *args) {
+  size_t i;
+  if (args) {
+    for (i = 0; i < args->num_args; i++) {
+      gpr_free(args->args[i].key);
+      if (args->args[i].type == GRPC_ARG_STRING) {
+        gpr_free(args->args[i].value.string);
+      }
+    }
+    gpr_free(args->args);
+    gpr_free(args);
+  }
+}
+
 /* Timespec */
 
 GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(void) { return gpr_now(); }
@@ -585,6 +625,34 @@ grpcsharp_server_request_call(grpc_server *server, grpc_completion_queue *cq,
       &(ctx->server_rpc_new.request_metadata), cq, ctx);
 }
 
+/* Security */
+
+GPR_EXPORT grpc_credentials *GPR_CALLTYPE
+grpcsharp_ssl_credentials_create(const char *pem_root_certs,
+                                 const char *key_cert_pair_cert_chain,
+                                 const char *key_cert_pair_private_key) {
+  grpc_ssl_pem_key_cert_pair key_cert_pair;
+  if (key_cert_pair_cert_chain || key_cert_pair_private_key) {
+    key_cert_pair.cert_chain = key_cert_pair_cert_chain;
+    key_cert_pair.private_key = key_cert_pair_private_key;
+    return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair);
+  } else {
+    GPR_ASSERT(!key_cert_pair_cert_chain);
+    GPR_ASSERT(!key_cert_pair_private_key);
+    return grpc_ssl_credentials_create(pem_root_certs, NULL);
+  }
+}
+
+GPR_EXPORT void grpcsharp_credentials_release(grpc_credentials *creds) {
+  grpc_credentials_release(creds);
+}
+
+GPR_EXPORT grpc_channel *GPR_CALLTYPE
+grpcsharp_secure_channel_create(grpc_credentials *creds, const char *target,
+                                const grpc_channel_args *args) {
+  return grpc_secure_channel_create(creds, target, args);
+}
+
 /* Logging */
 
 typedef void(GPR_CALLTYPE *grpcsharp_log_func)(const char *file, gpr_int32 line,
diff --git a/src/node/binding.gyp b/src/node/binding.gyp
index 10afaf696271f120ce1ef2fb10e80a36ee3d72e8..7ef3bdf4bdedaf150aaa75d8ef0b4f290d3d877d 100644
--- a/src/node/binding.gyp
+++ b/src/node/binding.gyp
@@ -1,9 +1,4 @@
 {
-  "variables" : {
-    'no_install': "<!(echo $GRPC_NO_INSTALL)",
-    'grpc_root': "<!(echo $GRPC_ROOT)",
-    'grpc_lib_subdir': "<!(echo $GRPC_LIB_SUBDIR)"
-    },
   "targets" : [
     {
       'include_dirs': [
@@ -24,7 +19,9 @@
       'link_settings': {
         'libraries': [
           '-lrt',
-          '-lpthread'
+          '-lpthread',
+          '-lgrpc',
+          '-lgpr'
         ],
       },
       "target_name": "grpc",
@@ -38,27 +35,6 @@
         "ext/server.cc",
         "ext/server_credentials.cc",
         "ext/timeval.cc"
-      ],
-      'conditions' : [
-        ['no_install=="yes"', {
-          'include_dirs': [
-            "<(grpc_root)/include"
-          ],
-          'link_settings': {
-            'libraries': [
-              '<(grpc_root)/<(grpc_lib_subdir)/libgrpc.a',
-              '<(grpc_root)/<(grpc_lib_subdir)/libgpr.a'
-            ]
-          }
-        }],
-        ['no_install!="yes"', {
-            'link_settings': {
-              'libraries': [
-                '-lgrpc',
-                '-lgpr'
-              ]
-            }
-          }]
       ]
     }
   ]
diff --git a/src/node/ext/byte_buffer.cc b/src/node/ext/byte_buffer.cc
index c165d26e47e1161a48cdc9b45ab43d7ebba7acf8..82b54b518c0e1c1221e632a9e783f8d74d1baa41 100644
--- a/src/node/ext/byte_buffer.cc
+++ b/src/node/ext/byte_buffer.cc
@@ -44,7 +44,6 @@
 namespace grpc {
 namespace node {
 
-using ::node::Buffer;
 using v8::Context;
 using v8::Function;
 using v8::Handle;
@@ -54,8 +53,8 @@ using v8::Value;
 
 grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) {
   NanScope();
-  int length = Buffer::Length(buffer);
-  char *data = Buffer::Data(buffer);
+  int length = ::node::Buffer::Length(buffer);
+  char *data = ::node::Buffer::Data(buffer);
   gpr_slice slice = gpr_slice_malloc(length);
   memcpy(GPR_SLICE_START_PTR(slice), data, length);
   grpc_byte_buffer *byte_buffer(grpc_byte_buffer_create(&slice, 1));
@@ -66,7 +65,7 @@ grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) {
 Handle<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) {
   NanEscapableScope();
   if (buffer == NULL) {
-    NanReturnNull();
+    return NanEscapeScope(NanNull());
   }
   size_t length = grpc_byte_buffer_length(buffer);
   char *result = reinterpret_cast<char *>(calloc(length, sizeof(char)));
@@ -82,12 +81,14 @@ Handle<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) {
 
 Handle<Value> MakeFastBuffer(Handle<Value> slowBuffer) {
   NanEscapableScope();
-  Handle<Object> globalObj = Context::GetCurrent()->Global();
+  Handle<Object> globalObj = NanGetCurrentContext()->Global();
   Handle<Function> bufferConstructor = Handle<Function>::Cast(
       globalObj->Get(NanNew("Buffer")));
-  Handle<Value> consArgs[3] = { slowBuffer,
-                                NanNew<Number>(Buffer::Length(slowBuffer)),
-                                NanNew<Number>(0) };
+  Handle<Value> consArgs[3] = {
+    slowBuffer,
+    NanNew<Number>(::node::Buffer::Length(slowBuffer)),
+    NanNew<Number>(0)
+  };
   Handle<Object> fastBuffer = bufferConstructor->NewInstance(3, consArgs);
   return NanEscapeScope(fastBuffer);
 }
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index 9ed389f3bc5faa410ae0e84e0d1bd4d69c2a44ff..afb654178318127ec38b454cc2379fd41ddf1eae 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -54,8 +54,6 @@ using std::vector;
 namespace grpc {
 namespace node {
 
-using ::node::Buffer;
-using v8::Arguments;
 using v8::Array;
 using v8::Boolean;
 using v8::Exception;
@@ -74,7 +72,7 @@ using v8::Uint32;
 using v8::String;
 using v8::Value;
 
-Persistent<Function> Call::constructor;
+NanCallback *Call::constructor;
 Persistent<FunctionTemplate> Call::fun_tpl;
 
 
@@ -101,11 +99,11 @@ bool CreateMetadataArray(Handle<Object> metadata, grpc_metadata_array *array,
       Handle<Value> value = values->Get(j);
       grpc_metadata *current = &array->metadata[array->count];
       current->key = **utf8_key;
-      if (Buffer::HasInstance(value)) {
-        current->value = Buffer::Data(value);
-        current->value_length = Buffer::Length(value);
-        Persistent<Value> handle;
-        NanAssignPersistent(handle, value);
+      if (::node::Buffer::HasInstance(value)) {
+        current->value = ::node::Buffer::Data(value);
+        current->value_length = ::node::Buffer::Length(value);
+        Persistent<Value> *handle = new Persistent<Value>();
+        NanAssignPersistent(*handle, value);
         resources->handles.push_back(unique_ptr<PersistentHolder>(
             new PersistentHolder(handle)));
       } else if (value->IsString()) {
@@ -140,7 +138,7 @@ Handle<Value> ParseMetadata(const grpc_metadata_array *metadata_array) {
   Handle<Object> metadata_object = NanNew<Object>();
   for (unsigned int i = 0; i < length; i++) {
     grpc_metadata* elem = &metadata_elements[i];
-    Handle<String> key_string = String::New(elem->key);
+    Handle<String> key_string = NanNew(elem->key);
     Handle<Array> array;
     if (metadata_object->Has(key_string)) {
       array = Handle<Array>::Cast(metadata_object->Get(key_string));
@@ -194,12 +192,12 @@ class SendMessageOp : public Op {
   }
   bool ParseOp(Handle<Value> value, grpc_op *out,
                shared_ptr<Resources> resources) {
-    if (!Buffer::HasInstance(value)) {
+    if (!::node::Buffer::HasInstance(value)) {
       return false;
     }
     out->data.send_message = BufferToByteBuffer(value);
-    Persistent<Value> handle;
-    NanAssignPersistent(handle, value);
+    Persistent<Value> *handle = new Persistent<Value>();
+    NanAssignPersistent(*handle, value);
     resources->handles.push_back(unique_ptr<PersistentHolder>(
         new PersistentHolder(handle)));
     return true;
@@ -357,7 +355,7 @@ class ClientStatusOp : public Op {
     Handle<Object> status_obj = NanNew<Object>();
     status_obj->Set(NanNew("code"), NanNew<Number>(status));
     if (status_details != NULL) {
-      status_obj->Set(NanNew("details"), String::New(status_details));
+      status_obj->Set(NanNew("details"), NanNew(status_details));
     }
     status_obj->Set(NanNew("metadata"), ParseMetadata(&metadata_array));
     return NanEscapeScope(status_obj);
@@ -436,20 +434,21 @@ Call::~Call() {
 
 void Call::Init(Handle<Object> exports) {
   NanScope();
-  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
   tpl->SetClassName(NanNew("Call"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NanSetPrototypeTemplate(tpl, "startBatch",
-                          FunctionTemplate::New(StartBatch)->GetFunction());
+                          NanNew<FunctionTemplate>(StartBatch)->GetFunction());
   NanSetPrototypeTemplate(tpl, "cancel",
-                          FunctionTemplate::New(Cancel)->GetFunction());
+                          NanNew<FunctionTemplate>(Cancel)->GetFunction());
   NanAssignPersistent(fun_tpl, tpl);
-  NanAssignPersistent(constructor, tpl->GetFunction());
-  constructor->Set(NanNew("WRITE_BUFFER_HINT"),
-                   NanNew<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT));
-  constructor->Set(NanNew("WRITE_NO_COMPRESS"),
-                   NanNew<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS));
-  exports->Set(String::NewSymbol("Call"), constructor);
+  Handle<Function> ctr = tpl->GetFunction();
+  ctr->Set(NanNew("WRITE_BUFFER_HINT"),
+           NanNew<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT));
+  ctr->Set(NanNew("WRITE_NO_COMPRESS"),
+           NanNew<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS));
+  exports->Set(NanNew("Call"), ctr);
+  constructor = new NanCallback(ctr);
 }
 
 bool Call::HasInstance(Handle<Value> val) {
@@ -463,8 +462,8 @@ Handle<Value> Call::WrapStruct(grpc_call *call) {
     return NanEscapeScope(NanNull());
   }
   const int argc = 1;
-  Handle<Value> argv[argc] = {External::New(reinterpret_cast<void *>(call))};
-  return NanEscapeScope(constructor->NewInstance(argc, argv));
+  Handle<Value> argv[argc] = {NanNew<External>(reinterpret_cast<void *>(call))};
+  return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv));
 }
 
 NAN_METHOD(Call::New) {
@@ -473,9 +472,10 @@ NAN_METHOD(Call::New) {
   if (args.IsConstructCall()) {
     Call *call;
     if (args[0]->IsExternal()) {
+      Handle<External> ext = args[0].As<External>();
       // This option is used for wrapping an existing call
       grpc_call *call_value =
-          reinterpret_cast<grpc_call *>(External::Unwrap(args[0]));
+          reinterpret_cast<grpc_call *>(ext->Value());
       call = new Call(call_value);
     } else {
       if (!Channel::HasInstance(args[0])) {
@@ -500,15 +500,14 @@ NAN_METHOD(Call::New) {
           wrapped_channel, CompletionQueueAsyncWorker::GetQueue(), *method,
           channel->GetHost(), MillisecondsToTimespec(deadline));
       call = new Call(wrapped_call);
-      args.This()->SetHiddenValue(String::NewSymbol("channel_"),
-                                  channel_object);
+      args.This()->SetHiddenValue(NanNew("channel_"), channel_object);
     }
     call->Wrap(args.This());
     NanReturnValue(args.This());
   } else {
     const int argc = 4;
     Local<Value> argv[argc] = {args[0], args[1], args[2], args[3]};
-    NanReturnValue(constructor->NewInstance(argc, argv));
+    NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv));
   }
 }
 
diff --git a/src/node/ext/call.h b/src/node/ext/call.h
index 933541ce5b654aed5bfedd9a91ae373f4d6e2ef1..43142c7091fe6c30b434a03d413c22f844853771 100644
--- a/src/node/ext/call.h
+++ b/src/node/ext/call.h
@@ -40,6 +40,7 @@
 #include <node.h>
 #include <nan.h>
 #include "grpc/grpc.h"
+#include "grpc/support/log.h"
 
 #include "channel.h"
 
@@ -54,16 +55,17 @@ v8::Handle<v8::Value> ParseMetadata(const grpc_metadata_array *metadata_array);
 
 class PersistentHolder {
  public:
-  explicit PersistentHolder(v8::Persistent<v8::Value> persist) :
+  explicit PersistentHolder(v8::Persistent<v8::Value> *persist) :
       persist(persist) {
   }
 
   ~PersistentHolder() {
-    NanDisposePersistent(persist);
+    NanDisposePersistent(*persist);
+    delete persist;
   }
 
  private:
-  v8::Persistent<v8::Value> persist;
+  v8::Persistent<v8::Value> *persist;
 };
 
 struct Resources {
@@ -118,7 +120,7 @@ class Call : public ::node::ObjectWrap {
   static NAN_METHOD(New);
   static NAN_METHOD(StartBatch);
   static NAN_METHOD(Cancel);
-  static v8::Persistent<v8::Function> constructor;
+  static NanCallback *constructor;
   // Used for typechecking instances of this javascript class
   static v8::Persistent<v8::FunctionTemplate> fun_tpl;
 
diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc
index bc9461d7dfd39535c287c8246a3fe1010855edab..787e274973fdf7b9cd248a7090c0f08041a3caa3 100644
--- a/src/node/ext/channel.cc
+++ b/src/node/ext/channel.cc
@@ -45,7 +45,6 @@
 namespace grpc {
 namespace node {
 
-using v8::Arguments;
 using v8::Array;
 using v8::Exception;
 using v8::Function;
@@ -59,7 +58,7 @@ using v8::Persistent;
 using v8::String;
 using v8::Value;
 
-Persistent<Function> Channel::constructor;
+NanCallback *Channel::constructor;
 Persistent<FunctionTemplate> Channel::fun_tpl;
 
 Channel::Channel(grpc_channel *channel, NanUtf8String *host)
@@ -74,14 +73,15 @@ Channel::~Channel() {
 
 void Channel::Init(Handle<Object> exports) {
   NanScope();
-  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
   tpl->SetClassName(NanNew("Channel"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NanSetPrototypeTemplate(tpl, "close",
-                          FunctionTemplate::New(Close)->GetFunction());
+                          NanNew<FunctionTemplate>(Close)->GetFunction());
   NanAssignPersistent(fun_tpl, tpl);
-  NanAssignPersistent(constructor, tpl->GetFunction());
-  exports->Set(NanNew("Channel"), constructor);
+  Handle<Function> ctr = tpl->GetFunction();
+  constructor = new NanCallback(ctr);
+  exports->Set(NanNew("Channel"), ctr);
 }
 
 bool Channel::HasInstance(Handle<Value> val) {
@@ -170,7 +170,7 @@ NAN_METHOD(Channel::New) {
   } else {
     const int argc = 2;
     Local<Value> argv[argc] = {args[0], args[1]};
-    NanReturnValue(constructor->NewInstance(argc, argv));
+    NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv));
   }
 }
 
diff --git a/src/node/ext/channel.h b/src/node/ext/channel.h
index bf793194d9a7ff1cb91bd90b5d15f043f83d983e..b3aa0f700fa98b16f47a4fa6bd4834b0f550a600 100644
--- a/src/node/ext/channel.h
+++ b/src/node/ext/channel.h
@@ -66,7 +66,7 @@ class Channel : public ::node::ObjectWrap {
 
   static NAN_METHOD(New);
   static NAN_METHOD(Close);
-  static v8::Persistent<v8::Function> constructor;
+  static NanCallback *constructor;
   static v8::Persistent<v8::FunctionTemplate> fun_tpl;
 
   grpc_channel *wrapped_channel;
diff --git a/src/node/ext/completion_queue_async_worker.cc b/src/node/ext/completion_queue_async_worker.cc
index ca22527e6f547a776d49cda075f88ca0272e97a5..cd7acd1d1b11d4ebdcebc8d3c392211d8f645928 100644
--- a/src/node/ext/completion_queue_async_worker.cc
+++ b/src/node/ext/completion_queue_async_worker.cc
@@ -80,7 +80,6 @@ void CompletionQueueAsyncWorker::HandleOKCallback() {
   NanScope();
   NanCallback *callback = GetTagCallback(result->tag);
   Handle<Value> argv[] = {NanNull(), GetTagNodeValue(result->tag)};
-
   callback->Call(2, argv);
 
   DestroyTag(result->tag);
diff --git a/src/node/ext/credentials.cc b/src/node/ext/credentials.cc
index 3f65d59c766be9641fefb804b4e1f26baf2fccd0..34872017ea186eb76e3f24a2f95f003f8e8e7f71 100644
--- a/src/node/ext/credentials.cc
+++ b/src/node/ext/credentials.cc
@@ -41,8 +41,6 @@
 namespace grpc {
 namespace node {
 
-using ::node::Buffer;
-using v8::Arguments;
 using v8::Exception;
 using v8::External;
 using v8::Function;
@@ -56,7 +54,7 @@ using v8::ObjectTemplate;
 using v8::Persistent;
 using v8::Value;
 
-Persistent<Function> Credentials::constructor;
+NanCallback *Credentials::constructor;
 Persistent<FunctionTemplate> Credentials::fun_tpl;
 
 Credentials::Credentials(grpc_credentials *credentials)
@@ -68,24 +66,25 @@ Credentials::~Credentials() {
 
 void Credentials::Init(Handle<Object> exports) {
   NanScope();
-  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
   tpl->SetClassName(NanNew("Credentials"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NanAssignPersistent(fun_tpl, tpl);
-  NanAssignPersistent(constructor, tpl->GetFunction());
-  constructor->Set(NanNew("createDefault"),
-                   FunctionTemplate::New(CreateDefault)->GetFunction());
-  constructor->Set(NanNew("createSsl"),
-                   FunctionTemplate::New(CreateSsl)->GetFunction());
-  constructor->Set(NanNew("createComposite"),
-                   FunctionTemplate::New(CreateComposite)->GetFunction());
-  constructor->Set(NanNew("createGce"),
-                   FunctionTemplate::New(CreateGce)->GetFunction());
-  constructor->Set(NanNew("createFake"),
-                   FunctionTemplate::New(CreateFake)->GetFunction());
-  constructor->Set(NanNew("createIam"),
-                   FunctionTemplate::New(CreateIam)->GetFunction());
-  exports->Set(NanNew("Credentials"), constructor);
+  Handle<Function> ctr = tpl->GetFunction();
+  ctr->Set(NanNew("createDefault"),
+           NanNew<FunctionTemplate>(CreateDefault)->GetFunction());
+  ctr->Set(NanNew("createSsl"),
+           NanNew<FunctionTemplate>(CreateSsl)->GetFunction());
+  ctr->Set(NanNew("createComposite"),
+           NanNew<FunctionTemplate>(CreateComposite)->GetFunction());
+  ctr->Set(NanNew("createGce"),
+           NanNew<FunctionTemplate>(CreateGce)->GetFunction());
+  ctr->Set(NanNew("createFake"),
+           NanNew<FunctionTemplate>(CreateFake)->GetFunction());
+  ctr->Set(NanNew("createIam"),
+           NanNew<FunctionTemplate>(CreateIam)->GetFunction());
+  constructor = new NanCallback(ctr);
+  exports->Set(NanNew("Credentials"), ctr);
 }
 
 bool Credentials::HasInstance(Handle<Value> val) {
@@ -100,8 +99,8 @@ Handle<Value> Credentials::WrapStruct(grpc_credentials *credentials) {
   }
   const int argc = 1;
   Handle<Value> argv[argc] = {
-      External::New(reinterpret_cast<void *>(credentials))};
-  return NanEscapeScope(constructor->NewInstance(argc, argv));
+    NanNew<External>(reinterpret_cast<void *>(credentials))};
+  return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv));
 }
 
 grpc_credentials *Credentials::GetWrappedCredentials() {
@@ -116,15 +115,16 @@ NAN_METHOD(Credentials::New) {
       return NanThrowTypeError(
           "Credentials can only be created with the provided functions");
     }
+    Handle<External> ext = args[0].As<External>();
     grpc_credentials *creds_value =
-        reinterpret_cast<grpc_credentials *>(External::Unwrap(args[0]));
+        reinterpret_cast<grpc_credentials *>(ext->Value());
     Credentials *credentials = new Credentials(creds_value);
     credentials->Wrap(args.This());
     NanReturnValue(args.This());
   } else {
     const int argc = 1;
     Local<Value> argv[argc] = {args[0]};
-    NanReturnValue(constructor->NewInstance(argc, argv));
+    NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv));
   }
 }
 
@@ -137,19 +137,19 @@ NAN_METHOD(Credentials::CreateSsl) {
   NanScope();
   char *root_certs = NULL;
   grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
-  if (Buffer::HasInstance(args[0])) {
-    root_certs = Buffer::Data(args[0]);
+  if (::node::Buffer::HasInstance(args[0])) {
+    root_certs = ::node::Buffer::Data(args[0]);
   } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) {
     return NanThrowTypeError("createSsl's first argument must be a Buffer");
   }
-  if (Buffer::HasInstance(args[1])) {
-    key_cert_pair.private_key = Buffer::Data(args[1]);
+  if (::node::Buffer::HasInstance(args[1])) {
+    key_cert_pair.private_key = ::node::Buffer::Data(args[1]);
   } else if (!(args[1]->IsNull() || args[1]->IsUndefined())) {
     return NanThrowTypeError(
         "createSSl's second argument must be a Buffer if provided");
   }
-  if (Buffer::HasInstance(args[2])) {
-    key_cert_pair.cert_chain = Buffer::Data(args[2]);
+  if (::node::Buffer::HasInstance(args[2])) {
+    key_cert_pair.cert_chain = ::node::Buffer::Data(args[2]);
   } else if (!(args[2]->IsNull() || args[2]->IsUndefined())) {
     return NanThrowTypeError(
         "createSSl's third argument must be a Buffer if provided");
diff --git a/src/node/ext/credentials.h b/src/node/ext/credentials.h
index e60be3d4e155b7581ff55e2649402cf2bf8e5b45..794736fe1ab51d74c5555a77a88f4ba21be2032f 100644
--- a/src/node/ext/credentials.h
+++ b/src/node/ext/credentials.h
@@ -68,7 +68,7 @@ class Credentials : public ::node::ObjectWrap {
   static NAN_METHOD(CreateGce);
   static NAN_METHOD(CreateFake);
   static NAN_METHOD(CreateIam);
-  static v8::Persistent<v8::Function> constructor;
+  static NanCallback *constructor;
   // Used for typechecking instances of this javascript class
   static v8::Persistent<v8::FunctionTemplate> fun_tpl;
 
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index 9f5095839cb7404ffe21a5ee320a11049d1292b2..4e31cbaa277bc6c164787b661b3d04aa3d5f87f9 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -51,7 +51,7 @@ using v8::String;
 
 void InitStatusConstants(Handle<Object> exports) {
   NanScope();
-  Handle<Object> status = Object::New();
+  Handle<Object> status = NanNew<Object>();
   exports->Set(NanNew("status"), status);
   Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_STATUS_OK));
   status->Set(NanNew("OK"), OK);
@@ -100,7 +100,7 @@ void InitStatusConstants(Handle<Object> exports) {
 
 void InitCallErrorConstants(Handle<Object> exports) {
   NanScope();
-  Handle<Object> call_error = Object::New();
+  Handle<Object> call_error = NanNew<Object>();
   exports->Set(NanNew("callError"), call_error);
   Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_CALL_OK));
   call_error->Set(NanNew("OK"), OK);
@@ -131,7 +131,7 @@ void InitCallErrorConstants(Handle<Object> exports) {
 
 void InitOpTypeConstants(Handle<Object> exports) {
   NanScope();
-  Handle<Object> op_type = Object::New();
+  Handle<Object> op_type = NanNew<Object>();
   exports->Set(NanNew("opType"), op_type);
   Handle<Value> SEND_INITIAL_METADATA(
       NanNew<Uint32, uint32_t>(GRPC_OP_SEND_INITIAL_METADATA));
diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc
index ab45da8d199692f0c90ebbcd55d65d76e697639b..e47bac833bd24c31cb58e965d700f2ccfb20faaf 100644
--- a/src/node/ext/server.cc
+++ b/src/node/ext/server.cc
@@ -53,7 +53,6 @@ namespace grpc {
 namespace node {
 
 using std::unique_ptr;
-using v8::Arguments;
 using v8::Array;
 using v8::Boolean;
 using v8::Date;
@@ -69,7 +68,7 @@ using v8::Persistent;
 using v8::String;
 using v8::Value;
 
-Persistent<Function> Server::constructor;
+NanCallback *Server::constructor;
 Persistent<FunctionTemplate> Server::fun_tpl;
 
 class NewCallOp : public Op {
@@ -121,28 +120,30 @@ Server::~Server() { grpc_server_destroy(wrapped_server); }
 
 void Server::Init(Handle<Object> exports) {
   NanScope();
-  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
-  tpl->SetClassName(String::NewSymbol("Server"));
+  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
+  tpl->SetClassName(NanNew("Server"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NanSetPrototypeTemplate(tpl, "requestCall",
-                          FunctionTemplate::New(RequestCall)->GetFunction());
+                          NanNew<FunctionTemplate>(RequestCall)->GetFunction());
 
-  NanSetPrototypeTemplate(tpl, "addHttp2Port",
-                          FunctionTemplate::New(AddHttp2Port)->GetFunction());
+  NanSetPrototypeTemplate(
+      tpl, "addHttp2Port",
+      NanNew<FunctionTemplate>(AddHttp2Port)->GetFunction());
 
   NanSetPrototypeTemplate(
       tpl, "addSecureHttp2Port",
-      FunctionTemplate::New(AddSecureHttp2Port)->GetFunction());
+      NanNew<FunctionTemplate>(AddSecureHttp2Port)->GetFunction());
 
   NanSetPrototypeTemplate(tpl, "start",
-                          FunctionTemplate::New(Start)->GetFunction());
+                          NanNew<FunctionTemplate>(Start)->GetFunction());
 
   NanSetPrototypeTemplate(tpl, "shutdown",
-                          FunctionTemplate::New(Shutdown)->GetFunction());
+                          NanNew<FunctionTemplate>(Shutdown)->GetFunction());
 
   NanAssignPersistent(fun_tpl, tpl);
-  NanAssignPersistent(constructor, tpl->GetFunction());
-  exports->Set(String::NewSymbol("Server"), constructor);
+  Handle<Function> ctr = tpl->GetFunction();
+  constructor = new NanCallback(ctr);
+  exports->Set(NanNew("Server"), ctr);
 }
 
 bool Server::HasInstance(Handle<Value> val) {
@@ -157,26 +158,14 @@ NAN_METHOD(Server::New) {
   if (!args.IsConstructCall()) {
     const int argc = 1;
     Local<Value> argv[argc] = {args[0]};
-    NanReturnValue(constructor->NewInstance(argc, argv));
+    NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv));
   }
   grpc_server *wrapped_server;
   grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue();
   if (args[0]->IsUndefined()) {
     wrapped_server = grpc_server_create(queue, NULL);
   } else if (args[0]->IsObject()) {
-    grpc_server_credentials *creds = NULL;
-    Handle<Object> args_hash(args[0]->ToObject()->Clone());
-    if (args_hash->HasOwnProperty(NanNew("credentials"))) {
-      Handle<Value> creds_value = args_hash->Get(NanNew("credentials"));
-      if (!ServerCredentials::HasInstance(creds_value)) {
-        return NanThrowTypeError(
-            "credentials arg must be a ServerCredentials object");
-      }
-      ServerCredentials *creds_object =
-          ObjectWrap::Unwrap<ServerCredentials>(creds_value->ToObject());
-      creds = creds_object->GetWrappedServerCredentials();
-      args_hash->Delete(NanNew("credentials"));
-    }
+    Handle<Object> args_hash(args[0]->ToObject());
     Handle<Array> keys(args_hash->GetOwnPropertyNames());
     grpc_channel_args channel_args;
     channel_args.num_args = keys->Length();
@@ -203,11 +192,7 @@ NAN_METHOD(Server::New) {
         return NanThrowTypeError("Arg values must be strings");
       }
     }
-    if (creds == NULL) {
-      wrapped_server = grpc_server_create(queue, &channel_args);
-    } else {
-      wrapped_server = grpc_secure_server_create(creds, queue, &channel_args);
-    }
+    wrapped_server = grpc_server_create(queue, &channel_args);
     free(channel_args.args);
   } else {
     return NanThrowTypeError("Server expects an object");
@@ -258,11 +243,19 @@ NAN_METHOD(Server::AddSecureHttp2Port) {
         "addSecureHttp2Port can only be called on a Server");
   }
   if (!args[0]->IsString()) {
-    return NanThrowTypeError("addSecureHttp2Port's argument must be a String");
+    return NanThrowTypeError(
+        "addSecureHttp2Port's first argument must be a String");
+  }
+  if (!ServerCredentials::HasInstance(args[1])) {
+    return NanThrowTypeError(
+        "addSecureHttp2Port's second argument must be ServerCredentials");
   }
   Server *server = ObjectWrap::Unwrap<Server>(args.This());
+  ServerCredentials *creds = ObjectWrap::Unwrap<ServerCredentials>(
+      args[1]->ToObject());
   NanReturnValue(NanNew<Number>(grpc_server_add_secure_http2_port(
-      server->wrapped_server, *NanUtf8String(args[0]))));
+      server->wrapped_server, *NanUtf8String(args[0]),
+      creds->GetWrappedServerCredentials())));
 }
 
 NAN_METHOD(Server::Start) {
diff --git a/src/node/ext/server.h b/src/node/ext/server.h
index 2056fe7d3f8a5b14ff2ae948493c089175ee4c12..641d5ccb3e4f7cc5acaba05e22f946336f04e801 100644
--- a/src/node/ext/server.h
+++ b/src/node/ext/server.h
@@ -67,7 +67,7 @@ class Server : public ::node::ObjectWrap {
   static NAN_METHOD(AddSecureHttp2Port);
   static NAN_METHOD(Start);
   static NAN_METHOD(Shutdown);
-  static v8::Persistent<v8::Function> constructor;
+  static NanCallback *constructor;
   static v8::Persistent<v8::FunctionTemplate> fun_tpl;
 
   grpc_server *wrapped_server;
diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc
index f75a2bf79c8f2cd2ec03e3e69311c43d530a654c..d2b63cdc4ec26b1983164b44c39f3899209a1fac 100644
--- a/src/node/ext/server_credentials.cc
+++ b/src/node/ext/server_credentials.cc
@@ -41,8 +41,6 @@
 namespace grpc {
 namespace node {
 
-using ::node::Buffer;
-using v8::Arguments;
 using v8::Exception;
 using v8::External;
 using v8::Function;
@@ -56,7 +54,7 @@ using v8::ObjectTemplate;
 using v8::Persistent;
 using v8::Value;
 
-Persistent<Function> ServerCredentials::constructor;
+NanCallback *ServerCredentials::constructor;
 Persistent<FunctionTemplate> ServerCredentials::fun_tpl;
 
 ServerCredentials::ServerCredentials(grpc_server_credentials *credentials)
@@ -68,16 +66,17 @@ ServerCredentials::~ServerCredentials() {
 
 void ServerCredentials::Init(Handle<Object> exports) {
   NanScope();
-  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
   tpl->SetClassName(NanNew("ServerCredentials"));
   tpl->InstanceTemplate()->SetInternalFieldCount(1);
   NanAssignPersistent(fun_tpl, tpl);
-  NanAssignPersistent(constructor, tpl->GetFunction());
-  constructor->Set(NanNew("createSsl"),
-                   FunctionTemplate::New(CreateSsl)->GetFunction());
-  constructor->Set(NanNew("createFake"),
-                   FunctionTemplate::New(CreateFake)->GetFunction());
-  exports->Set(NanNew("ServerCredentials"), constructor);
+  Handle<Function> ctr = tpl->GetFunction();
+  ctr->Set(NanNew("createSsl"),
+           NanNew<FunctionTemplate>(CreateSsl)->GetFunction());
+  ctr->Set(NanNew("createFake"),
+           NanNew<FunctionTemplate>(CreateFake)->GetFunction());
+  constructor = new NanCallback(ctr);
+  exports->Set(NanNew("ServerCredentials"), ctr);
 }
 
 bool ServerCredentials::HasInstance(Handle<Value> val) {
@@ -93,8 +92,8 @@ Handle<Value> ServerCredentials::WrapStruct(
   }
   const int argc = 1;
   Handle<Value> argv[argc] = {
-      External::New(reinterpret_cast<void *>(credentials))};
-  return NanEscapeScope(constructor->NewInstance(argc, argv));
+    NanNew<External>(reinterpret_cast<void *>(credentials))};
+  return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv));
 }
 
 grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() {
@@ -109,15 +108,16 @@ NAN_METHOD(ServerCredentials::New) {
       return NanThrowTypeError(
           "ServerCredentials can only be created with the provide functions");
     }
+    Handle<External> ext = args[0].As<External>();
     grpc_server_credentials *creds_value =
-        reinterpret_cast<grpc_server_credentials *>(External::Unwrap(args[0]));
+        reinterpret_cast<grpc_server_credentials *>(ext->Value());
     ServerCredentials *credentials = new ServerCredentials(creds_value);
     credentials->Wrap(args.This());
     NanReturnValue(args.This());
   } else {
     const int argc = 1;
     Local<Value> argv[argc] = {args[0]};
-    NanReturnValue(constructor->NewInstance(argc, argv));
+    NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv));
   }
 }
 
@@ -126,20 +126,20 @@ NAN_METHOD(ServerCredentials::CreateSsl) {
   NanScope();
   char *root_certs = NULL;
   grpc_ssl_pem_key_cert_pair key_cert_pair;
-  if (Buffer::HasInstance(args[0])) {
-    root_certs = Buffer::Data(args[0]);
+  if (::node::Buffer::HasInstance(args[0])) {
+    root_certs = ::node::Buffer::Data(args[0]);
   } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) {
     return NanThrowTypeError(
         "createSSl's first argument must be a Buffer if provided");
   }
-  if (!Buffer::HasInstance(args[1])) {
+  if (!::node::Buffer::HasInstance(args[1])) {
     return NanThrowTypeError("createSsl's second argument must be a Buffer");
   }
-  key_cert_pair.private_key = Buffer::Data(args[1]);
-  if (!Buffer::HasInstance(args[2])) {
+  key_cert_pair.private_key = ::node::Buffer::Data(args[1]);
+  if (!::node::Buffer::HasInstance(args[2])) {
     return NanThrowTypeError("createSsl's third argument must be a Buffer");
   }
-  key_cert_pair.cert_chain = Buffer::Data(args[2]);
+  key_cert_pair.cert_chain = ::node::Buffer::Data(args[2]);
   NanReturnValue(WrapStruct(
       grpc_ssl_server_credentials_create(root_certs, &key_cert_pair, 1)));
 }
diff --git a/src/node/ext/server_credentials.h b/src/node/ext/server_credentials.h
index f09902420c6e6413f02f4bfa086d4827294a66e6..aaa7ef297a40cc9f4d868429b0c08e42cc52a5aa 100644
--- a/src/node/ext/server_credentials.h
+++ b/src/node/ext/server_credentials.h
@@ -64,7 +64,7 @@ class ServerCredentials : public ::node::ObjectWrap {
   static NAN_METHOD(New);
   static NAN_METHOD(CreateSsl);
   static NAN_METHOD(CreateFake);
-  static v8::Persistent<v8::Function> constructor;
+  static NanCallback *constructor;
   // Used for typechecking instances of this javascript class
   static v8::Persistent<v8::FunctionTemplate> fun_tpl;
 
diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js
index 125ede174642f05bb0ee5f3d0831f5b69b797d7c..8e5c03666fa3b7ddcfb02ae9bd77a9856af52351 100644
--- a/src/node/interop/interop_server.js
+++ b/src/node/interop/interop_server.js
@@ -165,16 +165,16 @@ function handleHalfDuplex(call) {
 function getServer(port, tls) {
   // TODO(mlumish): enable TLS functionality
   var options = {};
+  var server_creds = null;
   if (tls) {
     var key_path = path.join(__dirname, '../test/data/server1.key');
     var pem_path = path.join(__dirname, '../test/data/server1.pem');
 
     var key_data = fs.readFileSync(key_path);
     var pem_data = fs.readFileSync(pem_path);
-    var server_creds = grpc.ServerCredentials.createSsl(null,
-                                                        key_data,
-                                                        pem_data);
-    options.credentials = server_creds;
+    server_creds = grpc.ServerCredentials.createSsl(null,
+                                                    key_data,
+                                                    pem_data);
   }
   var server = new Server({
     'grpc.testing.TestService' : {
@@ -186,7 +186,7 @@ function getServer(port, tls) {
       halfDuplexCall: handleHalfDuplex
     }
   }, null, options);
-  var port_num = server.bind('0.0.0.0:' + port, tls);
+  var port_num = server.bind('0.0.0.0:' + port, server_creds);
   return {server: server, port: port_num};
 }
 
diff --git a/src/node/package.json b/src/node/package.json
index 0ef1c990b1ee27001f8b14b0c5dff24f901e937d..f8c3ab079d9ef599b45c10249a7e7e517ecf28a2 100644
--- a/src/node/package.json
+++ b/src/node/package.json
@@ -1,6 +1,6 @@
 {
   "name": "grpc",
-  "version": "0.5.1",
+  "version": "0.5.3",
   "author": "Google Inc.",
   "description": "gRPC Library for Node",
   "homepage": "http://www.grpc.io/",
@@ -24,20 +24,23 @@
     "test": "node ./node_modules/mocha/bin/mocha && npm run-script lint"
   },
   "dependencies": {
-    "bindings": "^1.2.1",
-    "jshint": "^2.5.5",
-    "nan": "~1.3.0",
+    "bindings": "^1.2.0",
+    "nan": "^1.5.0",
     "protobufjs": "murgatroid99/ProtoBuf.js",
-    "underscore": "^1.7.0",
+    "underscore": "^1.6.0",
     "underscore.string": "^3.0.0"
   },
   "devDependencies": {
     "async": "^0.9.0",
     "google-auth-library": "^0.9.2",
+    "jshint": "^2.5.0",
     "minimist": "^1.1.0",
     "mocha": "~1.21.0",
     "strftime": "^0.8.2"
   },
+  "engines": {
+    "node": ">=0.10.13"
+  },
   "files": [
     "LICENSE",
     "README.md",
diff --git a/src/node/src/server.js b/src/node/src/server.js
index 91dde0225181cc9341fd454bbab111723493c313..b72d110666e833518ec60d889a2c6805b8c1a36c 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -517,14 +517,15 @@ Server.prototype.register = function(name, handler, serialize, deserialize,
 };
 
 /**
- * Binds the server to the given port, with SSL enabled if secure is specified
+ * Binds the server to the given port, with SSL enabled if creds is given
  * @param {string} port The port that the server should bind on, in the format
  *     "address:port"
- * @param {boolean=} secure Whether the server should open a secure port
+ * @param {boolean=} creds Server credential object to be used for SSL. Pass
+ *     nothing for an insecure port
  */
-Server.prototype.bind = function(port, secure) {
-  if (secure) {
-    return this._server.addSecureHttp2Port(port);
+Server.prototype.bind = function(port, creds) {
+  if (creds) {
+    return this._server.addSecureHttp2Port(port, creds);
   } else {
     return this._server.addHttp2Port(port);
   }
@@ -604,14 +605,14 @@ function makeServerConstructor(services) {
   }
 
   /**
-   * Binds the server to the given port, with SSL enabled if secure is specified
+   * Binds the server to the given port, with SSL enabled if creds is supplied
    * @param {string} port The port that the server should bind on, in the format
    *     "address:port"
-   * @param {boolean=} secure Whether the server should open a secure port
+   * @param {boolean=} creds Credentials to use for SSL
    * @return {SurfaceServer} this
    */
-  SurfaceServer.prototype.bind = function(port, secure) {
-    return this.inner_server.bind(port, secure);
+  SurfaceServer.prototype.bind = function(port, creds) {
+    return this.inner_server.bind(port, creds);
   };
 
   /**
diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js
index 1cc192869175032399f334a501dc84f6ba0e2ad0..c39364d410ef863b37988fd3581bdd2a416e13a5 100644
--- a/src/node/test/end_to_end_test.js
+++ b/src/node/test/end_to_end_test.js
@@ -235,4 +235,73 @@ describe('end-to-end', function() {
       });
     });
   });
+  it('should send multiple messages', function(complete) {
+    var done = multiDone(complete, 2);
+    var requests = ['req1', 'req2'];
+    var deadline = new Date();
+    deadline.setSeconds(deadline.getSeconds() + 3);
+    var status_text = 'xyz';
+    var call = new grpc.Call(channel,
+                             'dummy_method',
+                             Infinity);
+    var client_batch = {};
+    client_batch[grpc.opType.SEND_INITIAL_METADATA] = {};
+    client_batch[grpc.opType.SEND_MESSAGE] = new Buffer(requests[0]);
+    client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+    call.startBatch(client_batch, function(err, response) {
+      assert.ifError(err);
+      assert.deepEqual(response, {
+        'send metadata': true,
+        'send message': true,
+        'metadata': {}
+      });
+      var req2_batch = {};
+      req2_batch[grpc.opType.SEND_MESSAGE] = new Buffer(requests[1]);
+      req2_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
+      req2_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+      call.startBatch(req2_batch, function(err, resp) {
+        assert.ifError(err);
+        assert.deepEqual(resp, {
+          'send message': true,
+          'client close': true,
+          'status': {
+            'code': grpc.status.OK,
+            'details': status_text,
+            'metadata': {}
+          }
+        });
+        done();
+      });
+    });
+
+    server.requestCall(function(err, call_details) {
+      var new_call = call_details['new call'];
+      assert.notEqual(new_call, null);
+      var server_call = new_call.call;
+      assert.notEqual(server_call, null);
+      var server_batch = {};
+      server_batch[grpc.opType.SEND_INITIAL_METADATA] = {};
+      server_batch[grpc.opType.RECV_MESSAGE] = true;
+      server_call.startBatch(server_batch, function(err, response) {
+        assert.ifError(err);
+        assert(response['send metadata']);
+        assert.strictEqual(response.read.toString(), requests[0]);
+        var end_batch = {};
+        end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true;
+        end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
+          'metadata': {},
+          'code': grpc.status.OK,
+          'details': status_text
+        };
+        end_batch[grpc.opType.RECV_MESSAGE] = true;
+        server_call.startBatch(end_batch, function(err, response) {
+          assert.ifError(err);
+          assert(response['send status']);
+          assert(!response.cancelled);
+          assert.strictEqual(response.read.toString(), requests[1]);
+          done();
+        });
+      });
+    });
+  });
 });
diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js
new file mode 100644
index 0000000000000000000000000000000000000000..7cb34fa0cb5d24cf29918b779f3bdde9bc0149d5
--- /dev/null
+++ b/src/node/test/server_test.js
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+'use strict';
+
+var assert = require('assert');
+var grpc = require('bindings')('grpc.node');
+
+describe('server', function() {
+  describe('constructor', function() {
+    it('should work with no arguments', function() {
+      assert.doesNotThrow(function() {
+        new grpc.Server();
+      });
+    });
+    it('should work with an empty list argument', function() {
+      assert.doesNotThrow(function() {
+        new grpc.Server([]);
+      });
+    });
+  });
+  describe('addHttp2Port', function() {
+    var server;
+    before(function() {
+      server = new grpc.Server();
+    });
+    it('should bind to an unused port', function() {
+      var port;
+      assert.doesNotThrow(function() {
+        port = server.addHttp2Port('0.0.0.0:0');
+      });
+      assert(port > 0);
+    });
+  });
+  describe('addSecureHttp2Port', function() {
+    var server;
+    before(function() {
+      server = new grpc.Server();
+    });
+    it('should bind to an unused port with fake credentials', function() {
+      var port;
+      var creds = grpc.ServerCredentials.createFake();
+      assert.doesNotThrow(function() {
+        port = server.addSecureHttp2Port('0.0.0.0:0', creds);
+      });
+      assert(port > 0);
+    });
+  });
+  describe('listen', function() {
+    var server;
+    before(function() {
+      server = new grpc.Server();
+      server.addHttp2Port('0.0.0.0:0');
+    });
+    after(function() {
+      server.shutdown();
+    });
+    it('should listen without error', function() {
+      assert.doesNotThrow(function() {
+        server.start();
+      });
+    });
+  });
+});
diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c
index d1362c8b51a8bf580b9b4880f5d6fd04caf57e5b..055a7263b9b1a35b061643d2357cdc07d4530494 100644
--- a/src/php/ext/grpc/server.c
+++ b/src/php/ext/grpc/server.c
@@ -107,9 +107,6 @@ PHP_METHOD(Server, __construct) {
       (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC);
   zval *args_array = NULL;
   grpc_channel_args args;
-  HashTable *array_hash;
-  zval **creds_obj = NULL;
-  wrapped_grpc_server_credentials *creds = NULL;
   /* "a" == 1 optional array */
   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &args_array) ==
       FAILURE) {
@@ -122,28 +119,8 @@ PHP_METHOD(Server, __construct) {
   if (args_array == NULL) {
     server->wrapped = grpc_server_create(server->queue, NULL);
   } else {
-    array_hash = Z_ARRVAL_P(args_array);
-    if (zend_hash_find(array_hash, "credentials", sizeof("credentials"),
-                       (void **)&creds_obj) == SUCCESS) {
-      if (zend_get_class_entry(*creds_obj TSRMLS_CC) !=
-          grpc_ce_server_credentials) {
-        zend_throw_exception(spl_ce_InvalidArgumentException,
-                             "credentials must be a ServerCredentials object",
-                             1 TSRMLS_CC);
-        return;
-      }
-      creds = (wrapped_grpc_server_credentials *)zend_object_store_get_object(
-          *creds_obj TSRMLS_CC);
-      zend_hash_del(array_hash, "credentials", sizeof("credentials"));
-    }
     php_grpc_read_args_array(args_array, &args);
-    if (creds == NULL) {
-      server->wrapped = grpc_server_create(server->queue, &args);
-    } else {
-      gpr_log(GPR_DEBUG, "Initialized secure server");
-      server->wrapped =
-          grpc_secure_server_create(creds->wrapped, server->queue, &args);
-    }
+    server->wrapped = grpc_server_create(server->queue, &args);
     efree(args.args);
   }
 }
@@ -219,14 +196,21 @@ PHP_METHOD(Server, add_secure_http2_port) {
       (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC);
   const char *addr;
   int addr_len;
-  /* "s" == 1 string */
-  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) ==
+  zval *creds_obj;
+  /* "sO" == 1 string, 1 object */
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len,
+                            &creds_obj, grpc_ce_server_credentials) ==
       FAILURE) {
-    zend_throw_exception(spl_ce_InvalidArgumentException,
-                         "add_http2_port expects a string", 1 TSRMLS_CC);
+    zend_throw_exception(
+        spl_ce_InvalidArgumentException,
+        "add_http2_port expects a string and a ServerCredentials", 1 TSRMLS_CC);
     return;
   }
-  RETURN_LONG(grpc_server_add_secure_http2_port(server->wrapped, addr));
+  wrapped_grpc_server_credentials *creds =
+      (wrapped_grpc_server_credentials *)zend_object_store_get_object(
+          creds_obj TSRMLS_CC);
+  RETURN_LONG(grpc_server_add_secure_http2_port(server->wrapped, addr,
+                                                creds->wrapped));
 }
 
 /**
diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php
index c23dd791acf979aba0d2fdf798166dfc87a4b6ca..896afeac49ad7fa656eb3f8e7f9dfa221bf0ce25 100755
--- a/src/php/tests/unit_tests/SecureEndToEndTest.php
+++ b/src/php/tests/unit_tests/SecureEndToEndTest.php
@@ -41,9 +41,9 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
         null,
         file_get_contents(dirname(__FILE__) . '/../data/server1.key'),
         file_get_contents(dirname(__FILE__) . '/../data/server1.pem'));
-    $this->server = new Grpc\Server($this->server_queue,
-                                    ['credentials' => $server_credentials]);
-    $port = $this->server->add_secure_http2_port('0.0.0.0:0');
+    $this->server = new Grpc\Server($this->server_queue);
+    $port = $this->server->add_secure_http2_port('0.0.0.0:0',
+                                                 $server_credentials);
     $this->channel = new Grpc\Channel(
         'localhost:' . $port,
         [
diff --git a/src/python/README.md b/src/python/README.md
index 490a229d1d776a6f24c924f29a3d0759b3233e9a..c8057be38b380a86956d6fb8fc8819ebaf7eb4ed 100644
--- a/src/python/README.md
+++ b/src/python/README.md
@@ -46,7 +46,7 @@ Installing
 
 - Install gRPC Python's dependencies
 ```
-$ pip install -r requirements.txt
+$ pip install -r src/python/requirements.txt
 ```
 
 - Install gRPC Python
diff --git a/src/python/interop/interop/_insecure_interop_test.py b/src/python/interop/interop/_insecure_interop_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fa6b8b3f8f80a177dad0685dd2f4023351a4a9e
--- /dev/null
+++ b/src/python/interop/interop/_insecure_interop_test.py
@@ -0,0 +1,56 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Insecure client-server interoperability as a unit test."""
+
+import unittest
+
+from grpc.early_adopter import implementations
+
+from interop import _interop_test_case
+from interop import methods
+
+
+class InsecureInteropTest(
+    _interop_test_case.InteropTestCase,
+    unittest.TestCase):
+
+  def setUp(self):
+    self.server = implementations.insecure_server(methods.SERVER_METHODS, 0)
+    self.server.start()
+    port = self.server.port()
+    self.stub = implementations.insecure_stub(
+        methods.CLIENT_METHODS, 'localhost', port)
+
+  def tearDown(self):
+    self.server.stop()
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/src/python/interop/interop/_interop_test_case.py b/src/python/interop/interop/_interop_test_case.py
new file mode 100644
index 0000000000000000000000000000000000000000..fec8f1915d53972d5abe126900636f776275ffe9
--- /dev/null
+++ b/src/python/interop/interop/_interop_test_case.py
@@ -0,0 +1,55 @@
+# 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.
+
+"""Common code for unit tests of the interoperability test code."""
+
+from interop import methods
+
+
+class InteropTestCase(object):
+  """Unit test methods.
+
+  This class must be mixed in with unittest.TestCase and a class that defines
+  setUp and tearDown methods that manage a stub attribute.
+  """
+
+  def testEmptyUnary(self):
+    methods.TestCase.EMPTY_UNARY.test_interoperability(self.stub)
+
+  def testLargeUnary(self):
+    methods.TestCase.LARGE_UNARY.test_interoperability(self.stub)
+
+  def testServerStreaming(self):
+    methods.TestCase.SERVER_STREAMING.test_interoperability(self.stub)
+
+  def testClientStreaming(self):
+    methods.TestCase.CLIENT_STREAMING.test_interoperability(self.stub)
+
+  def testPingPong(self):
+    methods.TestCase.PING_PONG.test_interoperability(self.stub)
diff --git a/src/python/interop/interop/_secure_interop_test.py b/src/python/interop/interop/_secure_interop_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc9e93821ad1693e8b72d8e4f1aa21608761a70a
--- /dev/null
+++ b/src/python/interop/interop/_secure_interop_test.py
@@ -0,0 +1,63 @@
+# 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.
+
+"""Secure client-server interoperability as a unit test."""
+
+import unittest
+
+from grpc.early_adopter import implementations
+
+from interop import _interop_test_case
+from interop import methods
+from interop import resources
+
+_SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
+
+
+class SecureInteropTest(
+    _interop_test_case.InteropTestCase,
+    unittest.TestCase):
+
+  def setUp(self):
+    self.server = implementations.secure_server(
+        methods.SERVER_METHODS, 0, resources.private_key(),
+        resources.certificate_chain())
+    self.server.start()
+    port = self.server.port()
+    self.stub = implementations.secure_stub(
+        methods.CLIENT_METHODS, 'localhost', port,
+        resources.test_root_certificates(), None, None,
+        server_host_override=_SERVER_HOST_OVERRIDE)
+
+  def tearDown(self):
+    self.server.stop()
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/src/python/interop/interop/client.py b/src/python/interop/interop/client.py
index f4a449ef9e7ebab13c904289cc77965112979d56..b674a64f9d8583b9c5a435747c6f90ac0a6c98ff 100644
--- a/src/python/interop/interop/client.py
+++ b/src/python/interop/interop/client.py
@@ -65,21 +65,30 @@ def _stub(args):
       root_certificates = resources.test_root_certificates()
     else:
       root_certificates = resources.prod_root_certificates()
-    # TODO(nathaniel): server host override.
 
     stub = implementations.secure_stub(
         methods.CLIENT_METHODS, args.server_host, args.server_port,
-        root_certificates, None, None)
+        root_certificates, None, None,
+        server_host_override=args.server_host_override)
   else:
     stub = implementations.insecure_stub(
         methods.CLIENT_METHODS, args.server_host, args.server_port)
   return stub
 
 
+def _test_case_from_arg(test_case_arg):
+  for test_case in methods.TestCase:
+    if test_case_arg == test_case.value:
+      return test_case
+  else:
+    raise ValueError('No test case "%s"!' % test_case_arg)
+
+
 def _test_interoperability():
   args = _args()
   stub = _stub(args)
-  methods.test_interoperability(args.test_case, stub)
+  test_case = _test_case_from_arg(args.test_case)
+  test_case.test_interoperability(stub)
 
 
 if __name__ == '__main__':
diff --git a/src/python/interop/interop/methods.py b/src/python/interop/interop/methods.py
index 4da28ee7759b83e07d7653da717cdb38a49d3fec..2e15fac91573052ca385d2de1e9e1a9c6094948f 100644
--- a/src/python/interop/interop/methods.py
+++ b/src/python/interop/interop/methods.py
@@ -29,6 +29,7 @@
 
 """Implementations of interoperability test methods."""
 
+import enum
 import threading
 
 from grpc.early_adopter import utilities
@@ -265,16 +266,24 @@ def _ping_pong(stub):
     pipe.close()
 
 
-def test_interoperability(test_case, stub):
-  if test_case == 'empty_unary':
-    _empty_unary(stub)
-  elif test_case == 'large_unary':
-    _large_unary(stub)
-  elif test_case == 'server_streaming':
-    _server_streaming(stub)
-  elif test_case == 'client_streaming':
-    _client_streaming(stub)
-  elif test_case == 'ping_pong':
-    _ping_pong(stub)
-  else:
-    raise NotImplementedError('Test case "%s" not implemented!')
+@enum.unique
+class TestCase(enum.Enum):
+  EMPTY_UNARY = 'empty_unary'
+  LARGE_UNARY = 'large_unary'
+  SERVER_STREAMING = 'server_streaming'
+  CLIENT_STREAMING = 'client_streaming'
+  PING_PONG = 'ping_pong'
+
+  def test_interoperability(self, stub):
+    if self is TestCase.EMPTY_UNARY:
+      _empty_unary(stub)
+    elif self is TestCase.LARGE_UNARY:
+      _large_unary(stub)
+    elif self is TestCase.SERVER_STREAMING:
+      _server_streaming(stub)
+    elif self is TestCase.CLIENT_STREAMING:
+      _client_streaming(stub)
+    elif self is TestCase.PING_PONG:
+      _ping_pong(stub)
+    else:
+      raise NotImplementedError('Test case "%s" not implemented!' % self.name)
diff --git a/src/python/src/grpc/_adapter/_c_test.py b/src/python/src/grpc/_adapter/_c_test.py
index d81c63e3469643bcc1da143c97b1836ddf8e89c5..437a6730cd294560d483fed26b38aacbde97f7b9 100644
--- a/src/python/src/grpc/_adapter/_c_test.py
+++ b/src/python/src/grpc/_adapter/_c_test.py
@@ -70,7 +70,8 @@ class _CTest(unittest.TestCase):
   def testChannel(self):
     _c.init()
 
-    channel = _c.Channel('test host:12345', None)
+    channel = _c.Channel(
+        'test host:12345', None, server_host_override='ignored')
     del channel
 
     _c.shut_down()
@@ -92,7 +93,7 @@ class _CTest(unittest.TestCase):
     _c.init()
 
     completion_queue = _c.CompletionQueue()
-    server = _c.Server(completion_queue, None)
+    server = _c.Server(completion_queue)
     server.add_http2_addr('[::]:0')
     server.start()
     server.stop()
@@ -102,7 +103,7 @@ class _CTest(unittest.TestCase):
 
     service_tag = object()
     completion_queue = _c.CompletionQueue()
-    server = _c.Server(completion_queue, None)
+    server = _c.Server(completion_queue)
     server.add_http2_addr('[::]:0')
     server.start()
     server.service(service_tag)
@@ -119,7 +120,7 @@ class _CTest(unittest.TestCase):
     del completion_queue
 
     completion_queue = _c.CompletionQueue()
-    server = _c.Server(completion_queue, None)
+    server = _c.Server(completion_queue)
     server.add_http2_addr('[::]:0')
     server.start()
     thread = threading.Thread(target=completion_queue.get, args=(_FUTURE,))
diff --git a/src/python/src/grpc/_adapter/_channel.c b/src/python/src/grpc/_adapter/_channel.c
index 9cf580bcfb591e123f1571856d3c6cddd2ec5c1c..6be8f1c364330b56ab3a7988684c59427a9bdaf3 100644
--- a/src/python/src/grpc/_adapter/_channel.c
+++ b/src/python/src/grpc/_adapter/_channel.c
@@ -42,19 +42,35 @@
 static int pygrpc_channel_init(Channel *self, PyObject *args, PyObject *kwds) {
   const char *hostport;
   PyObject *client_credentials;
-  static char *kwlist[] = {"hostport", "client_credentials", NULL};
+  char *server_host_override = NULL;
+  static char *kwlist[] = {"hostport", "client_credentials",
+                           "server_host_override", NULL};
+  grpc_arg server_host_override_arg;
+  grpc_channel_args channel_args;
 
-  if (!(PyArg_ParseTupleAndKeywords(args, kwds, "sO:Channel", kwlist,
-                                    &hostport, &client_credentials))) {
+  if (!(PyArg_ParseTupleAndKeywords(args, kwds, "sO|z:Channel", kwlist,
+                                    &hostport, &client_credentials,
+                                    &server_host_override))) {
     return -1;
   }
   if (client_credentials == Py_None) {
     self->c_channel = grpc_channel_create(hostport, NULL);
     return 0;
   } else {
-    self->c_channel = grpc_secure_channel_create(
-        ((ClientCredentials *)client_credentials)->c_client_credentials,
-        hostport, NULL);
+    if (server_host_override == NULL) {
+      self->c_channel = grpc_secure_channel_create(
+	  ((ClientCredentials *)client_credentials)->c_client_credentials,
+          hostport, NULL);
+    } else {
+      server_host_override_arg.type = GRPC_ARG_STRING;
+      server_host_override_arg.key = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG;
+      server_host_override_arg.value.string = server_host_override;
+      channel_args.num_args = 1;
+      channel_args.args = &server_host_override_arg;
+      self->c_channel = grpc_secure_channel_create(
+          ((ClientCredentials *)client_credentials)->c_client_credentials,
+          hostport, &channel_args);
+    }
     return 0;
   }
 }
diff --git a/src/python/src/grpc/_adapter/_face_test_case.py b/src/python/src/grpc/_adapter/_face_test_case.py
index 475d780c950d1913e0671247f61f5239717315f8..2542eb6da4d794e5b4636e4e8723e130a3e2ca44 100644
--- a/src/python/src/grpc/_adapter/_face_test_case.py
+++ b/src/python/src/grpc/_adapter/_face_test_case.py
@@ -50,31 +50,12 @@ class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage):
   """Provides abstract Face-layer tests a GRPC-backed implementation."""
 
   def set_up_implementation(
-      self,
-      name,
-      methods,
-      inline_value_in_value_out_methods,
-      inline_value_in_stream_out_methods,
-      inline_stream_in_value_out_methods,
-      inline_stream_in_stream_out_methods,
-      event_value_in_value_out_methods,
-      event_value_in_stream_out_methods,
-      event_stream_in_value_out_methods,
-      event_stream_in_stream_out_methods,
-      multi_method):
+      self, name, methods, method_implementations,
+      multi_method_implementation):
     pool = logging_pool.pool(_MAXIMUM_POOL_SIZE)
 
     servicer = face_implementations.servicer(
-        pool,
-        inline_value_in_value_out_methods=inline_value_in_value_out_methods,
-        inline_value_in_stream_out_methods=inline_value_in_stream_out_methods,
-        inline_stream_in_value_out_methods=inline_stream_in_value_out_methods,
-        inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods,
-        event_value_in_value_out_methods=event_value_in_value_out_methods,
-        event_value_in_stream_out_methods=event_value_in_stream_out_methods,
-        event_stream_in_value_out_methods=event_stream_in_value_out_methods,
-        event_stream_in_stream_out_methods=event_stream_in_stream_out_methods,
-        multi_method=multi_method)
+        pool, method_implementations, multi_method_implementation)
 
     serialization = serial.serialization(methods)
 
@@ -96,9 +77,8 @@ class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage):
     rear_link.join_fore_link(front)
     front.join_rear_link(rear_link)
 
-    server = face_implementations.server()
-    stub = face_implementations.stub(front, pool)
-    return server, stub, (rear_link, fore_link, front, back)
+    stub = face_implementations.generic_stub(front, pool)
+    return stub, (rear_link, fore_link, front, back)
 
   def tear_down_implementation(self, memo):
     rear_link, fore_link, front, back = memo
diff --git a/src/python/src/grpc/_adapter/_low_test.py b/src/python/src/grpc/_adapter/_low_test.py
index 03e3f473a3ec0cfd708581386b93e1b7eb43d757..b04ac1c95098a0cf23b89ce304223ccb3ebec89d 100644
--- a/src/python/src/grpc/_adapter/_low_test.py
+++ b/src/python/src/grpc/_adapter/_low_test.py
@@ -82,7 +82,7 @@ class EchoTest(unittest.TestCase):
     self.host = 'localhost'
 
     self.server_completion_queue = _low.CompletionQueue()
-    self.server = _low.Server(self.server_completion_queue, None)
+    self.server = _low.Server(self.server_completion_queue)
     port = self.server.add_http2_addr('[::]:0')
     self.server.start()
 
@@ -260,7 +260,7 @@ class CancellationTest(unittest.TestCase):
     self.host = 'localhost'
 
     self.server_completion_queue = _low.CompletionQueue()
-    self.server = _low.Server(self.server_completion_queue, None)
+    self.server = _low.Server(self.server_completion_queue)
     port = self.server.add_http2_addr('[::]:0')
     self.server.start()
 
diff --git a/src/python/src/grpc/_adapter/_server.c b/src/python/src/grpc/_adapter/_server.c
index ae7ae5b5d23e9e99c7be78d4da4345fe682614ca..181b6c21fcf1470cb56ae4168ab74e5c0cadaea2 100644
--- a/src/python/src/grpc/_adapter/_server.c
+++ b/src/python/src/grpc/_adapter/_server.c
@@ -42,30 +42,16 @@
 
 static int pygrpc_server_init(Server *self, PyObject *args, PyObject *kwds) {
   const PyObject *completion_queue;
-  PyObject *server_credentials;
-  static char *kwlist[] = {"completion_queue", "server_credentials", NULL};
+  static char *kwlist[] = {"completion_queue", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O:Server", kwlist,
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!:Server", kwlist,
                                    &pygrpc_CompletionQueueType,
-                                   &completion_queue, &server_credentials)) {
-    return -1;
-  }
-  if (server_credentials == Py_None) {
-    self->c_server = grpc_server_create(
-        ((CompletionQueue *)completion_queue)->c_completion_queue, NULL);
-    return 0;
-  } else if (PyObject_TypeCheck(server_credentials,
-                                &pygrpc_ServerCredentialsType)) {
-    self->c_server = grpc_secure_server_create(
-        ((ServerCredentials *)server_credentials)->c_server_credentials,
-        ((CompletionQueue *)completion_queue)->c_completion_queue, NULL);
-    return 0;
-  } else {
-    PyErr_Format(PyExc_TypeError,
-                 "server_credentials must be _grpc.ServerCredentials, not %s",
-                 Py_TYPE(server_credentials)->tp_name);
+                                   &completion_queue)) {
     return -1;
   }
+  self->c_server = grpc_server_create(
+      ((CompletionQueue *)completion_queue)->c_completion_queue, NULL);
+  return 0;
 }
 
 static void pygrpc_server_dealloc(Server *self) {
@@ -92,13 +78,21 @@ static PyObject *pygrpc_server_add_http2_addr(Server *self, PyObject *args) {
 }
 
 static PyObject *pygrpc_server_add_secure_http2_addr(Server *self,
-                                                     PyObject *args) {
+                                                     PyObject *args,
+                                                     PyObject *kwargs) {
   const char *addr;
+  PyObject *server_credentials;
+  static char *kwlist[] = {"addr", "server_credentials", NULL};
   int port;
-  if (!PyArg_ParseTuple(args, "s:add_secure_http2_addr", &addr)) {
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!:add_secure_http2_addr",
+                                   kwlist, &addr, &pygrpc_ServerCredentialsType,
+                                   &server_credentials)) {
     return NULL;
   }
-  port = grpc_server_add_secure_http2_port(self->c_server, addr);
+  port = grpc_server_add_secure_http2_port(
+      self->c_server, addr,
+      ((ServerCredentials *)server_credentials)->c_server_credentials);
   if (port == 0) {
     PyErr_SetString(PyExc_RuntimeError, "Couldn't add port to server!");
     return NULL;
@@ -138,8 +132,7 @@ static PyMethodDef methods[] = {
      METH_VARARGS, "Add a secure HTTP2 address."},
     {"start", (PyCFunction)pygrpc_server_start, METH_NOARGS,
      "Starts the server."},
-    {"service", (PyCFunction)pygrpc_server_service, METH_O,
-     "Services a call."},
+    {"service", (PyCFunction)pygrpc_server_service, METH_O, "Services a call."},
     {"stop", (PyCFunction)pygrpc_server_stop, METH_NOARGS, "Stops the server."},
     {NULL}};
 
diff --git a/src/python/src/grpc/_adapter/fore.py b/src/python/src/grpc/_adapter/fore.py
index b08b9f48bc6b891ae2452b7a3ab9981093e347f7..339c0ef216cf54167fe1897429b8fa9d0c011a99 100644
--- a/src/python/src/grpc/_adapter/fore.py
+++ b/src/python/src/grpc/_adapter/fore.py
@@ -280,13 +280,14 @@ class ForeLink(ticket_interfaces.ForeLink, activated.Activated):
           0 if self._requested_port is None else self._requested_port)
       self._completion_queue = _low.CompletionQueue()
       if self._root_certificates is None and not self._key_chain_pairs:
-        self._server = _low.Server(self._completion_queue, None)
+        self._server = _low.Server(self._completion_queue)
         self._port = self._server.add_http2_addr(address)
       else:
         server_credentials = _low.ServerCredentials(
           self._root_certificates, self._key_chain_pairs)
-        self._server = _low.Server(self._completion_queue, server_credentials)
-        self._port = self._server.add_secure_http2_addr(address)
+        self._server = _low.Server(self._completion_queue)
+        self._port = self._server.add_secure_http2_addr(
+            address, server_credentials)
       self._server.start()
 
       self._server.service(None)
@@ -356,90 +357,3 @@ class ForeLink(ticket_interfaces.ForeLink, activated.Activated):
         self._complete(ticket.operation_id, ticket.payload)
       else:
         self._cancel(ticket.operation_id)
-
-
-class _ActivatedForeLink(ticket_interfaces.ForeLink, activated.Activated):
-
-  def __init__(
-      self, port, request_deserializers, response_serializers,
-      root_certificates, key_chain_pairs):
-    self._port = port
-    self._request_deserializers = request_deserializers
-    self._response_serializers = response_serializers
-    self._root_certificates = root_certificates
-    self._key_chain_pairs = key_chain_pairs
-
-    self._lock = threading.Lock()
-    self._pool = None
-    self._fore_link = None
-    self._rear_link = null.NULL_REAR_LINK
-
-  def join_rear_link(self, rear_link):
-    with self._lock:
-      self._rear_link = null.NULL_REAR_LINK if rear_link is None else rear_link
-      if self._fore_link is not None:
-        self._fore_link.join_rear_link(rear_link)
-
-  def _start(self):
-    with self._lock:
-      self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
-      self._fore_link = ForeLink(
-          self._pool, self._request_deserializers, self._response_serializers,
-          self._root_certificates, self._key_chain_pairs, port=self._port)
-      self._fore_link.join_rear_link(self._rear_link)
-      self._fore_link.start()
-      return self
-
-  def _stop(self):
-    with self._lock:
-      self._fore_link.stop()
-      self._fore_link = None
-      self._pool.shutdown(wait=True)
-      self._pool = None
-
-  def __enter__(self):
-    return self._start()
-
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    self._stop()
-    return False
-
-  def start(self):
-    return self._start()
-
-  def stop(self):
-    self._stop()
-
-  def port(self):
-    with self._lock:
-      return None if self._fore_link is None else self._fore_link.port()
-
-  def accept_back_to_front_ticket(self, ticket):
-    with self._lock:
-      if self._fore_link is not None:
-        self._fore_link.accept_back_to_front_ticket(ticket)
-
-
-def activated_fore_link(
-    port, request_deserializers, response_serializers, root_certificates,
-    key_chain_pairs):
-  """Creates a ForeLink that is also an activated.Activated.
-
-  The returned object is only valid for use between calls to its start and stop
-  methods (or in context when used as a context manager).
-
-  Args:
-    port: The port on which to serve RPCs, or None for a port to be
-      automatically selected.
-    request_deserializers: A dictionary from RPC method names to request object
-      deserializer behaviors.
-    response_serializers: A dictionary from RPC method names to response object
-      serializer behaviors.
-    root_certificates: The PEM-encoded client root certificates as a bytestring
-      or None.
-    key_chain_pairs: A sequence of PEM-encoded private key-certificate chain
-      pairs.
-  """
-  return _ActivatedForeLink(
-      port, request_deserializers, response_serializers, root_certificates,
-      key_chain_pairs)
diff --git a/src/python/src/grpc/_adapter/rear.py b/src/python/src/grpc/_adapter/rear.py
index bfde5f5c577571208de1e722aab78d6e44896dca..62703fab30e49d3d7b2b50daeb1e8fb8c8ea792c 100644
--- a/src/python/src/grpc/_adapter/rear.py
+++ b/src/python/src/grpc/_adapter/rear.py
@@ -93,7 +93,8 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
 
   def __init__(
       self, host, port, pool, request_serializers, response_deserializers,
-      secure, root_certificates, private_key, certificate_chain):
+      secure, root_certificates, private_key, certificate_chain,
+      server_host_override=None):
     """Constructor.
 
     Args:
@@ -111,6 +112,8 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
         key should be used.
       certificate_chain: The PEM-encoded certificate chain to use or None if
         no certificate chain should be used.
+      server_host_override: (For testing only) the target name used for SSL
+        host name checking.
     """
     self._condition = threading.Condition()
     self._host = host
@@ -132,6 +135,7 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
     self._root_certificates = root_certificates
     self._private_key = private_key
     self._certificate_chain = certificate_chain
+    self._server_host_override = server_host_override
 
   def _on_write_event(self, operation_id, event, rpc_state):
     if event.write_accepted:
@@ -327,7 +331,8 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
     with self._condition:
       self._completion_queue = _low.CompletionQueue()
       self._channel = _low.Channel(
-          '%s:%d' % (self._host, self._port), self._client_credentials)
+          '%s:%d' % (self._host, self._port), self._client_credentials,
+          server_host_override=self._server_host_override)
     return self
 
   def _stop(self):
@@ -382,121 +387,3 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
       else:
         # NOTE(nathaniel): All other categories are treated as cancellation.
         self._cancel(ticket.operation_id)
-
-
-class _ActivatedRearLink(ticket_interfaces.RearLink, activated.Activated):
-
-  def __init__(
-      self, host, port, request_serializers, response_deserializers, secure,
-      root_certificates, private_key, certificate_chain):
-    self._host = host
-    self._port = port
-    self._request_serializers = request_serializers
-    self._response_deserializers = response_deserializers
-    self._secure = secure
-    self._root_certificates = root_certificates
-    self._private_key = private_key
-    self._certificate_chain = certificate_chain
-
-    self._lock = threading.Lock()
-    self._pool = None
-    self._rear_link = None
-    self._fore_link = null.NULL_FORE_LINK
-
-  def join_fore_link(self, fore_link):
-    with self._lock:
-      self._fore_link = null.NULL_FORE_LINK if fore_link is None else fore_link
-      if self._rear_link is not None:
-        self._rear_link.join_fore_link(self._fore_link)
-
-  def _start(self):
-    with self._lock:
-      self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
-      self._rear_link = RearLink(
-          self._host, self._port, self._pool, self._request_serializers,
-          self._response_deserializers, self._secure, self._root_certificates,
-          self._private_key, self._certificate_chain)
-      self._rear_link.join_fore_link(self._fore_link)
-      self._rear_link.start()
-    return self
-
-  def _stop(self):
-    with self._lock:
-      self._rear_link.stop()
-      self._rear_link = None
-      self._pool.shutdown(wait=True)
-      self._pool = None
-
-  def __enter__(self):
-    return self._start()
-
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    self._stop()
-    return False
-
-  def start(self):
-    return self._start()
-
-  def stop(self):
-    self._stop()
-
-  def accept_front_to_back_ticket(self, ticket):
-    with self._lock:
-      if self._rear_link is not None:
-        self._rear_link.accept_front_to_back_ticket(ticket)
-
-
-# TODO(issue 726): reconcile these two creation functions.
-def activated_rear_link(
-    host, port, request_serializers, response_deserializers):
-  """Creates a RearLink that is also an activated.Activated.
-
-  The returned object is only valid for use between calls to its start and stop
-  methods (or in context when used as a context manager).
-
-  Args:
-    host: The host to which to connect for RPC service.
-    port: The port to which to connect for RPC service.
-    request_serializers: A dictionary from RPC method name to request object
-      serializer behavior.
-    response_deserializers: A dictionary from RPC method name to response
-      object deserializer behavior.
-    secure: A boolean indicating whether or not to use a secure connection.
-    root_certificates: The PEM-encoded root certificates or None to ask for
-      them to be retrieved from a default location.
-    private_key: The PEM-encoded private key to use or None if no private key
-      should be used.
-    certificate_chain: The PEM-encoded certificate chain to use or None if no
-      certificate chain should be used.
-  """
-  return _ActivatedRearLink(
-      host, port, request_serializers, response_deserializers, False, None,
-      None, None)
-
-
-
-def secure_activated_rear_link(
-    host, port, request_serializers, response_deserializers, root_certificates,
-    private_key, certificate_chain):
-  """Creates a RearLink that is also an activated.Activated.
-
-  The returned object is only valid for use between calls to its start and stop
-  methods (or in context when used as a context manager).
-
-  Args:
-    host: The host to which to connect for RPC service.
-    port: The port to which to connect for RPC service.
-    request_serializers: A dictionary from RPC method name to request object
-      serializer behavior.
-    response_deserializers: A dictionary from RPC method name to response
-      object deserializer behavior.
-    root_certificates: The PEM-encoded root certificates or None to ask for
-      them to be retrieved from a default location.
-    private_key: The PEM-encoded private key to use or None if no private key
-      should be used.
-    certificate_chain: The PEM-encoded certificate chain to use or None if no
-      certificate chain should be used.
-  """
-  return _ActivatedRearLink(
-      host, port, request_serializers, response_deserializers, True,
-      root_certificates, private_key, certificate_chain)
diff --git a/src/python/src/grpc/early_adopter/_assembly_utilities.py b/src/python/src/grpc/early_adopter/_face_utilities.py
similarity index 76%
rename from src/python/src/grpc/early_adopter/_assembly_utilities.py
rename to src/python/src/grpc/early_adopter/_face_utilities.py
index facfc2bf0e647a6fa581da08caeb89e4b217a72a..2cf576018d4cf521db7ad78555b35b78d19a234e 100644
--- a/src/python/src/grpc/early_adopter/_assembly_utilities.py
+++ b/src/python/src/grpc/early_adopter/_face_utilities.py
@@ -30,23 +30,20 @@
 import abc
 import collections
 
-# assembly_interfaces is referenced from specification in this module.
-from grpc.framework.assembly import interfaces as assembly_interfaces  # pylint: disable=unused-import
-from grpc.framework.assembly import utilities as assembly_utilities
+# face_interfaces is referenced from specification in this module.
+from grpc.framework.common import cardinality
+from grpc.framework.face import interfaces as face_interfaces  # pylint: disable=unused-import
+from grpc.framework.face import utilities as face_utilities
 from grpc.early_adopter import _reexport
 from grpc.early_adopter import interfaces
 
 
-# TODO(issue 726): Kill the "implementations" attribute of this in favor
-# of the same-information-less-bogusly-represented "cardinalities".
 class InvocationBreakdown(object):
   """An intermediate representation of invocation-side views of RPC methods.
 
   Attributes:
     cardinalities: A dictionary from RPC method name to interfaces.Cardinality
       value.
-    implementations: A dictionary from RPC method name to
-      assembly_interfaces.MethodImplementation describing the method.
     request_serializers: A dictionary from RPC method name to callable
       behavior to be used serializing request values for the RPC.
     response_deserializers: A dictionary from RPC method name to callable
@@ -59,8 +56,7 @@ class _EasyInvocationBreakdown(
     InvocationBreakdown,
     collections.namedtuple(
         '_EasyInvocationBreakdown',
-        ('cardinalities', 'implementations', 'request_serializers',
-         'response_deserializers'))):
+        ('cardinalities', 'request_serializers', 'response_deserializers'))):
   pass
 
 
@@ -68,8 +64,8 @@ class ServiceBreakdown(object):
   """An intermediate representation of service-side views of RPC methods.
 
   Attributes:
-    implementations: A dictionary from RPC method name
-      assembly_interfaces.MethodImplementation implementing the RPC method.
+    implementations: A dictionary from RPC method name to
+      face_interfaces.MethodImplementation implementing the RPC method.
     request_deserializers: A dictionary from RPC method name to callable
       behavior to be used deserializing request values for the RPC.
     response_serializers: A dictionary from RPC method name to callable
@@ -97,25 +93,14 @@ def break_down_invocation(method_descriptions):
     An InvocationBreakdown corresponding to the given method descriptions.
   """
   cardinalities = {}
-  implementations = {}
   request_serializers = {}
   response_deserializers = {}
   for name, method_description in method_descriptions.iteritems():
-    cardinality = method_description.cardinality()
-    cardinalities[name] = cardinality
-    if cardinality is interfaces.Cardinality.UNARY_UNARY:
-      implementations[name] = assembly_utilities.unary_unary_inline(None)
-    elif cardinality is interfaces.Cardinality.UNARY_STREAM:
-      implementations[name] = assembly_utilities.unary_stream_inline(None)
-    elif cardinality is interfaces.Cardinality.STREAM_UNARY:
-      implementations[name] = assembly_utilities.stream_unary_inline(None)
-    elif cardinality is interfaces.Cardinality.STREAM_STREAM:
-      implementations[name] = assembly_utilities.stream_stream_inline(None)
+    cardinalities[name] = method_description.cardinality()
     request_serializers[name] = method_description.serialize_request
     response_deserializers[name] = method_description.deserialize_response
   return _EasyInvocationBreakdown(
-      cardinalities, implementations, request_serializers,
-      response_deserializers)
+      cardinalities, request_serializers, response_deserializers)
 
 
 def break_down_service(method_descriptions):
@@ -139,28 +124,28 @@ def break_down_service(method_descriptions):
           service_behavior=method_description.service_unary_unary):
         return service_behavior(
             request, _reexport.rpc_context(face_rpc_context))
-      implementations[name] = assembly_utilities.unary_unary_inline(service)
+      implementations[name] = face_utilities.unary_unary_inline(service)
     elif cardinality is interfaces.Cardinality.UNARY_STREAM:
       def service(
           request, face_rpc_context,
           service_behavior=method_description.service_unary_stream):
         return service_behavior(
             request, _reexport.rpc_context(face_rpc_context))
-      implementations[name] = assembly_utilities.unary_stream_inline(service)
+      implementations[name] = face_utilities.unary_stream_inline(service)
     elif cardinality is interfaces.Cardinality.STREAM_UNARY:
       def service(
           request_iterator, face_rpc_context,
           service_behavior=method_description.service_stream_unary):
         return service_behavior(
             request_iterator, _reexport.rpc_context(face_rpc_context))
-      implementations[name] = assembly_utilities.stream_unary_inline(service)
+      implementations[name] = face_utilities.stream_unary_inline(service)
     elif cardinality is interfaces.Cardinality.STREAM_STREAM:
       def service(
           request_iterator, face_rpc_context,
           service_behavior=method_description.service_stream_stream):
         return service_behavior(
             request_iterator, _reexport.rpc_context(face_rpc_context))
-      implementations[name] = assembly_utilities.stream_stream_inline(service)
+      implementations[name] = face_utilities.stream_stream_inline(service)
     request_deserializers[name] = method_description.deserialize_request
     response_serializers[name] = method_description.serialize_response
 
diff --git a/src/python/src/grpc/early_adopter/_reexport.py b/src/python/src/grpc/early_adopter/_reexport.py
index 35f4e85a7287bf94d88be6230a829d953a5a4ea0..f3416028e8c6fa8bb7fa64df8589b4ef52e97696 100644
--- a/src/python/src/grpc/early_adopter/_reexport.py
+++ b/src/python/src/grpc/early_adopter/_reexport.py
@@ -27,12 +27,20 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from grpc.framework.common import cardinality
 from grpc.framework.face import exceptions as face_exceptions
 from grpc.framework.face import interfaces as face_interfaces
 from grpc.framework.foundation import future
 from grpc.early_adopter import exceptions
 from grpc.early_adopter import interfaces
 
+_EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY = {
+    interfaces.Cardinality.UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY,
+    interfaces.Cardinality.UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM,
+    interfaces.Cardinality.STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY,
+    interfaces.Cardinality.STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM,
+}
+
 _ABORTION_REEXPORT = {
     face_interfaces.Abortion.CANCELLED: interfaces.Abortion.CANCELLED,
     face_interfaces.Abortion.EXPIRED: interfaces.Abortion.EXPIRED,
@@ -142,71 +150,49 @@ class _RpcContext(interfaces.RpcContext):
 
 class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync):
 
-  def __init__(self, face_unary_unary_sync_async):
-    self._underlying = face_unary_unary_sync_async
+  def __init__(self, face_unary_unary_multi_callable):
+    self._underlying = face_unary_unary_multi_callable
 
   def __call__(self, request, timeout):
     return _call_reexporting_errors(
         self._underlying, request, timeout)
 
   def async(self, request, timeout):
-    return _ReexportedFuture(self._underlying.async(request, timeout))
+    return _ReexportedFuture(self._underlying.future(request, timeout))
 
 
 class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync):
 
-  def __init__(self, face_stream_unary_sync_async):
-    self._underlying = face_stream_unary_sync_async
+  def __init__(self, face_stream_unary_multi_callable):
+    self._underlying = face_stream_unary_multi_callable
 
   def __call__(self, request_iterator, timeout):
     return _call_reexporting_errors(
         self._underlying, request_iterator, timeout)
 
   def async(self, request_iterator, timeout):
-    return _ReexportedFuture(self._underlying.async(request_iterator, timeout))
-
+    return _ReexportedFuture(self._underlying.future(request_iterator, timeout))
 
-class _Stub(interfaces.Stub):
 
-  def __init__(self, assembly_stub, cardinalities):
-    self._assembly_stub = assembly_stub
-    self._cardinalities = cardinalities
-
-  def __enter__(self):
-    self._assembly_stub.__enter__()
-    return self
+def common_cardinalities(early_adopter_cardinalities):
+  common_cardinalities = {}
+  for name, early_adopter_cardinality in early_adopter_cardinalities.iteritems():
+    common_cardinalities[name] = _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[
+        early_adopter_cardinality]
+  return common_cardinalities
 
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    self._assembly_stub.__exit__(exc_type, exc_val, exc_tb)
-    return False
-
-  def __getattr__(self, attr):
-    underlying_attr = self._assembly_stub.__getattr__(attr)
-    cardinality = self._cardinalities.get(attr)
-    # TODO(nathaniel): unify this trick with its other occurrence in the code.
-    if cardinality is None:
-      for name, cardinality in self._cardinalities.iteritems():
-        last_slash_index = name.rfind('/')
-        if 0 <= last_slash_index and name[last_slash_index + 1:] == attr:
-          break
-      else:
-        raise AttributeError(attr)
-    if cardinality is interfaces.Cardinality.UNARY_UNARY:
-      return _UnaryUnarySyncAsync(underlying_attr)
-    elif cardinality is interfaces.Cardinality.UNARY_STREAM:
-      return lambda request, timeout: _CancellableIterator(
-          underlying_attr(request, timeout))
-    elif cardinality is interfaces.Cardinality.STREAM_UNARY:
-      return _StreamUnarySyncAsync(underlying_attr)
-    elif cardinality is interfaces.Cardinality.STREAM_STREAM:
-      return lambda request_iterator, timeout: _CancellableIterator(
-          underlying_attr(request_iterator, timeout))
-    else:
-      raise AttributeError(attr)
 
 def rpc_context(face_rpc_context):
   return _RpcContext(face_rpc_context)
 
 
-def stub(assembly_stub, cardinalities):
-  return _Stub(assembly_stub, cardinalities)
+def cancellable_iterator(face_cancellable_iterator):
+  return _CancellableIterator(face_cancellable_iterator)
+
+
+def unary_unary_sync_async(face_unary_unary_multi_callable):
+  return _UnaryUnarySyncAsync(face_unary_unary_multi_callable)
+
+
+def stream_unary_sync_async(face_stream_unary_multi_callable):
+  return _StreamUnarySyncAsync(face_stream_unary_multi_callable)
diff --git a/src/python/src/grpc/early_adopter/implementations.py b/src/python/src/grpc/early_adopter/implementations.py
index 619595862400bd731b52b915704ae3321dd01447..1c02f9e4d63f510a109f00e4a66f5e98f69ca7a3 100644
--- a/src/python/src/grpc/early_adopter/implementations.py
+++ b/src/python/src/grpc/early_adopter/implementations.py
@@ -33,10 +33,16 @@ import threading
 
 from grpc._adapter import fore as _fore
 from grpc._adapter import rear as _rear
-from grpc.early_adopter import _assembly_utilities
+from grpc.early_adopter import _face_utilities
 from grpc.early_adopter import _reexport
 from grpc.early_adopter import interfaces
-from grpc.framework.assembly import implementations as _assembly_implementations
+from grpc.framework.base import util as _base_utilities
+from grpc.framework.base.packets import implementations as _tickets_implementations
+from grpc.framework.face import implementations as _face_implementations
+from grpc.framework.foundation import logging_pool
+
+_THREAD_POOL_SIZE = 80
+_ONE_DAY_IN_SECONDS = 24 * 60 * 60
 
 
 class _Server(interfaces.Server):
@@ -50,30 +56,39 @@ class _Server(interfaces.Server):
     else:
       self._key_chain_pairs = ((private_key, certificate_chain),)
 
+    self._pool = None
+    self._back = None
     self._fore_link = None
-    self._server = None
 
   def _start(self):
     with self._lock:
-      if self._server is None:
-        self._fore_link = _fore.activated_fore_link(
-            self._port, self._breakdown.request_deserializers,
+      if self._pool is None:
+        self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
+        servicer = _face_implementations.servicer(
+            self._pool, self._breakdown.implementations, None)
+        self._back = _tickets_implementations.back(
+            servicer, self._pool, self._pool, self._pool, _ONE_DAY_IN_SECONDS,
+            _ONE_DAY_IN_SECONDS)
+        self._fore_link = _fore.ForeLink(
+            self._pool, self._breakdown.request_deserializers,
             self._breakdown.response_serializers, None, self._key_chain_pairs)
-
-        self._server = _assembly_implementations.assemble_service(
-            self._breakdown.implementations, self._fore_link)
-        self._server.start()
+        self._back.join_fore_link(self._fore_link)
+        self._fore_link.join_rear_link(self._back)
+        self._fore_link.start()
       else:
         raise ValueError('Server currently running!')
 
   def _stop(self):
     with self._lock:
-      if self._server is None:
+      if self._pool is None:
         raise ValueError('Server not running!')
       else:
-        self._server.stop()
-        self._server = None
+        self._fore_link.stop()
+        _base_utilities.wait_for_idle(self._back)
+        self._pool.shutdown(wait=True)
         self._fore_link = None
+        self._back = None
+        self._pool = None
 
   def __enter__(self):
     self._start()
@@ -93,14 +108,105 @@ class _Server(interfaces.Server):
     with self._lock:
       return self._fore_link.port()
 
-def _build_stub(breakdown, activated_rear_link):
-  assembly_stub = _assembly_implementations.assemble_dynamic_inline_stub(
-      breakdown.implementations, activated_rear_link)
-  return _reexport.stub(assembly_stub, breakdown.cardinalities)
+
+class _Stub(interfaces.Stub):
+
+  def __init__(
+      self, breakdown, host, port, secure, root_certificates, private_key,
+      certificate_chain, server_host_override=None):
+    self._lock = threading.Lock()
+    self._breakdown = breakdown
+    self._host = host
+    self._port = port
+    self._secure = secure
+    self._root_certificates = root_certificates
+    self._private_key = private_key
+    self._certificate_chain = certificate_chain
+    self._server_host_override = server_host_override
+
+    self._pool = None
+    self._front = None
+    self._rear_link = None
+    self._understub = None
+
+  def __enter__(self):
+    with self._lock:
+      if self._pool is None:
+        self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
+        self._front = _tickets_implementations.front(
+            self._pool, self._pool, self._pool)
+        self._rear_link = _rear.RearLink(
+            self._host, self._port, self._pool,
+            self._breakdown.request_serializers,
+            self._breakdown.response_deserializers, self._secure,
+            self._root_certificates, self._private_key, self._certificate_chain,
+            server_host_override=self._server_host_override)
+        self._front.join_rear_link(self._rear_link)
+        self._rear_link.join_fore_link(self._front)
+        self._rear_link.start()
+        self._understub = _face_implementations.dynamic_stub(
+            _reexport.common_cardinalities(self._breakdown.cardinalities),
+            self._front, self._pool, '')
+      else:
+        raise ValueError('Tried to __enter__ already-__enter__ed Stub!')
+    return self
+
+  def __exit__(self, exc_type, exc_val, exc_tb):
+    with self._lock:
+      if self._pool is None:
+        raise ValueError('Tried to __exit__ non-__enter__ed Stub!')
+      else:
+        self._rear_link.stop()
+        _base_utilities.wait_for_idle(self._front)
+        self._pool.shutdown(wait=True)
+        self._rear_link = None
+        self._front = None
+        self._pool = None
+        self._understub = None
+    return False
+
+  def __getattr__(self, attr):
+    with self._lock:
+      if self._pool is None:
+        raise ValueError('Tried to __getattr__ non-__enter__ed Stub!')
+      else:
+        underlying_attr = getattr(self._understub, attr, None)
+        method_cardinality = self._breakdown.cardinalities.get(attr)
+        # TODO(nathaniel): Eliminate this trick.
+        if underlying_attr is None:
+          for method_name, method_cardinality in self._breakdown.cardinalities.iteritems():
+            last_slash_index = method_name.rfind('/')
+            if 0 <= last_slash_index and method_name[last_slash_index + 1:] == attr:
+              underlying_attr = getattr(self._understub, method_name)
+              break
+          else:
+            raise AttributeError(attr)
+        if method_cardinality is interfaces.Cardinality.UNARY_UNARY:
+          return _reexport.unary_unary_sync_async(underlying_attr)
+        elif method_cardinality is interfaces.Cardinality.UNARY_STREAM:
+          return lambda request, timeout: _reexport.cancellable_iterator(
+              underlying_attr(request, timeout))
+        elif method_cardinality is interfaces.Cardinality.STREAM_UNARY:
+          return _reexport.stream_unary_sync_async(underlying_attr)
+        elif method_cardinality is interfaces.Cardinality.STREAM_STREAM:
+          return lambda request_iterator, timeout: (
+              _reexport.cancellable_iterator(underlying_attr(
+                  request_iterator, timeout)))
+        else:
+          raise AttributeError(attr)
+
+
+def _build_stub(
+    methods, host, port, secure, root_certificates, private_key,
+    certificate_chain, server_host_override=None):
+  breakdown = _face_utilities.break_down_invocation(methods)
+  return _Stub(
+      breakdown, host, port, secure, root_certificates, private_key,
+      certificate_chain, server_host_override=server_host_override)
 
 
 def _build_server(methods, port, private_key, certificate_chain):
-  breakdown = _assembly_utilities.break_down_service(methods)
+  breakdown = _face_utilities.break_down_service(methods)
   return _Server(breakdown, port, private_key, certificate_chain)
 
 
@@ -117,15 +223,12 @@ def insecure_stub(methods, host, port):
   Returns:
     An interfaces.Stub affording RPC invocation.
   """
-  breakdown = _assembly_utilities.break_down_invocation(methods)
-  activated_rear_link = _rear.activated_rear_link(
-      host, port, breakdown.request_serializers,
-      breakdown.response_deserializers)
-  return _build_stub(breakdown, activated_rear_link)
+  return _build_stub(methods, host, port, False, None, None, None)
 
 
 def secure_stub(
-    methods, host, port, root_certificates, private_key, certificate_chain):
+    methods, host, port, root_certificates, private_key, certificate_chain,
+    server_host_override=None):
   """Constructs an insecure interfaces.Stub.
 
   Args:
@@ -140,16 +243,15 @@ def secure_stub(
       should be used.
     certificate_chain: The PEM-encoded certificate chain to use or None if no
       certificate chain should be used.
+    server_host_override: (For testing only) the target name used for SSL
+      host name checking.
 
   Returns:
     An interfaces.Stub affording RPC invocation.
   """
-  breakdown = _assembly_utilities.break_down_invocation(methods)
-  activated_rear_link = _rear.secure_activated_rear_link(
-      host, port, breakdown.request_serializers,
-      breakdown.response_deserializers, root_certificates, private_key,
-      certificate_chain)
-  return _build_stub(breakdown, activated_rear_link)
+  return _build_stub(
+      methods, host, port, True, root_certificates, private_key,
+      certificate_chain, server_host_override=server_host_override)
 
 
 def insecure_server(methods, port):
diff --git a/src/python/src/grpc/framework/assembly/implementations.py b/src/python/src/grpc/framework/assembly/implementations.py
deleted file mode 100644
index f7166ed99d1178bc1af8dfbf95b904d906ba4586..0000000000000000000000000000000000000000
--- a/src/python/src/grpc/framework/assembly/implementations.py
+++ /dev/null
@@ -1,317 +0,0 @@
-# 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.
-
-"""Implementations for assembling RPC framework values."""
-
-import threading
-
-# tickets_interfaces, face_interfaces, and activated are referenced from
-# specification in this module.
-from grpc.framework.assembly import interfaces
-from grpc.framework.base import util as base_utilities
-from grpc.framework.base.packets import implementations as tickets_implementations
-from grpc.framework.base.packets import interfaces as tickets_interfaces  # pylint: disable=unused-import
-from grpc.framework.common import cardinality
-from grpc.framework.common import style
-from grpc.framework.face import implementations as face_implementations
-from grpc.framework.face import interfaces as face_interfaces  # pylint: disable=unused-import
-from grpc.framework.face import utilities as face_utilities
-from grpc.framework.foundation import activated  # pylint: disable=unused-import
-from grpc.framework.foundation import logging_pool
-
-_ONE_DAY_IN_SECONDS = 60 * 60 * 24
-_THREAD_POOL_SIZE = 100
-
-
-class _FaceStub(object):
-
-  def __init__(self, rear_link):
-    self._rear_link = rear_link
-    self._lock = threading.Lock()
-    self._pool = None
-    self._front = None
-    self._under_stub = None
-
-  def __enter__(self):
-    with self._lock:
-      self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
-      self._front = tickets_implementations.front(
-          self._pool, self._pool, self._pool)
-      self._rear_link.start()
-      self._rear_link.join_fore_link(self._front)
-      self._front.join_rear_link(self._rear_link)
-      self._under_stub = face_implementations.stub(self._front, self._pool)
-
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    with self._lock:
-      self._under_stub = None
-      self._rear_link.stop()
-      base_utilities.wait_for_idle(self._front)
-      self._front = None
-      self._pool.shutdown(wait=True)
-      self._pool = None
-    return False
-
-  def __getattr__(self, attr):
-    with self._lock:
-      if self._under_stub is None:
-        raise ValueError('Called out of context!')
-      else:
-        return getattr(self._under_stub, attr)
-
-
-def _behaviors(implementations, front, pool):
-  behaviors = {}
-  stub = face_implementations.stub(front, pool)
-  for name, implementation in implementations.iteritems():
-    if implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
-      behaviors[name] = stub.unary_unary_sync_async(name)
-    elif implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
-      behaviors[name] = lambda request, context, bound_name=name: (
-          stub.inline_value_in_stream_out(bound_name, request, context))
-    elif implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
-      behaviors[name] = stub.stream_unary_sync_async(name)
-    elif implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
-      behaviors[name] = lambda request_iterator, context, bound_name=name: (
-          stub.inline_stream_in_stream_out(
-              bound_name, request_iterator, context))
-  return behaviors
-
-
-class _DynamicInlineStub(object):
-
-  def __init__(self, implementations, rear_link):
-    self._implementations = implementations
-    self._rear_link = rear_link
-    self._lock = threading.Lock()
-    self._pool = None
-    self._front = None
-    self._behaviors = None
-
-  def __enter__(self):
-    with self._lock:
-      self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
-      self._front = tickets_implementations.front(
-          self._pool, self._pool, self._pool)
-      self._rear_link.start()
-      self._rear_link.join_fore_link(self._front)
-      self._front.join_rear_link(self._rear_link)
-      self._behaviors = _behaviors(
-          self._implementations, self._front, self._pool)
-      return self
-
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    with self._lock:
-      self._behaviors = None
-      self._rear_link.stop()
-      base_utilities.wait_for_idle(self._front)
-      self._front = None
-      self._pool.shutdown(wait=True)
-      self._pool = None
-    return False
-
-  def __getattr__(self, attr):
-    with self._lock:
-      behavior = self._behaviors.get(attr)
-      if behavior is None:
-        for name, behavior in self._behaviors.iteritems():
-          last_slash_index = name.rfind('/')
-          if 0 <= last_slash_index and name[last_slash_index + 1:] == attr:
-            return behavior
-        else:
-          raise AttributeError(
-              '_DynamicInlineStub instance has no attribute "%s"!' % attr)
-      else:
-        return behavior
-
-
-def _servicer(implementations, pool):
-  inline_value_in_value_out_methods = {}
-  inline_value_in_stream_out_methods = {}
-  inline_stream_in_value_out_methods = {}
-  inline_stream_in_stream_out_methods = {}
-  event_value_in_value_out_methods = {}
-  event_value_in_stream_out_methods = {}
-  event_stream_in_value_out_methods = {}
-  event_stream_in_stream_out_methods = {}
-
-  for name, implementation in implementations.iteritems():
-    if implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
-      if implementation.style is style.Service.INLINE:
-        inline_value_in_value_out_methods[name] = (
-            face_utilities.inline_unary_unary_method(implementation.unary_unary_inline))
-      elif implementation.style is style.Service.EVENT:
-        event_value_in_value_out_methods[name] = (
-            face_utilities.event_unary_unary_method(implementation.unary_unary_event))
-    elif implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
-      if implementation.style is style.Service.INLINE:
-        inline_value_in_stream_out_methods[name] = (
-            face_utilities.inline_unary_stream_method(implementation.unary_stream_inline))
-      elif implementation.style is style.Service.EVENT:
-        event_value_in_stream_out_methods[name] = (
-            face_utilities.event_unary_stream_method(implementation.unary_stream_event))
-    if implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
-      if implementation.style is style.Service.INLINE:
-        inline_stream_in_value_out_methods[name] = (
-            face_utilities.inline_stream_unary_method(implementation.stream_unary_inline))
-      elif implementation.style is style.Service.EVENT:
-        event_stream_in_value_out_methods[name] = (
-            face_utilities.event_stream_unary_method(implementation.stream_unary_event))
-    elif implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
-      if implementation.style is style.Service.INLINE:
-        inline_stream_in_stream_out_methods[name] = (
-            face_utilities.inline_stream_stream_method(implementation.stream_stream_inline))
-      elif implementation.style is style.Service.EVENT:
-        event_stream_in_stream_out_methods[name] = (
-            face_utilities.event_stream_stream_method(implementation.stream_stream_event))
-
-  return face_implementations.servicer(
-      pool,
-      inline_value_in_value_out_methods=inline_value_in_value_out_methods,
-      inline_value_in_stream_out_methods=inline_value_in_stream_out_methods,
-      inline_stream_in_value_out_methods=inline_stream_in_value_out_methods,
-      inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods,
-      event_value_in_value_out_methods=event_value_in_value_out_methods,
-      event_value_in_stream_out_methods=event_value_in_stream_out_methods,
-      event_stream_in_value_out_methods=event_stream_in_value_out_methods,
-      event_stream_in_stream_out_methods=event_stream_in_stream_out_methods)
-
-
-class _ServiceAssembly(interfaces.Server):
-
-  def __init__(self, implementations, fore_link):
-    self._implementations = implementations
-    self._fore_link = fore_link
-    self._lock = threading.Lock()
-    self._pool = None
-    self._back = None
-
-  def _start(self):
-    with self._lock:
-      self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
-      servicer = _servicer(self._implementations, self._pool)
-      self._back = tickets_implementations.back(
-          servicer, self._pool, self._pool, self._pool, _ONE_DAY_IN_SECONDS,
-          _ONE_DAY_IN_SECONDS)
-      self._fore_link.start()
-      self._fore_link.join_rear_link(self._back)
-      self._back.join_fore_link(self._fore_link)
-
-  def _stop(self):
-    with self._lock:
-      self._fore_link.stop()
-      base_utilities.wait_for_idle(self._back)
-      self._back = None
-      self._pool.shutdown(wait=True)
-      self._pool = None
-
-  def __enter__(self):
-    self._start()
-    return self
-
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    self._stop()
-    return False
-
-  def start(self):
-    return self._start()
-
-  def stop(self):
-    self._stop()
-
-  def port(self):
-    with self._lock:
-      return self._fore_link.port()
-
-
-def assemble_face_stub(activated_rear_link):
-  """Assembles a face_interfaces.Stub.
-
-  The returned object is a context manager and may only be used in context to
-  invoke RPCs.
-
-  Args:
-    activated_rear_link: An object that is both a tickets_interfaces.RearLink
-      and an activated.Activated. The object should be in the inactive state
-      when passed to this method.
-
-  Returns:
-    A face_interfaces.Stub on which, in context, RPCs can be invoked.
-  """
-  return _FaceStub(activated_rear_link)
-
-
-def assemble_dynamic_inline_stub(implementations, activated_rear_link):
-  """Assembles a stub with method names for attributes.
-
-  The returned object is a context manager and may only be used in context to
-  invoke RPCs.
-
-  The returned object, when used in context, will respond to attribute access
-  as follows: if the requested attribute is the name of a unary-unary RPC
-  method, the value of the attribute will be a
-  face_interfaces.UnaryUnarySyncAsync with which to invoke the RPC method. If
-  the requested attribute is the name of a unary-stream RPC method, the value
-  of the attribute will be a callable with the semantics of
-  face_interfaces.Stub.inline_value_in_stream_out, minus the "name" parameter,
-  with which to invoke the RPC method. If the requested attribute is the name
-  of a stream-unary RPC method, the value of the attribute will be a
-  face_interfaces.StreamUnarySyncAsync with which to invoke the RPC method. If
-  the requested attribute is the name of a stream-stream RPC method, the value
-  of the attribute will be a callable with the semantics of
-  face_interfaces.Stub.inline_stream_in_stream_out, minus the "name" parameter,
-  with which to invoke the RPC method.
-
-  Args:
-    implementations: A dictionary from RPC method name to
-      interfaces.MethodImplementation.
-    activated_rear_link: An object that is both a tickets_interfaces.RearLink
-      and an activated.Activated. The object should be in the inactive state
-      when passed to this method.
-
-  Returns:
-    A stub on which, in context, RPCs can be invoked.
-  """
-  return _DynamicInlineStub(implementations, activated_rear_link)
-
-
-def assemble_service(implementations, activated_fore_link):
-  """Assembles the service-side of the RPC Framework stack.
-
-  Args:
-    implementations: A dictionary from RPC method name to
-      interfaces.MethodImplementation.
-    activated_fore_link: An object that is both a tickets_interfaces.ForeLink
-      and an activated.Activated. The object should be in the inactive state
-      when passed to this method.
-
-  Returns:
-    An interfaces.Server encapsulating RPC service.
-  """
-  return _ServiceAssembly(implementations, activated_fore_link)
diff --git a/src/python/src/grpc/framework/assembly/implementations_test.py b/src/python/src/grpc/framework/assembly/implementations_test.py
deleted file mode 100644
index 74dc02ed83ddba887111be86668e193f8c1ea638..0000000000000000000000000000000000000000
--- a/src/python/src/grpc/framework/assembly/implementations_test.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# 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.
-
-# TODO(nathaniel): Expand this test coverage.
-
-"""Test of the GRPC-backed ForeLink and RearLink."""
-
-import threading
-import unittest
-
-from grpc.framework.assembly import implementations
-from grpc.framework.assembly import utilities
-from grpc.framework.base import interfaces
-from grpc.framework.base.packets import packets as tickets
-from grpc.framework.base.packets import interfaces as tickets_interfaces
-from grpc.framework.base.packets import null
-from grpc.framework.foundation import logging_pool
-from grpc._junkdrawer import math_pb2
-
-DIV = 'Div'
-DIV_MANY = 'DivMany'
-FIB = 'Fib'
-SUM = 'Sum'
-
-def _fibbonacci(limit):
-  left, right = 0, 1
-  for _ in xrange(limit):
-    yield left
-    left, right = right, left + right
-
-
-def _div(request, unused_context):
-  return math_pb2.DivReply(
-      quotient=request.dividend / request.divisor,
-      remainder=request.dividend % request.divisor)
-
-
-def _div_many(request_iterator, unused_context):
-  for request in request_iterator:
-    yield math_pb2.DivReply(
-        quotient=request.dividend / request.divisor,
-        remainder=request.dividend % request.divisor)
-
-
-def _fib(request, unused_context):
-  for number in _fibbonacci(request.limit):
-    yield math_pb2.Num(num=number)
-
-
-def _sum(request_iterator, unused_context):
-  accumulation = 0
-  for request in request_iterator:
-    accumulation += request.num
-  return math_pb2.Num(num=accumulation)
-
-
-_IMPLEMENTATIONS = {
-    DIV: utilities.unary_unary_inline(_div),
-    DIV_MANY: utilities.stream_stream_inline(_div_many),
-    FIB: utilities.unary_stream_inline(_fib),
-    SUM: utilities.stream_unary_inline(_sum),
-}
-
-_TIMEOUT = 10
-
-
-class PipeLink(tickets_interfaces.ForeLink, tickets_interfaces.RearLink):
-
-  def __init__(self):
-    self._fore_lock = threading.Lock()
-    self._fore_link = null.NULL_FORE_LINK
-    self._rear_lock = threading.Lock()
-    self._rear_link = null.NULL_REAR_LINK
-
-  def __enter__(self):
-    return self
-
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    return False
-
-  def start(self):
-    pass
-
-  def stop(self):
-    pass
-
-  def accept_back_to_front_ticket(self, ticket):
-    with self._fore_lock:
-      self._fore_link.accept_back_to_front_ticket(ticket)
-
-  def join_rear_link(self, rear_link):
-    with self._rear_lock:
-      self._rear_link = null.NULL_REAR_LINK if rear_link is None else rear_link
-
-  def accept_front_to_back_ticket(self, ticket):
-    with self._rear_lock:
-      self._rear_link.accept_front_to_back_ticket(ticket)
-
-  def join_fore_link(self, fore_link):
-    with self._fore_lock:
-      self._fore_link = null.NULL_FORE_LINK if fore_link is None else fore_link
-
-
-class FaceStubTest(unittest.TestCase):
-
-  def testUnaryUnary(self):
-    divisor = 7
-    dividend = 13
-    expected_quotient = 1
-    expected_remainder = 6
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    face_stub = implementations.assemble_face_stub(pipe)
-
-    service.start()
-    try:
-      with face_stub:
-        response = face_stub.blocking_value_in_value_out(
-            DIV, math_pb2.DivArgs(divisor=divisor, dividend=dividend),
-            _TIMEOUT)
-        self.assertEqual(expected_quotient, response.quotient)
-        self.assertEqual(expected_remainder, response.remainder)
-    finally:
-      service.stop()
-
-  def testUnaryStream(self):
-    stream_length = 29
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    face_stub = implementations.assemble_face_stub(pipe)
-
-    with service, face_stub:
-      responses = list(
-          face_stub.inline_value_in_stream_out(
-              FIB, math_pb2.FibArgs(limit=stream_length), _TIMEOUT))
-      numbers = [response.num for response in responses]
-      for early, middle, later in zip(numbers, numbers[1:], numbers[2:]):
-        self.assertEqual(early + middle, later)
-
-  def testStreamUnary(self):
-    stream_length = 13
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    face_stub = implementations.assemble_face_stub(pipe)
-
-    with service, face_stub:
-      sync_async = face_stub.stream_unary_sync_async(SUM)
-      response_future = sync_async.async(
-          (math_pb2.Num(num=index) for index in range(stream_length)),
-          _TIMEOUT)
-      self.assertEqual(
-          (stream_length * (stream_length - 1)) / 2,
-          response_future.result().num)
-
-  def testStreamStream(self):
-    stream_length = 17
-    divisor_offset = 7
-    dividend_offset = 17
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    face_stub = implementations.assemble_face_stub(pipe)
-
-    with service, face_stub:
-      response_iterator = face_stub.inline_stream_in_stream_out(
-          DIV_MANY,
-          (math_pb2.DivArgs(
-               divisor=divisor_offset + index,
-               dividend=dividend_offset + index)
-           for index in range(stream_length)),
-          _TIMEOUT)
-      for index, response in enumerate(response_iterator):
-        self.assertEqual(
-            (dividend_offset + index) / (divisor_offset + index),
-            response.quotient)
-        self.assertEqual(
-            (dividend_offset + index) % (divisor_offset + index),
-            response.remainder)
-      self.assertEqual(stream_length, index + 1)
-
-
-class DynamicInlineStubTest(unittest.TestCase):
-
-  def testUnaryUnary(self):
-    divisor = 59
-    dividend = 973
-    expected_quotient = dividend / divisor
-    expected_remainder = dividend % divisor
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    dynamic_stub = implementations.assemble_dynamic_inline_stub(
-        _IMPLEMENTATIONS, pipe)
-
-    service.start()
-    with dynamic_stub:
-      response = dynamic_stub.Div(
-          math_pb2.DivArgs(divisor=divisor, dividend=dividend), _TIMEOUT)
-      self.assertEqual(expected_quotient, response.quotient)
-      self.assertEqual(expected_remainder, response.remainder)
-    service.stop()
-
-  def testUnaryStream(self):
-    stream_length = 43
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    dynamic_stub = implementations.assemble_dynamic_inline_stub(
-        _IMPLEMENTATIONS, pipe)
-
-    with service, dynamic_stub:
-      response_iterator = dynamic_stub.Fib(
-          math_pb2.FibArgs(limit=stream_length), _TIMEOUT)
-      numbers = tuple(response.num for response in response_iterator)
-      for early, middle, later in zip(numbers, numbers[:1], numbers[:2]):
-        self.assertEqual(early + middle, later)
-      self.assertEqual(stream_length, len(numbers))
-
-  def testStreamUnary(self):
-    stream_length = 127
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    dynamic_stub = implementations.assemble_dynamic_inline_stub(
-        _IMPLEMENTATIONS, pipe)
-
-    with service, dynamic_stub:
-      response_future = dynamic_stub.Sum.async(
-          (math_pb2.Num(num=index) for index in range(stream_length)),
-          _TIMEOUT)
-      self.assertEqual(
-          (stream_length * (stream_length - 1)) / 2,
-          response_future.result().num)
-
-  def testStreamStream(self):
-    stream_length = 179
-    divisor_offset = 71
-    dividend_offset = 1763
-    pipe = PipeLink()
-    service = implementations.assemble_service(_IMPLEMENTATIONS, pipe)
-    dynamic_stub = implementations.assemble_dynamic_inline_stub(
-        _IMPLEMENTATIONS, pipe)
-
-    with service, dynamic_stub:
-      response_iterator = dynamic_stub.DivMany(
-          (math_pb2.DivArgs(
-               divisor=divisor_offset + index,
-               dividend=dividend_offset + index)
-           for index in range(stream_length)),
-          _TIMEOUT)
-      for index, response in enumerate(response_iterator):
-        self.assertEqual(
-            (dividend_offset + index) / (divisor_offset + index),
-            response.quotient)
-        self.assertEqual(
-            (dividend_offset + index) % (divisor_offset + index),
-            response.remainder)
-      self.assertEqual(stream_length, index + 1)
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/src/python/src/grpc/framework/assembly/interfaces.py b/src/python/src/grpc/framework/assembly/interfaces.py
deleted file mode 100644
index d1a6aad29e767e07f03795c6b3bc2f6abaab666e..0000000000000000000000000000000000000000
--- a/src/python/src/grpc/framework/assembly/interfaces.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# 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.
-
-# TODO(nathaniel): The assembly layer only exists to smooth out wrinkles in
-# the face layer. The two should be squashed together as soon as manageable.
-"""Interfaces for assembling RPC Framework values."""
-
-import abc
-
-# cardinality, style, and stream are referenced from specification in this
-# module.
-from grpc.framework.common import cardinality  # pylint: disable=unused-import
-from grpc.framework.common import style  # pylint: disable=unused-import
-from grpc.framework.foundation import activated
-from grpc.framework.foundation import stream  # pylint: disable=unused-import
-
-
-class MethodImplementation(object):
-  """A sum type that describes an RPC method implementation.
-
-  Attributes:
-    cardinality: A cardinality.Cardinality value.
-    style: A style.Service value.
-    unary_unary_inline: The implementation of the RPC method as a callable
-      value that takes a request value and a face_interfaces.RpcContext object
-      and returns a response value. Only non-None if cardinality is
-      cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE.
-    unary_stream_inline: The implementation of the RPC method as a callable
-      value that takes a request value and a face_interfaces.RpcContext object
-      and returns an iterator of response values. Only non-None if cardinality
-      is cardinality.Cardinality.UNARY_STREAM and style is
-      style.Service.INLINE.
-    stream_unary_inline: The implementation of the RPC method as a callable
-      value that takes an iterator of request values and a
-      face_interfaces.RpcContext object and returns a response value. Only
-      non-None if cardinality is cardinality.Cardinality.STREAM_UNARY and style
-      is style.Service.INLINE.
-    stream_stream_inline: The implementation of the RPC method as a callable
-      value that takes an iterator of request values and a
-      face_interfaces.RpcContext object and returns an iterator of response
-      values. Only non-None if cardinality is
-      cardinality.Cardinality.STREAM_STREAM and style is style.Service.INLINE.
-    unary_unary_event: The implementation of the RPC method as a callable value
-      that takes a request value, a response callback to which to pass the
-      response value of the RPC, and a face_interfaces.RpcContext. Only
-      non-None if cardinality is cardinality.Cardinality.UNARY_UNARY and style
-      is style.Service.EVENT.
-    unary_stream_event: The implementation of the RPC method as a callable
-      value that takes a request value, a stream.Consumer to which to pass the
-      the response values of the RPC, and a face_interfaces.RpcContext. Only
-      non-None if cardinality is cardinality.Cardinality.UNARY_STREAM and style
-      is style.Service.EVENT.
-    stream_unary_event: The implementation of the RPC method as a callable
-      value that takes a response callback to which to pass the response value
-      of the RPC and a face_interfaces.RpcContext and returns a stream.Consumer
-      to which the request values of the RPC should be passed. Only non-None if
-      cardinality is cardinality.Cardinality.STREAM_UNARY and style is
-      style.Service.EVENT.
-    stream_stream_event: The implementation of the RPC method as a callable
-      value that takes a stream.Consumer to which to pass the response values
-      of the RPC and a face_interfaces.RpcContext and returns a stream.Consumer
-      to which the request values of the RPC should be passed. Only non-None if
-      cardinality is cardinality.Cardinality.STREAM_STREAM and style is
-      style.Service.EVENT.
-  """
-  __metaclass__ = abc.ABCMeta
-
-
-class Server(activated.Activated):
-  """The server interface.
-
-  Aside from being able to be activated and deactivated, objects of this type
-  are able to report the port on which they are servicing RPCs.
-  """
-  __metaclass__ = abc.ABCMeta
-
-  # TODO(issue 726): This is an abstraction violation; not every Server is
-  # necessarily serving over a network at all.
-  @abc.abstractmethod
-  def port(self):
-    """Identifies the port on which this Server is servicing RPCs.
-
-    This method may only be called while the server is active.
-
-    Returns:
-      The number of the port on which this Server is servicing RPCs.
-    """
-    raise NotImplementedError()
diff --git a/src/python/src/grpc/framework/assembly/utilities.py b/src/python/src/grpc/framework/assembly/utilities.py
deleted file mode 100644
index 80e7f59c03c5f8b49696f2fffefe2827b2803f86..0000000000000000000000000000000000000000
--- a/src/python/src/grpc/framework/assembly/utilities.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# 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.
-
-"""Utilities for assembling RPC framework values."""
-
-import collections
-
-from grpc.framework.assembly import interfaces
-from grpc.framework.common import cardinality
-from grpc.framework.common import style
-from grpc.framework.face import interfaces as face_interfaces
-from grpc.framework.foundation import stream
-
-
-class _MethodImplementation(
-    interfaces.MethodImplementation,
-    collections.namedtuple(
-        '_MethodImplementation',
-        ['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline',
-         'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event',
-         'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])):
-  pass
-
-
-def unary_unary_inline(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a unary-unary RPC method as a callable value
-      that takes a request value and a face_interfaces.RpcContext object and
-      returns a response value.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior,
-      None, None, None, None, None, None, None)
-
-
-def unary_stream_inline(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a unary-stream RPC method as a callable
-      value that takes a request value and a face_interfaces.RpcContext object
-      and returns an iterator of response values.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None,
-      behavior, None, None, None, None, None, None)
-
-
-def stream_unary_inline(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a stream-unary RPC method as a callable
-      value that takes an iterator of request values and a
-      face_interfaces.RpcContext object and returns a response value.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None,
-      behavior, None, None, None, None, None)
-
-
-def stream_stream_inline(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a stream-stream RPC method as a callable
-      value that takes an iterator of request values and a
-      face_interfaces.RpcContext object and returns an iterator of response
-      values.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None,
-      None, behavior, None, None, None, None)
-
-
-def unary_unary_event(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a unary-unary RPC method as a callable
-      value that takes a request value, a response callback to which to pass
-      the response value of the RPC, and a face_interfaces.RpcContext.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None,
-      None, None, behavior, None, None, None)
-
-
-def unary_stream_event(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a unary-stream RPC method as a callable
-      value that takes a request value, a stream.Consumer to which to pass the
-      the response values of the RPC, and a face_interfaces.RpcContext.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None,
-      None, None, None, behavior, None, None)
-
-
-def stream_unary_event(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a stream-unary RPC method as a callable
-      value that takes a response callback to which to pass the response value
-      of the RPC and a face_interfaces.RpcContext and returns a stream.Consumer
-      to which the request values of the RPC should be passed.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None,
-      None, None, None, None, behavior, None)
-
-
-def stream_stream_event(behavior):
-  """Creates an interfaces.MethodImplementation for the given behavior.
-
-  Args:
-    behavior: The implementation of a stream-stream RPC method as a callable
-      value that takes a stream.Consumer to which to pass the response values
-      of the RPC and a face_interfaces.RpcContext and returns a stream.Consumer
-      to which the request values of the RPC should be passed.
-
-  Returns:
-    An interfaces.MethodImplementation derived from the given behavior.
-  """
-  return _MethodImplementation(
-      cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None,
-      None, None, None, None, None, behavior)
diff --git a/src/python/src/grpc/framework/face/_service.py b/src/python/src/grpc/framework/face/_service.py
index 26bde129687a5b48b6bb7ad849665b10ab19e722..cdf413356ad716e33bd45cdc9accda28202d50a6 100644
--- a/src/python/src/grpc/framework/face/_service.py
+++ b/src/python/src/grpc/framework/face/_service.py
@@ -105,15 +105,14 @@ def adapt_inline_value_in_value_out(method):
   def adaptation(response_consumer, operation_context):
     rpc_context = _control.RpcContext(operation_context)
     return stream_util.TransformingConsumer(
-        lambda request: method.service(request, rpc_context), response_consumer)
+        lambda request: method(request, rpc_context), response_consumer)
   return adaptation
 
 
 def adapt_inline_value_in_stream_out(method):
   def adaptation(response_consumer, operation_context):
     rpc_context = _control.RpcContext(operation_context)
-    return _ValueInStreamOutConsumer(
-        method.service, rpc_context, response_consumer)
+    return _ValueInStreamOutConsumer(method, rpc_context, response_consumer)
   return adaptation
 
 
@@ -123,7 +122,7 @@ def adapt_inline_stream_in_value_out(method, pool):
     operation_context.add_termination_callback(rendezvous.set_outcome)
     def in_pool_thread():
       response_consumer.consume_and_terminate(
-          method.service(rendezvous, _control.RpcContext(operation_context)))
+          method(rendezvous, _control.RpcContext(operation_context)))
     pool.submit(_pool_wrap(in_pool_thread, operation_context))
     return rendezvous
   return adaptation
@@ -149,7 +148,7 @@ def adapt_inline_stream_in_stream_out(method, pool):
     operation_context.add_termination_callback(rendezvous.set_outcome)
     def in_pool_thread():
       _control.pipe_iterator_to_consumer(
-          method.service(rendezvous, _control.RpcContext(operation_context)),
+          method(rendezvous, _control.RpcContext(operation_context)),
           response_consumer, operation_context.is_active, True)
     pool.submit(_pool_wrap(in_pool_thread, operation_context))
     return rendezvous
@@ -159,7 +158,7 @@ def adapt_inline_stream_in_stream_out(method, pool):
 def adapt_event_value_in_value_out(method):
   def adaptation(response_consumer, operation_context):
     def on_payload(payload):
-      method.service(
+      method(
           payload, response_consumer.consume_and_terminate,
           _control.RpcContext(operation_context))
     return _control.UnaryConsumer(on_payload)
@@ -169,7 +168,7 @@ def adapt_event_value_in_value_out(method):
 def adapt_event_value_in_stream_out(method):
   def adaptation(response_consumer, operation_context):
     def on_payload(payload):
-      method.service(
+      method(
           payload, response_consumer, _control.RpcContext(operation_context))
     return _control.UnaryConsumer(on_payload)
   return adaptation
@@ -178,12 +177,11 @@ def adapt_event_value_in_stream_out(method):
 def adapt_event_stream_in_value_out(method):
   def adaptation(response_consumer, operation_context):
     rpc_context = _control.RpcContext(operation_context)
-    return method.service(response_consumer.consume_and_terminate, rpc_context)
+    return method(response_consumer.consume_and_terminate, rpc_context)
   return adaptation
 
 
 def adapt_event_stream_in_stream_out(method):
   def adaptation(response_consumer, operation_context):
-    return method.service(
-        response_consumer, _control.RpcContext(operation_context))
+    return method(response_consumer, _control.RpcContext(operation_context))
   return adaptation
diff --git a/src/python/src/grpc/framework/face/_test_case.py b/src/python/src/grpc/framework/face/_test_case.py
index a4e17c464ce75bcca7e81f792aa34a2022f3e12d..b3a012db001ef44046ff13ee6247129f1bf7a62f 100644
--- a/src/python/src/grpc/framework/face/_test_case.py
+++ b/src/python/src/grpc/framework/face/_test_case.py
@@ -42,37 +42,17 @@ class FaceTestCase(test_case.FaceTestCase):
   """Provides abstract Face-layer tests an in-memory implementation."""
 
   def set_up_implementation(
-      self,
-      name,
-      methods,
-      inline_value_in_value_out_methods,
-      inline_value_in_stream_out_methods,
-      inline_stream_in_value_out_methods,
-      inline_stream_in_stream_out_methods,
-      event_value_in_value_out_methods,
-      event_value_in_stream_out_methods,
-      event_stream_in_value_out_methods,
-      event_stream_in_stream_out_methods,
-      multi_method):
+      self, name, methods, method_implementations,
+      multi_method_implementation):
     servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE)
     stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE)
 
     servicer = implementations.servicer(
-        servicer_pool,
-        inline_value_in_value_out_methods=inline_value_in_value_out_methods,
-        inline_value_in_stream_out_methods=inline_value_in_stream_out_methods,
-        inline_stream_in_value_out_methods=inline_stream_in_value_out_methods,
-        inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods,
-        event_value_in_value_out_methods=event_value_in_value_out_methods,
-        event_value_in_stream_out_methods=event_value_in_stream_out_methods,
-        event_stream_in_value_out_methods=event_stream_in_value_out_methods,
-        event_stream_in_stream_out_methods=event_stream_in_stream_out_methods,
-        multi_method=multi_method)
+        servicer_pool, method_implementations, multi_method_implementation)
 
     linked_pair = base_util.linked_pair(servicer, _TIMEOUT)
-    server = implementations.server()
-    stub = implementations.stub(linked_pair.front, stub_pool)
-    return server, stub, (servicer_pool, stub_pool, linked_pair)
+    stub = implementations.generic_stub(linked_pair.front, stub_pool)
+    return stub, (servicer_pool, stub_pool, linked_pair)
 
   def tear_down_implementation(self, memo):
     servicer_pool, stub_pool, linked_pair = memo
diff --git a/src/python/src/grpc/framework/face/implementations.py b/src/python/src/grpc/framework/face/implementations.py
index 86948b386faf6f8d62bbb4fc88cf7ca6bfca244e..4a6de52974ed623ce557c15282971d56bfdb4dfe 100644
--- a/src/python/src/grpc/framework/face/implementations.py
+++ b/src/python/src/grpc/framework/face/implementations.py
@@ -29,6 +29,8 @@
 
 """Entry points into the Face layer of RPC Framework."""
 
+from grpc.framework.common import cardinality
+from grpc.framework.common import style
 from grpc.framework.base import exceptions as _base_exceptions
 from grpc.framework.base import interfaces as base_interfaces
 from grpc.framework.face import _calls
@@ -56,7 +58,7 @@ class _BaseServicer(base_interfaces.Servicer):
       raise _base_exceptions.NoSuchMethodError()
 
 
-class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync):
+class _UnaryUnaryMultiCallable(interfaces.UnaryUnaryMultiCallable):
 
   def __init__(self, front, name):
     self._front = front
@@ -66,12 +68,33 @@ class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync):
     return _calls.blocking_value_in_value_out(
         self._front, self._name, request, timeout, 'unused trace ID')
 
-  def async(self, request, timeout):
+  def future(self, request, timeout):
     return _calls.future_value_in_value_out(
         self._front, self._name, request, timeout, 'unused trace ID')
 
+  def event(self, request, response_callback, abortion_callback, timeout):
+    return _calls.event_value_in_value_out(
+        self._front, self._name, request, response_callback, abortion_callback,
+        timeout, 'unused trace ID')
+
+
+class _UnaryStreamMultiCallable(interfaces.UnaryStreamMultiCallable):
+
+  def __init__(self, front, name):
+    self._front = front
+    self._name = name
 
-class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync):
+  def __call__(self, request, timeout):
+    return _calls.inline_value_in_stream_out(
+        self._front, self._name, request, timeout, 'unused trace ID')
+
+  def event(self, request, response_consumer, abortion_callback, timeout):
+    return _calls.event_value_in_stream_out(
+        self._front, self._name, request, response_consumer, abortion_callback,
+        timeout, 'unused trace ID')
+
+
+class _StreamUnaryMultiCallable(interfaces.StreamUnaryMultiCallable):
 
   def __init__(self, front, name, pool):
     self._front = front
@@ -82,18 +105,37 @@ class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync):
     return _calls.blocking_stream_in_value_out(
         self._front, self._name, request_iterator, timeout, 'unused trace ID')
 
-  def async(self, request_iterator, timeout):
+  def future(self, request_iterator, timeout):
     return _calls.future_stream_in_value_out(
         self._front, self._name, request_iterator, timeout, 'unused trace ID',
         self._pool)
 
+  def event(self, response_callback, abortion_callback, timeout):
+    return _calls.event_stream_in_value_out(
+        self._front, self._name, response_callback, abortion_callback, timeout,
+        'unused trace ID')
 
-class _Server(interfaces.Server):
-  """An interfaces.Server implementation."""
 
+class _StreamStreamMultiCallable(interfaces.StreamStreamMultiCallable):
 
-class _Stub(interfaces.Stub):
-  """An interfaces.Stub implementation."""
+  def __init__(self, front, name, pool):
+    self._front = front
+    self._name = name
+    self._pool = pool
+
+  def __call__(self, request_iterator, timeout):
+    return _calls.inline_stream_in_stream_out(
+        self._front, self._name, request_iterator, timeout, 'unused trace ID',
+        self._pool)
+
+  def event(self, response_consumer, abortion_callback, timeout):
+    return _calls.event_stream_in_stream_out(
+        self._front, self._name, response_consumer, abortion_callback, timeout,
+        'unused trace ID')
+
+
+class _GenericStub(interfaces.GenericStub):
+  """An interfaces.GenericStub implementation."""
 
   def __init__(self, front, pool):
     self._front = front
@@ -149,136 +191,128 @@ class _Stub(interfaces.Stub):
         self._front, name, response_consumer, abortion_callback, timeout,
         'unused trace ID')
 
-  def unary_unary_sync_async(self, name):
-    return _UnaryUnarySyncAsync(self._front, name)
-
-  def stream_unary_sync_async(self, name):
-    return _StreamUnarySyncAsync(self._front, name, self._pool)
-
-
-def _aggregate_methods(
-    pool,
-    inline_value_in_value_out_methods,
-    inline_value_in_stream_out_methods,
-    inline_stream_in_value_out_methods,
-    inline_stream_in_stream_out_methods,
-    event_value_in_value_out_methods,
-    event_value_in_stream_out_methods,
-    event_stream_in_value_out_methods,
-    event_stream_in_stream_out_methods):
-  """Aggregates methods coded in according to different interfaces."""
-  methods = {}
-
-  def adapt_unpooled_methods(adapted_methods, unadapted_methods, adaptation):
-    if unadapted_methods is not None:
-      for name, unadapted_method in unadapted_methods.iteritems():
-        adapted_methods[name] = adaptation(unadapted_method)
-
-  def adapt_pooled_methods(adapted_methods, unadapted_methods, adaptation):
-    if unadapted_methods is not None:
-      for name, unadapted_method in unadapted_methods.iteritems():
-        adapted_methods[name] = adaptation(unadapted_method, pool)
-
-  adapt_unpooled_methods(
-      methods, inline_value_in_value_out_methods,
-      _service.adapt_inline_value_in_value_out)
-  adapt_unpooled_methods(
-      methods, inline_value_in_stream_out_methods,
-      _service.adapt_inline_value_in_stream_out)
-  adapt_pooled_methods(
-      methods, inline_stream_in_value_out_methods,
-      _service.adapt_inline_stream_in_value_out)
-  adapt_pooled_methods(
-      methods, inline_stream_in_stream_out_methods,
-      _service.adapt_inline_stream_in_stream_out)
-  adapt_unpooled_methods(
-      methods, event_value_in_value_out_methods,
-      _service.adapt_event_value_in_value_out)
-  adapt_unpooled_methods(
-      methods, event_value_in_stream_out_methods,
-      _service.adapt_event_value_in_stream_out)
-  adapt_unpooled_methods(
-      methods, event_stream_in_value_out_methods,
-      _service.adapt_event_stream_in_value_out)
-  adapt_unpooled_methods(
-      methods, event_stream_in_stream_out_methods,
-      _service.adapt_event_stream_in_stream_out)
-
-  return methods
-
-
-def servicer(
-    pool,
-    inline_value_in_value_out_methods=None,
-    inline_value_in_stream_out_methods=None,
-    inline_stream_in_value_out_methods=None,
-    inline_stream_in_stream_out_methods=None,
-    event_value_in_value_out_methods=None,
-    event_value_in_stream_out_methods=None,
-    event_stream_in_value_out_methods=None,
-    event_stream_in_stream_out_methods=None,
-    multi_method=None):
+  def unary_unary_multi_callable(self, name):
+    return _UnaryUnaryMultiCallable(self._front, name)
+
+  def unary_stream_multi_callable(self, name):
+    return _UnaryStreamMultiCallable(self._front, name)
+
+  def stream_unary_multi_callable(self, name):
+    return _StreamUnaryMultiCallable(self._front, name, self._pool)
+
+  def stream_stream_multi_callable(self, name):
+    return _StreamStreamMultiCallable(self._front, name, self._pool)
+
+
+class _DynamicStub(interfaces.DynamicStub):
+  """An interfaces.DynamicStub implementation."""
+
+  def __init__(self, cardinalities, front, pool):
+    self._cardinalities = cardinalities
+    self._front = front
+    self._pool = pool
+
+  def __getattr__(self, attr):
+    method_cardinality = self._cardinalities.get(attr)
+    if method_cardinality is cardinality.Cardinality.UNARY_UNARY:
+      return _UnaryUnaryMultiCallable(self._front, attr)
+    elif method_cardinality is cardinality.Cardinality.UNARY_STREAM:
+      return _UnaryStreamMultiCallable(self._front, attr)
+    elif method_cardinality is cardinality.Cardinality.STREAM_UNARY:
+      return _StreamUnaryMultiCallable(self._front, attr, self._pool)
+    elif method_cardinality is cardinality.Cardinality.STREAM_STREAM:
+      return _StreamStreamMultiCallable(self._front, attr, self._pool)
+    else:
+      raise AttributeError('_DynamicStub object has no attribute "%s"!' % attr)
+
+
+def _adapt_method_implementations(method_implementations, pool):
+  adapted_implementations = {}
+  for name, method_implementation in method_implementations.iteritems():
+    if method_implementation.style is style.Service.INLINE:
+      if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
+        adapted_implementations[name] = _service.adapt_inline_value_in_value_out(
+            method_implementation.unary_unary_inline)
+      elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
+        adapted_implementations[name] = _service.adapt_inline_value_in_stream_out(
+            method_implementation.unary_stream_inline)
+      elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
+        adapted_implementations[name] = _service.adapt_inline_stream_in_value_out(
+            method_implementation.stream_unary_inline, pool)
+      elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
+        adapted_implementations[name] = _service.adapt_inline_stream_in_stream_out(
+            method_implementation.stream_stream_inline, pool)
+    elif method_implementation.style is style.Service.EVENT:
+      if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
+        adapted_implementations[name] = _service.adapt_event_value_in_value_out(
+            method_implementation.unary_unary_event)
+      elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
+        adapted_implementations[name] = _service.adapt_event_value_in_stream_out(
+            method_implementation.unary_stream_event)
+      elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
+        adapted_implementations[name] = _service.adapt_event_stream_in_value_out(
+            method_implementation.stream_unary_event)
+      elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
+        adapted_implementations[name] = _service.adapt_event_stream_in_stream_out(
+            method_implementation.stream_stream_event)
+  return adapted_implementations
+
+
+def servicer(pool, method_implementations, multi_method_implementation):
   """Creates a base_interfaces.Servicer.
 
-  The key sets of the passed dictionaries must be disjoint. It is guaranteed
-  that any passed MultiMethod implementation will only be called to service an
-  RPC if the RPC method name is not present in the key sets of the passed
-  dictionaries.
+  It is guaranteed that any passed interfaces.MultiMethodImplementation will
+  only be called to service an RPC if there is no
+  interfaces.MethodImplementation for the RPC method in the passed
+  method_implementations dictionary.
 
   Args:
     pool: A thread pool.
-    inline_value_in_value_out_methods: A dictionary mapping method names to
-      interfaces.InlineValueInValueOutMethod implementations.
-    inline_value_in_stream_out_methods: A dictionary mapping method names to
-      interfaces.InlineValueInStreamOutMethod implementations.
-    inline_stream_in_value_out_methods: A dictionary mapping method names to
-      interfaces.InlineStreamInValueOutMethod implementations.
-    inline_stream_in_stream_out_methods: A dictionary mapping method names to
-      interfaces.InlineStreamInStreamOutMethod implementations.
-    event_value_in_value_out_methods: A dictionary mapping method names to
-      interfaces.EventValueInValueOutMethod implementations.
-    event_value_in_stream_out_methods: A dictionary mapping method names to
-      interfaces.EventValueInStreamOutMethod implementations.
-    event_stream_in_value_out_methods: A dictionary mapping method names to
-      interfaces.EventStreamInValueOutMethod implementations.
-    event_stream_in_stream_out_methods: A dictionary mapping method names to
-      interfaces.EventStreamInStreamOutMethod implementations.
-    multi_method: An implementation of interfaces.MultiMethod.
+    method_implementations: A dictionary from RPC method name to
+      interfaces.MethodImplementation object to be used to service the named
+      RPC method.
+    multi_method_implementation: An interfaces.MultiMethodImplementation to be
+      used to service any RPCs not serviced by the
+      interfaces.MethodImplementations given in the method_implementations
+      dictionary, or None.
 
   Returns:
     A base_interfaces.Servicer that services RPCs via the given implementations.
   """
-  methods = _aggregate_methods(
-      pool,
-      inline_value_in_value_out_methods,
-      inline_value_in_stream_out_methods,
-      inline_stream_in_value_out_methods,
-      inline_stream_in_stream_out_methods,
-      event_value_in_value_out_methods,
-      event_value_in_stream_out_methods,
-      event_stream_in_value_out_methods,
-      event_stream_in_stream_out_methods)
+  adapted_implementations = _adapt_method_implementations(
+      method_implementations, pool)
+  return _BaseServicer(adapted_implementations, multi_method_implementation)
 
-  return _BaseServicer(methods, multi_method)
 
+def generic_stub(front, pool):
+  """Creates an interfaces.GenericStub.
 
-def server():
-  """Creates an interfaces.Server.
+  Args:
+    front: A base_interfaces.Front.
+    pool: A futures.ThreadPoolExecutor.
 
   Returns:
-    An interfaces.Server.
+    An interfaces.GenericStub that performs RPCs via the given
+      base_interfaces.Front.
   """
-  return _Server()
+  return _GenericStub(front, pool)
 
 
-def stub(front, pool):
-  """Creates an interfaces.Stub.
+def dynamic_stub(cardinalities, front, pool, prefix):
+  """Creates an interfaces.DynamicStub.
 
   Args:
+    cardinalities: A dict from RPC method name to cardinality.Cardinality
+      value identifying the cardinality of every RPC method to be supported by
+      the created interfaces.DynamicStub.
     front: A base_interfaces.Front.
     pool: A futures.ThreadPoolExecutor.
+    prefix: A string to prepend when mapping requested attribute name to RPC
+      method name during attribute access on the created
+      interfaces.DynamicStub.
 
   Returns:
-    An interfaces.Stub that performs RPCs via the given base_interfaces.Front.
+    An interfaces.DynamicStub that performs RPCs via the given
+      base_interfaces.Front.
   """
-  return _Stub(front, pool)
+  return _DynamicStub(cardinalities, front, pool)
diff --git a/src/python/src/grpc/framework/face/interfaces.py b/src/python/src/grpc/framework/face/interfaces.py
index 9e19106e6f342f1e93060a4ca439d9ae56c81a75..b7cc4c1169ab9e87ca29511105f8a227699c870f 100644
--- a/src/python/src/grpc/framework/face/interfaces.py
+++ b/src/python/src/grpc/framework/face/interfaces.py
@@ -32,11 +32,24 @@
 import abc
 import enum
 
-# exceptions, abandonment, and future are referenced from specification in this
-# module.
+# cardinality, style, exceptions, abandonment, future, and stream are
+# referenced from specification in this module.
+from grpc.framework.common import cardinality  # pylint: disable=unused-import
+from grpc.framework.common import style  # pylint: disable=unused-import
 from grpc.framework.face import exceptions  # pylint: disable=unused-import
 from grpc.framework.foundation import abandonment  # pylint: disable=unused-import
 from grpc.framework.foundation import future  # pylint: disable=unused-import
+from grpc.framework.foundation import stream  # pylint: disable=unused-import
+
+
+@enum.unique
+class Abortion(enum.Enum):
+  """Categories of RPC abortion."""
+  CANCELLED = 'cancelled'
+  EXPIRED = 'expired'
+  NETWORK_FAILURE = 'network failure'
+  SERVICED_FAILURE = 'serviced failure'
+  SERVICER_FAILURE = 'servicer failure'
 
 
 class CancellableIterator(object):
@@ -59,69 +72,61 @@ class CancellableIterator(object):
     raise NotImplementedError()
 
 
-class UnaryUnarySyncAsync(object):
-  """Affords invoking a unary-unary RPC synchronously or asynchronously.
-
-  Values implementing this interface are directly callable and present an
-  "async" method. Both calls take a request value and a numeric timeout.
-  Direct invocation of a value of this type invokes its associated RPC and
-  blocks until the RPC's response is available. Calling the "async" method
-  of a value of this type invokes its associated RPC and immediately returns a
-  future.Future bound to the asynchronous execution of the RPC.
-  """
+class RpcContext(object):
+  """Provides RPC-related information and action."""
   __metaclass__ = abc.ABCMeta
 
   @abc.abstractmethod
-  def __call__(self, request, timeout):
-    """Synchronously invokes the underlying RPC.
+  def is_active(self):
+    """Describes whether the RPC is active or has terminated."""
+    raise NotImplementedError()
 
-    Args:
-      request: The request value for the RPC.
-      timeout: A duration of time in seconds to allow for the RPC.
+  @abc.abstractmethod
+  def time_remaining(self):
+    """Describes the length of allowed time remaining for the RPC.
 
     Returns:
-      The response value for the RPC.
-
-    Raises:
-      exceptions.RpcError: Indicating that the RPC was aborted.
+      A nonnegative float indicating the length of allowed time in seconds
+      remaining for the RPC to complete before it is considered to have timed
+      out.
     """
     raise NotImplementedError()
 
   @abc.abstractmethod
-  def async(self, request, timeout):
-    """Asynchronously invokes the underlying RPC.
+  def add_abortion_callback(self, abortion_callback):
+    """Registers a callback to be called if the RPC is aborted.
 
     Args:
-      request: The request value for the RPC.
-      timeout: A duration of time in seconds to allow for the RPC.
-
-    Returns:
-      A future.Future representing the RPC. In the event of RPC completion, the
-        returned Future's result value will be the response value of the RPC.
-        In the event of RPC abortion, the returned Future's exception value
-        will be an exceptions.RpcError.
+      abortion_callback: A callable to be called and passed an Abortion value
+        in the event of RPC abortion.
     """
     raise NotImplementedError()
 
 
-class StreamUnarySyncAsync(object):
-  """Affords invoking a stream-unary RPC synchronously or asynchronously.
+class Call(object):
+  """Invocation-side representation of an RPC.
 
-  Values implementing this interface are directly callable and present an
-  "async" method. Both calls take an iterator of request values and a numeric
-  timeout. Direct invocation of a value of this type invokes its associated RPC
-  and blocks until the RPC's response is available. Calling the "async" method
-  of a value of this type invokes its associated RPC and immediately returns a
-  future.Future bound to the asynchronous execution of the RPC.
+  Attributes:
+    context: An RpcContext affording information about the RPC.
   """
   __metaclass__ = abc.ABCMeta
 
   @abc.abstractmethod
-  def __call__(self, request_iterator, timeout):
+  def cancel(self):
+    """Requests cancellation of the RPC."""
+    raise NotImplementedError()
+
+
+class UnaryUnaryMultiCallable(object):
+  """Affords invoking a unary-unary RPC in any call style."""
+  __metaclass__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def __call__(self, request, timeout):
     """Synchronously invokes the underlying RPC.
 
     Args:
-      request_iterator: An iterator that yields request values for the RPC.
+      request: The request value for the RPC.
       timeout: A duration of time in seconds to allow for the RPC.
 
     Returns:
@@ -133,11 +138,11 @@ class StreamUnarySyncAsync(object):
     raise NotImplementedError()
 
   @abc.abstractmethod
-  def async(self, request, timeout):
+  def future(self, request, timeout):
     """Asynchronously invokes the underlying RPC.
 
     Args:
-      request_iterator: An iterator that yields request values for the RPC.
+      request: The request value for the RPC.
       timeout: A duration of time in seconds to allow for the RPC.
 
     Returns:
@@ -148,248 +153,204 @@ class StreamUnarySyncAsync(object):
     """
     raise NotImplementedError()
 
-
-@enum.unique
-class Abortion(enum.Enum):
-  """Categories of RPC abortion."""
-
-  CANCELLED = 'cancelled'
-  EXPIRED = 'expired'
-  NETWORK_FAILURE = 'network failure'
-  SERVICED_FAILURE = 'serviced failure'
-  SERVICER_FAILURE = 'servicer failure'
-
-
-class RpcContext(object):
-  """Provides RPC-related information and action."""
-  __metaclass__ = abc.ABCMeta
-
-  @abc.abstractmethod
-  def is_active(self):
-    """Describes whether the RPC is active or has terminated."""
-    raise NotImplementedError()
-
-  @abc.abstractmethod
-  def time_remaining(self):
-    """Describes the length of allowed time remaining for the RPC.
-
-    Returns:
-      A nonnegative float indicating the length of allowed time in seconds
-      remaining for the RPC to complete before it is considered to have timed
-      out.
-    """
-    raise NotImplementedError()
-
   @abc.abstractmethod
-  def add_abortion_callback(self, abortion_callback):
-    """Registers a callback to be called if the RPC is aborted.
+  def event(self, request, response_callback, abortion_callback, timeout):
+    """Asynchronously invokes the underlying RPC.
 
     Args:
-      abortion_callback: A callable to be called and passed an Abortion value
+      request: The request value for the RPC.
+      response_callback: A callback to be called to accept the restponse value
+        of the RPC.
+      abortion_callback: A callback to be called and passed an Abortion value
         in the event of RPC abortion.
-    """
-    raise NotImplementedError()
-
-
-class InlineValueInValueOutMethod(object):
-  """A type for inline unary-request-unary-response RPC methods."""
-  __metaclass__ = abc.ABCMeta
-
-  @abc.abstractmethod
-  def service(self, request, context):
-    """Services an RPC that accepts one value and produces one value.
-
-    Args:
-      request: The single request value for the RPC.
-      context: An RpcContext object.
+      timeout: A duration of time in seconds to allow for the RPC.
 
     Returns:
-      The single response value for the RPC.
-
-    Raises:
-      abandonment.Abandoned: If no response is necessary because the RPC has
-        been aborted.
+      A Call object for the RPC.
     """
     raise NotImplementedError()
 
 
-class InlineValueInStreamOutMethod(object):
-  """A type for inline unary-request-stream-response RPC methods."""
+class UnaryStreamMultiCallable(object):
+  """Affords invoking a unary-stream RPC in any call style."""
   __metaclass__ = abc.ABCMeta
 
   @abc.abstractmethod
-  def service(self, request, context):
-    """Services an RPC that accepts one value and produces a stream of values.
+  def __call__(self, request, timeout):
+    """Synchronously invokes the underlying RPC.
 
     Args:
-      request: The single request value for the RPC.
-      context: An RpcContext object.
-
-    Yields:
-      The values that comprise the response stream of the RPC.
+      request: The request value for the RPC.
+      timeout: A duration of time in seconds to allow for the RPC.
 
-    Raises:
-      abandonment.Abandoned: If completing the response stream is not necessary
-        because the RPC has been aborted.
+    Returns:
+      A CancellableIterator that yields the response values of the RPC and
+        affords RPC cancellation. Drawing response values from the returned
+        CancellableIterator may raise exceptions.RpcError indicating abortion
+        of the RPC.
     """
     raise NotImplementedError()
 
-
-class InlineStreamInValueOutMethod(object):
-  """A type for inline stream-request-unary-response RPC methods."""
-  __metaclass__ = abc.ABCMeta
-
   @abc.abstractmethod
-  def service(self, request_iterator, context):
-    """Services an RPC that accepts a stream of values and produces one value.
+  def event(self, request, response_consumer, abortion_callback, timeout):
+    """Asynchronously invokes the underlying RPC.
 
     Args:
-      request_iterator: An iterator that yields the request values of the RPC.
-        Drawing values from this iterator may also raise exceptions.RpcError to
-        indicate abortion of the RPC.
-      context: An RpcContext object.
-
-    Yields:
-      The values that comprise the response stream of the RPC.
+      request: The request value for the RPC.
+      response_consumer: A stream.Consumer to be called to accept the restponse
+        values of the RPC.
+      abortion_callback: A callback to be called and passed an Abortion value
+        in the event of RPC abortion.
+      timeout: A duration of time in seconds to allow for the RPC.
 
-    Raises:
-      abandonment.Abandoned: If no response is necessary because the RPC has
-        been aborted.
-      exceptions.RpcError: Implementations of this method must not deliberately
-        raise exceptions.RpcError but may allow such errors raised by the
-        request_iterator passed to them to propagate through their bodies
-        uncaught.
+    Returns:
+      A Call object for the RPC.
     """
     raise NotImplementedError()
 
 
-class InlineStreamInStreamOutMethod(object):
-  """A type for inline stream-request-stream-response RPC methods."""
+class StreamUnaryMultiCallable(object):
+  """Affords invoking a stream-unary RPC in any call style."""
   __metaclass__ = abc.ABCMeta
 
   @abc.abstractmethod
-  def service(self, request_iterator, context):
-    """Services an RPC that accepts and produces streams of values.
+  def __call__(self, request_iterator, timeout):
+    """Synchronously invokes the underlying RPC.
 
     Args:
-      request_iterator: An iterator that yields the request values of the RPC.
-        Drawing values from this iterator may also raise exceptions.RpcError to
-        indicate abortion of the RPC.
-      context: An RpcContext object.
+      request_iterator: An iterator that yields request values for the RPC.
+      timeout: A duration of time in seconds to allow for the RPC.
 
-    Yields:
-      The values that comprise the response stream of the RPC.
+    Returns:
+      The response value for the RPC.
 
     Raises:
-      abandonment.Abandoned: If completing the response stream is not necessary
-        because the RPC has been aborted.
-      exceptions.RpcError: Implementations of this method must not deliberately
-        raise exceptions.RpcError but may allow such errors raised by the
-        request_iterator passed to them to propagate through their bodies
-        uncaught.
+      exceptions.RpcError: Indicating that the RPC was aborted.
     """
     raise NotImplementedError()
 
-
-class EventValueInValueOutMethod(object):
-  """A type for event-driven unary-request-unary-response RPC methods."""
-  __metaclass__ = abc.ABCMeta
-
   @abc.abstractmethod
-  def service(self, request, response_callback, context):
-    """Services an RPC that accepts one value and produces one value.
+  def future(self, request_iterator, timeout):
+    """Asynchronously invokes the underlying RPC.
 
     Args:
-      request: The single request value for the RPC.
-      response_callback: A callback to be called to accept the response value of
-        the RPC.
-      context: An RpcContext object.
+      request_iterator: An iterator that yields request values for the RPC.
+      timeout: A duration of time in seconds to allow for the RPC.
 
-    Raises:
-      abandonment.Abandoned: May or may not be raised when the RPC has been
-        aborted.
+    Returns:
+      A future.Future representing the RPC. In the event of RPC completion, the
+        returned Future's result value will be the response value of the RPC.
+        In the event of RPC abortion, the returned Future's exception value
+        will be an exceptions.RpcError.
     """
     raise NotImplementedError()
 
-
-class EventValueInStreamOutMethod(object):
-  """A type for event-driven unary-request-stream-response RPC methods."""
-  __metaclass__ = abc.ABCMeta
-
   @abc.abstractmethod
-  def service(self, request, response_consumer, context):
-    """Services an RPC that accepts one value and produces a stream of values.
+  def event(self, response_callback, abortion_callback, timeout):
+    """Asynchronously invokes the underlying RPC.
 
     Args:
-      request: The single request value for the RPC.
-      response_consumer: A stream.Consumer to be called to accept the response
-        values of the RPC.
-      context: An RpcContext object.
+      request: The request value for the RPC.
+      response_callback: A callback to be called to accept the restponse value
+        of the RPC.
+      abortion_callback: A callback to be called and passed an Abortion value
+        in the event of RPC abortion.
+      timeout: A duration of time in seconds to allow for the RPC.
 
-    Raises:
-      abandonment.Abandoned: May or may not be raised when the RPC has been
-        aborted.
+    Returns:
+      A pair of a Call object for the RPC and a stream.Consumer to which the
+        request values of the RPC should be passed.
     """
     raise NotImplementedError()
 
 
-class EventStreamInValueOutMethod(object):
-  """A type for event-driven stream-request-unary-response RPC methods."""
+class StreamStreamMultiCallable(object):
+  """Affords invoking a stream-stream RPC in any call style."""
   __metaclass__ = abc.ABCMeta
 
   @abc.abstractmethod
-  def service(self, response_callback, context):
-    """Services an RPC that accepts a stream of values and produces one value.
+  def __call__(self, request_iterator, timeout):
+    """Synchronously invokes the underlying RPC.
 
     Args:
-      response_callback: A callback to be called to accept the response value of
-        the RPC.
-      context: An RpcContext object.
+      request_iterator: An iterator that yields request values for the RPC.
+      timeout: A duration of time in seconds to allow for the RPC.
 
     Returns:
-      A stream.Consumer with which to accept the request values of the RPC. The
-        consumer returned from this method may or may not be invoked to
-        completion: in the case of RPC abortion, RPC Framework will simply stop
-        passing values to this object. Implementations must not assume that this
-        object will be called to completion of the request stream or even called
-        at all.
-
-    Raises:
-      abandonment.Abandoned: May or may not be raised when the RPC has been
-        aborted.
+      A CancellableIterator that yields the response values of the RPC and
+        affords RPC cancellation. Drawing response values from the returned
+        CancellableIterator may raise exceptions.RpcError indicating abortion
+        of the RPC.
     """
     raise NotImplementedError()
 
-
-class EventStreamInStreamOutMethod(object):
-  """A type for event-driven stream-request-stream-response RPC methods."""
-  __metaclass__ = abc.ABCMeta
-
   @abc.abstractmethod
-  def service(self, response_consumer, context):
-    """Services an RPC that accepts and produces streams of values.
+  def event(self, response_consumer, abortion_callback, timeout):
+    """Asynchronously invokes the underlying RPC.
 
-    Args:
-      response_consumer: A stream.Consumer to be called to accept the response
+l    Args:
+      response_consumer: A stream.Consumer to be called to accept the restponse
         values of the RPC.
-      context: An RpcContext object.
+      abortion_callback: A callback to be called and passed an Abortion value
+        in the event of RPC abortion.
+      timeout: A duration of time in seconds to allow for the RPC.
 
     Returns:
-      A stream.Consumer with which to accept the request values of the RPC. The
-        consumer returned from this method may or may not be invoked to
-        completion: in the case of RPC abortion, RPC Framework will simply stop
-        passing values to this object. Implementations must not assume that this
-        object will be called to completion of the request stream or even called
-        at all.
-
-    Raises:
-      abandonment.Abandoned: May or may not be raised when the RPC has been
-        aborted.
+      A pair of a Call object for the RPC and a stream.Consumer to which the
+        request values of the RPC should be passed.
     """
     raise NotImplementedError()
 
 
-class MultiMethod(object):
+class MethodImplementation(object):
+  """A sum type that describes an RPC method implementation.
+
+  Attributes:
+    cardinality: A cardinality.Cardinality value.
+    style: A style.Service value.
+    unary_unary_inline: The implementation of the RPC method as a callable
+      value that takes a request value and an RpcContext object and returns a
+      response value. Only non-None if cardinality is
+      cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE.
+    unary_stream_inline: The implementation of the RPC method as a callable
+      value that takes a request value and an RpcContext object and returns an
+      iterator of response values. Only non-None if cardinality is
+      cardinality.Cardinality.UNARY_STREAM and style is style.Service.INLINE.
+    stream_unary_inline: The implementation of the RPC method as a callable
+      value that takes an iterator of request values and an RpcContext object
+      and returns a response value. Only non-None if cardinality is
+      cardinality.Cardinality.STREAM_UNARY and style is style.Service.INLINE.
+    stream_stream_inline: The implementation of the RPC method as a callable
+      value that takes an iterator of request values and an RpcContext object
+      and returns an iterator of response values. Only non-None if cardinality
+      is cardinality.Cardinality.STREAM_STREAM and style is
+      style.Service.INLINE.
+    unary_unary_event: The implementation of the RPC method as a callable value
+      that takes a request value, a response callback to which to pass the
+      response value of the RPC, and an RpcContext. Only non-None if
+      cardinality is cardinality.Cardinality.UNARY_UNARY and style is
+      style.Service.EVENT.
+    unary_stream_event: The implementation of the RPC method as a callable
+      value that takes a request value, a stream.Consumer to which to pass the
+      the response values of the RPC, and an RpcContext. Only non-None if
+      cardinality is cardinality.Cardinality.UNARY_STREAM and style is
+      style.Service.EVENT.
+    stream_unary_event: The implementation of the RPC method as a callable
+      value that takes a response callback to which to pass the response value
+      of the RPC and an RpcContext and returns a stream.Consumer to which the
+      request values of the RPC should be passed. Only non-None if cardinality
+      is cardinality.Cardinality.STREAM_UNARY and style is style.Service.EVENT.
+    stream_stream_event: The implementation of the RPC method as a callable
+      value that takes a stream.Consumer to which to pass the response values
+      of the RPC and an RpcContext and returns a stream.Consumer to which the
+      request values of the RPC should be passed. Only non-None if cardinality
+      is cardinality.Cardinality.STREAM_STREAM and style is
+      style.Service.EVENT.
+  """
+  __metaclass__ = abc.ABCMeta
+
+
+class MultiMethodImplementation(object):
   """A general type able to service many RPC methods."""
   __metaclass__ = abc.ABCMeta
 
@@ -420,26 +381,7 @@ class MultiMethod(object):
     raise NotImplementedError()
 
 
-class Server(object):
-  """Specification of a running server that services RPCs."""
-  __metaclass__ = abc.ABCMeta
-
-
-class Call(object):
-  """Invocation-side representation of an RPC.
-
-  Attributes:
-    context: An RpcContext affording information about the RPC.
-  """
-  __metaclass__ = abc.ABCMeta
-
-  @abc.abstractmethod
-  def cancel(self):
-    """Requests cancellation of the RPC."""
-    raise NotImplementedError()
-
-
-class Stub(object):
+class GenericStub(object):
   """Affords RPC methods to callers."""
   __metaclass__ = abc.ABCMeta
 
@@ -632,25 +574,67 @@ class Stub(object):
     raise NotImplementedError()
 
   @abc.abstractmethod
-  def unary_unary_sync_async(self, name):
-    """Creates a UnaryUnarySyncAsync value for a unary-unary RPC method.
+  def unary_unary_multi_callable(self, name):
+    """Creates a UnaryUnaryMultiCallable for a unary-unary RPC method.
+
+    Args:
+      name: The RPC method name.
+
+    Returns:
+      A UnaryUnaryMultiCallable value for the named unary-unary RPC method.
+    """
+    raise NotImplementedError()
+
+  @abc.abstractmethod
+  def unary_stream_multi_callable(self, name):
+    """Creates a UnaryStreamMultiCallable for a unary-stream RPC method.
 
     Args:
       name: The RPC method name.
 
     Returns:
-      A UnaryUnarySyncAsync value for the named unary-unary RPC method.
+      A UnaryStreamMultiCallable value for the name unary-stream RPC method.
     """
     raise NotImplementedError()
 
   @abc.abstractmethod
-  def stream_unary_sync_async(self, name):
-    """Creates a StreamUnarySyncAsync value for a stream-unary RPC method.
+  def stream_unary_multi_callable(self, name):
+    """Creates a StreamUnaryMultiCallable for a stream-unary RPC method.
 
     Args:
       name: The RPC method name.
 
     Returns:
-      A StreamUnarySyncAsync value for the named stream-unary RPC method.
+      A StreamUnaryMultiCallable value for the named stream-unary RPC method.
     """
     raise NotImplementedError()
+
+  @abc.abstractmethod
+  def stream_stream_multi_callable(self, name):
+    """Creates a StreamStreamMultiCallable for a stream-stream RPC method.
+
+    Args:
+      name: The RPC method name.
+
+    Returns:
+      A StreamStreamMultiCallable value for the named stream-stream RPC method.
+    """
+    raise NotImplementedError()
+
+
+class DynamicStub(object):
+  """A stub with RPC-method-bound multi-callable attributes.
+
+  Instances of this type responsd to attribute access as follows: if the
+  requested attribute is the name of a unary-unary RPC method, the value of the
+  attribute will be a UnaryUnaryMultiCallable with which to invoke the RPC
+  method; if the requested attribute is the name of a unary-stream RPC method,
+  the value of the attribute will be a UnaryStreamMultiCallable with which to
+  invoke the RPC method; if the requested attribute is the name of a
+  stream-unary RPC method, the value of the attribute will be a
+  StreamUnaryMultiCallable with which to invoke the RPC method; and if the
+  requested attribute is the name of a stream-stream RPC method, the value of
+  the attribute will be a StreamStreamMultiCallable with which to invoke the
+  RPC method.
+  """
+  __metaclass__ = abc.ABCMeta
diff --git a/src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py
index 233486f2110368ca95f56f00dabb4e05965f0729..e57ee001045a487b0359e2401c2ffe83724ade2f 100644
--- a/src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py
+++ b/src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py
@@ -61,13 +61,9 @@ class BlockingInvocationInlineServiceTestCase(
     self.digest = digest.digest(
         stock_service.STOCK_TEST_SERVICE, self.control, None)
 
-    self.server, self.stub, self.memo = self.set_up_implementation(
+    self.stub, self.memo = self.set_up_implementation(
         self.digest.name, self.digest.methods,
-        self.digest.inline_unary_unary_methods,
-        self.digest.inline_unary_stream_methods,
-        self.digest.inline_stream_unary_methods,
-        self.digest.inline_stream_stream_methods,
-        {}, {}, {}, {}, None)
+        self.digest.inline_method_implementations, None)
 
   def tearDown(self):
     """See unittest.TestCase.tearDown for full specification.
@@ -147,8 +143,8 @@ class BlockingInvocationInlineServiceTestCase(
 
         with self.control.pause(), self.assertRaises(
             exceptions.ExpirationError):
-          sync_async = self.stub.unary_unary_sync_async(name)
-          sync_async(request, _TIMEOUT)
+          multi_callable = self.stub.unary_unary_multi_callable(name)
+          multi_callable(request, _TIMEOUT)
 
   def testExpiredUnaryRequestStreamResponse(self):
     for name, test_messages_sequence in (
@@ -170,8 +166,8 @@ class BlockingInvocationInlineServiceTestCase(
 
         with self.control.pause(), self.assertRaises(
             exceptions.ExpirationError):
-          sync_async = self.stub.stream_unary_sync_async(name)
-          sync_async(iter(requests), _TIMEOUT)
+          multi_callable = self.stub.stream_unary_multi_callable(name)
+          multi_callable(iter(requests), _TIMEOUT)
 
   def testExpiredStreamRequestStreamResponse(self):
     for name, test_messages_sequence in (
diff --git a/src/python/src/grpc/framework/face/testing/digest.py b/src/python/src/grpc/framework/face/testing/digest.py
index b8fb573301370e0e5556065bde548ed071169662..db8fcbb0184efaca4d239a1436b788d49712e9d2 100644
--- a/src/python/src/grpc/framework/face/testing/digest.py
+++ b/src/python/src/grpc/framework/face/testing/digest.py
@@ -34,6 +34,8 @@ import threading
 
 # testing_control, interfaces, and testing_service are referenced from
 # specification in this module.
+from grpc.framework.common import cardinality
+from grpc.framework.common import style
 from grpc.framework.face import exceptions
 from grpc.framework.face import interfaces as face_interfaces
 from grpc.framework.face.testing import control as testing_control  # pylint: disable=unused-import
@@ -50,15 +52,9 @@ class TestServiceDigest(
         'TestServiceDigest',
         ['name',
          'methods',
-         'inline_unary_unary_methods',
-         'inline_unary_stream_methods',
-         'inline_stream_unary_methods',
-         'inline_stream_stream_methods',
-         'event_unary_unary_methods',
-         'event_unary_stream_methods',
-         'event_stream_unary_methods',
-         'event_stream_stream_methods',
-         'multi_method',
+         'inline_method_implementations',
+         'event_method_implementations',
+         'multi_method_implementation',
          'unary_unary_messages_sequences',
          'unary_stream_messages_sequences',
          'stream_unary_messages_sequences',
@@ -69,32 +65,14 @@ class TestServiceDigest(
     name: The RPC service name to be used in the test.
     methods: A sequence of interfaces.Method objects describing the RPC
       methods that will be called during the test.
-    inline_unary_unary_methods: A dict from method name to
-      face_interfaces.InlineValueInValueOutMethod object to be used in tests of
+    inline_method_implementations: A dict from RPC method name to
+      face_interfaces.MethodImplementation object to be used in tests of
       in-line calls to behaviors under test.
-    inline_unary_stream_methods: A dict from method name to
-      face_interfaces.InlineValueInStreamOutMethod object to be used in tests of
-      in-line calls to behaviors under test.
-    inline_stream_unary_methods: A dict from method name to
-      face_interfaces.InlineStreamInValueOutMethod object to be used in tests of
-      in-line calls to behaviors under test.
-    inline_stream_stream_methods: A dict from method name to
-      face_interfaces.InlineStreamInStreamOutMethod object to be used in tests
-      of in-line calls to behaviors under test.
-    event_unary_unary_methods: A dict from method name to
-      face_interfaces.EventValueInValueOutMethod object to be used in tests of
-      event-driven calls to behaviors under test.
-    event_unary_stream_methods: A dict from method name to
-      face_interfaces.EventValueInStreamOutMethod object to be used in tests of
-      event-driven calls to behaviors under test.
-    event_stream_unary_methods: A dict from method name to
-      face_interfaces.EventStreamInValueOutMethod object to be used in tests of
+    event_method_implementations: A dict from RPC method name to
+      face_interfaces.MethodImplementation object to be used in tests of
       event-driven calls to behaviors under test.
-    event_stream_stream_methods: A dict from method name to
-      face_interfaces.EventStreamInStreamOutMethod object to be used in tests of
-      event-driven calls to behaviors under test.
-    multi_method: A face_interfaces.MultiMethod to be used in tests of generic
-      calls to behaviors under test.
+    multi_method_implementation: A face_interfaces.MultiMethodImplementation to
+      be used in tests of generic calls to behaviors under test.
     unary_unary_messages_sequences: A dict from method name to sequence of
       service.UnaryUnaryTestMessages objects to be used to test the method
       with the given name.
@@ -130,27 +108,33 @@ class _BufferingConsumer(stream.Consumer):
     self.terminated = True
 
 
-class _InlineUnaryUnaryMethod(face_interfaces.InlineValueInValueOutMethod):
+class _InlineUnaryUnaryMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, unary_unary_test_method, control):
     self._test_method = unary_unary_test_method
     self._control = control
 
-  def service(self, request, context):
+    self.cardinality = cardinality.Cardinality.UNARY_UNARY
+    self.style = style.Service.INLINE
+
+  def unary_unary_inline(self, request, context):
     response_list = []
     self._test_method.service(
         request, response_list.append, context, self._control)
     return response_list.pop(0)
 
 
-class _EventUnaryUnaryMethod(face_interfaces.EventValueInValueOutMethod):
+class _EventUnaryUnaryMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, unary_unary_test_method, control, pool):
     self._test_method = unary_unary_test_method
     self._control = control
     self._pool = pool
 
-  def service(self, request, response_callback, context):
+    self.cardinality = cardinality.Cardinality.UNARY_UNARY
+    self.style = style.Service.EVENT
+
+  def unary_unary_event(self, request, response_callback, context):
     if self._pool is None:
       self._test_method.service(
           request, response_callback, context, self._control)
@@ -160,13 +144,16 @@ class _EventUnaryUnaryMethod(face_interfaces.EventValueInValueOutMethod):
           self._control)
 
 
-class _InlineUnaryStreamMethod(face_interfaces.InlineValueInStreamOutMethod):
+class _InlineUnaryStreamMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, unary_stream_test_method, control):
     self._test_method = unary_stream_test_method
     self._control = control
 
-  def service(self, request, context):
+    self.cardinality = cardinality.Cardinality.UNARY_STREAM
+    self.style = style.Service.INLINE
+
+  def unary_stream_inline(self, request, context):
     response_consumer = _BufferingConsumer()
     self._test_method.service(
         request, response_consumer, context, self._control)
@@ -174,14 +161,17 @@ class _InlineUnaryStreamMethod(face_interfaces.InlineValueInStreamOutMethod):
       yield response
 
 
-class _EventUnaryStreamMethod(face_interfaces.EventValueInStreamOutMethod):
+class _EventUnaryStreamMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, unary_stream_test_method, control, pool):
     self._test_method = unary_stream_test_method
     self._control = control
     self._pool = pool
 
-  def service(self, request, response_consumer, context):
+    self.cardinality = cardinality.Cardinality.UNARY_STREAM
+    self.style = style.Service.EVENT
+
+  def unary_stream_event(self, request, response_consumer, context):
     if self._pool is None:
       self._test_method.service(
           request, response_consumer, context, self._control)
@@ -191,13 +181,16 @@ class _EventUnaryStreamMethod(face_interfaces.EventValueInStreamOutMethod):
           self._control)
 
 
-class _InlineStreamUnaryMethod(face_interfaces.InlineStreamInValueOutMethod):
+class _InlineStreamUnaryMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, stream_unary_test_method, control):
     self._test_method = stream_unary_test_method
     self._control = control
 
-  def service(self, request_iterator, context):
+    self.cardinality = cardinality.Cardinality.STREAM_UNARY
+    self.style = style.Service.INLINE
+
+  def stream_unary_inline(self, request_iterator, context):
     response_list = []
     request_consumer = self._test_method.service(
         response_list.append, context, self._control)
@@ -207,14 +200,17 @@ class _InlineStreamUnaryMethod(face_interfaces.InlineStreamInValueOutMethod):
     return response_list.pop(0)
 
 
-class _EventStreamUnaryMethod(face_interfaces.EventStreamInValueOutMethod):
+class _EventStreamUnaryMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, stream_unary_test_method, control, pool):
     self._test_method = stream_unary_test_method
     self._control = control
     self._pool = pool
 
-  def service(self, response_callback, context):
+    self.cardinality = cardinality.Cardinality.STREAM_UNARY
+    self.style = style.Service.EVENT
+
+  def stream_unary_event(self, response_callback, context):
     request_consumer = self._test_method.service(
         response_callback, context, self._control)
     if self._pool is None:
@@ -223,13 +219,16 @@ class _EventStreamUnaryMethod(face_interfaces.EventStreamInValueOutMethod):
       return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool)
 
 
-class _InlineStreamStreamMethod(face_interfaces.InlineStreamInStreamOutMethod):
+class _InlineStreamStreamMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, stream_stream_test_method, control):
     self._test_method = stream_stream_test_method
     self._control = control
 
-  def service(self, request_iterator, context):
+    self.cardinality = cardinality.Cardinality.STREAM_STREAM
+    self.style = style.Service.INLINE
+
+  def stream_stream_inline(self, request_iterator, context):
     response_consumer = _BufferingConsumer()
     request_consumer = self._test_method.service(
         response_consumer, context, self._control)
@@ -241,14 +240,17 @@ class _InlineStreamStreamMethod(face_interfaces.InlineStreamInStreamOutMethod):
     response_consumer.terminate()
 
 
-class _EventStreamStreamMethod(face_interfaces.EventStreamInStreamOutMethod):
+class _EventStreamStreamMethod(face_interfaces.MethodImplementation):
 
   def __init__(self, stream_stream_test_method, control, pool):
     self._test_method = stream_stream_test_method
     self._control = control
     self._pool = pool
 
-  def service(self, response_consumer, context):
+    self.cardinality = cardinality.Cardinality.STREAM_STREAM
+    self.style = style.Service.EVENT
+
+  def stream_stream_event(self, response_consumer, context):
     request_consumer = self._test_method.service(
         response_consumer, context, self._control)
     if self._pool is None:
@@ -332,7 +334,7 @@ class _StreamUnaryAdaptation(object):
         response_consumer.consume_and_terminate, context, control)
 
 
-class _MultiMethod(face_interfaces.MultiMethod):
+class _MultiMethodImplementation(face_interfaces.MultiMethodImplementation):
 
   def __init__(self, methods, control, pool):
     self._methods = methods
@@ -427,19 +429,21 @@ def digest(service, control, pool):
   adaptations.update(unary_stream.adaptations)
   adaptations.update(stream_unary.adaptations)
   adaptations.update(stream_stream.adaptations)
+  inlines = dict(unary_unary.inlines)
+  inlines.update(unary_stream.inlines)
+  inlines.update(stream_unary.inlines)
+  inlines.update(stream_stream.inlines)
+  events = dict(unary_unary.events)
+  events.update(unary_stream.events)
+  events.update(stream_unary.events)
+  events.update(stream_stream.events)
 
   return TestServiceDigest(
       service.name(),
       methods,
-      unary_unary.inlines,
-      unary_stream.inlines,
-      stream_unary.inlines,
-      stream_stream.inlines,
-      unary_unary.events,
-      unary_stream.events,
-      stream_unary.events,
-      stream_stream.events,
-      _MultiMethod(adaptations, control, pool),
+      inlines,
+      events,
+      _MultiMethodImplementation(adaptations, control, pool),
       unary_unary.messages,
       unary_stream.messages,
       stream_unary.messages,
diff --git a/src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py
index 21e669b9080fb138101718c0e7ffebdda96c4566..0f0b0e3d5232a7014fc8be7e6861a516750d7485 100644
--- a/src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py
+++ b/src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py
@@ -60,14 +60,9 @@ class EventInvocationSynchronousEventServiceTestCase(
     self.digest = digest.digest(
         stock_service.STOCK_TEST_SERVICE, self.control, None)
 
-    self.server, self.stub, self.memo = self.set_up_implementation(
+    self.stub, self.memo = self.set_up_implementation(
         self.digest.name, self.digest.methods,
-        {}, {}, {}, {},
-        self.digest.event_unary_unary_methods,
-        self.digest.event_unary_stream_methods,
-        self.digest.event_stream_unary_methods,
-        self.digest.event_stream_stream_methods,
-        None)
+        self.digest.event_method_implementations, None)
 
   def tearDown(self):
     """See unittest.TestCase.tearDown for full specification.
diff --git a/src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py
index c87846f2ef67280aab8c1cc243d75a7c260c8076..0d51b64f1b3b51f4ac0275b83967a68be9e57edf 100644
--- a/src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py
+++ b/src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py
@@ -91,14 +91,9 @@ class FutureInvocationAsynchronousEventServiceTestCase(
     self.digest = digest.digest(
         stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool)
 
-    self.server, self.stub, self.memo = self.set_up_implementation(
+    self.stub, self.memo = self.set_up_implementation(
         self.digest.name, self.digest.methods,
-        {}, {}, {}, {},
-        self.digest.event_unary_unary_methods,
-        self.digest.event_unary_stream_methods,
-        self.digest.event_stream_unary_methods,
-        self.digest.event_stream_stream_methods,
-        None)
+        self.digest.event_method_implementations, None)
 
   def tearDown(self):
     """See unittest.TestCase.tearDown for full specification.
@@ -190,8 +185,8 @@ class FutureInvocationAsynchronousEventServiceTestCase(
         request = test_messages.request()
 
         with self.control.pause():
-          sync_async = self.stub.unary_unary_sync_async(name)
-          response_future = sync_async.async(request, _TIMEOUT)
+          multi_callable = self.stub.unary_unary_multi_callable(name)
+          response_future = multi_callable.future(request, _TIMEOUT)
           self.assertIsInstance(
               response_future.exception(), exceptions.ExpirationError)
           with self.assertRaises(exceptions.ExpirationError):
@@ -216,8 +211,8 @@ class FutureInvocationAsynchronousEventServiceTestCase(
         requests = test_messages.requests()
 
         with self.control.pause():
-          sync_async = self.stub.stream_unary_sync_async(name)
-          response_future = sync_async.async(iter(requests), _TIMEOUT)
+          multi_callable = self.stub.stream_unary_multi_callable(name)
+          response_future = multi_callable.future(iter(requests), _TIMEOUT)
           self.assertIsInstance(
               response_future.exception(), exceptions.ExpirationError)
           with self.assertRaises(exceptions.ExpirationError):
diff --git a/src/python/src/grpc/framework/face/testing/service.py b/src/python/src/grpc/framework/face/testing/service.py
index a58e2ee42e41ed30be3f88e972942e85fa74f7d1..bf54d41d6643f66222c884419e50b2ca9066d440 100644
--- a/src/python/src/grpc/framework/face/testing/service.py
+++ b/src/python/src/grpc/framework/face/testing/service.py
@@ -36,8 +36,8 @@ from grpc.framework.face import interfaces as face_interfaces  # pylint: disable
 from grpc.framework.face.testing import interfaces
 
 
-class UnaryUnaryTestMethod(interfaces.Method):
-  """Like face_interfaces.EventValueInValueOutMethod but with a control."""
+class UnaryUnaryTestMethodImplementation(interfaces.Method):
+  """A controllable implementation of a unary-unary RPC method."""
 
   __metaclass__ = abc.ABCMeta
 
@@ -93,8 +93,8 @@ class UnaryUnaryTestMessages(object):
     raise NotImplementedError()
 
 
-class UnaryStreamTestMethod(interfaces.Method):
-  """Like face_interfaces.EventValueInStreamOutMethod but with a control."""
+class UnaryStreamTestMethodImplementation(interfaces.Method):
+  """A controllable implementation of a unary-stream RPC method."""
 
   __metaclass__ = abc.ABCMeta
 
@@ -106,7 +106,7 @@ class UnaryStreamTestMethod(interfaces.Method):
       request: The single request message for the RPC.
       response_consumer: A stream.Consumer to be called to accept the response
         messages of the RPC.
-      context: An RpcContext object.
+      context: A face_interfaces.RpcContext object.
       control: A test_control.Control to control execution of this method.
 
     Raises:
@@ -150,8 +150,8 @@ class UnaryStreamTestMessages(object):
     raise NotImplementedError()
 
 
-class StreamUnaryTestMethod(interfaces.Method):
-  """Like face_interfaces.EventStreamInValueOutMethod but with a control."""
+class StreamUnaryTestMethodImplementation(interfaces.Method):
+  """A controllable implementation of a stream-unary RPC method."""
 
   __metaclass__ = abc.ABCMeta
 
@@ -162,7 +162,7 @@ class StreamUnaryTestMethod(interfaces.Method):
     Args:
       response_callback: A callback to be called to accept the response message
         of the RPC.
-      context: An RpcContext object.
+      context: A face_interfaces.RpcContext object.
       control: A test_control.Control to control execution of this method.
 
     Returns:
@@ -214,8 +214,8 @@ class StreamUnaryTestMessages(object):
     raise NotImplementedError()
 
 
-class StreamStreamTestMethod(interfaces.Method):
-  """Like face_interfaces.EventStreamInStreamOutMethod but with a control."""
+class StreamStreamTestMethodImplementation(interfaces.Method):
+  """A controllable implementation of a stream-stream RPC method."""
 
   __metaclass__ = abc.ABCMeta
 
@@ -226,7 +226,7 @@ class StreamStreamTestMethod(interfaces.Method):
     Args:
       response_consumer: A stream.Consumer to be called to accept the response
         messages of the RPC.
-      context: An RpcContext object.
+      context: A face_interfaces.RpcContext object.
       control: A test_control.Control to control execution of this method.
 
     Returns:
@@ -298,8 +298,8 @@ class TestService(object):
 
     Returns:
       A dict from method name to pair. The first element of the pair
-        is a UnaryUnaryTestMethod object and the second element is a sequence
-        of UnaryUnaryTestMethodMessages objects.
+        is a UnaryUnaryTestMethodImplementation object and the second element
+        is a sequence of UnaryUnaryTestMethodMessages objects.
     """
     raise NotImplementedError()
 
@@ -309,8 +309,8 @@ class TestService(object):
 
     Returns:
       A dict from method name to pair. The first element of the pair is a
-        UnaryStreamTestMethod object and the second element is a sequence of
-        UnaryStreamTestMethodMessages objects.
+        UnaryStreamTestMethodImplementation object and the second element is a
+        sequence of UnaryStreamTestMethodMessages objects.
     """
     raise NotImplementedError()
 
@@ -320,8 +320,8 @@ class TestService(object):
 
     Returns:
       A dict from method name to pair. The first element of the pair is a
-        StreamUnaryTestMethod object and the second element is a sequence of
-        StreamUnaryTestMethodMessages objects.
+        StreamUnaryTestMethodImplementation object and the second element is a
+        sequence of StreamUnaryTestMethodMessages objects.
     """
     raise NotImplementedError()
 
@@ -331,7 +331,7 @@ class TestService(object):
 
     Returns:
       A dict from method name to pair. The first element of the pair is a
-        StreamStreamTestMethod object and the second element is a sequence of
-        StreamStreamTestMethodMessages objects.
+        StreamStreamTestMethodImplementation object and the second element is a
+        sequence of StreamStreamTestMethodMessages objects.
     """
     raise NotImplementedError()
diff --git a/src/python/src/grpc/framework/face/testing/stock_service.py b/src/python/src/grpc/framework/face/testing/stock_service.py
index 83c9418b074a3eadeaf3980487a2dc380a77b7f3..61aaf444a00fd42232053276f4da819b00ef297e 100644
--- a/src/python/src/grpc/framework/face/testing/stock_service.py
+++ b/src/python/src/grpc/framework/face/testing/stock_service.py
@@ -139,7 +139,7 @@ def _get_highest_trade_price(stock_reply_callback, control, active):
   return StockRequestConsumer()
 
 
-class GetLastTradePrice(service.UnaryUnaryTestMethod):
+class GetLastTradePrice(service.UnaryUnaryTestMethodImplementation):
   """GetLastTradePrice for use in tests."""
 
   def name(self):
@@ -186,7 +186,7 @@ class GetLastTradePriceMessages(service.UnaryUnaryTestMessages):
     test_case.assertEqual(_price(request.symbol), response.price)
 
 
-class GetLastTradePriceMultiple(service.StreamStreamTestMethod):
+class GetLastTradePriceMultiple(service.StreamStreamTestMethodImplementation):
   """GetLastTradePriceMultiple for use in tests."""
 
   def name(self):
@@ -238,7 +238,7 @@ class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages):
       test_case.assertEqual(_price(stock_request.symbol), stock_reply.price)
 
 
-class WatchFutureTrades(service.UnaryStreamTestMethod):
+class WatchFutureTrades(service.UnaryStreamTestMethodImplementation):
   """WatchFutureTrades for use in tests."""
 
   def name(self):
@@ -288,7 +288,7 @@ class WatchFutureTradesMessages(service.UnaryStreamTestMessages):
       test_case.assertEqual(base_price + index, response.price)
 
 
-class GetHighestTradePrice(service.StreamUnaryTestMethod):
+class GetHighestTradePrice(service.StreamUnaryTestMethodImplementation):
   """GetHighestTradePrice for use in tests."""
 
   def name(self):
diff --git a/src/python/src/grpc/framework/face/testing/test_case.py b/src/python/src/grpc/framework/face/testing/test_case.py
index 218a2a8549bebd91b90825b16faebff79f4d98bb..e60e3d1d405c2960f1423bf88132dc71376dad49 100644
--- a/src/python/src/grpc/framework/face/testing/test_case.py
+++ b/src/python/src/grpc/framework/face/testing/test_case.py
@@ -46,55 +46,24 @@ class FaceTestCase(object):
 
   @abc.abstractmethod
   def set_up_implementation(
-      self,
-      name,
-      methods,
-      inline_value_in_value_out_methods,
-      inline_value_in_stream_out_methods,
-      inline_stream_in_value_out_methods,
-      inline_stream_in_stream_out_methods,
-      event_value_in_value_out_methods,
-      event_value_in_stream_out_methods,
-      event_stream_in_value_out_methods,
-      event_stream_in_stream_out_methods,
-      multi_method):
+      self, name, methods, method_implementations,
+      multi_method_implementation):
     """Instantiates the Face Layer implementation under test.
 
     Args:
       name: The service name to be used in the test.
       methods: A sequence of interfaces.Method objects describing the RPC
         methods that will be called during the test.
-      inline_value_in_value_out_methods: A dictionary from string method names
-        to face_interfaces.InlineValueInValueOutMethod implementations of those
-        methods.
-      inline_value_in_stream_out_methods: A dictionary from string method names
-        to face_interfaces.InlineValueInStreamOutMethod implementations of those
-        methods.
-      inline_stream_in_value_out_methods: A dictionary from string method names
-        to face_interfaces.InlineStreamInValueOutMethod implementations of those
-        methods.
-      inline_stream_in_stream_out_methods: A dictionary from string method names
-        to face_interfaces.InlineStreamInStreamOutMethod implementations of
-        those methods.
-      event_value_in_value_out_methods: A dictionary from string method names
-        to face_interfaces.EventValueInValueOutMethod implementations of those
-        methods.
-      event_value_in_stream_out_methods: A dictionary from string method names
-        to face_interfaces.EventValueInStreamOutMethod implementations of those
-        methods.
-      event_stream_in_value_out_methods: A dictionary from string method names
-        to face_interfaces.EventStreamInValueOutMethod implementations of those
-        methods.
-      event_stream_in_stream_out_methods: A dictionary from string method names
-        to face_interfaces.EventStreamInStreamOutMethod implementations of those
-        methods.
-      multi_method: An face_interfaces.MultiMethod, or None.
+      method_implementations: A dictionary from string RPC method name to
+        face_interfaces.MethodImplementation object specifying
+        implementation of an RPC method.
+      multi_method_implementation: An face_interfaces.MultiMethodImplementation
+        or None.
 
     Returns:
-      A sequence of length three the first element of which is a
-        face_interfaces.Server, the second element of which is a
-        face_interfaces.Stub, (both of which are backed by the given method
-        implementations), and the third element of which is an arbitrary memo
+      A sequence of length two the first element of which is a
+        face_interfaces.GenericStub (backed by the given method
+        implementations), and the second element of which is an arbitrary memo
         object to be kept and passed to tearDownImplementation at the conclusion
         of the test.
     """
@@ -105,7 +74,7 @@ class FaceTestCase(object):
     """Destroys the Face layer implementation under test.
 
     Args:
-      memo: The object from the third position of the return value of
+      memo: The object from the second position of the return value of
         set_up_implementation.
     """
     raise NotImplementedError()
diff --git a/src/python/src/grpc/framework/face/utilities.py b/src/python/src/grpc/framework/face/utilities.py
index 5e34be37da7a5e9c537db9f280b609bd6d649ece..a63fe8c60dfbb9d365625168a1b5a4075881edf2 100644
--- a/src/python/src/grpc/framework/face/utilities.py
+++ b/src/python/src/grpc/framework/face/utilities.py
@@ -27,101 +27,44 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-"""Utilities for the face layer of RPC Framework."""
+"""Utilities for RPC framework's face layer."""
 
-# stream is referenced from specification in this module.
-from grpc.framework.face import interfaces
-from grpc.framework.foundation import stream  # pylint: disable=unused-import
-
-
-class _InlineUnaryUnaryMethod(interfaces.InlineValueInValueOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
-
-  def service(self, request, context):
-    return self._behavior(request, context)
-
-
-class _InlineUnaryStreamMethod(interfaces.InlineValueInStreamOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
-
-  def service(self, request, context):
-    return self._behavior(request, context)
-
-
-class _InlineStreamUnaryMethod(interfaces.InlineStreamInValueOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
-
-  def service(self, request_iterator, context):
-    return self._behavior(request_iterator, context)
-
-
-class _InlineStreamStreamMethod(interfaces.InlineStreamInStreamOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
-
-  def service(self, request_iterator, context):
-    return self._behavior(request_iterator, context)
+import collections
 
+from grpc.framework.common import cardinality
+from grpc.framework.common import style
+from grpc.framework.face import interfaces
+from grpc.framework.foundation import stream
 
-class _EventUnaryUnaryMethod(interfaces.EventValueInValueOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
-
-  def service(self, request, response_callback, context):
-    return self._behavior(request, response_callback, context)
-
-
-class _EventUnaryStreamMethod(interfaces.EventValueInStreamOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
-
-  def service(self, request, response_consumer, context):
-    return self._behavior(request, response_consumer, context)
-
-
-class _EventStreamUnaryMethod(interfaces.EventStreamInValueOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
-
-  def service(self, response_callback, context):
-    return self._behavior(response_callback, context)
-
-
-class _EventStreamStreamMethod(interfaces.EventStreamInStreamOutMethod):
-
-  def __init__(self, behavior):
-    self._behavior = behavior
 
-  def service(self, response_consumer, context):
-    return self._behavior(response_consumer, context)
+class _MethodImplementation(
+    interfaces.MethodImplementation,
+    collections.namedtuple(
+        '_MethodImplementation',
+        ['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline',
+         'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event',
+         'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])):
+  pass
 
 
-def inline_unary_unary_method(behavior):
-  """Creates an interfaces.InlineValueInValueOutMethod from a behavior.
+def unary_unary_inline(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
-    behavior: The implementation of a unary-unary RPC method as a callable
-      value that takes a request value and an interfaces.RpcContext object and
+    behavior: The implementation of a unary-unary RPC method as a callable value
+      that takes a request value and an interfaces.RpcContext object and
       returns a response value.
 
   Returns:
-    An interfaces.InlineValueInValueOutMethod derived from the given behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _InlineUnaryUnaryMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior,
+      None, None, None, None, None, None, None)
 
 
-def inline_unary_stream_method(behavior):
-  """Creates an interfaces.InlineValueInStreamOutMethod from a behavior.
+def unary_stream_inline(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
     behavior: The implementation of a unary-stream RPC method as a callable
@@ -129,13 +72,15 @@ def inline_unary_stream_method(behavior):
       returns an iterator of response values.
 
   Returns:
-    An interfaces.InlineValueInStreamOutMethod derived from the given behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _InlineUnaryStreamMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None,
+      behavior, None, None, None, None, None, None)
 
 
-def inline_stream_unary_method(behavior):
-  """Creates an interfaces.InlineStreamInValueOutMethod from a behavior.
+def stream_unary_inline(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
     behavior: The implementation of a stream-unary RPC method as a callable
@@ -143,13 +88,15 @@ def inline_stream_unary_method(behavior):
       interfaces.RpcContext object and returns a response value.
 
   Returns:
-    An interfaces.InlineStreamInValueOutMethod derived from the given behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _InlineStreamUnaryMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None,
+      behavior, None, None, None, None, None)
 
 
-def inline_stream_stream_method(behavior):
-  """Creates an interfaces.InlineStreamInStreamOutMethod from a behavior.
+def stream_stream_inline(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
     behavior: The implementation of a stream-stream RPC method as a callable
@@ -157,14 +104,15 @@ def inline_stream_stream_method(behavior):
       interfaces.RpcContext object and returns an iterator of response values.
 
   Returns:
-    An interfaces.InlineStreamInStreamOutMethod derived from the given
-      behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _InlineStreamStreamMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None,
+      None, behavior, None, None, None, None)
 
 
-def event_unary_unary_method(behavior):
-  """Creates an interfaces.EventValueInValueOutMethod from a behavior.
+def unary_unary_event(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
     behavior: The implementation of a unary-unary RPC method as a callable
@@ -172,27 +120,31 @@ def event_unary_unary_method(behavior):
       the response value of the RPC, and an interfaces.RpcContext.
 
   Returns:
-    An interfaces.EventValueInValueOutMethod derived from the given behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _EventUnaryUnaryMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None,
+      None, None, behavior, None, None, None)
 
 
-def event_unary_stream_method(behavior):
-  """Creates an interfaces.EventValueInStreamOutMethod from a behavior.
+def unary_stream_event(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
     behavior: The implementation of a unary-stream RPC method as a callable
       value that takes a request value, a stream.Consumer to which to pass the
-      response values of the RPC, and an interfaces.RpcContext.
+      the response values of the RPC, and an interfaces.RpcContext.
 
   Returns:
-    An interfaces.EventValueInStreamOutMethod derived from the given behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _EventUnaryStreamMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None,
+      None, None, None, behavior, None, None)
 
 
-def event_stream_unary_method(behavior):
-  """Creates an interfaces.EventStreamInValueOutMethod from a behavior.
+def stream_unary_event(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
     behavior: The implementation of a stream-unary RPC method as a callable
@@ -201,13 +153,15 @@ def event_stream_unary_method(behavior):
       which the request values of the RPC should be passed.
 
   Returns:
-    An interfaces.EventStreamInValueOutMethod derived from the given behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _EventStreamUnaryMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None,
+      None, None, None, None, behavior, None)
 
 
-def event_stream_stream_method(behavior):
-  """Creates an interfaces.EventStreamInStreamOutMethod from a behavior.
+def stream_stream_event(behavior):
+  """Creates an interfaces.MethodImplementation for the given behavior.
 
   Args:
     behavior: The implementation of a stream-stream RPC method as a callable
@@ -216,6 +170,8 @@ def event_stream_stream_method(behavior):
       which the request values of the RPC should be passed.
 
   Returns:
-    An interfaces.EventStreamInStreamOutMethod derived from the given behavior.
+    An interfaces.MethodImplementation derived from the given behavior.
   """
-  return _EventStreamStreamMethod(behavior)
+  return _MethodImplementation(
+      cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None,
+      None, None, None, None, None, behavior)
diff --git a/src/python/src/setup.py b/src/python/src/setup.py
index cdb82a9dc35208c03aa86c04edef50b5c260f6c2..a513a2811bdc25c52999784653df7800a44028fe 100644
--- a/src/python/src/setup.py
+++ b/src/python/src/setup.py
@@ -64,7 +64,6 @@ _PACKAGES = (
     'grpc._junkdrawer',
     'grpc.early_adopter',
     'grpc.framework',
-    'grpc.framework.assembly',
     'grpc.framework.base',
     'grpc.framework.base.packets',
     'grpc.framework.common',
diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb
index b3b7d0c5a3a90f3eca27fac7ae34673f6197e6a6..0819ba9bbcc1d791091a7a9de38514e0e296d434 100755
--- a/src/ruby/bin/interop/interop_server.rb
+++ b/src/ruby/bin/interop/interop_server.rb
@@ -176,12 +176,11 @@ end
 def main
   opts = parse_options
   host = "0.0.0.0:#{opts['port']}"
+  s = GRPC::RpcServer.new
   if opts['secure']
-    s = GRPC::RpcServer.new(creds: test_server_creds)
-    s.add_http2_port(host, true)
+    s.add_http2_port(host, test_server_creds)
     logger.info("... running securely on #{host}")
   else
-    s = GRPC::RpcServer.new
     s.add_http2_port(host)
     logger.info("... running insecurely on #{host}")
   end
diff --git a/src/ruby/bin/math_server.rb b/src/ruby/bin/math_server.rb
index 93277e39320c4948cdd5fc46815da2feab4716ad..5cc76134893c225f12e5f22d9feb5f18da64a525 100755
--- a/src/ruby/bin/math_server.rb
+++ b/src/ruby/bin/math_server.rb
@@ -173,12 +173,11 @@ def main
     end
   end.parse!
 
+  s = GRPC::RpcServer.new
   if options['secure']
-    s = GRPC::RpcServer.new(creds: test_server_creds)
-    s.add_http2_port(options['host'], true)
+    s.add_http2_port(options['host'], test_server_creds)
     logger.info("... running securely on #{options['host']}")
   else
-    s = GRPC::RpcServer.new
     s.add_http2_port(options['host'])
     logger.info("... running insecurely on #{options['host']}")
   end
diff --git a/src/ruby/bin/noproto_server.rb b/src/ruby/bin/noproto_server.rb
index 435f8f4ebf495aec964d271a1a609a47fc532014..9979cb7ebbdd693e08f730d281f6213f316331b8 100755
--- a/src/ruby/bin/noproto_server.rb
+++ b/src/ruby/bin/noproto_server.rb
@@ -95,12 +95,11 @@ def main
     end
   end.parse!
 
+  s = GRPC::RpcServer.new
   if options['secure']
-    s = GRPC::RpcServer.new(creds: test_server_creds)
-    s.add_http2_port(options['host'], true)
+    s.add_http2_port(options['host'], test_server_creds)
     logger.info("... running securely on #{options['host']}")
   else
-    s = GRPC::RpcServer.new
     s.add_http2_port(options['host'])
     logger.info("... running insecurely on #{options['host']}")
   end
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index 483a31f60cce5df7f0bc4a58c312534444ff1d26..f15f85bf56dca4b52e0ac57fa11fb3085df46448 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -77,7 +77,6 @@ end
 
 dir_config('grpc', HEADER_DIRS, LIB_DIRS)
 
-$CFLAGS << ' -std=c89 '
 $CFLAGS << ' -Wno-implicit-function-declaration '
 $CFLAGS << ' -Wno-pointer-sign '
 $CFLAGS << ' -Wno-return-type '
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
index c7671c8a775098597e4f8b752d001de4c4d274cc..400efd0dfad8d3909e7fe4049a3e6ebef235f351 100644
--- a/src/ruby/ext/grpc/rb_grpc.c
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -119,12 +119,12 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
       break;
 
     case T_FLOAT:
-      if (interval && RFLOAT(time)->float_value < 0.0)
+      if (interval && RFLOAT_VALUE(time) < 0.0)
         rb_raise(rb_eArgError, "%s must be positive", tstr);
       else {
         double f, d;
 
-        d = modf(RFLOAT(time)->float_value, &f);
+        d = modf(RFLOAT_VALUE(time), &f);
         if (d < 0) {
           d += 1;
           f -= 1;
@@ -132,7 +132,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
         t.tv_sec = (time_t)f;
         if (f != t.tv_sec) {
           rb_raise(rb_eRangeError, "%f out of Time range",
-                   RFLOAT(time)->float_value);
+                   RFLOAT_VALUE(time));
         }
         t.tv_nsec = (time_t)(d * 1e9 + 0.5);
       }
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index 5954e27d0249e8315f67553dc2aeadf4b0cf396a..c54f02e87afac11a2ea8f3c4a28406a460fe51dd 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -97,35 +97,19 @@ static VALUE grpc_rb_server_alloc(VALUE cls) {
 /*
   call-seq:
     cq = CompletionQueue.new
-    insecure_server = Server.new(cq, {'arg1': 'value1'})
-    server_creds = ...
-    secure_server = Server.new(cq, {'arg1': 'value1'}, server_creds)
+    server = Server.new(cq, {'arg1': 'value1'})
 
   Initializes server instances. */
-static VALUE grpc_rb_server_init(int argc, VALUE *argv, VALUE self) {
-  VALUE cqueue = Qnil;
-  VALUE credentials = Qnil;
-  VALUE channel_args = Qnil;
+static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
   grpc_completion_queue *cq = NULL;
-  grpc_server_credentials *creds = NULL;
   grpc_rb_server *wrapper = NULL;
   grpc_server *srv = NULL;
   grpc_channel_args args;
   MEMZERO(&args, grpc_channel_args, 1);
-
-  /* "21" == 2 mandatory args, 1 (credentials) is optional */
-  rb_scan_args(argc, argv, "21", &cqueue, &channel_args, &credentials);
   cq = grpc_rb_get_wrapped_completion_queue(cqueue);
-
   Data_Get_Struct(self, grpc_rb_server, wrapper);
   grpc_rb_hash_convert_to_channel_args(channel_args, &args);
   srv = grpc_server_create(cq, &args);
-  if (credentials == Qnil) {
-    srv = grpc_server_create(cq, &args);
-  } else {
-    creds = grpc_rb_get_wrapped_server_credentials(credentials);
-    srv = grpc_secure_server_create(creds, cq, &args);
-  }
 
   if (args.args != NULL) {
     xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
@@ -215,33 +199,36 @@ static VALUE grpc_rb_server_destroy(VALUE self) {
 
     // secure port
     server_creds = ...
-    secure_server = Server.new(cq, {'arg1': 'value1'}, creds)
-    secure_server.add_http_port('mydomain:7575', True)
+    secure_server = Server.new(cq, {'arg1': 'value1'})
+    secure_server.add_http_port('mydomain:7575', server_creds)
 
     Adds a http2 port to server */
 static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
   VALUE port = Qnil;
-  VALUE is_secure = Qnil;
+  VALUE rb_creds = Qnil;
   grpc_rb_server *s = NULL;
+  grpc_server_credentials *creds = NULL;
   int recvd_port = 0;
 
-  /* "11" == 1 mandatory args, 1 (is_secure) is optional */
-  rb_scan_args(argc, argv, "11", &port, &is_secure);
+  /* "11" == 1 mandatory args, 1 (rb_creds) is optional */
+  rb_scan_args(argc, argv, "11", &port, &rb_creds);
 
   Data_Get_Struct(self, grpc_rb_server, s);
   if (s->wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
     return Qnil;
-  } else if (is_secure == Qnil || TYPE(is_secure) != T_TRUE) {
+  } else if (rb_creds == Qnil) {
     recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
     if (recvd_port == 0) {
       rb_raise(rb_eRuntimeError,
                "could not add port %s to server, not sure why",
                StringValueCStr(port));
     }
-  } else if (TYPE(is_secure) != T_FALSE) {
+  } else {
+    creds = grpc_rb_get_wrapped_server_credentials(rb_creds);
     recvd_port =
-        grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port));
+        grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port),
+			                  creds);
     if (recvd_port == 0) {
       rb_raise(rb_eRuntimeError,
                "could not add secure port %s to server, not sure why",
@@ -258,7 +245,7 @@ void Init_grpc_server() {
   rb_define_alloc_func(rb_cServer, grpc_rb_server_alloc);
 
   /* Provides a ruby constructor and support for dup/clone. */
-  rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, -1);
+  rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, 2);
   rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1);
 
   /* Add the server methods. */
diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec
index ed26fef4a97a3e381d30cf9503dc62f2a240b34e..45cbacfeb04de3cf6ca43bbf2ee227cf1861e4e4 100755
--- a/src/ruby/grpc.gemspec
+++ b/src/ruby/grpc.gemspec
@@ -21,14 +21,10 @@ Gem::Specification.new do |s|
   s.require_paths = ['lib']
   s.platform      = Gem::Platform::RUBY
 
-  s.add_dependency 'faraday', '~> 0.9'
   s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
   s.add_dependency 'googleauth', '~> 0.1'
   s.add_dependency 'logging', '~> 1.8'
-  s.add_dependency 'jwt', '~> 1.2.1'
   s.add_dependency 'minitest', '~> 5.4'  # reqd for interop tests
-  s.add_dependency 'multi_json', '1.10.1'
-  s.add_dependency 'signet', '~> 0.6.0'
   s.add_dependency 'xray', '~> 1.1'
 
   s.add_development_dependency 'bundler', '~> 1.7'
diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb
index f234984eec3a4f4bde3d0d7cfe5528e56c1fcd65..01328d4a5bc68895716942b1f360ed0c9b911d7e 100644
--- a/src/ruby/lib/grpc/generic/client_stub.rb
+++ b/src/ruby/lib/grpc/generic/client_stub.rb
@@ -39,6 +39,25 @@ module GRPC
     # Default deadline is 5 seconds.
     DEFAULT_DEADLINE = 5
 
+    # setup_channel is used by #initialize to constuct a channel from its
+    # arguments.
+    def self.setup_channel(alt_chan, host, creds, **kw)
+      unless alt_chan.nil?
+        fail(TypeError, '!Channel') unless alt_chan.is_a?(Core::Channel)
+        return alt_chan
+      end
+      return Core::Channel.new(host, kw) if creds.nil?
+      fail(TypeError, '!Credentials') unless creds.is_a?(Core::Credentials)
+      Core::Channel.new(host, kw, creds)
+    end
+
+    # check_update_metadata is used by #initialize verify that it's a Proc.
+    def self.check_update_metadata(update_metadata)
+      return update_metadata if update_metadata.nil?
+      fail(TypeError, '!is_a?Proc') unless update_metadata.is_a?(Proc)
+      update_metadata
+    end
+
     # Creates a new ClientStub.
     #
     # Minimally, a stub is created with the just the host of the gRPC service
@@ -73,40 +92,17 @@ module GRPC
     # @param update_metadata a func that updates metadata as described above
     # @param kw [KeywordArgs]the channel arguments
     def initialize(host, q,
-                   channel_override:nil,
+                   channel_override: nil,
                    deadline: DEFAULT_DEADLINE,
                    creds: nil,
                    update_metadata: nil,
                    **kw)
-      unless q.is_a? Core::CompletionQueue
-        fail(ArgumentError, 'not a CompletionQueue')
-      end
+      fail(TypeError, '!CompletionQueue') unless q.is_a?(Core::CompletionQueue)
       @queue = q
-
-      # set the channel instance
-      if !channel_override.nil?
-        ch = channel_override
-        fail(ArgumentError, 'not a Channel') unless ch.is_a? Core::Channel
-      else
-        if creds.nil?
-          ch = Core::Channel.new(host, kw)
-        elsif !creds.is_a?(Core::Credentials)
-          fail(ArgumentError, 'not a Credentials')
-        else
-          ch = Core::Channel.new(host, kw, creds)
-        end
-      end
-      @ch = ch
-
-      @update_metadata = nil
-      unless update_metadata.nil?
-        unless update_metadata.is_a? Proc
-          fail(ArgumentError, 'update_metadata is not a Proc')
-        end
-        @update_metadata = update_metadata
-      end
-
-      @host = host
+      @ch = ClientStub.setup_channel(channel_override, host, creds, **kw)
+      @update_metadata = ClientStub.check_update_metadata(update_metadata)
+      alt_host = kw[Core::Channel::SSL_TARGET]
+      @host = alt_host.nil? ? host : alt_host
       @deadline = deadline
     end
 
@@ -400,12 +396,7 @@ module GRPC
     # @param deadline [TimeConst]
     def new_active_call(ch, marshal, unmarshal, deadline = nil)
       absolute_deadline = Core::TimeConsts.from_relative_time(deadline)
-      # It should be OK to to pass the hostname:port to create_call, but at
-      # the moment this fails a security check.  This will be corrected.
-      #
-      # TODO: # remove this after create_call is updated
-      host = @host.split(':')[0]
-      call = @ch.create_call(ch, host, absolute_deadline)
+      call = @ch.create_call(ch, @host, absolute_deadline)
       ActiveCall.new(call, @queue, marshal, unmarshal, absolute_deadline,
                      started: false)
     end
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index 6938f718922cfc21b463b2cbf8aa70f12218d038..35e84023be952df4fa5e846a14a8d26d4c166e26 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -81,7 +81,6 @@ module GRPC
                    max_waiting_requests:DEFAULT_MAX_WAITING_REQUESTS,
                    poll_period:INFINITE_FUTURE,
                    completion_queue_override:nil,
-                   creds:nil,
                    server_override:nil,
                    **kw)
       if completion_queue_override.nil?
@@ -95,13 +94,7 @@ module GRPC
       @cq = cq
 
       if server_override.nil?
-        if creds.nil?
-          srv = Core::Server.new(@cq, kw)
-        elsif !creds.is_a? Core::ServerCredentials
-          fail(ArgumentError, 'not a ServerCredentials')
-        else
-          srv = Core::Server.new(@cq, kw, creds)
-        end
+        srv = Core::Server.new(@cq, kw)
       else
         srv = server_override
         fail(ArgumentError, 'not a Server') unless srv.is_a? Core::Server
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 030ff328f217db820f857ed2619cc9da11a679ca..49a2d3bb4df9fd00b7f5955b3fb70f66bba77e6d 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -95,7 +95,7 @@ shared_context 'setup: tags' do
   end
 
   def new_client_call
-    @ch.create_call('/method', 'localhost', deadline)
+    @ch.create_call('/method', 'foo.test.google.fr', deadline)
   end
 end
 
@@ -346,12 +346,12 @@ end
 describe 'the secure http client/server' do
   before(:example) do
     certs = load_test_certs
-    server_host = 'localhost:0'
+    server_host = '0.0.0.0:0'
     @client_queue = GRPC::Core::CompletionQueue.new
     @server_queue = GRPC::Core::CompletionQueue.new
     server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
-    @server = GRPC::Core::Server.new(@server_queue, nil, server_creds)
-    server_port = @server.add_http2_port(server_host, true)
+    @server = GRPC::Core::Server.new(@server_queue, nil)
+    server_port = @server.add_http2_port(server_host, server_creds)
     @server.start
     args = { Channel::SSL_TARGET => 'foo.test.google.fr' }
     @ch = Channel.new("0.0.0.0:#{server_port}", args,
@@ -362,11 +362,9 @@ describe 'the secure http client/server' do
     @server.close
   end
 
-  # TODO: uncomment after updating the to the new c api
-  # it_behaves_like 'basic GRPC message delivery is OK' do
-  # end
+  it_behaves_like 'basic GRPC message delivery is OK' do
+  end
 
-  # TODO: uncomment after updating the to the new c api
-  # it_behaves_like 'GRPC metadata delivery works OK' do
-  # end
+  it_behaves_like 'GRPC metadata delivery works OK' do
+  end
 end
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index e8c706044617a7775d84ce65f5bb448438dc4b92..d5421d400c99c9f14ae2c6c0609760ee623abe45 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -164,19 +164,6 @@ describe GRPC::RpcServer do
       expect(&blk).to raise_error
     end
 
-    it 'can be created with the creds as valid ServerCedentials' do
-      certs = load_test_certs
-      server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
-      blk = proc do
-        opts = {
-          a_channel_arg: 'an_arg',
-          creds: server_creds
-        }
-        RpcServer.new(**opts)
-      end
-      expect(&blk).to_not raise_error
-    end
-
     it 'can be created with a server override' do
       opts = { a_channel_arg: 'an_arg', server_override: @server }
       blk = proc do
diff --git a/src/ruby/spec/server_spec.rb b/src/ruby/spec/server_spec.rb
index 5b81f195371d9b8a3b2301cc335b8bf160d7f63b..a47e484f9719c28c27fd4c6c485c74e08942bc45 100644
--- a/src/ruby/spec/server_spec.rb
+++ b/src/ruby/spec/server_spec.rb
@@ -118,10 +118,11 @@ describe Server do
     end
 
     describe 'for secure servers' do
+      let(:cert) { create_test_cert }
       it 'runs without failing' do
         blk = proc do
           s = Server.new(@cq, nil)
-          s.add_http2_port('localhost:0', true)
+          s.add_http2_port('localhost:0', cert)
           s.close
         end
         expect(&blk).to_not raise_error
@@ -130,7 +131,7 @@ describe Server do
       it 'fails if the server is closed' do
         s = Server.new(@cq, nil)
         s.close
-        blk = proc { s.add_http2_port('localhost:0', true) }
+        blk = proc { s.add_http2_port('localhost:0', cert) }
         expect(&blk).to raise_error(RuntimeError)
       end
     end
@@ -138,7 +139,7 @@ describe Server do
 
   shared_examples '#new' do
     it 'takes a completion queue with nil channel args' do
-      expect { Server.new(@cq, nil, create_test_cert) }.to_not raise_error
+      expect { Server.new(@cq, nil) }.to_not raise_error
     end
 
     it 'does not take a hash with bad keys as channel args' do
@@ -195,14 +196,6 @@ describe Server do
     it_behaves_like '#new'
   end
 
-  describe '#new with a secure channel' do
-    def construct_with_args(a)
-      proc { Server.new(@cq, a, create_test_cert) }
-    end
-
-    it_behaves_like '#new'
-  end
-
   def start_a_server
     s = Server.new(@cq, nil)
     s.add_http2_port('0.0.0.0:0')
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 24fd450dff58d3bad204d37e6980a53fade756d5..6573e03f7f55746d73cc48d8fdcd9e1fd4236ada 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -917,7 +917,7 @@ PUBLIC_HEADERS_C += \\
 LIB${lib.name.upper()}_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIB${lib.name.upper()}_SRC))))
 
 ## If the library requires OpenSSL with ALPN, let's add some restrictions.
-% if lib.get('secure', True):
+% if lib.get('secure', 'check') == 'yes' or lib.get('secure', 'check') == 'check':
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure libraries if you don't have OpenSSL with ALPN.
@@ -993,14 +993,14 @@ $(LIBDIR)/$(CONFIG)/lib${lib.name}.a: $(ZLIB_DEP)\
 	$(Q) rm -f $(LIBDIR)/$(CONFIG)/lib${lib.name}.a
 	$(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/lib${lib.name}.a $(LIB${lib.name.upper()}_OBJS)
 % if lib.get('baselib', False):
-% if lib.get('secure', True):
-	$(Q) rm -rf tmp-merge
-	$(Q) mkdir tmp-merge
-	$(Q) ( cd tmp-merge ; $(AR) x ../$(LIBDIR)/$(CONFIG)/lib${lib.name}.a )
-	$(Q) for l in $(OPENSSL_MERGE_LIBS) ; do ( cd tmp-merge ; <%text>ar x ../$${l}</%text> ) ; done
-	$(Q) rm -f $(LIBDIR)/$(CONFIG)/lib${lib.name}.a tmp-merge/__.SYMDEF*
-	$(Q) ar rcs $(LIBDIR)/$(CONFIG)/lib${lib.name}.a tmp-merge/*
-	$(Q) rm -rf tmp-merge
+% if lib.get('secure', 'check') == 'yes':
+	$(Q) rm -rf tmp-merge-${lib.name}
+	$(Q) mkdir tmp-merge-${lib.name}
+	$(Q) ( cd tmp-merge-${lib.name} ; $(AR) x ../$(LIBDIR)/$(CONFIG)/lib${lib.name}.a )
+	$(Q) for l in $(OPENSSL_MERGE_LIBS) ; do ( cd tmp-merge-${lib.name} ; <%text>ar x ../$${l}</%text> ) ; done
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/lib${lib.name}.a tmp-merge-${lib.name}/__.SYMDEF*
+	$(Q) ar rcs $(LIBDIR)/$(CONFIG)/lib${lib.name}.a tmp-merge-${lib.name}/*
+	$(Q) rm -rf tmp-merge-${lib.name}
 % endif
 % endif
 ifeq ($(SYSTEM),Darwin)
@@ -1028,8 +1028,10 @@ endif
     mingw_libs = mingw_libs + ' -l' + dep + '-imp'
     mingw_lib_deps = mingw_lib_deps + '$(LIBDIR)/$(CONFIG)/' + dep + '.$(SHARED_EXT)'
 
-  if lib.get('secure', True):
+  if lib.get('secure', 'check') == 'yes':
     common = common + ' $(LDLIBS_SECURE) $(OPENSSL_MERGE_LIBS)'
+
+  if lib.get('secure', 'check') == 'yes' or lib.get('secure', 'check') == 'check':
     lib_deps = lib_deps + ' $(OPENSSL_DEP)'
     mingw_lib_deps = mingw_lib_deps + ' $(OPENSSL_DEP)'
 
@@ -1056,7 +1058,7 @@ else
 endif
 endif
 % endif
-% if lib.get('secure', True):
+% if lib.get('secure', 'check') == 'yes' or lib.get('secure', 'check') == 'check':
 ## If the lib was secure, we have to close the Makefile's if that tested
 ## the presence of an ALPN-capable OpenSSL.
 
@@ -1069,13 +1071,13 @@ endif
 endif
 % endif
 
-% if lib.get('secure', True):
+% if lib.get('secure', 'check') == 'yes' or lib.get('secure', 'check') == 'check':
 ifneq ($(NO_SECURE),true)
 % endif
 ifneq ($(NO_DEPS),true)
 -include $(LIB${lib.name.upper()}_OBJS:.o=.dep)
 endif
-% if lib.get('secure', True):
+% if lib.get('secure', 'check') == 'yes' or lib.get('secure', 'check') == 'check':
 endif
 % endif
 
@@ -1102,7 +1104,7 @@ ${tgt.name.upper()}_SRC = \\
 
 ${tgt.name.upper()}_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(${tgt.name.upper()}_SRC))))
 
-% if tgt.get('secure', True):
+% if tgt.get('secure', 'check') == 'yes' or tgt.get('secure', 'check') == 'check':
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL with ALPN.
@@ -1176,7 +1178,7 @@ $(BINDIR)/$(CONFIG)/${tgt.name}: $(${tgt.name.upper()}_OBJS)\
 % endif
 % if tgt.build == 'protoc':
  $(HOST_LDLIBS_PROTOC)\
-% elif tgt.get('secure', True):
+% elif tgt.get('secure', 'check') == 'yes' or tgt.get('secure', 'check') == 'check':
  $(LDLIBS_SECURE)\
 % endif
  -o $(BINDIR)/$(CONFIG)/${tgt.name}
@@ -1184,7 +1186,7 @@ $(BINDIR)/$(CONFIG)/${tgt.name}: $(${tgt.name.upper()}_OBJS)\
 
 endif
 % endif
-% if tgt.get('secure', True):
+% if tgt.get('secure', 'check') == 'yes' or tgt.get('secure', 'check') == 'check':
 
 endif
 % endif
@@ -1199,13 +1201,13 @@ $(OBJDIR)/$(CONFIG)/${os.path.splitext(src)[0]}.o: \
 
 deps_${tgt.name}: $(${tgt.name.upper()}_OBJS:.o=.dep)
 
-% if tgt.get('secure', True):
+% if tgt.get('secure', 'check') == 'yes' or tgt.get('secure', 'check') == 'check':
 ifneq ($(NO_SECURE),true)
 % endif
 ifneq ($(NO_DEPS),true)
 -include $(${tgt.name.upper()}_OBJS:.o=.dep)
 endif
-% if tgt.get('secure', True):
+% if tgt.get('secure', 'check') == 'yes' or tgt.get('secure', 'check') == 'check':
 endif
 % endif
 </%def>
diff --git a/test/compiler/python_plugin_test.py b/test/compiler/python_plugin_test.py
index f16682862c32a76ac635f2e95390dede819140f3..9cf3c624c05d3be3731bc1c60f164971b0da7a8c 100644
--- a/test/compiler/python_plugin_test.py
+++ b/test/compiler/python_plugin_test.py
@@ -32,8 +32,10 @@ import contextlib
 import errno
 import itertools
 import os
+import shutil
 import subprocess
 import sys
+import tempfile
 import time
 import unittest
 
@@ -55,8 +57,8 @@ DOES_NOT_MATTER_DELAY = 0
 NO_DELAY = 0
 LONG_DELAY = 1
 
-# Assigned in __main__.
-_build_mode = None
+# Build mode environment variable set by tools/run_tests/run_tests.py.
+_build_mode = os.environ['CONFIG']
 
 
 class _ServicerMethods(object):
@@ -227,24 +229,26 @@ class PythonPluginTest(unittest.TestCase):
       protoc_command = 'protoc'
 
     # Ensure that the output directory exists.
-    outdir = '../../gens/test/compiler/python'
-    try:
-      os.makedirs(outdir)
-    except OSError as exception:
-      if exception.errno != errno.EEXIST:
-        raise
+    self.outdir = tempfile.mkdtemp()
 
     # Invoke protoc with the plugin.
     cmd = [
         protoc_command,
         '--plugin=protoc-gen-python-grpc=%s' % protoc_plugin_filename,
         '-I %s' % os.path.dirname(test_proto_filename),
-        '--python_out=%s' % outdir,
-        '--python-grpc_out=%s' % outdir,
+        '--python_out=%s' % self.outdir,
+        '--python-grpc_out=%s' % self.outdir,
         os.path.basename(test_proto_filename),
     ]
     subprocess.call(' '.join(cmd), shell=True)
-    sys.path.append(outdir)
+    sys.path.append(self.outdir)
+
+  def tearDown(self):
+    try:
+      shutil.rmtree(self.outdir)
+    except OSError as exc:
+      if exc.errno != errno.ENOENT:
+        raise
 
   # TODO(atash): Figure out which of theses tests is hanging flakily with small
   # probability.
@@ -296,6 +300,8 @@ class PythonPluginTest(unittest.TestCase):
         with self.assertRaises(exceptions.ExpirationError):
           response_future.result()
 
+  @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs '
+                 'forever and fix.')
   def testUnaryCallAsyncCancelled(self):
     import test_pb2  # pylint: disable=g-import-not-at-top
     request = test_pb2.SimpleRequest(response_size=13)
@@ -325,6 +331,8 @@ class PythonPluginTest(unittest.TestCase):
         expected_response, response = check
         self.assertEqual(expected_response, response)
 
+  @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs '
+                 'forever and fix.')
   def testStreamingOutputCallExpired(self):
     import test_pb2  # pylint: disable=g-import-not-at-top
     request = StreamingOutputRequest(test_pb2)
@@ -335,6 +343,8 @@ class PythonPluginTest(unittest.TestCase):
         with self.assertRaises(exceptions.ExpirationError):
           list(responses)
 
+  @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs '
+                 'forever and fix.')
   def testStreamingOutputCallCancelled(self):
     import test_pb2  # pylint: disable=g-import-not-at-top
     request = StreamingOutputRequest(test_pb2)
@@ -359,6 +369,8 @@ class PythonPluginTest(unittest.TestCase):
         with self.assertRaises(exceptions.ServicerError):
           next(responses)
 
+  @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs '
+                 'forever and fix.')
   def testStreamingInputCall(self):
     import test_pb2  # pylint: disable=g-import-not-at-top
     with _CreateService(test_pb2, NO_DELAY) as (servicer, stub, unused_server):
@@ -426,6 +438,8 @@ class PythonPluginTest(unittest.TestCase):
         expected_response, response = check
         self.assertEqual(expected_response, response)
 
+  @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs '
+                 'forever and fix.')
   def testFullDuplexCallExpired(self):
     import test_pb2  # pylint: disable=g-import-not-at-top
     request = FullDuplexRequest(test_pb2)
@@ -436,6 +450,8 @@ class PythonPluginTest(unittest.TestCase):
         with self.assertRaises(exceptions.ExpirationError):
           list(responses)
 
+  @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs '
+                 'forever and fix.')
   def testFullDuplexCallCancelled(self):
     import test_pb2  # pylint: disable=g-import-not-at-top
     with _CreateService(test_pb2, NO_DELAY) as (servicer, stub, unused_server):
@@ -459,6 +475,8 @@ class PythonPluginTest(unittest.TestCase):
         with self.assertRaises(exceptions.ServicerError):
           next(responses)
 
+  @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs '
+                 'forever and fix.')
   def testHalfDuplexCall(self):
     import test_pb2  # pylint: disable=g-import-not-at-top
     with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as (
@@ -502,14 +520,4 @@ class PythonPluginTest(unittest.TestCase):
 
 if __name__ == '__main__':
   os.chdir(os.path.dirname(sys.argv[0]))
-  parser = argparse.ArgumentParser(
-      description='Run Python compiler plugin test.')
-  parser.add_argument(
-      '--build_mode', dest='build_mode', type=str, default='dbg',
-      help='The build mode of the targets to test, e.g. "dbg", "opt", "asan", '
-      'etc.')
-  parser.add_argument('--port', dest='port', type=int, default=0)
-  args, remainder = parser.parse_known_args()
-  _build_mode = args.build_mode
-  sys.argv[1:] = remainder
   unittest.main()
diff --git a/test/core/echo/server.c b/test/core/echo/server.c
index bc84645a0405572d4a9a122a42c761095a614797..e888a0c8770984b2242d2ef1af9aa89f1a0908da 100644
--- a/test/core/echo/server.c
+++ b/test/core/echo/server.c
@@ -143,8 +143,8 @@ int main(int argc, char **argv) {
                                                     test_server1_cert};
     grpc_server_credentials *ssl_creds =
         grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1);
-    server = grpc_secure_server_create(ssl_creds, cq, &args);
-    GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr));
+    server = grpc_server_create(cq, &args);
+    GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds));
     grpc_server_credentials_release(ssl_creds);
   } else {
     server = grpc_server_create(cq, &args);
diff --git a/test/core/end2end/end2end_tests.h b/test/core/end2end/end2end_tests.h
index b6d93252eb282cddce9cf115a21055e7f65c31a1..41c6e2ee0c63f1aabd08d894eeec9a8c97c30ea4 100644
--- a/test/core/end2end/end2end_tests.h
+++ b/test/core/end2end/end2end_tests.h
@@ -40,6 +40,7 @@ typedef struct grpc_end2end_test_fixture grpc_end2end_test_fixture;
 typedef struct grpc_end2end_test_config grpc_end2end_test_config;
 
 #define FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION 1
+#define FEATURE_MASK_SUPPORTS_HOSTNAME_VERIFICATION 2
 
 struct grpc_end2end_test_fixture {
   grpc_completion_queue *server_cq;
diff --git a/test/core/end2end/fixtures/chttp2_fake_security.c b/test/core/end2end/fixtures/chttp2_fake_security.c
index 247d1fd32232d2a2183e91764004e3c53d9051f6..047d482be340f75da8495a6941471f12c112240d 100644
--- a/test/core/end2end/fixtures/chttp2_fake_security.c
+++ b/test/core/end2end/fixtures/chttp2_fake_security.c
@@ -84,9 +84,9 @@ static void chttp2_init_server_secure_fullstack(
     grpc_server_destroy(f->server);
   }
   f->server =
-      grpc_secure_server_create(server_creds, f->server_cq, server_args);
+      grpc_server_create(f->server_cq, server_args);
+  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, server_creds));
   grpc_server_credentials_release(server_creds);
-  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr));
   grpc_server_start(f->server);
 }
 
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
index 16946d5f97eef828367cd319d230457bad167db3..16433f5f87cdfee694bccf088c0b4865b05ef428 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
@@ -87,9 +87,9 @@ static void chttp2_init_server_secure_fullstack(
     grpc_server_destroy(f->server);
   }
   f->server =
-      grpc_secure_server_create(server_creds, f->server_cq, server_args);
+      grpc_server_create(f->server_cq, server_args);
+  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, server_creds));
   grpc_server_credentials_release(server_creds);
-  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr));
   grpc_server_start(f->server);
 }
 
@@ -123,7 +123,9 @@ static void chttp2_init_server_simple_ssl_secure_fullstack(
 /* All test configurations */
 
 static grpc_end2end_test_config configs[] = {
-    {"chttp2/simple_ssl_fullstack", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+    {"chttp2/simple_ssl_fullstack",
+     FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
+         FEATURE_MASK_SUPPORTS_HOSTNAME_VERIFICATION,
      chttp2_create_fixture_secure_fullstack,
      chttp2_init_client_simple_ssl_secure_fullstack,
      chttp2_init_server_simple_ssl_secure_fullstack,
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
index c451e01024a412f3d7a44856f3b05a9f67d0bd03..99031df8e5459b996314626607511e06d7982e2a 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
@@ -85,9 +85,9 @@ static void chttp2_init_server_secure_fullstack(
     grpc_server_destroy(f->server);
   }
   f->server =
-      grpc_secure_server_create(server_creds, f->server_cq, server_args);
+      grpc_server_create(f->server_cq, server_args);
+  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, server_creds));
   grpc_server_credentials_release(server_creds);
-  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr));
   grpc_server_start(f->server);
 }
 
@@ -129,7 +129,8 @@ static void chttp2_init_server_simple_ssl_secure_fullstack(
 
 static grpc_end2end_test_config configs[] = {
     {"chttp2/simple_ssl_with_oauth2_fullstack",
-     FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+     FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
+         FEATURE_MASK_SUPPORTS_HOSTNAME_VERIFICATION,
      chttp2_create_fixture_secure_fullstack,
      chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack,
      chttp2_init_server_simple_ssl_secure_fullstack,
diff --git a/test/core/end2end/gen_build_json.py b/test/core/end2end/gen_build_json.py
index 4e297b30becb793c606fb43f8c5050619142abdb..67fc0a6e539457c4a914d6a24b028b0db187c0ac 100755
--- a/test/core/end2end/gen_build_json.py
+++ b/test/core/end2end/gen_build_json.py
@@ -46,6 +46,7 @@ END2END_FIXTURES = [
 
 
 END2END_TESTS = [
+    'bad_hostname',
     'cancel_after_accept',
     'cancel_after_accept_and_writes_closed',
     'cancel_after_invoke',
@@ -106,7 +107,7 @@ def main():
               'name': 'end2end_fixture_%s' % f,
               'build': 'private',
               'language': 'c',
-              'secure': True,
+              'secure': 'check',
               'src': ['test/core/end2end/fixtures/%s.c' % f]
           }
           for f in END2END_FIXTURES] + [
@@ -114,7 +115,7 @@ def main():
               'name': 'end2end_test_%s' % t,
               'build': 'private',
               'language': 'c',
-              'secure': False,
+              'secure': 'no',
               'src': ['test/core/end2end/tests/%s.c' % t],
               'headers': ['test/core/end2end/tests/cancel_test_helpers.h']
           }
diff --git a/test/core/end2end/tests/bad_hostname.c b/test/core/end2end/tests/bad_hostname.c
new file mode 100644
index 0000000000000000000000000000000000000000..80922f3b6f9e390180acde425887608d0fb5715d
--- /dev/null
+++ b/test/core/end2end/tests/bad_hostname.c
@@ -0,0 +1,176 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "src/core/support/string.h"
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_client(&f, client_args);
+  config.init_server(&f, server_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event *ev;
+  grpc_completion_type type;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time());
+    GPR_ASSERT(ev);
+    type = ev->type;
+    grpc_event_finish(ev);
+  } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown(f->server);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->server_cq);
+  drain_cq(f->server_cq);
+  grpc_completion_queue_destroy(f->server_cq);
+  grpc_completion_queue_shutdown(f->client_cq);
+  drain_cq(f->client_cq);
+  grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void simple_request_body(grpc_end2end_test_fixture f) {
+  grpc_call *c;
+  gpr_timespec deadline = five_seconds_time();
+  cq_verifier *v_client = cq_verifier_create(f.client_cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_status_code status;
+  char *details = NULL;
+  size_t details_capacity = 0;
+
+  c = grpc_channel_create_call(f.client, f.client_cq, "/foo",
+                               "slartibartfast.local", deadline);
+  GPR_ASSERT(c);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op++;
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(c, ops, op - ops, tag(1)));
+
+  cq_expect_completion(v_client, tag(1), GRPC_OP_OK);
+  cq_verify(v_client);
+
+  GPR_ASSERT(status == GRPC_STATUS_UNAUTHENTICATED);
+
+  gpr_free(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+
+  grpc_call_destroy(c);
+
+  cq_verifier_destroy(v_client);
+}
+
+static void test_invoke_simple_request(grpc_end2end_test_config config) {
+  grpc_end2end_test_fixture f;
+
+  f = begin_test(config, __FUNCTION__, NULL, NULL);
+  simple_request_body(f);
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+  if (config.feature_mask & FEATURE_MASK_SUPPORTS_HOSTNAME_VERIFICATION) {
+    test_invoke_simple_request(config);
+  }
+}
diff --git a/test/core/fling/server.c b/test/core/fling/server.c
index 5c1ab14d03477cced84afb85cd07cc1d2408e73b..ca39cd84b13407d0d6c01c5b7a5f3d4737f26fd6 100644
--- a/test/core/fling/server.c
+++ b/test/core/fling/server.c
@@ -205,8 +205,8 @@ int main(int argc, char **argv) {
                                                     test_server1_cert};
     grpc_server_credentials *ssl_creds =
         grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1);
-    server = grpc_secure_server_create(ssl_creds, cq, NULL);
-    GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr));
+    server = grpc_server_create(cq, NULL);
+    GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds));
     grpc_server_credentials_release(ssl_creds);
   } else {
     server = grpc_server_create(cq, NULL);
diff --git a/test/core/network_benchmarks/low_level_ping_pong.c b/test/core/network_benchmarks/low_level_ping_pong.c
index f0a3e26c4eeb82bf6aa794de3ee9d4e3f0b779d0..7d74d0e0781f015c8c219ee756eb23067251a762 100644
--- a/test/core/network_benchmarks/low_level_ping_pong.c
+++ b/test/core/network_benchmarks/low_level_ping_pong.c
@@ -81,7 +81,7 @@ typedef struct thread_args {
 
 /* Basic call to read() */
 static int read_bytes(int fd, char *buf, size_t read_size, int spin) {
-  int bytes_read = 0;
+  size_t bytes_read = 0;
   int err;
   do {
     err = read(fd, buf + bytes_read, read_size - bytes_read);
@@ -198,7 +198,7 @@ static int epoll_read_bytes_spin(struct thread_args *args, char *buf) {
    writes go directly out to the kernel.
  */
 static int blocking_write_bytes(struct thread_args *args, char *buf) {
-  int bytes_written = 0;
+  size_t bytes_written = 0;
   int err;
   size_t write_size = args->msg_size;
   do {
@@ -586,10 +586,10 @@ static int run_benchmark(char *socket_type, thread_args *client_args,
 
 static int run_all_benchmarks(int msg_size) {
   int error = 0;
-  int i;
+  size_t i;
   for (i = 0; i < GPR_ARRAY_SIZE(test_strategies); ++i) {
     test_strategy *test_strategy = &test_strategies[i];
-    int j;
+    size_t j;
     for (j = 0; j < GPR_ARRAY_SIZE(socket_types); ++j) {
       thread_args *client_args = malloc(sizeof(thread_args));
       thread_args *server_args = malloc(sizeof(thread_args));
@@ -620,7 +620,7 @@ int main(int argc, char **argv) {
   int msg_size = -1;
   char *read_strategy = NULL;
   char *socket_type = NULL;
-  int i;
+  size_t i;
   const test_strategy *test_strategy = NULL;
   int error = 0;
 
@@ -654,7 +654,7 @@ int main(int argc, char **argv) {
   }
 
   for (i = 0; i < GPR_ARRAY_SIZE(test_strategies); ++i) {
-    if (!strcmp(test_strategies[i].name, read_strategy)) {
+    if (strcmp(test_strategies[i].name, read_strategy) == 0) {
       test_strategy = &test_strategies[i];
     }
   }
diff --git a/test/core/security/base64_test.c b/test/core/security/base64_test.c
index bfd5c4877728f5c363aef374d1976dea9ae50315..a922896bc3d59d2019706e623b2e050d7b4c0e03 100644
--- a/test/core/security/base64_test.c
+++ b/test/core/security/base64_test.c
@@ -58,8 +58,8 @@ static void test_simple_encode_decode_b64(int url_safe, int multiline) {
       grpc_base64_encode(hello, strlen(hello), url_safe, multiline);
   gpr_slice hello_slice = grpc_base64_decode(hello_b64, url_safe);
   GPR_ASSERT(GPR_SLICE_LENGTH(hello_slice) == strlen(hello));
-  GPR_ASSERT(!strncmp((const char *)GPR_SLICE_START_PTR(hello_slice), hello,
-                      GPR_SLICE_LENGTH(hello_slice)));
+  GPR_ASSERT(strncmp((const char *)GPR_SLICE_START_PTR(hello_slice), hello,
+                     GPR_SLICE_LENGTH(hello_slice)) == 0);
 
   gpr_slice_unref(hello_slice);
   gpr_free(hello_b64);
@@ -141,31 +141,31 @@ static void test_rfc4648_test_vectors(void) {
   char *b64;
 
   b64 = grpc_base64_encode("", 0, 0, 0);
-  GPR_ASSERT(!strcmp("", b64));
+  GPR_ASSERT(strcmp("", b64) == 0);
   gpr_free(b64);
 
   b64 = grpc_base64_encode("f", 1, 0, 0);
-  GPR_ASSERT(!strcmp("Zg==", b64));
+  GPR_ASSERT(strcmp("Zg==", b64) == 0);
   gpr_free(b64);
 
   b64 = grpc_base64_encode("fo", 2, 0, 0);
-  GPR_ASSERT(!strcmp("Zm8=", b64));
+  GPR_ASSERT(strcmp("Zm8=", b64) == 0);
   gpr_free(b64);
 
   b64 = grpc_base64_encode("foo", 3, 0, 0);
-  GPR_ASSERT(!strcmp("Zm9v", b64));
+  GPR_ASSERT(strcmp("Zm9v", b64) == 0);
   gpr_free(b64);
 
   b64 = grpc_base64_encode("foob", 4, 0, 0);
-  GPR_ASSERT(!strcmp("Zm9vYg==", b64));
+  GPR_ASSERT(strcmp("Zm9vYg==", b64) == 0);
   gpr_free(b64);
 
   b64 = grpc_base64_encode("fooba", 5, 0, 0);
-  GPR_ASSERT(!strcmp("Zm9vYmE=", b64));
+  GPR_ASSERT(strcmp("Zm9vYmE=", b64) == 0);
   gpr_free(b64);
 
   b64 = grpc_base64_encode("foobar", 6, 0, 0);
-  GPR_ASSERT(!strcmp("Zm9vYmFy", b64));
+  GPR_ASSERT(strcmp("Zm9vYmFy", b64) == 0);
   gpr_free(b64);
 }
 
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
index 50ef2d7657e3ee2a907e49e97dd0744ed2dfe40a..078462410ad6df115e3efc064e763561f7c3f818 100644
--- a/test/core/security/credentials_test.c
+++ b/test/core/security/credentials_test.c
@@ -143,9 +143,10 @@ static void test_oauth2_token_fetcher_creds_parsing_ok(void) {
              GRPC_CREDENTIALS_OK);
   GPR_ASSERT(token_lifetime.tv_sec == 3599);
   GPR_ASSERT(token_lifetime.tv_nsec == 0);
-  GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(token_elem->key), "Authorization"));
-  GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(token_elem->value),
-                     "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"));
+  GPR_ASSERT(strcmp(grpc_mdstr_as_c_string(token_elem->key),
+                    "Authorization") == 0);
+  GPR_ASSERT(strcmp(grpc_mdstr_as_c_string(token_elem->value),
+                    "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_") == 0);
   grpc_mdelem_unref(token_elem);
   grpc_mdctx_unref(ctx);
 }
@@ -294,15 +295,16 @@ static void test_ssl_oauth2_composite_creds(void) {
       grpc_composite_credentials_create(ssl_creds, oauth2_creds);
   grpc_credentials_unref(ssl_creds);
   grpc_credentials_unref(oauth2_creds);
-  GPR_ASSERT(!strcmp(composite_creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
+  GPR_ASSERT(strcmp(composite_creds->type,
+                    GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0);
   GPR_ASSERT(grpc_credentials_has_request_metadata(composite_creds));
   GPR_ASSERT(!grpc_credentials_has_request_metadata_only(composite_creds));
   creds_array = grpc_composite_credentials_get_credentials(composite_creds);
   GPR_ASSERT(creds_array->num_creds == 2);
-  GPR_ASSERT(
-      !strcmp(creds_array->creds_array[0]->type, GRPC_CREDENTIALS_TYPE_SSL));
-  GPR_ASSERT(
-      !strcmp(creds_array->creds_array[1]->type, GRPC_CREDENTIALS_TYPE_OAUTH2));
+  GPR_ASSERT(strcmp(creds_array->creds_array[0]->type,
+                    GRPC_CREDENTIALS_TYPE_SSL) == 0);
+  GPR_ASSERT(strcmp(creds_array->creds_array[1]->type,
+                    GRPC_CREDENTIALS_TYPE_OAUTH2) == 0);
   grpc_credentials_get_request_metadata(composite_creds, test_service_url,
                                         check_ssl_oauth2_composite_metadata,
                                         composite_creds);
@@ -338,17 +340,18 @@ static void test_ssl_oauth2_iam_composite_creds(void) {
   grpc_credentials_unref(oauth2_creds);
   grpc_credentials_unref(aux_creds);
   grpc_credentials_unref(iam_creds);
-  GPR_ASSERT(!strcmp(composite_creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
+  GPR_ASSERT(strcmp(composite_creds->type,
+                    GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0);
   GPR_ASSERT(grpc_credentials_has_request_metadata(composite_creds));
   GPR_ASSERT(!grpc_credentials_has_request_metadata_only(composite_creds));
   creds_array = grpc_composite_credentials_get_credentials(composite_creds);
   GPR_ASSERT(creds_array->num_creds == 3);
-  GPR_ASSERT(
-      !strcmp(creds_array->creds_array[0]->type, GRPC_CREDENTIALS_TYPE_SSL));
-  GPR_ASSERT(
-      !strcmp(creds_array->creds_array[1]->type, GRPC_CREDENTIALS_TYPE_OAUTH2));
-  GPR_ASSERT(
-      !strcmp(creds_array->creds_array[2]->type, GRPC_CREDENTIALS_TYPE_IAM));
+  GPR_ASSERT(strcmp(creds_array->creds_array[0]->type,
+                    GRPC_CREDENTIALS_TYPE_SSL) == 0);
+  GPR_ASSERT(strcmp(creds_array->creds_array[1]->type,
+                    GRPC_CREDENTIALS_TYPE_OAUTH2) == 0);
+  GPR_ASSERT(strcmp(creds_array->creds_array[2]->type,
+                    GRPC_CREDENTIALS_TYPE_IAM) == 0);
   grpc_credentials_get_request_metadata(composite_creds, test_service_url,
                                         check_ssl_oauth2_iam_composite_metadata,
                                         composite_creds);
@@ -359,12 +362,12 @@ static void on_oauth2_creds_get_metadata_success(
     grpc_credentials_status status) {
   GPR_ASSERT(status == GRPC_CREDENTIALS_OK);
   GPR_ASSERT(num_md == 1);
-  GPR_ASSERT(
-      !strcmp(grpc_mdstr_as_c_string(md_elems[0]->key), "Authorization"));
-  GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(md_elems[0]->value),
-                     "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"));
+  GPR_ASSERT(strcmp(grpc_mdstr_as_c_string(md_elems[0]->key),
+                    "Authorization") == 0);
+  GPR_ASSERT(strcmp(grpc_mdstr_as_c_string(md_elems[0]->value),
+                    "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_") == 0);
   GPR_ASSERT(user_data != NULL);
-  GPR_ASSERT(!strcmp((const char *)user_data, test_user_data));
+  GPR_ASSERT(strcmp((const char *)user_data, test_user_data) == 0);
 }
 
 static void on_oauth2_creds_get_metadata_failure(
@@ -373,19 +376,19 @@ static void on_oauth2_creds_get_metadata_failure(
   GPR_ASSERT(status == GRPC_CREDENTIALS_ERROR);
   GPR_ASSERT(num_md == 0);
   GPR_ASSERT(user_data != NULL);
-  GPR_ASSERT(!strcmp((const char *)user_data, test_user_data));
+  GPR_ASSERT(strcmp((const char *)user_data, test_user_data) == 0);
 }
 
 static void validate_compute_engine_http_request(
     const grpc_httpcli_request *request) {
   GPR_ASSERT(!request->use_ssl);
-  GPR_ASSERT(!strcmp(request->host, "metadata"));
-  GPR_ASSERT(
-      !strcmp(request->path,
-              "/computeMetadata/v1/instance/service-accounts/default/token"));
+  GPR_ASSERT(strcmp(request->host, "metadata") == 0);
+  GPR_ASSERT(strcmp(request->path,
+             "/computeMetadata/v1/instance/service-accounts/default/token")
+             == 0);
   GPR_ASSERT(request->hdr_count == 1);
-  GPR_ASSERT(!strcmp(request->hdrs[0].key, "Metadata-Flavor"));
-  GPR_ASSERT(!strcmp(request->hdrs[0].value, "Google"));
+  GPR_ASSERT(strcmp(request->hdrs[0].key, "Metadata-Flavor") == 0);
+  GPR_ASSERT(strcmp(request->hdrs[0].value, "Google") == 0);
 }
 
 static int compute_engine_httpcli_get_success_override(
@@ -467,19 +470,19 @@ static void validate_jwt_encode_and_sign_params(
   GPR_ASSERT(json_key->private_key != NULL);
   GPR_ASSERT(RSA_check_key(json_key->private_key));
   GPR_ASSERT(json_key->type != NULL &&
-             !(strcmp(json_key->type, "service_account")));
+             strcmp(json_key->type, "service_account") == 0);
   GPR_ASSERT(json_key->private_key_id != NULL &&
-             !strcmp(json_key->private_key_id,
-                     "e6b5137873db8d2ef81e06a47289e6434ec8a165"));
+             strcmp(json_key->private_key_id,
+                    "e6b5137873db8d2ef81e06a47289e6434ec8a165") == 0);
   GPR_ASSERT(json_key->client_id != NULL &&
-             !strcmp(json_key->client_id,
-                     "777-abaslkan11hlb6nmim3bpspl31ud.apps."
-                     "googleusercontent.com"));
+             strcmp(json_key->client_id,
+                    "777-abaslkan11hlb6nmim3bpspl31ud.apps."
+                    "googleusercontent.com") == 0);
   GPR_ASSERT(json_key->client_email != NULL &&
-             !strcmp(json_key->client_email,
-                     "777-abaslkan11hlb6nmim3bpspl31ud@developer."
-                     "gserviceaccount.com"));
-  if (scope != NULL) GPR_ASSERT(!strcmp(scope, test_scope));
+             strcmp(json_key->client_email,
+                    "777-abaslkan11hlb6nmim3bpspl31ud@developer."
+                    "gserviceaccount.com") == 0);
+  if (scope != NULL) GPR_ASSERT(strcmp(scope, test_scope) == 0);
   GPR_ASSERT(!gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime));
 }
 
@@ -517,12 +520,12 @@ static void validate_service_account_http_request(
   GPR_ASSERT(!memcmp(expected_body, body, body_size));
   gpr_free(expected_body);
   GPR_ASSERT(request->use_ssl);
-  GPR_ASSERT(!strcmp(request->host, "www.googleapis.com"));
-  GPR_ASSERT(!strcmp(request->path, "/oauth2/v3/token"));
+  GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
+  GPR_ASSERT(strcmp(request->path, "/oauth2/v3/token") == 0);
   GPR_ASSERT(request->hdr_count == 1);
-  GPR_ASSERT(!strcmp(request->hdrs[0].key, "Content-Type"));
-  GPR_ASSERT(
-      !strcmp(request->hdrs[0].value, "application/x-www-form-urlencoded"));
+  GPR_ASSERT(strcmp(request->hdrs[0].key, "Content-Type") == 0);
+  GPR_ASSERT(strcmp(request->hdrs[0].value,
+                    "application/x-www-form-urlencoded") == 0);
 }
 
 static int service_account_httpcli_post_success(
@@ -626,12 +629,12 @@ static void on_jwt_creds_get_metadata_success(void *user_data,
   gpr_asprintf(&expected_md_value, "Bearer %s", test_signed_jwt);
   GPR_ASSERT(status == GRPC_CREDENTIALS_OK);
   GPR_ASSERT(num_md == 1);
-  GPR_ASSERT(
-      !strcmp(grpc_mdstr_as_c_string(md_elems[0]->key), "Authorization"));
-  GPR_ASSERT(
-      !strcmp(grpc_mdstr_as_c_string(md_elems[0]->value), expected_md_value));
+  GPR_ASSERT(strcmp(grpc_mdstr_as_c_string(md_elems[0]->key),
+                    "Authorization") == 0);
+  GPR_ASSERT(strcmp(grpc_mdstr_as_c_string(md_elems[0]->value),
+                    expected_md_value) == 0);
   GPR_ASSERT(user_data != NULL);
-  GPR_ASSERT(!strcmp((const char *)user_data, test_user_data));
+  GPR_ASSERT(strcmp((const char *)user_data, test_user_data) == 0);
   gpr_free(expected_md_value);
 }
 
@@ -642,7 +645,7 @@ static void on_jwt_creds_get_metadata_failure(void *user_data,
   GPR_ASSERT(status == GRPC_CREDENTIALS_ERROR);
   GPR_ASSERT(num_md == 0);
   GPR_ASSERT(user_data != NULL);
-  GPR_ASSERT(!strcmp((const char *)user_data, test_user_data));
+  GPR_ASSERT(strcmp((const char *)user_data, test_user_data) == 0);
 }
 
 static void test_jwt_creds_success(void) {
diff --git a/test/core/security/json_token_test.c b/test/core/security/json_token_test.c
index fae911721adf8b6063ab13bc1441084628d7abf3..ca5b8891023e7e216e5aea9d5eccbd5ccdb68dda 100644
--- a/test/core/security/json_token_test.c
+++ b/test/core/security/json_token_test.c
@@ -102,18 +102,18 @@ static void test_parse_json_key_success(void) {
       grpc_auth_json_key_create_from_string(json_string);
   GPR_ASSERT(grpc_auth_json_key_is_valid(&json_key));
   GPR_ASSERT(json_key.type != NULL &&
-             !(strcmp(json_key.type, "service_account")));
+             strcmp(json_key.type, "service_account") == 0);
   GPR_ASSERT(json_key.private_key_id != NULL &&
-             !strcmp(json_key.private_key_id,
-                     "e6b5137873db8d2ef81e06a47289e6434ec8a165"));
+             strcmp(json_key.private_key_id,
+                    "e6b5137873db8d2ef81e06a47289e6434ec8a165") == 0);
   GPR_ASSERT(json_key.client_id != NULL &&
-             !strcmp(json_key.client_id,
-                     "777-abaslkan11hlb6nmim3bpspl31ud.apps."
-                     "googleusercontent.com"));
+             strcmp(json_key.client_id,
+                    "777-abaslkan11hlb6nmim3bpspl31ud.apps."
+                    "googleusercontent.com") == 0);
   GPR_ASSERT(json_key.client_email != NULL &&
-             !strcmp(json_key.client_email,
-                     "777-abaslkan11hlb6nmim3bpspl31ud@developer."
-                     "gserviceaccount.com"));
+             strcmp(json_key.client_email,
+                    "777-abaslkan11hlb6nmim3bpspl31ud@developer."
+                    "gserviceaccount.com") == 0);
   GPR_ASSERT(json_key.private_key != NULL);
   gpr_free(json_string);
   grpc_auth_json_key_destruct(&json_key);
@@ -248,15 +248,16 @@ static void check_jwt_header(grpc_json *header) {
   }
   GPR_ASSERT(alg != NULL);
   GPR_ASSERT(alg->type == GRPC_JSON_STRING);
-  GPR_ASSERT(!strcmp(alg->value, "RS256"));
+  GPR_ASSERT(strcmp(alg->value, "RS256") == 0);
 
   GPR_ASSERT(typ != NULL);
   GPR_ASSERT(typ->type == GRPC_JSON_STRING);
-  GPR_ASSERT(!strcmp(typ->value, "JWT"));
+  GPR_ASSERT(strcmp(typ->value, "JWT") == 0);
 
   GPR_ASSERT(kid != NULL);
   GPR_ASSERT(kid->type == GRPC_JSON_STRING);
-  GPR_ASSERT(!strcmp(kid->value, "e6b5137873db8d2ef81e06a47289e6434ec8a165"));
+  GPR_ASSERT(strcmp(kid->value,
+                    "e6b5137873db8d2ef81e06a47289e6434ec8a165") == 0);
 }
 
 static void check_jwt_claim(grpc_json *claim, const char *expected_audience,
@@ -290,27 +291,26 @@ static void check_jwt_claim(grpc_json *claim, const char *expected_audience,
 
   GPR_ASSERT(iss != NULL);
   GPR_ASSERT(iss->type == GRPC_JSON_STRING);
-  GPR_ASSERT(
-      !strcmp(
-          iss->value,
-          "777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount.com"));
+  GPR_ASSERT(strcmp(iss->value,
+          "777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount.com")
+             ==0);
 
   if (expected_scope != NULL) {
     GPR_ASSERT(scope != NULL);
     GPR_ASSERT(sub == NULL);
     GPR_ASSERT(scope->type == GRPC_JSON_STRING);
-    GPR_ASSERT(!strcmp(scope->value, expected_scope));
+    GPR_ASSERT(strcmp(scope->value, expected_scope) == 0);
   } else {
     /* Claims without scope must have a sub. */
     GPR_ASSERT(scope == NULL);
     GPR_ASSERT(sub != NULL);
     GPR_ASSERT(sub->type == GRPC_JSON_STRING);
-    GPR_ASSERT(!strcmp(iss->value, sub->value));
+    GPR_ASSERT(strcmp(iss->value, sub->value) == 0);
   }
 
   GPR_ASSERT(aud != NULL);
   GPR_ASSERT(aud->type == GRPC_JSON_STRING);
-  GPR_ASSERT(!strcmp(aud->value, expected_audience));
+  GPR_ASSERT(strcmp(aud->value, expected_audience) == 0);
 
   GPR_ASSERT(exp != NULL);
   GPR_ASSERT(exp->type == GRPC_JSON_NUMBER);
diff --git a/test/core/support/env_test.c b/test/core/support/env_test.c
index 1f16af87a5a6973ec01e58965404d5e9973b0596..aedf9313f3222f28e5ac7bc855ae1deba18cfa43 100644
--- a/test/core/support/env_test.c
+++ b/test/core/support/env_test.c
@@ -53,7 +53,7 @@ static void test_setenv_getenv(void) {
   gpr_setenv(name, value);
   retrieved_value = gpr_getenv(name);
   GPR_ASSERT(retrieved_value != NULL);
-  GPR_ASSERT(!strcmp(value, retrieved_value));
+  GPR_ASSERT(strcmp(value, retrieved_value) == 0);
   gpr_free(retrieved_value);
 }
 
diff --git a/test/core/support/slice_buffer_test.c b/test/core/support/slice_buffer_test.c
index 8301795dbfda7b275785c8a71e8f9f4c728b73d5..a48278434f1be3e0a81ebbb403a3c807c8f0b37a 100644
--- a/test/core/support/slice_buffer_test.c
+++ b/test/core/support/slice_buffer_test.c
@@ -62,8 +62,13 @@ int main(int argc, char **argv) {
   }
   GPR_ASSERT(buf.count > 0);
   GPR_ASSERT(buf.length == 50);
-  gpr_slice_unref(aaa);
-  gpr_slice_unref(bb);
+  for (i = 0; i < 10; i++) {
+    gpr_slice_buffer_pop(&buf);
+    gpr_slice_unref(aaa);
+    gpr_slice_unref(bb);
+  }
+  GPR_ASSERT(buf.count == 0);
+  GPR_ASSERT(buf.length == 0);
   gpr_slice_buffer_destroy(&buf);
 
   return 0;
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index 3653c5a1b0d48ba2a9298422fec8ade3e096a98e..f0420896eeb2754c7c89e2a1c1fdbc774ee8f790 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/surface/lame_client.h"
+#include <grpc/grpc.h>
 
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/util/test_config.h"
diff --git a/test/core/util/grpc_profiler.c b/test/core/util/grpc_profiler.c
index 35b9361c70c981bba4b03a107c079e76310ed78c..d5b6cfeef10da8408dc6c8d06c5dbe9629c28d8d 100644
--- a/test/core/util/grpc_profiler.c
+++ b/test/core/util/grpc_profiler.c
@@ -44,7 +44,7 @@ void grpc_profiler_stop() { ProfilerStop(); }
 
 void grpc_profiler_start(const char *filename) {
   gpr_log(GPR_DEBUG,
-          "You do not have google-perftools installed, profiling is disabled");
+          "You do not have google-perftools installed, profiling is disabled [for %s]", filename);
   gpr_log(GPR_DEBUG,
           "To install on ubuntu: sudo apt-get install google-perftools "
           "libgoogle-perftools-dev");
diff --git a/test/cpp/client/credentials_test.cc b/test/cpp/client/credentials_test.cc
index dc8d76d7eff5a920e4564120b982d79e9dafe21b..59ca33cc2979f33eb88a676ced1c305c99ba6c1e 100644
--- a/test/cpp/client/credentials_test.cc
+++ b/test/cpp/client/credentials_test.cc
@@ -47,8 +47,7 @@ class CredentialsTest : public ::testing::Test {
 
 TEST_F(CredentialsTest, InvalidServiceAccountCreds) {
   std::unique_ptr<Credentials> bad1 =
-      CredentialsFactory::ServiceAccountCredentials("", "",
-                                                    std::chrono::seconds(1));
+      ServiceAccountCredentials("", "", std::chrono::seconds(1));
   EXPECT_EQ(nullptr, bad1.get());
 }
 
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index 331a5ef8a02238692800fdb7075ea79121a5ccbb..70df9e14b21941206fa2cf92be65cf565c73e6d3 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -47,6 +47,7 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc++/server_credentials.h>
 #include <grpc++/status.h>
 #include <grpc++/stream.h>
 #include "test/core/util/port.h"
@@ -84,7 +85,7 @@ class AsyncEnd2endTest : public ::testing::Test {
     server_address_ << "localhost:" << port;
     // Setup server
     ServerBuilder builder;
-    builder.AddPort(server_address_.str());
+    builder.AddPort(server_address_.str(), grpc::InsecureServerCredentials());
     builder.RegisterAsyncService(&service_);
     server_ = builder.BuildAndStart();
   }
@@ -102,8 +103,8 @@ class AsyncEnd2endTest : public ::testing::Test {
   }
 
   void ResetStub() {
-    std::shared_ptr<ChannelInterface> channel =
-        CreateChannelDeprecated(server_address_.str(), ChannelArguments());
+    std::shared_ptr<ChannelInterface> channel = CreateChannel(
+        server_address_.str(), InsecureCredentials(), ChannelArguments());
     stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel));
   }
 
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 1d5dfc4e34263c25a18c1476586717aa4b49f202..c586849349cbfa7237f5a1d373c0bb3eb5b74e16 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -47,6 +47,7 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc++/server_credentials.h>
 #include <grpc++/status.h>
 #include <grpc++/stream.h>
 #include "test/core/util/port.h"
@@ -150,7 +151,7 @@ class End2endTest : public ::testing::Test {
     server_address_ << "localhost:" << port;
     // Setup server
     ServerBuilder builder;
-    builder.AddPort(server_address_.str());
+    builder.AddPort(server_address_.str(), InsecureServerCredentials());
     builder.RegisterService(&service_);
     builder.RegisterService(&dup_pkg_service_);
     builder.SetThreadPool(&thread_pool_);
@@ -160,8 +161,8 @@ class End2endTest : public ::testing::Test {
   void TearDown() GRPC_OVERRIDE { server_->Shutdown(); }
 
   void ResetStub() {
-    std::shared_ptr<ChannelInterface> channel =
-        CreateChannelDeprecated(server_address_.str(), ChannelArguments());
+    std::shared_ptr<ChannelInterface> channel = CreateChannel(
+        server_address_.str(), InsecureCredentials(), ChannelArguments());
     stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel));
   }
 
@@ -371,8 +372,8 @@ TEST_F(End2endTest, BidiStream) {
 // Talk to the two services with the same name but different package names.
 // The two stubs are created on the same channel.
 TEST_F(End2endTest, DiffPackageServices) {
-  std::shared_ptr<ChannelInterface> channel =
-      CreateChannelDeprecated(server_address_.str(), ChannelArguments());
+  std::shared_ptr<ChannelInterface> channel = CreateChannel(
+      server_address_.str(), InsecureCredentials(), ChannelArguments());
 
   EchoRequest request;
   EchoResponse response;
@@ -397,8 +398,7 @@ TEST_F(End2endTest, DiffPackageServices) {
 // rpc and stream should fail on bad credentials.
 TEST_F(End2endTest, BadCredentials) {
   std::unique_ptr<Credentials> bad_creds =
-      CredentialsFactory::ServiceAccountCredentials("", "",
-                                                    std::chrono::seconds(1));
+      ServiceAccountCredentials("", "", std::chrono::seconds(1));
   EXPECT_EQ(nullptr, bad_creds.get());
   std::shared_ptr<ChannelInterface> channel =
       CreateChannel(server_address_.str(), bad_creds, ChannelArguments());
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index f7537c2d7b2ebaa53715364dfa21a797baaa1dc2..ae68f7a55649b79be08f46123f690c0058a98c8e 100644
--- a/test/cpp/interop/client.cc
+++ b/test/cpp/interop/client.cc
@@ -82,9 +82,10 @@ 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::CredentialsFactory;
+using grpc::ServiceAccountCredentials;
 using grpc::testing::ResponseParameters;
 using grpc::testing::SimpleRequest;
 using grpc::testing::SimpleResponse;
@@ -96,8 +97,8 @@ using grpc::testing::TestService;
 
 // 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 { }
+namespace google {}
+namespace gflags {}
 using namespace google;
 using namespace gflags;
 
@@ -135,14 +136,14 @@ std::shared_ptr<ChannelInterface> CreateChannelForTestCase(
     std::unique_ptr<Credentials> creds;
     GPR_ASSERT(FLAGS_enable_ssl);
     grpc::string json_key = GetServiceAccountJsonKey();
-    creds = CredentialsFactory::ServiceAccountCredentials(
-        json_key, FLAGS_oauth_scope, std::chrono::hours(1));
+    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 = CredentialsFactory::ComputeEngineCredentials();
+    creds = ComputeEngineCredentials();
     return CreateTestChannel(host_port, FLAGS_server_host_override,
                              FLAGS_enable_ssl, FLAGS_use_prod_roots, creds);
   } else {
@@ -202,7 +203,7 @@ void DoComputeEngineCreds() {
   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();
+  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.");
 }
@@ -221,7 +222,7 @@ void DoServiceAccountCreds() {
   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();
+  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.");
 }
diff --git a/test/cpp/interop/server.cc b/test/cpp/interop/server.cc
index 9810ff6622b2bb920ed09c1a98b2937c9b929ed7..743482e967103d291f151f391179ccab735152f3 100644
--- a/test/cpp/interop/server.cc
+++ b/test/cpp/interop/server.cc
@@ -60,7 +60,6 @@ using grpc::Server;
 using grpc::ServerBuilder;
 using grpc::ServerContext;
 using grpc::ServerCredentials;
-using grpc::ServerCredentialsFactory;
 using grpc::ServerReader;
 using grpc::ServerReaderWriter;
 using grpc::ServerWriter;
@@ -78,8 +77,8 @@ using grpc::Status;
 
 // 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 { }
+namespace google {}
+namespace gflags {}
 using namespace google;
 using namespace gflags;
 
@@ -211,15 +210,14 @@ void RunServer() {
   SimpleResponse response;
 
   ServerBuilder builder;
-  builder.AddPort(server_address.str());
   builder.RegisterService(&service);
+  std::shared_ptr<ServerCredentials> creds = grpc::InsecureServerCredentials();
   if (FLAGS_enable_ssl) {
     SslServerCredentialsOptions ssl_opts = {
         "", {{test_server1_key, test_server1_cert}}};
-    std::shared_ptr<ServerCredentials> creds =
-        ServerCredentialsFactory::SslCredentials(ssl_opts);
-    builder.SetCredentials(creds);
+    creds = grpc::SslServerCredentials(ssl_opts);
   }
+  builder.AddPort(server_address.str(), creds);
   std::unique_ptr<Server> server(builder.BuildAndStart());
   gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());
   while (!got_sigint) {
diff --git a/test/cpp/qps/client.cc b/test/cpp/qps/client.cc
deleted file mode 100644
index 11c39eb4f52619a9695c1dedc391cf196b8219fa..0000000000000000000000000000000000000000
--- a/test/cpp/qps/client.cc
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- *
- * 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 <cassert>
-#include <memory>
-#include <string>
-#include <thread>
-#include <vector>
-#include <sstream>
-
-#include <grpc/grpc.h>
-#include <grpc/support/histogram.h>
-#include <grpc/support/log.h>
-#include <gflags/gflags.h>
-#include <grpc++/client_context.h>
-#include <grpc++/status.h>
-#include "test/core/util/grpc_profiler.h"
-#include "test/cpp/util/create_test_channel.h"
-#include "test/cpp/qps/qpstest.pb.h"
-
-DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
-DEFINE_int32(server_port, 0, "Server port.");
-DEFINE_string(server_host, "127.0.0.1", "Server host.");
-DEFINE_int32(client_threads, 4, "Number of client threads.");
-
-// We have a configurable number of channels for sending RPCs.
-// RPCs are sent round-robin on the available channels by the
-// various threads. Interesting cases are 1 global channel or
-// 1 per-thread channel, but we can support any number.
-// The channels are assigned round-robin on an RPC by RPC basis
-// rather than just at initialization time in order to also measure the
-// impact of cache thrashing caused by channel changes. This is an issue
-// if you are not in one of the above "interesting cases"
-DEFINE_int32(client_channels, 4, "Number of client channels.");
-
-DEFINE_int32(num_rpcs, 1000, "Number of RPCs per thread.");
-DEFINE_int32(payload_size, 1, "Payload size in bytes");
-
-// Alternatively, specify parameters for test as a workload so that multiple
-// tests are initiated back-to-back. This is convenient for keeping a borg
-// allocation consistent. This is a space-separated list of
-// [threads channels num_rpcs payload_size ]*
-DEFINE_string(workload, "", "Workload parameters");
-
-using grpc::ChannelInterface;
-using grpc::CreateTestChannel;
-using grpc::testing::ServerStats;
-using grpc::testing::SimpleRequest;
-using grpc::testing::SimpleResponse;
-using grpc::testing::StatsRequest;
-using grpc::testing::TestService;
-
-// 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;
-
-static double now() {
-  gpr_timespec tv = gpr_now();
-  return 1e9 * tv.tv_sec + tv.tv_nsec;
-}
-
-void RunTest(const int client_threads, const int client_channels,
-             const int num_rpcs, const int payload_size) {
-  gpr_log(GPR_INFO,
-          "QPS test with parameters\n"
-          "enable_ssl = %d\n"
-          "client_channels = %d\n"
-          "client_threads = %d\n"
-          "num_rpcs = %d\n"
-          "payload_size = %d\n"
-          "server_host:server_port = %s:%d\n\n",
-          FLAGS_enable_ssl, client_channels, client_threads, num_rpcs,
-          payload_size, FLAGS_server_host.c_str(), FLAGS_server_port);
-
-  std::ostringstream oss;
-  oss << FLAGS_server_host << ":" << FLAGS_server_port;
-
-  class ClientChannelInfo {
-   public:
-    explicit ClientChannelInfo(const grpc::string &server)
-        : channel_(CreateTestChannel(server, FLAGS_enable_ssl)),
-          stub_(TestService::NewStub(channel_)) {}
-    ChannelInterface *get_channel() { return channel_.get(); }
-    TestService::Stub *get_stub() { return stub_.get(); }
-
-   private:
-    std::shared_ptr<ChannelInterface> channel_;
-    std::unique_ptr<TestService::Stub> stub_;
-  };
-
-  std::vector<ClientChannelInfo> channels;
-  for (int i = 0; i < client_channels; i++) {
-    channels.push_back(ClientChannelInfo(oss.str()));
-  }
-
-  std::vector<std::thread> threads;  // Will add threads when ready to execute
-  std::vector< ::gpr_histogram *> thread_stats(client_threads);
-
-  TestService::Stub *stub_stats = channels[0].get_stub();
-  grpc::ClientContext context_stats_begin;
-  StatsRequest stats_request;
-  ServerStats server_stats_begin;
-  stats_request.set_test_num(0);
-  grpc::Status status_beg = stub_stats->CollectServerStats(
-      &context_stats_begin, stats_request, &server_stats_begin);
-
-  grpc_profiler_start("qps_client.prof");
-
-  for (int i = 0; i < client_threads; i++) {
-    gpr_histogram *hist = gpr_histogram_create(0.01, 60e9);
-    GPR_ASSERT(hist != NULL);
-    thread_stats[i] = hist;
-
-    threads.push_back(
-        std::thread([hist, client_threads, client_channels, num_rpcs,
-                     payload_size, &channels](int channel_num) {
-                      SimpleRequest request;
-                      SimpleResponse response;
-                      request.set_response_type(
-                          grpc::testing::PayloadType::COMPRESSABLE);
-                      request.set_response_size(payload_size);
-
-                      for (int j = 0; j < num_rpcs; j++) {
-                        TestService::Stub *stub =
-                            channels[channel_num].get_stub();
-                        double start = now();
-                        grpc::ClientContext context;
-                        grpc::Status s =
-                            stub->UnaryCall(&context, request, &response);
-                        gpr_histogram_add(hist, now() - start);
-
-                        GPR_ASSERT((s.code() == grpc::StatusCode::OK) &&
-                                   (response.payload().type() ==
-                                    grpc::testing::PayloadType::COMPRESSABLE) &&
-                                   (response.payload().body().length() ==
-                                    static_cast<size_t>(payload_size)));
-
-                        // Now do runtime round-robin assignment of the next
-                        // channel number
-                        channel_num += client_threads;
-                        channel_num %= client_channels;
-                      }
-                    },
-                    i % client_channels));
-  }
-
-  gpr_histogram *hist = gpr_histogram_create(0.01, 60e9);
-  GPR_ASSERT(hist != NULL);
-  for (auto &t : threads) {
-    t.join();
-  }
-
-  grpc_profiler_stop();
-
-  for (int i = 0; i < client_threads; i++) {
-    gpr_histogram *h = thread_stats[i];
-    gpr_log(GPR_INFO, "latency at thread %d (50/90/95/99/99.9): %f/%f/%f/%f/%f",
-            i, gpr_histogram_percentile(h, 50), gpr_histogram_percentile(h, 90),
-            gpr_histogram_percentile(h, 95), gpr_histogram_percentile(h, 99),
-            gpr_histogram_percentile(h, 99.9));
-    gpr_histogram_merge(hist, h);
-    gpr_histogram_destroy(h);
-  }
-
-  gpr_log(
-      GPR_INFO,
-      "latency across %d threads with %d channels and %d payload "
-      "(50/90/95/99/99.9): %f / %f / %f / %f / %f",
-      client_threads, client_channels, payload_size,
-      gpr_histogram_percentile(hist, 50), gpr_histogram_percentile(hist, 90),
-      gpr_histogram_percentile(hist, 95), gpr_histogram_percentile(hist, 99),
-      gpr_histogram_percentile(hist, 99.9));
-  gpr_histogram_destroy(hist);
-
-  grpc::ClientContext context_stats_end;
-  ServerStats server_stats_end;
-  grpc::Status status_end = stub_stats->CollectServerStats(
-      &context_stats_end, stats_request, &server_stats_end);
-
-  double elapsed = server_stats_end.time_now() - server_stats_begin.time_now();
-  int total_rpcs = client_threads * num_rpcs;
-  double utime = server_stats_end.time_user() - server_stats_begin.time_user();
-  double stime =
-      server_stats_end.time_system() - server_stats_begin.time_system();
-  gpr_log(GPR_INFO,
-          "Elapsed time: %.3f\n"
-          "RPC Count: %d\n"
-          "QPS: %.3f\n"
-          "System time: %.3f\n"
-          "User time: %.3f\n"
-          "Resource usage: %.1f%%\n",
-          elapsed, total_rpcs, total_rpcs / elapsed, stime, utime,
-          (stime + utime) / elapsed * 100.0);
-}
-
-int main(int argc, char **argv) {
-  grpc_init();
-  ParseCommandLineFlags(&argc, &argv, true);
-
-  GPR_ASSERT(FLAGS_server_port);
-
-  if (FLAGS_workload.length() == 0) {
-    RunTest(FLAGS_client_threads, FLAGS_client_channels, FLAGS_num_rpcs,
-            FLAGS_payload_size);
-  } else {
-    std::istringstream workload(FLAGS_workload);
-    int client_threads, client_channels, num_rpcs, payload_size;
-    workload >> client_threads;
-    while (!workload.eof()) {
-      workload >> client_channels >> num_rpcs >> payload_size;
-      RunTest(client_threads, client_channels, num_rpcs, payload_size);
-      workload >> client_threads;
-    }
-    gpr_log(GPR_INFO, "Done with specified workload.");
-  }
-
-  grpc_shutdown();
-  return 0;
-}
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
new file mode 100644
index 0000000000000000000000000000000000000000..221fb30fc59deac0a8c664baa99160fbe00f1c00
--- /dev/null
+++ b/test/cpp/qps/client.h
@@ -0,0 +1,173 @@
+/*
+ *
+ * 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 TEST_QPS_CLIENT_H
+#define TEST_QPS_CLIENT_H
+
+#include "test/cpp/qps/histogram.h"
+#include "test/cpp/qps/timer.h"
+#include "test/cpp/qps/qpstest.pb.h"
+
+#include <condition_variable>
+#include <mutex>
+
+namespace grpc {
+namespace testing {
+
+class Client {
+ public:
+  explicit Client(const ClientConfig& config) : timer_(new Timer) {
+    for (int i = 0; i < config.client_channels(); i++) {
+      channels_.push_back(ClientChannelInfo(
+          config.server_targets(i % config.server_targets_size()), config));
+    }
+    request_.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
+    request_.set_response_size(config.payload_size());
+  }
+  virtual ~Client() {}
+
+  ClientStats Mark() {
+    Histogram latencies;
+    std::vector<Histogram> to_merge(threads_.size());
+    for (size_t i = 0; i < threads_.size(); i++) {
+      threads_[i]->BeginSwap(&to_merge[i]);
+    }
+    std::unique_ptr<Timer> timer(new Timer);
+    timer_.swap(timer);
+    for (size_t i = 0; i < threads_.size(); i++) {
+      threads_[i]->EndSwap();
+      latencies.Merge(&to_merge[i]);
+    }
+
+    auto timer_result = timer->Mark();
+
+    ClientStats stats;
+    latencies.FillProto(stats.mutable_latencies());
+    stats.set_time_elapsed(timer_result.wall);
+    stats.set_time_system(timer_result.system);
+    stats.set_time_user(timer_result.user);
+    return stats;
+  }
+
+ protected:
+  SimpleRequest request_;
+
+  class ClientChannelInfo {
+   public:
+    ClientChannelInfo(const grpc::string& target, const ClientConfig& config)
+        : channel_(CreateTestChannel(target, config.enable_ssl())),
+          stub_(TestService::NewStub(channel_)) {}
+    ChannelInterface* get_channel() { return channel_.get(); }
+    TestService::Stub* get_stub() { return stub_.get(); }
+
+   private:
+    std::shared_ptr<ChannelInterface> channel_;
+    std::unique_ptr<TestService::Stub> stub_;
+  };
+  std::vector<ClientChannelInfo> channels_;
+
+  void StartThreads(size_t num_threads) {
+    for (size_t i = 0; i < num_threads; i++) {
+      threads_.emplace_back(new Thread(this, i));
+    }
+  }
+
+  void EndThreads() { threads_.clear(); }
+
+  virtual void ThreadFunc(Histogram* histogram, size_t thread_idx) = 0;
+
+ private:
+  class Thread {
+   public:
+    Thread(Client* client, size_t idx)
+        : done_(false),
+          new_(nullptr),
+          impl_([this, idx, client]() {
+            for (;;) {
+              // run the loop body
+              client->ThreadFunc(&histogram_, idx);
+              // lock, see if we're done
+              std::lock_guard<std::mutex> g(mu_);
+              if (done_) return;
+              // also check if we're marking, and swap out the histogram if so
+              if (new_) {
+                new_->Swap(&histogram_);
+                new_ = nullptr;
+                cv_.notify_one();
+              }
+            }
+          }) {}
+
+    ~Thread() {
+      {
+        std::lock_guard<std::mutex> g(mu_);
+        done_ = true;
+      }
+      impl_.join();
+    }
+
+    void BeginSwap(Histogram* n) {
+      std::lock_guard<std::mutex> g(mu_);
+      new_ = n;
+    }
+
+    void EndSwap() {
+      std::unique_lock<std::mutex> g(mu_);
+      cv_.wait(g, [this]() { return new_ == nullptr; });
+    }
+
+   private:
+    Thread(const Thread&);
+    Thread& operator=(const Thread&);
+
+    TestService::Stub* stub_;
+    ClientConfig config_;
+    std::mutex mu_;
+    std::condition_variable cv_;
+    bool done_;
+    Histogram* new_;
+    Histogram histogram_;
+    std::thread impl_;
+  };
+
+  std::vector<std::unique_ptr<Thread>> threads_;
+  std::unique_ptr<Timer> timer_;
+};
+
+std::unique_ptr<Client> CreateSynchronousClient(const ClientConfig& args);
+std::unique_ptr<Client> CreateAsyncClient(const ClientConfig& args);
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 9ea9cfe8b9b8a8ccbdae16f0b33cd1e5ba2905b7..c6535bebf8856cb02fb5b89270f689f123114109 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -49,86 +49,53 @@
 #include "test/core/util/grpc_profiler.h"
 #include "test/cpp/util/create_test_channel.h"
 #include "test/cpp/qps/qpstest.pb.h"
+#include "test/cpp/qps/timer.h"
+#include "test/cpp/qps/client.h"
 
-DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
-DEFINE_int32(server_port, 0, "Server port.");
-DEFINE_string(server_host, "127.0.0.1", "Server host.");
-DEFINE_int32(client_threads, 4, "Number of client threads.");
-
-// We have a configurable number of channels for sending RPCs.
-// RPCs are sent round-robin on the available channels by the
-// various threads. Interesting cases are 1 global channel or
-// 1 per-thread channel, but we can support any number.
-// The channels are assigned round-robin on an RPC by RPC basis
-// rather than just at initialization time in order to also measure the
-// impact of cache thrashing caused by channel changes. This is an issue
-// if you are not in one of the above "interesting cases"
-DEFINE_int32(client_channels, 4, "Number of client channels.");
-
-DEFINE_int32(num_rpcs, 1000, "Number of RPCs per thread.");
-DEFINE_int32(payload_size, 1, "Payload size in bytes");
-
-// Alternatively, specify parameters for test as a workload so that multiple
-// tests are initiated back-to-back. This is convenient for keeping a borg
-// allocation consistent. This is a space-separated list of
-// [threads channels num_rpcs payload_size ]*
-DEFINE_string(workload, "", "Workload parameters");
-
-using grpc::ChannelInterface;
-using grpc::CreateTestChannel;
-using grpc::testing::ServerStats;
-using grpc::testing::SimpleRequest;
-using grpc::testing::SimpleResponse;
-using grpc::testing::StatsRequest;
-using grpc::testing::TestService;
-
-// 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;
-
-static double now() {
-  gpr_timespec tv = gpr_now();
-  return 1e9 * tv.tv_sec + tv.tv_nsec;
-}
+namespace grpc {
+namespace testing {
 
 class ClientRpcContext {
  public:
   ClientRpcContext() {}
   virtual ~ClientRpcContext() {}
   virtual bool RunNextState() = 0;  // do next state, return false if steps done
+  virtual void StartNewClone() = 0;
   static void *tag(ClientRpcContext *c) { return reinterpret_cast<void *>(c); }
   static ClientRpcContext *detag(void *t) {
     return reinterpret_cast<ClientRpcContext *>(t);
   }
-  virtual void report_stats(gpr_histogram *hist) = 0;
+  virtual void report_stats(Histogram *hist) = 0;
 };
+
 template <class RequestType, class ResponseType>
 class ClientRpcContextUnaryImpl : public ClientRpcContext {
  public:
   ClientRpcContextUnaryImpl(
-      TestService::Stub *stub,
-      const RequestType &req,
+      TestService::Stub *stub, const RequestType &req,
       std::function<
           std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
-	      TestService::Stub *, grpc::ClientContext *, const RequestType &,
-	      void *)> start_req,
+              TestService::Stub *, grpc::ClientContext *, const RequestType &,
+              void *)> start_req,
       std::function<void(grpc::Status, ResponseType *)> on_done)
       : context_(),
-	stub_(stub),
+        stub_(stub),
         req_(req),
         response_(),
         next_state_(&ClientRpcContextUnaryImpl::ReqSent),
         callback_(on_done),
-        start_(now()),
+        start_req_(start_req),
+        start_(Timer::Now()),
         response_reader_(
-	    start_req(stub_, &context_, req_, ClientRpcContext::tag(this))) {}
+            start_req(stub_, &context_, req_, ClientRpcContext::tag(this))) {}
   ~ClientRpcContextUnaryImpl() GRPC_OVERRIDE {}
   bool RunNextState() GRPC_OVERRIDE { return (this->*next_state_)(); }
-  void report_stats(gpr_histogram *hist) GRPC_OVERRIDE {
-    gpr_histogram_add(hist, now() - start_);
+  void report_stats(Histogram *hist) GRPC_OVERRIDE {
+    hist->Add((Timer::Now() - start_) * 1e9);
+  }
+
+  void StartNewClone() GRPC_OVERRIDE {
+    new ClientRpcContextUnaryImpl(stub_, req_, start_req_, callback_);
   }
 
  private:
@@ -151,191 +118,84 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
   ResponseType response_;
   bool (ClientRpcContextUnaryImpl::*next_state_)();
   std::function<void(grpc::Status, ResponseType *)> callback_;
+  std::function<std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
+      TestService::Stub *, grpc::ClientContext *, const RequestType &, void *)>
+      start_req_;
   grpc::Status status_;
   double start_;
   std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>
       response_reader_;
 };
 
-static void RunTest(const int client_threads, const int client_channels,
-                    const int num_rpcs, const int payload_size) {
-  gpr_log(GPR_INFO,
-          "QPS test with parameters\n"
-          "enable_ssl = %d\n"
-          "client_channels = %d\n"
-          "client_threads = %d\n"
-          "num_rpcs = %d\n"
-          "payload_size = %d\n"
-          "server_host:server_port = %s:%d\n\n",
-          FLAGS_enable_ssl, client_channels, client_threads, num_rpcs,
-          payload_size, FLAGS_server_host.c_str(), FLAGS_server_port);
-
-  std::ostringstream oss;
-  oss << FLAGS_server_host << ":" << FLAGS_server_port;
-
-  class ClientChannelInfo {
-   public:
-    explicit ClientChannelInfo(const grpc::string &server)
-        : channel_(CreateTestChannel(server, FLAGS_enable_ssl)),
-          stub_(TestService::NewStub(channel_)) {}
-    ChannelInterface *get_channel() { return channel_.get(); }
-    TestService::Stub *get_stub() { return stub_.get(); }
+class AsyncClient GRPC_FINAL : public Client {
+ public:
+  explicit AsyncClient(const ClientConfig &config) : Client(config) {
+    for (int i = 0; i < config.async_client_threads(); i++) {
+      cli_cqs_.emplace_back(new CompletionQueue);
+    }
 
-   private:
-    std::shared_ptr<ChannelInterface> channel_;
-    std::unique_ptr<TestService::Stub> stub_;
-  };
+    auto payload_size = config.payload_size();
+    auto check_done = [payload_size](grpc::Status s, SimpleResponse *response) {
+      GPR_ASSERT(s.IsOk() && (response->payload().type() ==
+                              grpc::testing::PayloadType::COMPRESSABLE) &&
+                 (response->payload().body().length() ==
+                  static_cast<size_t>(payload_size)));
+    };
+
+    int t = 0;
+    for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) {
+      for (auto &channel : channels_) {
+        auto *cq = cli_cqs_[t].get();
+        t = (t + 1) % cli_cqs_.size();
+        auto start_req = [cq](TestService::Stub *stub, grpc::ClientContext *ctx,
+                              const SimpleRequest &request, void *tag) {
+          return stub->AsyncUnaryCall(ctx, request, cq, tag);
+        };
+
+        TestService::Stub *stub = channel.get_stub();
+        const SimpleRequest &request = request_;
+        new ClientRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(
+            stub, request, start_req, check_done);
+      }
+    }
 
-  std::vector<ClientChannelInfo> channels;
-  for (int i = 0; i < client_channels; i++) {
-    channels.push_back(ClientChannelInfo(oss.str()));
+    StartThreads(config.async_client_threads());
   }
 
-  std::vector<std::thread> threads;  // Will add threads when ready to execute
-  std::vector< ::gpr_histogram *> thread_stats(client_threads);
-
-  TestService::Stub *stub_stats = channels[0].get_stub();
-  grpc::ClientContext context_stats_begin;
-  StatsRequest stats_request;
-  ServerStats server_stats_begin;
-  stats_request.set_test_num(0);
-  grpc::Status status_beg = stub_stats->CollectServerStats(
-      &context_stats_begin, stats_request, &server_stats_begin);
-
-  grpc_profiler_start("qps_client_async.prof");
-
-  auto CheckDone = [=](grpc::Status s, SimpleResponse *response) {
-    GPR_ASSERT(s.IsOk() && (response->payload().type() ==
-                            grpc::testing::PayloadType::COMPRESSABLE) &&
-               (response->payload().body().length() ==
-                static_cast<size_t>(payload_size)));
-  };
+  ~AsyncClient() GRPC_OVERRIDE {
+    EndThreads();
 
-  for (int i = 0; i < client_threads; i++) {
-    gpr_histogram *hist = gpr_histogram_create(0.01, 60e9);
-    GPR_ASSERT(hist != NULL);
-    thread_stats[i] = hist;
-
-    threads.push_back(std::thread(
-        [hist, client_threads, client_channels, num_rpcs, payload_size,
-         &channels, &CheckDone](int channel_num) {
-          using namespace std::placeholders;
-          SimpleRequest request;
-          request.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
-          request.set_response_size(payload_size);
-
-          grpc::CompletionQueue cli_cq;
-	  auto start_req = std::bind(&TestService::Stub::AsyncUnaryCall, _1,
-				     _2, _3, &cli_cq, _4);
-
-          int rpcs_sent = 0;
-          while (rpcs_sent < num_rpcs) {
-            rpcs_sent++;
-            TestService::Stub *stub = channels[channel_num].get_stub();
-            new ClientRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(stub,
-                request, start_req, CheckDone);
-            void *got_tag;
-            bool ok;
-
-            // Need to call 2 next for every 1 RPC (1 for req done, 1 for resp
-            // done)
-            cli_cq.Next(&got_tag, &ok);
-            if (!ok) break;
-            ClientRpcContext *ctx = ClientRpcContext::detag(got_tag);
-            if (ctx->RunNextState() == false) {
-              // call the callback and then delete it
-              ctx->report_stats(hist);
-              ctx->RunNextState();
-              delete ctx;
-            }
-            cli_cq.Next(&got_tag, &ok);
-            if (!ok) break;
-            ctx = ClientRpcContext::detag(got_tag);
-            if (ctx->RunNextState() == false) {
-              // call the callback and then delete it
-              ctx->report_stats(hist);
-	      ctx->RunNextState();
-              delete ctx;
-            }
-            // Now do runtime round-robin assignment of the next
-            // channel number
-            channel_num += client_threads;
-            channel_num %= client_channels;
-          }
-        },
-        i % client_channels));
-  }
-
-  gpr_histogram *hist = gpr_histogram_create(0.01, 60e9);
-  GPR_ASSERT(hist != NULL);
-  for (auto &t : threads) {
-    t.join();
+    for (auto &cq : cli_cqs_) {
+      cq->Shutdown();
+      void *got_tag;
+      bool ok;
+      while (cq->Next(&got_tag, &ok)) {
+        delete ClientRpcContext::detag(got_tag);
+      }
+    }
   }
 
-  grpc_profiler_stop();
-
-  for (int i = 0; i < client_threads; i++) {
-    gpr_histogram *h = thread_stats[i];
-    gpr_log(GPR_INFO, "latency at thread %d (50/90/95/99/99.9): %f/%f/%f/%f/%f",
-            i, gpr_histogram_percentile(h, 50), gpr_histogram_percentile(h, 90),
-            gpr_histogram_percentile(h, 95), gpr_histogram_percentile(h, 99),
-            gpr_histogram_percentile(h, 99.9));
-    gpr_histogram_merge(hist, h);
-    gpr_histogram_destroy(h);
+  void ThreadFunc(Histogram *histogram, size_t thread_idx) GRPC_OVERRIDE {
+    void *got_tag;
+    bool ok;
+    cli_cqs_[thread_idx]->Next(&got_tag, &ok);
+
+    ClientRpcContext *ctx = ClientRpcContext::detag(got_tag);
+    if (ctx->RunNextState() == false) {
+      // call the callback and then delete it
+      ctx->report_stats(histogram);
+      ctx->RunNextState();
+      ctx->StartNewClone();
+      delete ctx;
+    }
   }
 
-  gpr_log(
-      GPR_INFO,
-      "latency across %d threads with %d channels and %d payload "
-      "(50/90/95/99/99.9): %f / %f / %f / %f / %f",
-      client_threads, client_channels, payload_size,
-      gpr_histogram_percentile(hist, 50), gpr_histogram_percentile(hist, 90),
-      gpr_histogram_percentile(hist, 95), gpr_histogram_percentile(hist, 99),
-      gpr_histogram_percentile(hist, 99.9));
-  gpr_histogram_destroy(hist);
-
-  grpc::ClientContext context_stats_end;
-  ServerStats server_stats_end;
-  grpc::Status status_end = stub_stats->CollectServerStats(
-      &context_stats_end, stats_request, &server_stats_end);
+  std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_;
+};
 
-  double elapsed = server_stats_end.time_now() - server_stats_begin.time_now();
-  int total_rpcs = client_threads * num_rpcs;
-  double utime = server_stats_end.time_user() - server_stats_begin.time_user();
-  double stime =
-      server_stats_end.time_system() - server_stats_begin.time_system();
-  gpr_log(GPR_INFO,
-          "Elapsed time: %.3f\n"
-          "RPC Count: %d\n"
-          "QPS: %.3f\n"
-          "System time: %.3f\n"
-          "User time: %.3f\n"
-          "Resource usage: %.1f%%\n",
-          elapsed, total_rpcs, total_rpcs / elapsed, stime, utime,
-          (stime + utime) / elapsed * 100.0);
+std::unique_ptr<Client> CreateAsyncClient(const ClientConfig &args) {
+  return std::unique_ptr<Client>(new AsyncClient(args));
 }
 
-int main(int argc, char **argv) {
-  grpc_init();
-  ParseCommandLineFlags(&argc, &argv, true);
-
-  GPR_ASSERT(FLAGS_server_port);
-
-  if (FLAGS_workload.length() == 0) {
-    RunTest(FLAGS_client_threads, FLAGS_client_channels, FLAGS_num_rpcs,
-            FLAGS_payload_size);
-  } else {
-    std::istringstream workload(FLAGS_workload);
-    int client_threads, client_channels, num_rpcs, payload_size;
-    workload >> client_threads;
-    while (!workload.eof()) {
-      workload >> client_channels >> num_rpcs >> payload_size;
-      RunTest(client_threads, client_channels, num_rpcs, payload_size);
-      workload >> client_threads;
-    }
-    gpr_log(GPR_INFO, "Done with specified workload.");
-  }
-
-  grpc_shutdown();
-  return 0;
-}
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7bb7231c6f6538bee9542d014a671e12c9c4fc84
--- /dev/null
+++ b/test/cpp/qps/client_sync.cc
@@ -0,0 +1,93 @@
+/*
+ *
+ * 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 <cassert>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+#include <sstream>
+
+#include <sys/signal.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/histogram.h>
+#include <grpc/support/log.h>
+#include <grpc/support/host_port.h>
+#include <gflags/gflags.h>
+#include <grpc++/client_context.h>
+#include <grpc++/status.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include "test/core/util/grpc_profiler.h"
+#include "test/cpp/util/create_test_channel.h"
+#include "test/cpp/qps/client.h"
+#include "test/cpp/qps/qpstest.pb.h"
+#include "test/cpp/qps/histogram.h"
+#include "test/cpp/qps/timer.h"
+
+namespace grpc {
+namespace testing {
+
+class SynchronousClient GRPC_FINAL : public Client {
+ public:
+  SynchronousClient(const ClientConfig& config) : Client(config) {
+    size_t num_threads =
+        config.outstanding_rpcs_per_channel() * config.client_channels();
+    responses_.resize(num_threads);
+    StartThreads(num_threads);
+  }
+
+  ~SynchronousClient() { EndThreads(); }
+
+  void ThreadFunc(Histogram* histogram, size_t thread_idx) {
+    auto* stub = channels_[thread_idx % channels_.size()].get_stub();
+    double start = Timer::Now();
+    grpc::ClientContext context;
+    grpc::Status s =
+        stub->UnaryCall(&context, request_, &responses_[thread_idx]);
+    histogram->Add((Timer::Now() - start) * 1e9);
+  }
+
+ private:
+  std::vector<SimpleResponse> responses_;
+};
+
+std::unique_ptr<Client> CreateSynchronousClient(const ClientConfig& config) {
+  return std::unique_ptr<Client>(new SynchronousClient(config));
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d29ca1de94776b60efc1b5502b9b373a78c98c25
--- /dev/null
+++ b/test/cpp/qps/driver.cc
@@ -0,0 +1,210 @@
+/*
+ *
+ * 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/qps/driver.h"
+#include "src/core/support/env.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/host_port.h>
+#include <grpc++/channel_arguments.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/stream.h>
+#include <list>
+#include <thread>
+#include <vector>
+#include "test/cpp/qps/histogram.h"
+
+using std::list;
+using std::thread;
+using std::unique_ptr;
+using std::vector;
+
+namespace grpc {
+namespace testing {
+static vector<string> get_hosts(const string& name) {
+  char* env = gpr_getenv(name.c_str());
+  if (!env) return vector<string>();
+
+  vector<string> out;
+  char* p = env;
+  for (;;) {
+    char* comma = strchr(p, ',');
+    if (comma) {
+      out.emplace_back(p, comma);
+      p = comma + 1;
+    } else {
+      out.emplace_back(p);
+      gpr_free(env);
+      return out;
+    }
+  }
+}
+
+ScenarioResult RunScenario(const ClientConfig& initial_client_config,
+                           size_t num_clients,
+                           const ServerConfig& server_config,
+                           size_t num_servers) {
+  // ClientContext allocator (all are destroyed at scope exit)
+  list<ClientContext> contexts;
+  auto alloc_context = [&contexts]() {
+    contexts.emplace_back();
+    return &contexts.back();
+  };
+
+  // Get client, server lists
+  auto workers = get_hosts("QPS_WORKERS");
+  ClientConfig client_config = initial_client_config;
+
+  // TODO(ctiller): support running multiple configurations, and binpack
+  // client/server pairs
+  // to available workers
+  GPR_ASSERT(workers.size() >= num_clients + num_servers);
+
+  // Trim to just what we need
+  workers.resize(num_clients + num_servers);
+
+  // Start servers
+  struct ServerData {
+    unique_ptr<Worker::Stub> stub;
+    unique_ptr<ClientReaderWriter<ServerArgs, ServerStatus>> stream;
+  };
+  vector<ServerData> servers;
+  for (size_t i = 0; i < num_servers; i++) {
+    ServerData sd;
+    sd.stub = std::move(Worker::NewStub(
+        CreateChannel(workers[i], InsecureCredentials(), ChannelArguments())));
+    ServerArgs args;
+    *args.mutable_setup() = server_config;
+    sd.stream = std::move(sd.stub->RunServer(alloc_context()));
+    GPR_ASSERT(sd.stream->Write(args));
+    ServerStatus init_status;
+    GPR_ASSERT(sd.stream->Read(&init_status));
+    char* host;
+    char* driver_port;
+    char* cli_target;
+    gpr_split_host_port(workers[i].c_str(), &host, &driver_port);
+    gpr_join_host_port(&cli_target, host, init_status.port());
+    client_config.add_server_targets(cli_target);
+    gpr_free(host);
+    gpr_free(driver_port);
+    gpr_free(cli_target);
+
+    servers.push_back(std::move(sd));
+  }
+
+  // Start clients
+  struct ClientData {
+    unique_ptr<Worker::Stub> stub;
+    unique_ptr<ClientReaderWriter<ClientArgs, ClientStatus>> stream;
+  };
+  vector<ClientData> clients;
+  for (size_t i = 0; i < num_clients; i++) {
+    ClientData cd;
+    cd.stub = std::move(Worker::NewStub(CreateChannel(
+        workers[i + num_servers], InsecureCredentials(), ChannelArguments())));
+    ClientArgs args;
+    *args.mutable_setup() = client_config;
+    cd.stream = std::move(cd.stub->RunTest(alloc_context()));
+    GPR_ASSERT(cd.stream->Write(args));
+    ClientStatus init_status;
+    GPR_ASSERT(cd.stream->Read(&init_status));
+
+    clients.push_back(std::move(cd));
+  }
+
+  // Let everything warmup
+  gpr_log(GPR_INFO, "Warming up");
+  gpr_timespec start = gpr_now();
+  gpr_sleep_until(gpr_time_add(start, gpr_time_from_seconds(5)));
+
+  // Start a run
+  gpr_log(GPR_INFO, "Starting");
+  ServerArgs server_mark;
+  server_mark.mutable_mark();
+  ClientArgs client_mark;
+  client_mark.mutable_mark();
+  for (auto& server : servers) {
+    GPR_ASSERT(server.stream->Write(server_mark));
+  }
+  for (auto& client : clients) {
+    GPR_ASSERT(client.stream->Write(client_mark));
+  }
+  ServerStatus server_status;
+  ClientStatus client_status;
+  for (auto& server : servers) {
+    GPR_ASSERT(server.stream->Read(&server_status));
+  }
+  for (auto& client : clients) {
+    GPR_ASSERT(client.stream->Read(&client_status));
+  }
+
+  // Wait some time
+  gpr_log(GPR_INFO, "Running");
+  gpr_sleep_until(gpr_time_add(start, gpr_time_from_seconds(15)));
+
+  // Finish a run
+  ScenarioResult result;
+  gpr_log(GPR_INFO, "Finishing");
+  for (auto& server : servers) {
+    GPR_ASSERT(server.stream->Write(server_mark));
+  }
+  for (auto& client : clients) {
+    GPR_ASSERT(client.stream->Write(client_mark));
+  }
+  for (auto& server : servers) {
+    GPR_ASSERT(server.stream->Read(&server_status));
+    const auto& stats = server_status.stats();
+    result.server_resources.push_back(ResourceUsage{
+        stats.time_elapsed(), stats.time_user(), stats.time_system()});
+  }
+  for (auto& client : clients) {
+    GPR_ASSERT(client.stream->Read(&client_status));
+    const auto& stats = client_status.stats();
+    result.latencies.MergeProto(stats.latencies());
+    result.client_resources.push_back(ResourceUsage{
+        stats.time_elapsed(), stats.time_user(), stats.time_system()});
+  }
+
+  for (auto& client : clients) {
+    GPR_ASSERT(client.stream->WritesDone());
+    GPR_ASSERT(client.stream->Finish().IsOk());
+  }
+  for (auto& server : servers) {
+    GPR_ASSERT(server.stream->WritesDone());
+    GPR_ASSERT(server.stream->Finish().IsOk());
+  }
+  return result;
+}
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/qps/driver.h b/test/cpp/qps/driver.h
new file mode 100644
index 0000000000000000000000000000000000000000..d87e80dc552a593a7b5230e34eaece33e1e19c04
--- /dev/null
+++ b/test/cpp/qps/driver.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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 TEST_QPS_DRIVER_H
+#define TEST_QPS_DRIVER_H
+
+#include "test/cpp/qps/histogram.h"
+#include "test/cpp/qps/qpstest.pb.h"
+
+namespace grpc {
+namespace testing {
+struct ResourceUsage {
+  double wall_time;
+  double user_time;
+  double system_time;
+};
+
+struct ScenarioResult {
+  Histogram latencies;
+  std::vector<ResourceUsage> client_resources;
+  std::vector<ResourceUsage> server_resources;
+};
+
+ScenarioResult RunScenario(const grpc::testing::ClientConfig& client_config,
+                           size_t num_clients,
+                           const grpc::testing::ServerConfig& server_config,
+                           size_t num_servers);
+}  // namespace testing
+}  // namespace grpc
+
+#endif
diff --git a/test/cpp/qps/histogram.h b/test/cpp/qps/histogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..7ba00e94c3947423e9f39ca721742437f9cee622
--- /dev/null
+++ b/test/cpp/qps/histogram.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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 TEST_QPS_HISTOGRAM_H
+#define TEST_QPS_HISTOGRAM_H
+
+#include <grpc/support/histogram.h>
+#include "test/cpp/qps/qpstest.pb.h"
+
+namespace grpc {
+namespace testing {
+
+class Histogram {
+ public:
+  Histogram() : impl_(gpr_histogram_create(0.01, 60e9)) {}
+  ~Histogram() {
+    if (impl_) gpr_histogram_destroy(impl_);
+  }
+  Histogram(Histogram&& other) : impl_(other.impl_) { other.impl_ = nullptr; }
+
+  void Merge(Histogram* h) { gpr_histogram_merge(impl_, h->impl_); }
+  void Add(double value) { gpr_histogram_add(impl_, value); }
+  double Percentile(double pctile) {
+    return gpr_histogram_percentile(impl_, pctile);
+  }
+  double Count() { return gpr_histogram_count(impl_); }
+  void Swap(Histogram* other) { std::swap(impl_, other->impl_); }
+  void FillProto(HistogramData* p) {
+    size_t n;
+    const auto* data = gpr_histogram_get_contents(impl_, &n);
+    for (size_t i = 0; i < n; i++) {
+      p->add_bucket(data[i]);
+    }
+    p->set_min_seen(gpr_histogram_minimum(impl_));
+    p->set_max_seen(gpr_histogram_maximum(impl_));
+    p->set_sum(gpr_histogram_sum(impl_));
+    p->set_sum_of_squares(gpr_histogram_sum_of_squares(impl_));
+    p->set_count(gpr_histogram_count(impl_));
+  }
+  void MergeProto(const HistogramData& p) {
+    gpr_histogram_merge_contents(impl_, &*p.bucket().begin(), p.bucket_size(),
+                                 p.min_seen(), p.max_seen(), p.sum(),
+                                 p.sum_of_squares(), p.count());
+  }
+
+ private:
+  Histogram(const Histogram&);
+  Histogram& operator=(const Histogram&);
+
+  gpr_histogram* impl_;
+};
+}
+}
+
+#endif /* TEST_QPS_HISTOGRAM_H */
diff --git a/test/cpp/qps/qps_driver.cc b/test/cpp/qps/qps_driver.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bf51e7408e9f6f0ae382f6f752f4e3e585e833df
--- /dev/null
+++ b/test/cpp/qps/qps_driver.cc
@@ -0,0 +1,132 @@
+/*
+ *
+ * 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 <gflags/gflags.h>
+#include <grpc/support/log.h>
+
+#include "test/cpp/qps/driver.h"
+#include "test/cpp/qps/stats.h"
+
+DEFINE_int32(num_clients, 1, "Number of client binaries");
+DEFINE_int32(num_servers, 1, "Number of server binaries");
+
+// Common config
+DEFINE_bool(enable_ssl, false, "Use SSL");
+
+// Server config
+DEFINE_int32(server_threads, 1, "Number of server threads");
+DEFINE_string(server_type, "SYNCHRONOUS_SERVER", "Server type");
+
+// Client config
+DEFINE_int32(outstanding_rpcs_per_channel, 1,
+             "Number of outstanding rpcs per channel");
+DEFINE_int32(client_channels, 1, "Number of client channels");
+DEFINE_int32(payload_size, 1, "Payload size");
+DEFINE_string(client_type, "SYNCHRONOUS_CLIENT", "Client type");
+DEFINE_int32(async_client_threads, 1, "Async client threads");
+
+using grpc::testing::ClientConfig;
+using grpc::testing::ServerConfig;
+using grpc::testing::ClientType;
+using grpc::testing::ServerType;
+using grpc::testing::ResourceUsage;
+using grpc::testing::sum;
+
+// 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;
+
+int main(int argc, char **argv) {
+  grpc_init();
+  ParseCommandLineFlags(&argc, &argv, true);
+
+  ClientType client_type;
+  ServerType server_type;
+  GPR_ASSERT(ClientType_Parse(FLAGS_client_type, &client_type));
+  GPR_ASSERT(ServerType_Parse(FLAGS_server_type, &server_type));
+
+  ClientConfig client_config;
+  client_config.set_client_type(client_type);
+  client_config.set_enable_ssl(FLAGS_enable_ssl);
+  client_config.set_outstanding_rpcs_per_channel(
+      FLAGS_outstanding_rpcs_per_channel);
+  client_config.set_client_channels(FLAGS_client_channels);
+  client_config.set_payload_size(FLAGS_payload_size);
+  client_config.set_async_client_threads(FLAGS_async_client_threads);
+
+  ServerConfig server_config;
+  server_config.set_server_type(server_type);
+  server_config.set_threads(FLAGS_server_threads);
+  server_config.set_enable_ssl(FLAGS_enable_ssl);
+
+  auto result = RunScenario(client_config, FLAGS_num_clients, server_config,
+                            FLAGS_num_servers);
+
+  gpr_log(GPR_INFO, "QPS: %.1f",
+          result.latencies.Count() /
+              average(result.client_resources,
+                      [](ResourceUsage u) { return u.wall_time; }));
+
+  gpr_log(GPR_INFO, "Latencies (50/95/99/99.9%%-ile): %.1f/%.1f/%.1f/%.1f us",
+          result.latencies.Percentile(50) / 1000,
+          result.latencies.Percentile(95) / 1000,
+          result.latencies.Percentile(99) / 1000,
+          result.latencies.Percentile(99.9) / 1000);
+
+  gpr_log(GPR_INFO, "Server system time: %.2f%%",
+          100.0 * sum(result.server_resources,
+                      [](ResourceUsage u) { return u.system_time; }) /
+              sum(result.server_resources,
+                  [](ResourceUsage u) { return u.wall_time; }));
+  gpr_log(GPR_INFO, "Server user time:   %.2f%%",
+          100.0 * sum(result.server_resources,
+                      [](ResourceUsage u) { return u.user_time; }) /
+              sum(result.server_resources,
+                  [](ResourceUsage u) { return u.wall_time; }));
+  gpr_log(GPR_INFO, "Client system time: %.2f%%",
+          100.0 * sum(result.client_resources,
+                      [](ResourceUsage u) { return u.system_time; }) /
+              sum(result.client_resources,
+                  [](ResourceUsage u) { return u.wall_time; }));
+  gpr_log(GPR_INFO, "Client user time:   %.2f%%",
+          100.0 * sum(result.client_resources,
+                      [](ResourceUsage u) { return u.user_time; }) /
+              sum(result.client_resources,
+                  [](ResourceUsage u) { return u.wall_time; }));
+
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/cpp/qps/qpstest.proto b/test/cpp/qps/qpstest.proto
index 68ec6149f59669954b03bbe6425c6de271604a1c..6a7170bf5807630fb1fdf4958ddf024b0d94094f 100644
--- a/test/cpp/qps/qpstest.proto
+++ b/test/cpp/qps/qpstest.proto
@@ -51,17 +51,14 @@ message StatsRequest {
 }
 
 message ServerStats {
-  // wall clock time for timestamp
-  required double time_now = 1;
+  // wall clock time
+  required double time_elapsed = 1;
 
   // user time used by the server process and threads
   required double time_user = 2;
 
   // server time used by the server process and all threads
   required double time_system = 3;
-
-  // RPC count so far
-  optional int32 num_rpcs = 4;
 }
 
 message Payload {
@@ -71,31 +68,75 @@ message Payload {
   optional bytes body = 2;
 }
 
-message Latencies {
-  required double l_50 = 1;
-  required double l_90 = 2;
-  required double l_99 = 3;
-  required double l_999 = 4;
+message HistogramData {
+  repeated uint32 bucket = 1;
+  required double min_seen = 2;
+  required double max_seen = 3;
+  required double sum = 4;
+  required double sum_of_squares = 5;
+  required double count = 6;
+}
+
+enum ClientType {
+  SYNCHRONOUS_CLIENT = 1;
+  ASYNC_CLIENT = 2;
+}
+
+enum ServerType {
+  SYNCHRONOUS_SERVER = 1;
+  ASYNC_SERVER = 2;
+}
+
+message ClientConfig {
+  repeated string server_targets = 1;
+  required ClientType client_type = 2;
+  required bool enable_ssl = 3;
+  required int32 outstanding_rpcs_per_channel = 4;
+  required int32 client_channels = 5;
+  required int32 payload_size = 6;
+  // only for async client:
+  optional int32 async_client_threads = 7;
 }
 
-message StartArgs {
-  required string server_host = 1;
-  required int32 server_port = 2;
-  optional bool enable_ssl = 3 [default = false];
-  optional int32 client_threads = 4 [default = 1];
-  optional int32 client_channels = 5 [default = -1];
-  optional int32 num_rpcs = 6 [default = 1];
-  optional int32 payload_size = 7 [default = 1];
+// Request current stats
+message Mark {}
+
+message ClientArgs {
+  oneof argtype {
+    ClientConfig setup = 1;
+    Mark mark = 2;
+  }
 }
 
-message StartResult {
-  required Latencies latencies = 1;
-  required int32 num_rpcs = 2;
+message ClientStats {
+  required HistogramData latencies = 1;
   required double time_elapsed = 3;
   required double time_user = 4;
   required double time_system = 5;
 }
 
+message ClientStatus {
+  optional ClientStats stats = 1;
+}
+
+message ServerConfig {
+  required ServerType server_type = 1;
+  required int32 threads = 2;
+  required bool enable_ssl = 3;
+}
+
+message ServerArgs {
+  oneof argtype {
+    ServerConfig setup = 1;
+    Mark mark = 2;
+  }
+}
+
+message ServerStatus {
+  optional ServerStats stats = 1;
+  required int32 port = 2;
+}
+
 message SimpleRequest {
   // Desired payload type in the response from the server.
   // If response_type is RANDOM, server randomly chooses one from other formats.
@@ -153,12 +194,6 @@ message StreamingOutputCallResponse {
 }
 
 service TestService {
-  // Start test with specified workload
-  rpc StartTest(StartArgs) returns (Latencies);
-
-  // Collect stats from server, ignore request content
-  rpc CollectServerStats(StatsRequest) returns (ServerStats);
-
   // One request followed by one response.
   // The server returns the client payload as-is.
   rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
@@ -186,3 +221,10 @@ service TestService {
   rpc HalfDuplexCall(stream StreamingOutputCallRequest)
       returns (stream StreamingOutputCallResponse);
 }
+
+service Worker {
+  // Start test with specified workload
+  rpc RunTest(stream ClientArgs) returns (stream ClientStatus);
+  // Start test with specified workload
+  rpc RunServer(stream ServerArgs) returns (stream ServerStatus);
+}
diff --git a/test/cpp/qps/server.cc b/test/cpp/qps/server.cc
index be27c12b30a701b1c2537df3ad56ffdb49e6ac1b..005f0f9c5e2d78f440480a7e0440fdb94dc5d739 100644
--- a/test/cpp/qps/server.cc
+++ b/test/cpp/qps/server.cc
@@ -45,6 +45,7 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc++/server_credentials.h>
 #include <grpc++/status.h>
 #include "src/cpp/server/thread_pool.h"
 #include "test/core/util/grpc_profiler.h"
@@ -136,7 +137,7 @@ static void RunServer() {
   SimpleResponse response;
 
   ServerBuilder builder;
-  builder.AddPort(server_address);
+  builder.AddPort(server_address, grpc::InsecureServerCredentials());
   builder.RegisterService(&service);
 
   std::unique_ptr<ThreadPool> pool(new ThreadPool(FLAGS_server_threads));
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef71cb94d00a379e47ec1c9233105a24ad80b69d
--- /dev/null
+++ b/test/cpp/qps/server.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * 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 TEST_QPS_SERVER_H
+#define TEST_QPS_SERVER_H
+
+#include "test/cpp/qps/timer.h"
+#include "test/cpp/qps/qpstest.pb.h"
+
+namespace grpc {
+namespace testing {
+
+class Server {
+ public:
+  Server() : timer_(new Timer) {}
+  virtual ~Server() {}
+
+  ServerStats Mark() {
+    std::unique_ptr<Timer> timer(new Timer);
+    timer.swap(timer_);
+
+    auto timer_result = timer->Mark();
+
+    ServerStats stats;
+    stats.set_time_elapsed(timer_result.wall);
+    stats.set_time_system(timer_result.system);
+    stats.set_time_user(timer_result.user);
+    return stats;
+  }
+
+  static bool SetPayload(PayloadType type, int size, Payload* payload) {
+    PayloadType response_type = type;
+    // TODO(yangg): Support UNCOMPRESSABLE payload.
+    if (type != PayloadType::COMPRESSABLE) {
+      return false;
+    }
+    payload->set_type(response_type);
+    std::unique_ptr<char[]> body(new char[size]());
+    payload->set_body(body.get(), size);
+    return true;
+  }
+
+ private:
+  std::unique_ptr<Timer> timer_;
+};
+
+std::unique_ptr<Server> CreateSynchronousServer(const ServerConfig& config,
+                                                int port);
+std::unique_ptr<Server> CreateAsyncServer(const ServerConfig& config, int port);
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index c797d8af9630bf4fdddee06c838b1e3ef39a5d94..19778e5a7cd09c6c231c17aa09d5e8c4458f59bc 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -46,125 +46,71 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc++/server_credentials.h>
 #include <grpc++/status.h>
 #include <gtest/gtest.h>
 #include "src/cpp/server/thread_pool.h"
 #include "test/core/util/grpc_profiler.h"
 #include "test/cpp/qps/qpstest.pb.h"
+#include "test/cpp/qps/server.h"
 
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 
-DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
-DEFINE_int32(port, 0, "Server port.");
-DEFINE_int32(server_threads, 4, "Number of server threads.");
+namespace grpc {
+namespace testing {
 
-using grpc::CompletionQueue;
-using grpc::Server;
-using grpc::ServerBuilder;
-using grpc::ServerContext;
-using grpc::ThreadPool;
-using grpc::testing::Payload;
-using grpc::testing::PayloadType;
-using grpc::testing::ServerStats;
-using grpc::testing::SimpleRequest;
-using grpc::testing::SimpleResponse;
-using grpc::testing::StatsRequest;
-using grpc::testing::TestService;
-using grpc::Status;
-
-// 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;
-
-static bool got_sigint = false;
-
-static void sigint_handler(int x) { got_sigint = 1; }
-
-static double time_double(struct timeval *tv) {
-  return tv->tv_sec + 1e-6 * tv->tv_usec;
-}
-
-static bool SetPayload(PayloadType type, int size, Payload *payload) {
-  PayloadType response_type = type;
-  // TODO(yangg): Support UNCOMPRESSABLE payload.
-  if (type != PayloadType::COMPRESSABLE) {
-    return false;
-  }
-  payload->set_type(response_type);
-  std::unique_ptr<char[]> body(new char[size]());
-  payload->set_body(body.get(), size);
-  return true;
-}
-
-namespace {
-
-class AsyncQpsServerTest {
+class AsyncQpsServerTest : public Server {
  public:
-  AsyncQpsServerTest() : srv_cq_(), async_service_(&srv_cq_), server_(nullptr) {
+  AsyncQpsServerTest(const ServerConfig &config, int port)
+      : srv_cq_(), async_service_(&srv_cq_), server_(nullptr) {
     char *server_address = NULL;
-    gpr_join_host_port(&server_address, "::", FLAGS_port);
+    gpr_join_host_port(&server_address, "::", port);
 
     ServerBuilder builder;
-    builder.AddPort(server_address);
+    builder.AddPort(server_address, InsecureServerCredentials());
+    gpr_free(server_address);
 
     builder.RegisterAsyncService(&async_service_);
 
     server_ = builder.BuildAndStart();
-    gpr_log(GPR_INFO, "Server listening on %s\n", server_address);
-    gpr_free(server_address);
 
     using namespace std::placeholders;
     request_unary_ = std::bind(&TestService::AsyncService::RequestUnaryCall,
                                &async_service_, _1, _2, _3, &srv_cq_, _4);
-    request_stats_ =
-        std::bind(&TestService::AsyncService::RequestCollectServerStats,
-                  &async_service_, _1, _2, _3, &srv_cq_, _4);
     for (int i = 0; i < 100; i++) {
       contexts_.push_front(
           new ServerRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(
               request_unary_, UnaryCall));
-      contexts_.push_front(
-          new ServerRpcContextUnaryImpl<StatsRequest, ServerStats>(
-              request_stats_, CollectServerStats));
-    }
-  }
-  ~AsyncQpsServerTest() {
-    server_->Shutdown();
-    void *ignored_tag;
-    bool ignored_ok;
-    srv_cq_.Shutdown();
-    while (srv_cq_.Next(&ignored_tag, &ignored_ok)) {
-    }
-    while (!contexts_.empty()) {
-      delete contexts_.front();
-      contexts_.pop_front();
     }
-  }
-  void ServeRpcs(int num_threads) {
-    std::vector<std::thread> threads;
-    for (int i = 0; i < num_threads; i++) {
-      threads.push_back(std::thread([=]() {
+    for (int i = 0; i < config.threads(); i++) {
+      threads_.push_back(std::thread([=]() {
         // Wait until work is available or we are shutting down
         bool ok;
         void *got_tag;
         while (srv_cq_.Next(&got_tag, &ok)) {
-          EXPECT_EQ(ok, true);
-          ServerRpcContext *ctx = detag(got_tag);
-          // The tag is a pointer to an RPC context to invoke
-          if (ctx->RunNextState() == false) {
-            // this RPC context is done, so refresh it
-            ctx->Reset();
+          if (ok) {
+            ServerRpcContext *ctx = detag(got_tag);
+            // The tag is a pointer to an RPC context to invoke
+            if (ctx->RunNextState() == false) {
+              // this RPC context is done, so refresh it
+              ctx->Reset();
+            }
           }
         }
         return;
       }));
     }
-    while (!got_sigint) {
-      std::this_thread::sleep_for(std::chrono::seconds(5));
+  }
+  ~AsyncQpsServerTest() {
+    server_->Shutdown();
+    srv_cq_.Shutdown();
+    for (auto &thr : threads_) {
+      thr.join();
+    }
+    while (!contexts_.empty()) {
+      delete contexts_.front();
+      contexts_.pop_front();
     }
   }
 
@@ -173,8 +119,8 @@ class AsyncQpsServerTest {
    public:
     ServerRpcContext() {}
     virtual ~ServerRpcContext(){};
-    virtual bool RunNextState() = 0;// do next state, return false if all done
-    virtual void Reset() = 0;     // start this back at a clean state
+    virtual bool RunNextState() = 0;  // do next state, return false if all done
+    virtual void Reset() = 0;         // start this back at a clean state
   };
   static void *tag(ServerRpcContext *func) {
     return reinterpret_cast<void *>(func);
@@ -237,17 +183,6 @@ class AsyncQpsServerTest {
     grpc::ServerAsyncResponseWriter<ResponseType> response_writer_;
   };
 
-  static Status CollectServerStats(const StatsRequest *,
-                                   ServerStats *response) {
-    struct rusage usage;
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    getrusage(RUSAGE_SELF, &usage);
-    response->set_time_now(time_double(&tv));
-    response->set_time_user(time_double(&usage.ru_utime));
-    response->set_time_system(time_double(&usage.ru_stime));
-    return Status::OK;
-  }
   static Status UnaryCall(const SimpleRequest *request,
                           SimpleResponse *response) {
     if (request->has_response_size() && request->response_size() > 0) {
@@ -260,40 +195,18 @@ class AsyncQpsServerTest {
   }
   CompletionQueue srv_cq_;
   TestService::AsyncService async_service_;
-  std::unique_ptr<Server> server_;
+  std::vector<std::thread> threads_;
+  std::unique_ptr<grpc::Server> server_;
   std::function<void(ServerContext *, SimpleRequest *,
                      grpc::ServerAsyncResponseWriter<SimpleResponse> *, void *)>
       request_unary_;
-  std::function<void(ServerContext *, StatsRequest *,
-                     grpc::ServerAsyncResponseWriter<ServerStats> *, void *)>
-      request_stats_;
   std::forward_list<ServerRpcContext *> contexts_;
 };
 
-}  // namespace
-
-static void RunServer() {
-  AsyncQpsServerTest server;
-
-  grpc_profiler_start("qps_server_async.prof");
-
-  server.ServeRpcs(FLAGS_server_threads);
-
-  grpc_profiler_stop();
+std::unique_ptr<Server> CreateAsyncServer(const ServerConfig &config,
+                                          int port) {
+  return std::unique_ptr<Server>(new AsyncQpsServerTest(config, port));
 }
 
-int main(int argc, char **argv) {
-  grpc_init();
-  ParseCommandLineFlags(&argc, &argv, true);
-  GPR_ASSERT(FLAGS_port != 0);
-  GPR_ASSERT(!FLAGS_enable_ssl);
-
-  signal(SIGINT, sigint_handler);
-
-  RunServer();
-
-  grpc_shutdown();
-  google::protobuf::ShutdownProtobufLibrary();
-
-  return 0;
-}
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/qps/server_sync.cc b/test/cpp/qps/server_sync.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5c6541989c6bd164b5a11a2c83af36461e1aaa5c
--- /dev/null
+++ b/test/cpp/qps/server_sync.cc
@@ -0,0 +1,108 @@
+/*
+ *
+ * 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 <sys/signal.h>
+#include <thread>
+
+#include <unistd.h>
+
+#include <gflags/gflags.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc++/config.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/server_credentials.h>
+#include <grpc++/status.h>
+#include <grpc++/stream.h>
+#include "src/cpp/server/thread_pool.h"
+#include "test/core/util/grpc_profiler.h"
+#include "test/cpp/qps/qpstest.pb.h"
+#include "test/cpp/qps/server.h"
+#include "test/cpp/qps/timer.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+
+namespace grpc {
+namespace testing {
+
+class TestServiceImpl GRPC_FINAL : public TestService::Service {
+ public:
+  Status UnaryCall(ServerContext* context, const SimpleRequest* request,
+                   SimpleResponse* response) GRPC_OVERRIDE {
+    if (request->has_response_size() && request->response_size() > 0) {
+      if (!Server::SetPayload(request->response_type(),
+                              request->response_size(),
+                              response->mutable_payload())) {
+        return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
+      }
+    }
+    return Status::OK;
+  }
+};
+
+class SynchronousServer GRPC_FINAL : public grpc::testing::Server {
+ public:
+  SynchronousServer(const ServerConfig& config, int port)
+      : thread_pool_(config.threads()), impl_(MakeImpl(port)) {}
+
+ private:
+  std::unique_ptr<grpc::Server> MakeImpl(int port) {
+    ServerBuilder builder;
+
+    char* server_address = NULL;
+    gpr_join_host_port(&server_address, "::", port);
+    builder.AddPort(server_address, InsecureServerCredentials());
+    gpr_free(server_address);
+
+    builder.RegisterService(&service_);
+
+    builder.SetThreadPool(&thread_pool_);
+
+    return builder.BuildAndStart();
+  }
+
+  TestServiceImpl service_;
+  ThreadPool thread_pool_;
+  std::unique_ptr<grpc::Server> impl_;
+};
+
+std::unique_ptr<grpc::testing::Server> CreateSynchronousServer(
+    const ServerConfig& config, int port) {
+  return std::unique_ptr<Server>(new SynchronousServer(config, port));
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/qps/single_run_localhost.sh b/test/cpp/qps/single_run_localhost.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2f60b4e49dec55a36bc11cd6c8aa5963e9a5c702
--- /dev/null
+++ b/test/cpp/qps/single_run_localhost.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# performs a single qps run with one client and one server
+
+set -ex
+
+cd $(dirname $0)/../../..
+
+killall qps_worker || true
+
+config=opt
+
+NUMCPUS=`python2.7 -c 'import multiprocessing; print multiprocessing.cpu_count()'`
+
+make CONFIG=$config qps_worker qps_driver -j$NUMCPUS
+
+bins/$config/qps_worker -driver_port 10000 -server_port 10001 &
+PID1=$!
+bins/$config/qps_worker -driver_port 10010 -server_port 10011 &
+PID2=$!
+
+export QPS_WORKERS="localhost:10000,localhost:10010"
+
+bins/$config/qps_driver $*
+
+kill -2 $PID1 $PID2
+wait
+
diff --git a/test/cpp/qps/stats.h b/test/cpp/qps/stats.h
new file mode 100644
index 0000000000000000000000000000000000000000..ca59390ad79958a371dec9dda4747d8f5ebc65d9
--- /dev/null
+++ b/test/cpp/qps/stats.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * 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 TEST_QPS_STATS_UTILS_H
+#define TEST_QPS_STATS_UTILS_H
+
+#include "test/cpp/qps/histogram.h"
+#include <string>
+
+namespace grpc {
+namespace testing {
+
+template <class T, class F>
+double sum(const T& container, F functor) {
+  double r = 0;
+  for (auto v : container) {
+    r += functor(v);
+  }
+  return r;
+}
+
+template <class T, class F>
+double average(const T& container, F functor) {
+  return sum(container, functor) / container.size();
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif
diff --git a/test/cpp/qps/timer.cc b/test/cpp/qps/timer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3c1342041cf1f01b6c8ef077a010554c1a7ce2e1
--- /dev/null
+++ b/test/cpp/qps/timer.cc
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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/qps/timer.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <grpc/support/time.h>
+
+Timer::Timer() : start_(Sample()) {}
+
+double Timer::Now() {
+  auto ts = gpr_now();
+  return ts.tv_sec + 1e-9 * ts.tv_nsec;
+}
+
+static double time_double(struct timeval* tv) {
+  return tv->tv_sec + 1e-6 * tv->tv_usec;
+}
+
+Timer::Result Timer::Sample() {
+  struct rusage usage;
+  struct timeval tv;
+  gettimeofday(&tv, nullptr);
+  getrusage(RUSAGE_SELF, &usage);
+
+  Result r;
+  r.wall = time_double(&tv);
+  r.user = time_double(&usage.ru_utime);
+  r.system = time_double(&usage.ru_stime);
+  return r;
+}
+
+Timer::Result Timer::Mark() {
+  Result s = Sample();
+  Result r;
+  r.wall = s.wall - start_.wall;
+  r.user = s.user - start_.user;
+  r.system = s.system - start_.system;
+  return r;
+}
diff --git a/src/core/surface/lame_client.h b/test/cpp/qps/timer.h
similarity index 83%
rename from src/core/surface/lame_client.h
rename to test/cpp/qps/timer.h
index b13e8cb6efe690d437c60effc5667d9d809c305e..30dbd7e7d50dd6c5cdeefafc15f4f45c08696857 100644
--- a/src/core/surface/lame_client.h
+++ b/test/cpp/qps/timer.h
@@ -31,12 +31,27 @@
  *
  */
 
-#ifndef GRPC_INTERNAL_CORE_SURFACE_LAME_CLIENT_H
-#define GRPC_INTERNAL_CORE_SURFACE_LAME_CLIENT_H
+#ifndef TEST_QPS_TIMER_H
+#define TEST_QPS_TIMER_H
 
-#include <grpc/grpc.h>
+class Timer {
+ public:
+  Timer();
 
-/* Create a lame client: this client fails every operation attempted on it. */
-grpc_channel *grpc_lame_client_channel_create(void);
+  struct Result {
+    double wall;
+    double user;
+    double system;
+  };
 
-#endif  /* GRPC_INTERNAL_CORE_SURFACE_LAME_CLIENT_H */
+  Result Mark();
+
+  static double Now();
+
+ private:
+  static Result Sample();
+
+  const Result start_;
+};
+
+#endif  // TEST_QPS_TIMER_H
diff --git a/test/cpp/qps/worker.cc b/test/cpp/qps/worker.cc
new file mode 100644
index 0000000000000000000000000000000000000000..faabfd1147796149e127fcc4df0abc875f307a45
--- /dev/null
+++ b/test/cpp/qps/worker.cc
@@ -0,0 +1,236 @@
+/*
+ *
+ * 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 <cassert>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+#include <sstream>
+
+#include <sys/signal.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/histogram.h>
+#include <grpc/support/log.h>
+#include <grpc/support/host_port.h>
+#include <gflags/gflags.h>
+#include <grpc++/client_context.h>
+#include <grpc++/status.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_credentials.h>
+#include <grpc++/stream.h>
+#include "test/core/util/grpc_profiler.h"
+#include "test/cpp/util/create_test_channel.h"
+#include "test/cpp/qps/qpstest.pb.h"
+#include "test/cpp/qps/client.h"
+#include "test/cpp/qps/server.h"
+
+DEFINE_int32(driver_port, 0, "Driver server port.");
+DEFINE_int32(server_port, 0, "Spawned server port.");
+
+// 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;
+
+static bool got_sigint = false;
+
+namespace grpc {
+namespace testing {
+
+std::unique_ptr<Client> CreateClient(const ClientConfig& config) {
+  switch (config.client_type()) {
+    case ClientType::SYNCHRONOUS_CLIENT:
+      return CreateSynchronousClient(config);
+    case ClientType::ASYNC_CLIENT:
+      return CreateAsyncClient(config);
+  }
+  abort();
+}
+
+std::unique_ptr<Server> CreateServer(const ServerConfig& config) {
+  switch (config.server_type()) {
+    case ServerType::SYNCHRONOUS_SERVER:
+      return CreateSynchronousServer(config, FLAGS_server_port);
+    case ServerType::ASYNC_SERVER:
+      return CreateAsyncServer(config, FLAGS_server_port);
+  }
+  abort();
+}
+
+class WorkerImpl GRPC_FINAL : public Worker::Service {
+ public:
+  WorkerImpl() : acquired_(false) {}
+
+  Status RunTest(ServerContext* ctx,
+                 ServerReaderWriter<ClientStatus, ClientArgs>* stream)
+      GRPC_OVERRIDE {
+    InstanceGuard g(this);
+    if (!g.Acquired()) {
+      return Status(RESOURCE_EXHAUSTED);
+    }
+
+    ClientArgs args;
+    if (!stream->Read(&args)) {
+      return Status(INVALID_ARGUMENT);
+    }
+    if (!args.has_setup()) {
+      return Status(INVALID_ARGUMENT);
+    }
+    auto client = CreateClient(args.setup());
+    if (!client) {
+      return Status(INVALID_ARGUMENT);
+    }
+    ClientStatus status;
+    if (!stream->Write(status)) {
+      return Status(UNKNOWN);
+    }
+    while (stream->Read(&args)) {
+      if (!args.has_mark()) {
+        return Status(INVALID_ARGUMENT);
+      }
+      *status.mutable_stats() = client->Mark();
+      stream->Write(status);
+    }
+
+    return Status::OK;
+  }
+
+  Status RunServer(ServerContext* ctx,
+                   ServerReaderWriter<ServerStatus, ServerArgs>* stream)
+      GRPC_OVERRIDE {
+    InstanceGuard g(this);
+    if (!g.Acquired()) {
+      return Status(RESOURCE_EXHAUSTED);
+    }
+
+    ServerArgs args;
+    if (!stream->Read(&args)) {
+      return Status(INVALID_ARGUMENT);
+    }
+    if (!args.has_setup()) {
+      return Status(INVALID_ARGUMENT);
+    }
+    auto server = CreateServer(args.setup());
+    if (!server) {
+      return Status(INVALID_ARGUMENT);
+    }
+    ServerStatus status;
+    status.set_port(FLAGS_server_port);
+    if (!stream->Write(status)) {
+      return Status(UNKNOWN);
+    }
+    while (stream->Read(&args)) {
+      if (!args.has_mark()) {
+        return Status(INVALID_ARGUMENT);
+      }
+      *status.mutable_stats() = server->Mark();
+      stream->Write(status);
+    }
+
+    return Status::OK;
+  }
+
+ private:
+  // Protect against multiple clients using this worker at once.
+  class InstanceGuard {
+   public:
+    InstanceGuard(WorkerImpl* impl)
+        : impl_(impl), acquired_(impl->TryAcquireInstance()) {}
+    ~InstanceGuard() {
+      if (acquired_) {
+        impl_->ReleaseInstance();
+      }
+    }
+
+    bool Acquired() const { return acquired_; }
+
+   private:
+    WorkerImpl* const impl_;
+    const bool acquired_;
+  };
+
+  bool TryAcquireInstance() {
+    std::lock_guard<std::mutex> g(mu_);
+    if (acquired_) return false;
+    acquired_ = true;
+    return true;
+  }
+
+  void ReleaseInstance() {
+    std::lock_guard<std::mutex> g(mu_);
+    GPR_ASSERT(acquired_);
+    acquired_ = false;
+  }
+
+  std::mutex mu_;
+  bool acquired_;
+};
+
+static void RunServer() {
+  char* server_address = NULL;
+  gpr_join_host_port(&server_address, "::", FLAGS_driver_port);
+
+  WorkerImpl service;
+
+  ServerBuilder builder;
+  builder.AddPort(server_address, InsecureServerCredentials());
+  builder.RegisterService(&service);
+
+  gpr_free(server_address);
+
+  auto server = builder.BuildAndStart();
+
+  while (!got_sigint) {
+    std::this_thread::sleep_for(std::chrono::seconds(5));
+  }
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_init();
+  ParseCommandLineFlags(&argc, &argv, true);
+
+  grpc::testing::RunServer();
+
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/cpp/util/create_test_channel.cc b/test/cpp/util/create_test_channel.cc
index 745496f463013dac0b106192a9ba59f94e2cd599..d3b84b29653631133dbc2070d24a5d75f6620fd5 100644
--- a/test/cpp/util/create_test_channel.cc
+++ b/test/cpp/util/create_test_channel.cc
@@ -61,12 +61,10 @@ std::shared_ptr<ChannelInterface> CreateTestChannel(
     const std::unique_ptr<Credentials>& creds) {
   ChannelArguments channel_args;
   if (enable_ssl) {
-    const char* roots_certs =
-        use_prod_roots ? "" : test_root_cert;
+    const char* roots_certs = use_prod_roots ? "" : test_root_cert;
     SslCredentialsOptions ssl_opts = {roots_certs, "", ""};
 
-    std::unique_ptr<Credentials> channel_creds =
-        CredentialsFactory::SslCredentials(ssl_opts);
+    std::unique_ptr<Credentials> channel_creds = SslCredentials(ssl_opts);
 
     if (!server.empty() && !override_hostname.empty()) {
       channel_args.SetSslTargetNameOverride(override_hostname);
@@ -75,11 +73,11 @@ std::shared_ptr<ChannelInterface> CreateTestChannel(
         server.empty() ? override_hostname : server;
     if (creds.get()) {
       channel_creds =
-          CredentialsFactory::CompositeCredentials(creds, channel_creds);
+          CompositeCredentials(creds, channel_creds);
     }
     return CreateChannel(connect_to, channel_creds, channel_args);
   } else {
-    return CreateChannelDeprecated(server, channel_args);
+    return CreateChannel(server, InsecureCredentials(), channel_args);
   }
 }
 
diff --git a/tools/dockerfile/grpc_csharp_mono/Dockerfile b/tools/dockerfile/grpc_csharp_mono/Dockerfile
index d0e2d2be7e44f9123d91737491290f498b881076..8f8636656062640c47f79a5a476ba7a63db711fa 100644
--- a/tools/dockerfile/grpc_csharp_mono/Dockerfile
+++ b/tools/dockerfile/grpc_csharp_mono/Dockerfile
@@ -38,12 +38,8 @@ RUN cd /var/local/git/grpc \
 # Install the gRPC C# extension library
 RUN make install_grpc_csharp_ext -j12 -C /var/local/git/grpc
 
-# TODO: download NuGet from web. The problem is there seems to be no direct link
-# we could use :-)
-ADD NuGet.exe NuGet.exe
-
 # Restore the NuGet dependencies
-RUN cd /var/local/git/grpc/src/csharp && mono /NuGet.exe restore Grpc.sln
+RUN cd /var/local/git/grpc/src/csharp && mono /var/local/NuGet.exe restore Grpc.sln
 
 # Build gRPC solution
 RUN cd /var/local/git/grpc/src/csharp && xbuild Grpc.sln
diff --git a/tools/dockerfile/grpc_csharp_mono_base/Dockerfile b/tools/dockerfile/grpc_csharp_mono_base/Dockerfile
index 74919a7fdb5786a4a27433112c98949cb6f0c8e2..88766a4545a526e40daab1fa17c6b754092134cc 100644
--- a/tools/dockerfile/grpc_csharp_mono_base/Dockerfile
+++ b/tools/dockerfile/grpc_csharp_mono_base/Dockerfile
@@ -43,6 +43,9 @@ RUN apt-get update && apt-get install -y \
     nunit-console \
     monodevelop
 
+# Download NuGet
+RUN cd /var/local && wget www.nuget.org/NuGet.exe
+
 # Get the source from GitHub
 RUN git clone git@github.com:grpc/grpc.git /var/local/git/grpc
 RUN cd /var/local/git/grpc && \
diff --git a/tools/dockerfile/grpc_python/Dockerfile b/tools/dockerfile/grpc_python/Dockerfile
index 2e72752cdd78e7769c9f903acbe350f3e53041bd..58a3d8c14f1c2aa6c5bdb1d913f750dd361865a4 100644
--- a/tools/dockerfile/grpc_python/Dockerfile
+++ b/tools/dockerfile/grpc_python/Dockerfile
@@ -44,21 +44,21 @@ RUN cd /var/local/git/grpc \
   && pip install src/python/interop
 
 # Run Python GRPC's tests
+# TODO(nathaniel): It would be nice for these to be auto-discoverable?
 RUN cd /var/local/git/grpc \
-  # TODO(nathaniel): It would be nice for these to be auto-discoverable?
-  && python2.7 -B -m grpc._adapter._blocking_invocation_inline_service_test
-  && python2.7 -B -m grpc._adapter._c_test
-  && python2.7 -B -m grpc._adapter._event_invocation_synchronous_event_service_test
-  && python2.7 -B -m grpc._adapter._future_invocation_asynchronous_event_service_test
-  && python2.7 -B -m grpc._adapter._links_test
-  && python2.7 -B -m grpc._adapter._lonely_rear_link_test
-  && python2.7 -B -m grpc._adapter._low_test
-  && python2.7 -B -m grpc.framework.assembly.implementations_test
-  && python2.7 -B -m grpc.framework.base.packets.implementations_test
-  && python2.7 -B -m grpc.framework.face.blocking_invocation_inline_service_test
-  && python2.7 -B -m grpc.framework.face.event_invocation_synchronous_event_service_test
-  && python2.7 -B -m grpc.framework.face.future_invocation_asynchronous_event_service_test
-  && python2.7 -B -m grpc.framework.foundation._later_test
+  && python2.7 -B -m grpc._adapter._blocking_invocation_inline_service_test \
+  && python2.7 -B -m grpc._adapter._c_test \
+  && python2.7 -B -m grpc._adapter._event_invocation_synchronous_event_service_test \
+  && python2.7 -B -m grpc._adapter._future_invocation_asynchronous_event_service_test \
+  && python2.7 -B -m grpc._adapter._links_test \
+  && python2.7 -B -m grpc._adapter._lonely_rear_link_test \
+  && python2.7 -B -m grpc._adapter._low_test \
+  && python2.7 -B -m grpc.framework.assembly.implementations_test \
+  && python2.7 -B -m grpc.framework.base.packets.implementations_test \
+  && python2.7 -B -m grpc.framework.face.blocking_invocation_inline_service_test \
+  && python2.7 -B -m grpc.framework.face.event_invocation_synchronous_event_service_test \
+  && python2.7 -B -m grpc.framework.face.future_invocation_asynchronous_event_service_test \
+  && python2.7 -B -m grpc.framework.foundation._later_test \
   && python2.7 -B -m grpc.framework.foundation._logging_pool_test
 
 # Add a cacerts directory containing the Google root pem file, allowing the interop client to access the production test instance
diff --git a/tools/gce_setup/build_images.sh b/tools/gce_setup/build_images.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0b1c32b7d6dc9cda37a080be81de5e736d4b5950
--- /dev/null
+++ b/tools/gce_setup/build_images.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# 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.
+
+
+main() {
+  # rebuild images on all languages on existing builder vm. 
+  source grpc_docker.sh
+  cd ../../
+
+  # build images for all languages
+  languages=(cxx java go ruby node python csharp_mono)
+  for lan in "${languages[@]}"
+  do
+    grpc_update_image $lan
+  done
+}
+
+set -x
+main "$@"
diff --git a/tools/gce_setup/builder.sh b/tools/gce_setup/builder.sh
index d4dbd75426cf22e13610477b54ef51f1827e0a67..8815d082da7931de19404824305728eb69339bfa 100755
--- a/tools/gce_setup/builder.sh
+++ b/tools/gce_setup/builder.sh
@@ -37,7 +37,7 @@ main() {
   sleep 3600
 
   # build images for all languages
-  languages=(cxx java go ruby node)
+  languages=(cxx java go ruby node python csharp_mono)
   for lan in "${languages[@]}"
   do
     grpc_update_image $lan
diff --git a/tools/gce_setup/cloud_prod_runner.sh b/tools/gce_setup/cloud_prod_runner.sh
index e11185c226b3e100f703774d000c4008f6ba2bcc..3a9ae51b769572239b4475e57afd3bd6d483ea0f 100755
--- a/tools/gce_setup/cloud_prod_runner.sh
+++ b/tools/gce_setup/cloud_prod_runner.sh
@@ -28,21 +28,25 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+thisfile=$(readlink -ne "${BASH_SOURCE[0]}")
+current_time=$(date "+%Y-%m-%d-%H-%M-%S")
+result_file_name=cloud_prod_result.$current_time.html
+echo $result_file_name
 
 main() {
   source grpc_docker.sh
   test_cases=(large_unary empty_unary ping_pong client_streaming server_streaming cancel_after_begin cancel_after_first_response)
   auth_test_cases=(service_account_creds compute_engine_creds)
-  clients=(cxx java go ruby node)
+  clients=(cxx java go ruby node csharp_mono)
   for test_case in "${test_cases[@]}"
   do
     for client in "${clients[@]}"
     do
       if grpc_cloud_prod_test $test_case grpc-docker-testclients $client
       then
-        echo "$test_case $client $server passed" >> /tmp/cloud_prod_result.txt
+        echo "          ['$test_case', '$client', 'prod', true]," >> /tmp/cloud_prod_result.txt
       else
-        echo "$test_case $client $server failed" >> /tmp/cloud_prod_result.txt
+        echo "          ['$test_case', '$client', 'prod', false]," >> /tmp/cloud_prod_result.txt
       fi
     done
   done
@@ -52,14 +56,20 @@ main() {
     do
       if grpc_cloud_prod_auth_test $test_case grpc-docker-testclients $client
       then
-        echo "$test_case $client $server passed" >> /tmp/cloud_prod_result.txt
+        echo "          ['$test_case', '$client', 'prod', true]," >> /tmp/cloud_prod_result.txt
       else
-        echo "$test_case $client $server failed" >> /tmp/cloud_prod_result.txt
+        echo "          ['$test_case', '$client', 'prod', false]," >> /tmp/cloud_prod_result.txt
       fi
     done
   done
-  gsutil cp /tmp/cloud_prod_result.txt gs://stoked-keyword-656-output/cloud_prod_result.txt
-  rm /tmp/cloud_prod_result.txt
+  if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+    cat pre.html /tmp/cloud_prod_result.txt post.html > /tmp/cloud_prod_result.html
+    gsutil cp /tmp/cloud_prod_result.txt gs://stoked-keyword-656-output/cloud_prod_result.txt
+    gsutil cp /tmp/cloud_prod_result.html gs://stoked-keyword-656-output/cloud_prod_result.html
+    gsutil cp /tmp/cloud_prod_result.html gs://stoked-keyword-656-output/result_history/$result_file_name
+    rm /tmp/cloud_prod_result.txt
+    rm /tmp/cloud_prod_result.html
+  fi
 }
 
 set -x
diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh
index 02039261a35a9bfe6f9b30edcb22453a0ecbf523..3deef05ef35d888c6ff8c81f70d6968b6c72b693 100755
--- a/tools/gce_setup/grpc_docker.sh
+++ b/tools/gce_setup/grpc_docker.sh
@@ -383,7 +383,7 @@ grpc_interop_test_args() {
 
   [[ -n $1 ]] && {  # client_type
     case $1 in
-      cxx|go|java|node|php|python|ruby)
+      cxx|go|java|node|php|python|ruby|csharp_mono)
         grpc_gen_test_cmd="grpc_interop_gen_$1_cmd"
         declare -F $grpc_gen_test_cmd >> /dev/null || {
           echo "-f: test_func for $1 => $grpc_gen_test_cmd is not defined" 1>&2
@@ -411,12 +411,13 @@ grpc_interop_test_args() {
 
   [[ -n $1 ]] && {  # server_type
     case $1 in
-      cxx)    grpc_port=8010 ;;
-      go)     grpc_port=8020 ;;
-      java)   grpc_port=8030 ;;
-      node)   grpc_port=8040 ;;
-      python) grpc_port=8050 ;;
-      ruby)   grpc_port=8060 ;;
+      cxx)          grpc_port=8010 ;;
+      go)           grpc_port=8020 ;;
+      java)         grpc_port=8030 ;;
+      node)         grpc_port=8040 ;;
+      python)       grpc_port=8050 ;;
+      ruby)         grpc_port=8060 ;;
+      csharp_mono)  grpc_port=8070 ;;
       *) echo "bad server_type: $1" 1>&2; return 1 ;;
     esac
     shift
@@ -454,7 +455,7 @@ grpc_cloud_prod_test_args() {
 
   [[ -n $1 ]] && {  # client_type
     case $1 in
-      cxx|go|java|node|php|python|ruby)
+      cxx|go|java|node|php|python|ruby|csharp_mono)
         grpc_gen_test_cmd="grpc_cloud_prod_gen_$1_cmd"
         declare -F $grpc_gen_test_cmd >> /dev/null || {
           echo "-f: test_func for $1 => $grpc_gen_test_cmd is not defined" 1>&2
@@ -673,7 +674,7 @@ _grpc_launch_servers_args() {
   [[ -n $1 ]] && {
     servers="$@"
   } || {
-    servers="cxx java go node ruby python"
+    servers="cxx java go node ruby python csharp_mono"
     echo "$FUNCNAME: no servers specified, will launch defaults '$servers'"
   }
 }
@@ -709,6 +710,7 @@ grpc_launch_servers() {
       node)   grpc_port=8040 ;;
       python) grpc_port=8050 ;;
       ruby)   grpc_port=8060 ;;
+      csharp_mono)   grpc_port=8070 ;;
       *) echo "bad server_type: $1" 1>&2; return 1 ;;
     esac
     local docker_label="grpc/$server"
@@ -729,6 +731,44 @@ grpc_launch_servers() {
   done
 }
 
+# Runs a test command on a docker instance
+#
+# The test command is issued via gcloud compute
+#
+# There are 3 possible results:
+# 1. successful return code and finished within 60 seconds
+# 2. failure return code and finished within 60 seconds
+# 3. command does not return within 60 seconds, in which case it will be killed.
+test_runner() {
+  local project_opt="--project $grpc_project"
+  local zone_opt="--zone $grpc_zone"
+  local ssh_cmd="bash -l -c \"$cmd\""
+  echo "will run:"
+  echo "  $ssh_cmd"
+  echo "on $host"
+  [[ $dry_run == 1 ]] && return 0  # don't run the command on a dry run
+  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" &
+  PID=$!
+  echo "pid is $PID"
+  for x in {0..5}
+  do
+    if ps -p $PID
+    then
+      # test command has not returned and 60 seconds timeout has not reached
+      sleep 10
+    else
+      # test command has returned, return the return code from the test command
+      wait $PID
+      local ret=$?
+      echo " test runner return $ret before timeout"
+      return $ret
+    fi
+  done
+  kill $PID
+  echo "test got killed by timeout return as failure"
+  return 1
+}
+
 # Runs a test command on a docker instance.
 #
 # call-seq:
@@ -788,14 +828,7 @@ grpc_interop_test() {
   cmd=$($grpc_gen_test_cmd $flags)
   [[ -n $cmd ]] || return 1
 
-  local project_opt="--project $grpc_project"
-  local zone_opt="--zone $grpc_zone"
-  local ssh_cmd="bash -l -c \"$cmd\""
-  echo "will run:"
-  echo "  $ssh_cmd"
-  echo "on $host"
-  [[ $dry_run == 1 ]] && return 0  # don't run the command on a dry run
-  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" 
+  test_runner
 }
 
 # Runs a test command on a docker instance.
@@ -834,14 +867,7 @@ grpc_cloud_prod_test() {
   cmd=$($grpc_gen_test_cmd $test_case_flag)
   [[ -n $cmd ]] || return 1
 
-  local project_opt="--project $grpc_project"
-  local zone_opt="--zone $grpc_zone"
-  local ssh_cmd="bash -l -c \"$cmd\""
-  echo "will run:"
-  echo "  $ssh_cmd"
-  echo "on $host"
-  [[ $dry_run == 1 ]] && return 0  # don't run the command on a dry run
-  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
+  test_runner
 }
 
 # Runs a test command on a docker instance.
@@ -880,14 +906,7 @@ grpc_cloud_prod_auth_test() {
   cmd=$($grpc_gen_test_cmd $test_case_flag)
   [[ -n $cmd ]] || return 1
 
-  local project_opt="--project $grpc_project"
-  local zone_opt="--zone $grpc_zone"
-  local ssh_cmd="bash -l -c \"$cmd\""
-  echo "will run:"
-  echo "  $ssh_cmd"
-  echo "on $host"
-  [[ $dry_run == 1 ]] && return 0  # don't run the command on a dry run
-  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
+  test_runner
 }
 
 # constructs the full dockerized ruby interop test cmd.
@@ -902,6 +921,17 @@ grpc_interop_gen_ruby_cmd() {
   echo $the_cmd
 }
 
+# constructs the full dockerized python interop test cmd.
+#
+# call-seq:
+#   flags= .... # generic flags to include the command
+#   cmd=$($grpc_gen_test_cmd $flags)
+grpc_interop_gen_python_cmd() {
+  local cmd_prefix="sudo docker run grpc/python bin/bash -l -c"
+  local the_cmd="$cmd_prefix 'python -B -m interop.client --use_test_ca --use_tls $@'"
+  echo $the_cmd
+}
+
 # constructs the full dockerized java interop test cmd.
 #
 # call-seq:
@@ -1162,6 +1192,34 @@ grpc_cloud_prod_auth_compute_engine_creds_gen_cxx_cmd() {
     echo $the_cmd
 }
 
+# constructs the full dockerized csharp-mono interop test cmd.
+#
+# call-seq:
+#   flags= .... # generic flags to include the command
+#   cmd=$($grpc_gen_test_cmd $flags)
+grpc_interop_gen_csharp_mono_cmd() {
+  local workdir_flag="-w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug"
+  local cmd_prefix="sudo docker run $workdir_flag grpc/csharp_mono";
+  local test_script="mono Grpc.IntegrationTesting.Client.exe --use_tls=true --use_test_ca=true";
+  local the_cmd="$cmd_prefix $test_script $@";
+  echo $the_cmd
+}
+
+# constructs the full dockerized csharp-mono gce=>prod interop test cmd.
+#
+# call-seq:
+#   flags= .... # generic flags to include the command
+#   cmd=$($grpc_gen_test_cmd $flags)
+grpc_cloud_prod_gen_csharp_mono_cmd() {
+  local env_flag="-e SSL_CERT_FILE=/cacerts/roots.pem "
+  local workdir_flag="-w /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Client/bin/Debug"
+  local cmd_prefix="sudo docker run $env_flag $workdir_flag grpc/csharp_mono";
+  local test_script="mono Grpc.IntegrationTesting.Client.exe --use_tls=true";
+  local gfe_flags=$(_grpc_prod_gfe_flags);
+  local the_cmd="$cmd_prefix $test_script $gfe_flags $@";
+  echo $the_cmd
+}
+
 # outputs the flags passed to gfe tests
 _grpc_prod_gfe_flags() {
   echo " --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
diff --git a/tools/gce_setup/interop_test_runner.sh b/tools/gce_setup/interop_test_runner.sh
index 5f8c0e70645aba5e597f1fbdd4eb603e60047013..430ad09b8c6308ffd952b083171a8c5b2b64d9e0 100755
--- a/tools/gce_setup/interop_test_runner.sh
+++ b/tools/gce_setup/interop_test_runner.sh
@@ -36,7 +36,7 @@ echo $result_file_name
 main() {
   source grpc_docker.sh
   test_cases=(large_unary empty_unary ping_pong client_streaming server_streaming cancel_after_begin cancel_after_first_response)
-  clients=(cxx java go ruby node)
+  clients=(cxx java go ruby node python csharp_mono)
   servers=(cxx java go ruby node python)
   for test_case in "${test_cases[@]}"
   do
diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh
index 195f8f28a886f982a242de516c3ca79253ba74f2..e6eecc56db62de9e92aafb60eaeed2700449c3b4 100755
--- a/tools/gce_setup/shared_startup_funcs.sh
+++ b/tools/gce_setup/shared_startup_funcs.sh
@@ -364,7 +364,7 @@ grpc_docker_launch_registry() {
 grpc_docker_pull_known() {
   local addr=$1
   [[ -n $addr ]] || addr="0.0.0.0:5000"
-  local known="base cxx php_base php ruby_base ruby java_base java go node_base node python_base python"
+  local known="base cxx php_base php ruby_base ruby java_base java go node_base node python_base python csharp_mono_base csharp_mono"
   echo "... pulling docker images for '$known'"
   for i in $known
   do
@@ -429,7 +429,10 @@ grpc_dockerfile_install() {
     grpc_docker_sync_roots_pem $dockerfile_dir/cacerts || return 1;
     grpc_docker_sync_service_account $dockerfile_dir/service_account || return 1;
   }
-
+  [[ $image_label == "grpc/csharp_mono" ]] && {
+    grpc_docker_sync_roots_pem $dockerfile_dir/cacerts || return 1;
+    grpc_docker_sync_service_account $dockerfile_dir/service_account || return 1;
+  }
 
   # TODO(temiola): maybe make cache/no-cache a func option?
   sudo docker build $cache_opt -t $image_label $dockerfile_dir || {
diff --git a/src/python/src/grpc/framework/assembly/__init__.py b/tools/run_tests/build_csharp.sh
old mode 100644
new mode 100755
similarity index 93%
rename from src/python/src/grpc/framework/assembly/__init__.py
rename to tools/run_tests/build_csharp.sh
index 708651910607ffb686d781713f6893567821b9fd..8227ad37bca5cd5f4ce06c32876106d4292f5df8
--- a/src/python/src/grpc/framework/assembly/__init__.py
+++ b/tools/run_tests/build_csharp.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -27,4 +28,11 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+set -ex
 
+# change to gRPC repo root
+cd $(dirname $0)/../..
+
+root=`pwd`
+
+xbuild src/csharp/Grpc.sln
diff --git a/tools/run_tests/build_node.sh b/tools/run_tests/build_node.sh
index c3e88c565d276cac7f804d545f215a417541e443..c85ecf1b2595c6fc594539400a383690c76bd08f 100755
--- a/tools/run_tests/build_node.sh
+++ b/tools/run_tests/build_node.sh
@@ -36,12 +36,8 @@ CONFIG=${CONFIG:-opt}
 # change to grpc repo root
 cd $(dirname $0)/../..
 
-# tells npm install to look for files in that directory
-export GRPC_ROOT=`pwd`
-# tells npm install the subdirectory with library files
-export GRPC_LIB_SUBDIR=libs/$CONFIG
-# tells npm install not to use default locations
-export GRPC_NO_INSTALL=yes
+export CXXFLAGS=-I`pwd`/include
+export LDFLAGS=-L`pwd`/libs/$CONFIG
 
 cd src/node
 
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
index de633083c352649acddb96e34e591f2003c106c4..0eba1c637798a4e17b0624a2810829eac03eaa69 100755
--- a/tools/run_tests/build_python.sh
+++ b/tools/run_tests/build_python.sh
@@ -39,3 +39,4 @@ virtualenv -p /usr/bin/python2.7 python2.7_virtual_environment
 source python2.7_virtual_environment/bin/activate
 pip install enum34==1.0.4 futures==2.2.0 protobuf==3.0.0-alpha-1
 CFLAGS=-I$root/include LDFLAGS=-L$root/libs/opt pip install src/python/src
+pip install src/python/interop
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py
index ad65da535b42bccf7ff642b61b092b27ecf808d4..26caf031c3a5f8b5d6a71371e58e270734824f88 100755
--- a/tools/run_tests/jobset.py
+++ b/tools/run_tests/jobset.py
@@ -136,7 +136,7 @@ def which(filename):
 class JobSpec(object):
   """Specifies what to run for a job."""
 
-  def __init__(self, cmdline, shortname=None, environ={}, hash_targets=[]):
+  def __init__(self, cmdline, shortname=None, environ=None, hash_targets=None):
     """
     Arguments:
       cmdline: a list of arguments to pass as the command line
@@ -144,6 +144,10 @@ class JobSpec(object):
       hash_targets: which files to include in the hash representing the jobs version
                     (or empty, indicating the job should not be hashed)
     """
+    if environ is None:
+      environ = {}
+    if hash_targets is None:
+      hash_targets = []
     self.cmdline = cmdline
     self.environ = environ
     self.shortname = cmdline[0] if shortname is None else shortname
diff --git a/tools/run_tests/python_tests.json b/tools/run_tests/python_tests.json
new file mode 100755
index 0000000000000000000000000000000000000000..ef483d9799c38a96ad8707d9bc37245cd46ca014
--- /dev/null
+++ b/tools/run_tests/python_tests.json
@@ -0,0 +1,53 @@
+[
+  {
+    "file": "test/compiler/python_plugin_test.py"
+  },
+  {
+    "module": "grpc._adapter._blocking_invocation_inline_service_test"
+  },
+  {
+    "module": "grpc._adapter._c_test"
+  },
+  {
+    "module": "grpc._adapter._event_invocation_synchronous_event_service_test"
+  },
+  {
+    "module": "grpc._adapter._future_invocation_asynchronous_event_service_test"
+  },
+  {
+    "module": "grpc._adapter._links_test"
+  },
+  {
+    "module": "grpc._adapter._lonely_rear_link_test"
+  },
+  {
+    "module": "grpc._adapter._low_test"
+  },
+  {
+    "module": "grpc.early_adopter.implementations_test"
+  },
+  {
+    "module": "grpc.framework.base.packets.implementations_test"
+  },
+  {
+    "module": "grpc.framework.face.blocking_invocation_inline_service_test"
+  },
+  {
+    "module": "grpc.framework.face.event_invocation_synchronous_event_service_test"
+  },
+  {
+    "module": "grpc.framework.face.future_invocation_asynchronous_event_service_test"
+  },
+  {
+    "module": "grpc.framework.foundation._later_test"
+  },
+  {
+    "module": "grpc.framework.foundation._logging_pool_test"
+  },
+  {
+    "module": "interop._insecure_interop_test"
+  },
+  {
+    "module": "interop._secure_interop_test"
+  }
+]
diff --git a/tools/run_tests/run_csharp.sh b/tools/run_tests/run_csharp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d10a41ae9f4858bd5d2bcd410cf79b2d1e8a2d4c
--- /dev/null
+++ b/tools/run_tests/run_csharp.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# 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.
+
+set -ex
+
+# change to gRPC repo root
+cd $(dirname $0)/../..
+
+root=`pwd`
+cd src/csharp
+
+# TODO: All the tests run pretty fast. In the future, we might need to teach
+# run_tests.py about separate tests to make them run in parallel.
+for assembly_name in Grpc.Core.Tests Grpc.Examples.Tests Grpc.IntegrationTesting
+do
+  LD_LIBRARY_PATH=$root/libs/dbg nunit-console -labels $assembly_name/bin/Debug/$assembly_name.dll
+done
+
+
diff --git a/tools/run_tests/run_node.sh b/tools/run_tests/run_node.sh
index ccf1b9d6f54918d9dd713f95ee1c516a5884a9d8..3a82c04a8ef2f961fbd831ddbff42536f082ffd0 100755
--- a/tools/run_tests/run_node.sh
+++ b/tools/run_tests/run_node.sh
@@ -30,9 +30,13 @@
 
 set -ex
 
+CONFIG=${CONFIG:-opt}
+
 # change to grpc repo root
 cd $(dirname $0)/../..
 
 root=`pwd`
 
+export LD_LIBRARY_PATH=$root/libs/$CONFIG
+
 $root/src/node/node_modules/mocha/bin/mocha $root/src/node/test
diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/run_python.sh
index 9c7dea008dba914fe873ee2cbd28df8ff053bcce..fa1497aee478a8e2b262effc1f729a03704eb517 100755
--- a/tools/run_tests/run_python.sh
+++ b/tools/run_tests/run_python.sh
@@ -36,24 +36,4 @@ cd $(dirname $0)/../..
 root=`pwd`
 export LD_LIBRARY_PATH=$root/libs/opt
 source python2.7_virtual_environment/bin/activate
-# TODO(issue 215): Properly itemize these in run_tests.py so that they can be parallelized.
-# TODO(atash): Enable dynamic unused port discovery for this test.
-# TODO(mlumish): Re-enable this test when we can install protoc
-# python2.7 -B test/compiler/python_plugin_test.py --build_mode=opt
-python2.7 -B -m grpc._adapter._blocking_invocation_inline_service_test
-python2.7 -B -m grpc._adapter._c_test
-python2.7 -B -m grpc._adapter._event_invocation_synchronous_event_service_test
-python2.7 -B -m grpc._adapter._future_invocation_asynchronous_event_service_test
-python2.7 -B -m grpc._adapter._links_test
-python2.7 -B -m grpc._adapter._lonely_rear_link_test
-python2.7 -B -m grpc._adapter._low_test
-python2.7 -B -m grpc.early_adopter.implementations_test
-python2.7 -B -m grpc.framework.assembly.implementations_test
-python2.7 -B -m grpc.framework.base.packets.implementations_test
-python2.7 -B -m grpc.framework.face.blocking_invocation_inline_service_test
-python2.7 -B -m grpc.framework.face.event_invocation_synchronous_event_service_test
-python2.7 -B -m grpc.framework.face.future_invocation_asynchronous_event_service_test
-python2.7 -B -m grpc.framework.foundation._later_test
-python2.7 -B -m grpc.framework.foundation._logging_pool_test
-# TODO(nathaniel): Get tests working under 3.4 (requires 3.X-friendly protobuf)
-# python3.4 -B -m unittest discover -s src/python -p '*.py'
+python2.7 -B $*
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index e949670b8c9de1b0e84d3fadb490eca84040b8b4..aee19cdc42d6ec9b106ce39d045dc679922584a7 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -51,14 +51,28 @@ os.chdir(ROOT)
 # SimpleConfig: just compile with CONFIG=config, and run the binary to test
 class SimpleConfig(object):
 
-  def __init__(self, config, environ={}):
+  def __init__(self, config, environ=None):
+    if environ is None:
+      environ = {}
     self.build_config = config
     self.maxjobs = 2 * multiprocessing.cpu_count()
     self.allow_hashing = (config != 'gcov')
     self.environ = environ
-
-  def job_spec(self, binary, hash_targets):
-    return jobset.JobSpec(cmdline=[binary],
+    self.environ['CONFIG'] = config
+
+  def job_spec(self, cmdline, hash_targets):
+    """Construct a jobset.JobSpec for a test under this config
+
+       Args:
+         cmdline:      a list of strings specifying the command line the test
+                       would like to run
+         hash_targets: either None (don't do caching of test results), or
+                       a list of strings specifying files to include in a
+                       binary hash to check if a test has changed
+                       -- if used, all artifacts needed to run the test must
+                          be listed
+    """
+    return jobset.JobSpec(cmdline=cmdline,
                           environ=self.environ,
                           hash_targets=hash_targets
                               if self.allow_hashing else None)
@@ -67,16 +81,18 @@ class SimpleConfig(object):
 # ValgrindConfig: compile with some CONFIG=config, but use valgrind to run
 class ValgrindConfig(object):
 
-  def __init__(self, config, tool, args=[]):
+  def __init__(self, config, tool, args=None):
+    if args is None:
+      args = []
     self.build_config = config
     self.tool = tool
     self.args = args
     self.maxjobs = 2 * multiprocessing.cpu_count()
     self.allow_hashing = False
 
-  def job_spec(self, binary, hash_targets):
+  def job_spec(self, cmdline, hash_targets):
     return jobset.JobSpec(cmdline=['valgrind', '--tool=%s' % self.tool] +
-                          self.args + [binary],
+                          self.args + cmdline,
                           shortname='valgrind %s' % binary,
                           hash_targets=None)
 
@@ -95,7 +111,7 @@ class CLanguage(object):
       if travis and target['flaky']:
         continue
       binary = 'bins/%s/%s' % (config.build_config, target['name'])
-      out.append(config.job_spec(binary, [binary]))
+      out.append(config.job_spec([binary], [binary]))
     return out
 
   def make_targets(self):
@@ -104,11 +120,17 @@ class CLanguage(object):
   def build_steps(self):
     return []
 
+  def supports_multi_config(self):
+    return True
+
+  def __str__(self):
+    return self.make_target
+
 
 class NodeLanguage(object):
 
   def test_specs(self, config, travis):
-    return [config.job_spec('tools/run_tests/run_node.sh', None)]
+    return [config.job_spec(['tools/run_tests/run_node.sh'], None)]
 
   def make_targets(self):
     return ['static_c']
@@ -116,11 +138,17 @@ class NodeLanguage(object):
   def build_steps(self):
     return [['tools/run_tests/build_node.sh']]
 
+  def supports_multi_config(self):
+    return False
+
+  def __str__(self):
+    return 'node'
+
 
 class PhpLanguage(object):
 
   def test_specs(self, config, travis):
-    return [config.job_spec('src/php/bin/run_tests.sh', None)]
+    return [config.job_spec(['src/php/bin/run_tests.sh'], None)]
 
   def make_targets(self):
     return ['static_c']
@@ -128,22 +156,44 @@ class PhpLanguage(object):
   def build_steps(self):
     return [['tools/run_tests/build_php.sh']]
 
+  def supports_multi_config(self):
+    return False
+
+  def __str__(self):
+    return 'php'
+
 
 class PythonLanguage(object):
 
+  def __init__(self):
+    with open('tools/run_tests/python_tests.json') as f:
+      self._tests = json.load(f)
+
   def test_specs(self, config, travis):
-    return [config.job_spec('tools/run_tests/run_python.sh', None)]
+    modules = [config.job_spec(['tools/run_tests/run_python.sh', '-m',
+                                test['module']], None)
+               for test in self._tests if 'module' in test]
+    files = [config.job_spec(['tools/run_tests/run_python.sh',
+                              test['file']], None)
+             for test in self._tests if 'file' in test]
+    return files + modules
 
   def make_targets(self):
-    return ['static_c']
+    return ['static_c', 'grpc_python_plugin']
 
   def build_steps(self):
     return [['tools/run_tests/build_python.sh']]
 
+  def supports_multi_config(self):
+    return False
+
+  def __str__(self):
+    return 'python'
+
 class RubyLanguage(object):
 
   def test_specs(self, config, travis):
-    return [config.job_spec('tools/run_tests/run_ruby.sh', None)]
+    return [config.job_spec(['tools/run_tests/run_ruby.sh'], None)]
 
   def make_targets(self):
     return ['static_c']
@@ -151,6 +201,28 @@ class RubyLanguage(object):
   def build_steps(self):
     return [['tools/run_tests/build_ruby.sh']]
 
+  def supports_multi_config(self):
+    return False
+
+  def __str__(self):
+    return 'ruby'
+
+class CSharpLanguage(object):
+
+  def test_specs(self, config, travis):
+    return [config.job_spec('tools/run_tests/run_csharp.sh', None)]
+
+  def make_targets(self):
+    return ['grpc_csharp_ext']
+
+  def build_steps(self):
+    return [['tools/run_tests/build_csharp.sh']]
+
+  def supports_multi_config(self):
+    return False
+
+  def __str__(self):
+    return 'csharp'
 
 # different configurations we can run under
 _CONFIGS = {
@@ -175,7 +247,8 @@ _LANGUAGES = {
     'node': NodeLanguage(),
     'php': PhpLanguage(),
     'python': PythonLanguage(),
-    'ruby': RubyLanguage()
+    'ruby': RubyLanguage(),
+    'csharp': CSharpLanguage()
     }
 
 # parse command line
@@ -215,6 +288,13 @@ build_configs = set(cfg.build_config for cfg in run_configs)
 
 make_targets = []
 languages = set(_LANGUAGES[l] for l in args.language)
+
+if len(build_configs) > 1:
+  for language in languages:
+    if not language.supports_multi_config():
+      print language, 'does not support multiple build configurations'
+      sys.exit(1)
+
 build_steps = [jobset.JobSpec(['make',
                                '-j', '%d' % (multiprocessing.cpu_count() + 1),
                                'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
@@ -222,7 +302,8 @@ build_steps = [jobset.JobSpec(['make',
                                    itertools.chain.from_iterable(
                                        l.make_targets() for l in languages))))
                for cfg in build_configs] + list(set(
-                   jobset.JobSpec(cmdline)
+                   jobset.JobSpec(cmdline, environ={'CONFIG': cfg})
+                   for cfg in build_configs
                    for l in languages
                    for cmdline in l.build_steps()))
 one_run = set(
@@ -298,7 +379,7 @@ test_cache.maybe_load()
 if forever:
   success = True
   while True:
-    dw = watch_dirs.DirWatcher(['src', 'include', 'test'])
+    dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
     initial_time = dw.most_recent_change()
     have_files_changed = lambda: dw.most_recent_change() != initial_time
     previous_success = success
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index 06bf58f93e8f8655bb23b50b9711c861eada67d9..6f354452aa509d5c7d6e102d50bc44ea9e981e51 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -381,6 +381,11 @@
     "language": "c++", 
     "name": "thread_pool_test"
   }, 
+  {
+    "flaky": false, 
+    "language": "c", 
+    "name": "chttp2_fake_security_bad_hostname_test"
+  }, 
   {
     "flaky": false, 
     "language": "c", 
@@ -621,6 +626,11 @@
     "language": "c", 
     "name": "chttp2_fake_security_writes_done_hangs_with_pending_read_legacy_test"
   }, 
+  {
+    "flaky": false, 
+    "language": "c", 
+    "name": "chttp2_fullstack_bad_hostname_test"
+  }, 
   {
     "flaky": false, 
     "language": "c", 
@@ -861,6 +871,11 @@
     "language": "c", 
     "name": "chttp2_fullstack_writes_done_hangs_with_pending_read_legacy_test"
   }, 
+  {
+    "flaky": false, 
+    "language": "c", 
+    "name": "chttp2_fullstack_uds_bad_hostname_test"
+  }, 
   {
     "flaky": false, 
     "language": "c", 
@@ -1101,6 +1116,11 @@
     "language": "c", 
     "name": "chttp2_fullstack_uds_writes_done_hangs_with_pending_read_legacy_test"
   }, 
+  {
+    "flaky": false, 
+    "language": "c", 
+    "name": "chttp2_simple_ssl_fullstack_bad_hostname_test"
+  }, 
   {
     "flaky": false, 
     "language": "c", 
@@ -1341,6 +1361,11 @@
     "language": "c", 
     "name": "chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_legacy_test"
   }, 
+  {
+    "flaky": false, 
+    "language": "c", 
+    "name": "chttp2_simple_ssl_with_oauth2_fullstack_bad_hostname_test"
+  }, 
   {
     "flaky": false, 
     "language": "c", 
@@ -1581,6 +1606,11 @@
     "language": "c", 
     "name": "chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_legacy_test"
   }, 
+  {
+    "flaky": false, 
+    "language": "c", 
+    "name": "chttp2_socket_pair_bad_hostname_test"
+  }, 
   {
     "flaky": false, 
     "language": "c", 
@@ -1821,6 +1851,11 @@
     "language": "c", 
     "name": "chttp2_socket_pair_writes_done_hangs_with_pending_read_legacy_test"
   }, 
+  {
+    "flaky": false, 
+    "language": "c", 
+    "name": "chttp2_socket_pair_one_byte_at_a_time_bad_hostname_test"
+  }, 
   {
     "flaky": false, 
     "language": "c", 
diff --git a/vsprojects/vs2013/grpc.vcxproj b/vsprojects/vs2013/grpc.vcxproj
index 60dc98024204664be8b8776949deb42c01e05c38..9d0bcb327163f30a4e6899b08b7c7974763a4315 100644
--- a/vsprojects/vs2013/grpc.vcxproj
+++ b/vsprojects/vs2013/grpc.vcxproj
@@ -161,7 +161,6 @@
     <ClInclude Include="..\..\src\core\surface\completion_queue.h" />
     <ClInclude Include="..\..\src\core\surface\event_string.h" />
     <ClInclude Include="..\..\src\core\surface\init.h" />
-    <ClInclude Include="..\..\src\core\surface\lame_client.h" />
     <ClInclude Include="..\..\src\core\surface\server.h" />
     <ClInclude Include="..\..\src\core\surface\surface_trace.h" />
     <ClInclude Include="..\..\src\core\transport\chttp2\bin_encoder.h" />
@@ -224,8 +223,6 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\surface\secure_channel_create.c">
     </ClCompile>
-    <ClCompile Include="..\..\src\core\surface\secure_server_create.c">
-    </ClCompile>
     <ClCompile Include="..\..\src\core\tsi\fake_transport_security.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\tsi\ssl_transport_security.c">
diff --git a/vsprojects/vs2013/grpc.vcxproj.filters b/vsprojects/vs2013/grpc.vcxproj.filters
index 9fc70e74d9de6dbd3573a6984f14b83e384a9677..af38d8de35ad2d37df2b247db721290a646fb91b 100644
--- a/vsprojects/vs2013/grpc.vcxproj.filters
+++ b/vsprojects/vs2013/grpc.vcxproj.filters
@@ -55,9 +55,6 @@
     <ClCompile Include="..\..\src\core\surface\secure_channel_create.c">
       <Filter>src\core\surface</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\core\surface\secure_server_create.c">
-      <Filter>src\core\surface</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\src\core\tsi\fake_transport_security.c">
       <Filter>src\core\tsi</Filter>
     </ClCompile>
@@ -599,9 +596,6 @@
     <ClInclude Include="..\..\src\core\surface\init.h">
       <Filter>src\core\surface</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\core\surface\lame_client.h">
-      <Filter>src\core\surface</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\src\core\surface\server.h">
       <Filter>src\core\surface</Filter>
     </ClInclude>
diff --git a/vsprojects/vs2013/grpc_shared.vcxproj b/vsprojects/vs2013/grpc_shared.vcxproj
index 5c2e23b51398b81cd3a051c40813b376ecc8091c..f5575dc3f12db5abf050bdf6d44168443c848b47 100644
--- a/vsprojects/vs2013/grpc_shared.vcxproj
+++ b/vsprojects/vs2013/grpc_shared.vcxproj
@@ -165,7 +165,6 @@
     <ClInclude Include="..\..\src\core\surface\completion_queue.h" />
     <ClInclude Include="..\..\src\core\surface\event_string.h" />
     <ClInclude Include="..\..\src\core\surface\init.h" />
-    <ClInclude Include="..\..\src\core\surface\lame_client.h" />
     <ClInclude Include="..\..\src\core\surface\server.h" />
     <ClInclude Include="..\..\src\core\surface\surface_trace.h" />
     <ClInclude Include="..\..\src\core\transport\chttp2\bin_encoder.h" />
@@ -228,8 +227,6 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\surface\secure_channel_create.c">
     </ClCompile>
-    <ClCompile Include="..\..\src\core\surface\secure_server_create.c">
-    </ClCompile>
     <ClCompile Include="..\..\src\core\tsi\fake_transport_security.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\tsi\ssl_transport_security.c">
diff --git a/vsprojects/vs2013/grpc_shared.vcxproj.filters b/vsprojects/vs2013/grpc_shared.vcxproj.filters
index 9fc70e74d9de6dbd3573a6984f14b83e384a9677..af38d8de35ad2d37df2b247db721290a646fb91b 100644
--- a/vsprojects/vs2013/grpc_shared.vcxproj.filters
+++ b/vsprojects/vs2013/grpc_shared.vcxproj.filters
@@ -55,9 +55,6 @@
     <ClCompile Include="..\..\src\core\surface\secure_channel_create.c">
       <Filter>src\core\surface</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\core\surface\secure_server_create.c">
-      <Filter>src\core\surface</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\src\core\tsi\fake_transport_security.c">
       <Filter>src\core\tsi</Filter>
     </ClCompile>
@@ -599,9 +596,6 @@
     <ClInclude Include="..\..\src\core\surface\init.h">
       <Filter>src\core\surface</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\core\surface\lame_client.h">
-      <Filter>src\core\surface</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\src\core\surface\server.h">
       <Filter>src\core\surface</Filter>
     </ClInclude>
diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj
index 9181db0b4e126f3e7d6caf919d45068ebfae4adc..ad7bf4762f57d451709e44349da6105c1276bef2 100644
--- a/vsprojects/vs2013/grpc_unsecure.vcxproj
+++ b/vsprojects/vs2013/grpc_unsecure.vcxproj
@@ -146,7 +146,6 @@
     <ClInclude Include="..\..\src\core\surface\completion_queue.h" />
     <ClInclude Include="..\..\src\core\surface\event_string.h" />
     <ClInclude Include="..\..\src\core\surface\init.h" />
-    <ClInclude Include="..\..\src\core\surface\lame_client.h" />
     <ClInclude Include="..\..\src\core\surface\server.h" />
     <ClInclude Include="..\..\src\core\surface\surface_trace.h" />
     <ClInclude Include="..\..\src\core\transport\chttp2\bin_encoder.h" />
diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
index 4583fa41eab8a2baeb584cde59c31e8162bfed42..205942a45099d7d05ab7db874c5f6ef7edf102f4 100644
--- a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
@@ -491,9 +491,6 @@
     <ClInclude Include="..\..\src\core\surface\init.h">
       <Filter>src\core\surface</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\core\surface\lame_client.h">
-      <Filter>src\core\surface</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\src\core\surface\server.h">
       <Filter>src\core\surface</Filter>
     </ClInclude>