diff --git a/.travis.yml b/.travis.yml
index b6c80629852dbb9c86a5103b5bef94c514517efb..d2d1c8ba63994226dc3401323252ca7364fa8951 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,42 +1,32 @@
-language: cpp
-before_install:
-  - sudo add-apt-repository ppa:yjwong/gflags -y
-  - sudo add-apt-repository ppa:h-rayflood/llvm -y
-  - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
-  - echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
-  - echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
-  - sudo apt-get update -qq
-  - sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv python-dev python3-dev clang-3.5
-  - sudo pip install --upgrade virtualenv
-  - sudo pip install cpp-coveralls mako simplejson
-  - sudo apt-get install -qq mono-devel nunit
-  - wget www.nuget.org/NuGet.exe -O nuget.exe
+language: objective-c
+osx_image: xcode7.1
 env:
   global:
-    - RUBY_VERSION=2.1
-    - COVERALLS_PARALLEL=true
-    - CPPFLAGS=-I/tmp/prebuilt/include
-    - NUGET="mono nuget.exe"
-  matrix:
-    - CONFIG=opt TEST=sanity JOBS=1
-    - CONFIG=gcov TEST=c JOBS=16
-    - CONFIG=gcov TEST=c++ JOBS=16
-    - CONFIG=opt TEST=c JOBS=16
-    - CONFIG=opt TEST=c++ JOBS=16
-    - CONFIG=opt TEST=node JOBS=16
-    - CONFIG=opt TEST=ruby JOBS=16
-    - CONFIG=opt TEST=python JOBS=1
-    - CONFIG=opt TEST=csharp JOBS=16
-    - USE_GCC=4.4 CONFIG=opt TEST=build JOBS=16
-script:
-  - rvm use $RUBY_VERSION
-  - gem install bundler
-  - ./tools/run_tests/prepare_travis.sh
-  - if [ ! -z "$USE_GCC" ] ; then export CC=gcc-$USE_GCC ; export CXX=g++-$USE_GCC ; fi
-  - ./tools/run_tests/run_tests.py -l $TEST -t -j $JOBS -c $CONFIG -s 4.0
-after_success:
-  - if [ "$CONFIG" = "gcov" ] ; then coveralls --exclude third_party --exclude gens --exclude test --exclude tools --exclude src/compiler -b. --gcov-options '\-p' ; fi
+    - CONFIG=opt
+    - TEST=objc
+    - JOBS=1
+before_install:
+  - brew install gflags
+  # Pod install does this too, but we don't want the output.
+  - pod repo update --silent
+install:
+  - make grpc_objective_c_plugin
+  - pushd src/objective-c/tests
+  # Needs to be verbose, or otherwise OpenSSL's prepare_command makes Travis
+  # time out:
+  - pod install --verbose
+  - popd
+before_script:
+  - make interop_server
+  - bins/$CONFIG/interop_server --port=5050 &
+  - bins/$CONFIG/interop_server --port=5051 --use_tls &
+xcode_workspace: src/objective-c/tests/Tests.xcworkspace
+xcode_scheme:
+  - RxLibraryUnitTests
+  - InteropTestsLocalSSL
+  - InteropTestsLocalCleartext
+  # TODO(jcanizales): Investigate why they time out:
+  # - InteropTestsRemote
+xcode_sdk: iphonesimulator9.1
 notifications:
   email: false
-  webhooks:
-    - https://coveralls.io/webhook?repo_token=54IxAHPjJNdQJzJAhPU0MFpCtg7KvcydQ
diff --git a/Makefile b/Makefile
index c4183914e6354c44f7e66e6d84ea1a3f83328f60..9d94ee8599209309fd800b9616dc820e8a411bd0 100644
--- a/Makefile
+++ b/Makefile
@@ -889,6 +889,7 @@ qps_worker: $(BINDIR)/$(CONFIG)/qps_worker
 reconnect_interop_client: $(BINDIR)/$(CONFIG)/reconnect_interop_client
 reconnect_interop_server: $(BINDIR)/$(CONFIG)/reconnect_interop_server
 secure_auth_context_test: $(BINDIR)/$(CONFIG)/secure_auth_context_test
+secure_sync_unary_ping_pong_test: $(BINDIR)/$(CONFIG)/secure_sync_unary_ping_pong_test
 server_crash_test: $(BINDIR)/$(CONFIG)/server_crash_test
 server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client
 shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test
@@ -1802,7 +1803,7 @@ buildtests: buildtests_c buildtests_cxx buildtests_zookeeper
 
 buildtests_c: privatelibs_c $(BINDIR)/$(CONFIG)/alpn_test $(BINDIR)/$(CONFIG)/bin_encoder_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/compression_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/endpoint_pair_test $(BINDIR)/$(CONFIG)/fd_conservation_posix_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_cmdline_test $(BINDIR)/$(CONFIG)/gpr_cpu_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_stack_lockfree_test $(BINDIR)/$(CONFIG)/gpr_string_test $(BINDIR)/$(CONFIG)/gpr_sync_test $(BINDIR)/$(CONFIG)/gpr_thd_test $(BINDIR)/$(CONFIG)/gpr_time_test $(BINDIR)/$(CONFIG)/gpr_tls_test $(BINDIR)/$(CONFIG)/gpr_useful_test $(BINDIR)/$(CONFIG)/grpc_auth_context_test $(BINDIR)/$(CONFIG)/grpc_base64_test $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test $(BINDIR)/$(CONFIG)/grpc_channel_args_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_jwt_verifier_test $(BINDIR)/$(CONFIG)/grpc_security_connector_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)/lb_policies_test $(BINDIR)/$(CONFIG)/message_compress_test $(BINDIR)/$(CONFIG)/multi_init_test $(BINDIR)/$(CONFIG)/multiple_server_queues_test $(BINDIR)/$(CONFIG)/murmur_hash_test $(BINDIR)/$(CONFIG)/no_server_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)/timeout_encoding_test $(BINDIR)/$(CONFIG)/timer_heap_test $(BINDIR)/$(CONFIG)/timer_list_test $(BINDIR)/$(CONFIG)/timers_test $(BINDIR)/$(CONFIG)/transport_metadata_test $(BINDIR)/$(CONFIG)/transport_security_test $(BINDIR)/$(CONFIG)/udp_server_test $(BINDIR)/$(CONFIG)/uri_parser_test $(BINDIR)/$(CONFIG)/workqueue_test $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_call_creds_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_compress_default_host_test $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_test $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_test $(BINDIR)/$(CONFIG)/h2_compress_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_no_op_test $(BINDIR)/$(CONFIG)/h2_compress_payload_test $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_compress_registered_call_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_compress_simple_request_test $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_fakesec_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_call_creds_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_fakesec_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_fakesec_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_fakesec_default_host_test $(BINDIR)/$(CONFIG)/h2_fakesec_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_fakesec_empty_batch_test $(BINDIR)/$(CONFIG)/h2_fakesec_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_fakesec_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_fakesec_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_large_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_fakesec_max_message_length_test $(BINDIR)/$(CONFIG)/h2_fakesec_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_no_op_test $(BINDIR)/$(CONFIG)/h2_fakesec_payload_test $(BINDIR)/$(CONFIG)/h2_fakesec_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_fakesec_registered_call_test $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_fakesec_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_fakesec_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_simple_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_full_call_creds_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_full_default_host_test $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_full_empty_batch_test $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_full_large_metadata_test $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_full_max_message_length_test $(BINDIR)/$(CONFIG)/h2_full_metadata_test $(BINDIR)/$(CONFIG)/h2_full_no_op_test $(BINDIR)/$(CONFIG)/h2_full_payload_test $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_full_registered_call_test $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_full_simple_request_test $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_call_creds_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_test $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_test $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_test $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_test $(BINDIR)/$(CONFIG)/h2_full+poll_payload_test $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_oauth2_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_call_creds_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_oauth2_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_oauth2_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_oauth2_default_host_test $(BINDIR)/$(CONFIG)/h2_oauth2_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_oauth2_empty_batch_test $(BINDIR)/$(CONFIG)/h2_oauth2_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_oauth2_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_oauth2_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_large_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_oauth2_max_message_length_test $(BINDIR)/$(CONFIG)/h2_oauth2_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_no_op_test $(BINDIR)/$(CONFIG)/h2_oauth2_payload_test $(BINDIR)/$(CONFIG)/h2_oauth2_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_oauth2_registered_call_test $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_oauth2_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_oauth2_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_simple_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_call_creds_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_proxy_default_host_test $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_test $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_test $(BINDIR)/$(CONFIG)/h2_proxy_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_no_op_test $(BINDIR)/$(CONFIG)/h2_proxy_payload_test $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_test $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_test $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_call_creds_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_test $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_test $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_test $(BINDIR)/$(CONFIG)/h2_sockpair_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_call_creds_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_call_creds_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_ssl_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_call_creds_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_ssl_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_ssl_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_default_host_test $(BINDIR)/$(CONFIG)/h2_ssl_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_ssl_empty_batch_test $(BINDIR)/$(CONFIG)/h2_ssl_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_ssl_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_ssl_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_ssl_large_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_ssl_max_message_length_test $(BINDIR)/$(CONFIG)/h2_ssl_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_no_op_test $(BINDIR)/$(CONFIG)/h2_ssl_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_ssl_registered_call_test $(BINDIR)/$(CONFIG)/h2_ssl_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_ssl_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_ssl_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_ssl_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_call_creds_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_default_host_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_empty_batch_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_large_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_message_length_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_no_op_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_payload_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_registered_call_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_call_creds_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_default_host_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_empty_batch_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_large_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_max_message_length_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_no_op_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_registered_call_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_uchannel_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_uchannel_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_uchannel_call_creds_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_uchannel_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_uchannel_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_uchannel_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_uchannel_default_host_test $(BINDIR)/$(CONFIG)/h2_uchannel_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_uchannel_empty_batch_test $(BINDIR)/$(CONFIG)/h2_uchannel_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_uchannel_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_uchannel_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_uchannel_large_metadata_test $(BINDIR)/$(CONFIG)/h2_uchannel_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_uchannel_max_message_length_test $(BINDIR)/$(CONFIG)/h2_uchannel_metadata_test $(BINDIR)/$(CONFIG)/h2_uchannel_no_op_test $(BINDIR)/$(CONFIG)/h2_uchannel_payload_test $(BINDIR)/$(CONFIG)/h2_uchannel_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_uchannel_registered_call_test $(BINDIR)/$(CONFIG)/h2_uchannel_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_uchannel_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_uchannel_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_uchannel_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_uchannel_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_uchannel_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_uchannel_simple_request_test $(BINDIR)/$(CONFIG)/h2_uchannel_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_call_creds_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_test $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_test $(BINDIR)/$(CONFIG)/h2_uds_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_no_op_test $(BINDIR)/$(CONFIG)/h2_uds_payload_test $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_uds_registered_call_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_uds_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_call_creds_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_test $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_test $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_test $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_test $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_full_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_full_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_full_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_full_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_full_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uchannel_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test
 
-buildtests_cxx: buildtests_zookeeper privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test $(BINDIR)/$(CONFIG)/auth_property_iterator_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/cli_call_test $(BINDIR)/$(CONFIG)/client_crash_test $(BINDIR)/$(CONFIG)/client_crash_test_server $(BINDIR)/$(CONFIG)/credentials_test $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test $(BINDIR)/$(CONFIG)/cxx_slice_test $(BINDIR)/$(CONFIG)/cxx_string_ref_test $(BINDIR)/$(CONFIG)/cxx_time_test $(BINDIR)/$(CONFIG)/end2end_test $(BINDIR)/$(CONFIG)/generic_end2end_test $(BINDIR)/$(CONFIG)/grpc_cli $(BINDIR)/$(CONFIG)/interop_client $(BINDIR)/$(CONFIG)/interop_server $(BINDIR)/$(CONFIG)/interop_test $(BINDIR)/$(CONFIG)/mock_test $(BINDIR)/$(CONFIG)/qps_interarrival_test $(BINDIR)/$(CONFIG)/qps_openloop_test $(BINDIR)/$(CONFIG)/qps_test $(BINDIR)/$(CONFIG)/reconnect_interop_client $(BINDIR)/$(CONFIG)/reconnect_interop_server $(BINDIR)/$(CONFIG)/secure_auth_context_test $(BINDIR)/$(CONFIG)/server_crash_test $(BINDIR)/$(CONFIG)/server_crash_test_client $(BINDIR)/$(CONFIG)/shutdown_test $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/streaming_throughput_test $(BINDIR)/$(CONFIG)/stress_test $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test $(BINDIR)/$(CONFIG)/thread_stress_test
+buildtests_cxx: buildtests_zookeeper privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test $(BINDIR)/$(CONFIG)/auth_property_iterator_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/cli_call_test $(BINDIR)/$(CONFIG)/client_crash_test $(BINDIR)/$(CONFIG)/client_crash_test_server $(BINDIR)/$(CONFIG)/credentials_test $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test $(BINDIR)/$(CONFIG)/cxx_slice_test $(BINDIR)/$(CONFIG)/cxx_string_ref_test $(BINDIR)/$(CONFIG)/cxx_time_test $(BINDIR)/$(CONFIG)/end2end_test $(BINDIR)/$(CONFIG)/generic_end2end_test $(BINDIR)/$(CONFIG)/grpc_cli $(BINDIR)/$(CONFIG)/interop_client $(BINDIR)/$(CONFIG)/interop_server $(BINDIR)/$(CONFIG)/interop_test $(BINDIR)/$(CONFIG)/mock_test $(BINDIR)/$(CONFIG)/qps_interarrival_test $(BINDIR)/$(CONFIG)/qps_openloop_test $(BINDIR)/$(CONFIG)/qps_test $(BINDIR)/$(CONFIG)/reconnect_interop_client $(BINDIR)/$(CONFIG)/reconnect_interop_server $(BINDIR)/$(CONFIG)/secure_auth_context_test $(BINDIR)/$(CONFIG)/secure_sync_unary_ping_pong_test $(BINDIR)/$(CONFIG)/server_crash_test $(BINDIR)/$(CONFIG)/server_crash_test_client $(BINDIR)/$(CONFIG)/shutdown_test $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/streaming_throughput_test $(BINDIR)/$(CONFIG)/stress_test $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test $(BINDIR)/$(CONFIG)/thread_stress_test
 
 ifeq ($(HAS_ZOOKEEPER),true)
 buildtests_zookeeper: privatelibs_zookeeper $(BINDIR)/$(CONFIG)/zookeeper_test
@@ -3546,12 +3547,12 @@ test_cxx: test_zookeeper buildtests_cxx
 	$(Q) $(BINDIR)/$(CONFIG)/interop_test || ( echo test interop_test failed ; exit 1 )
 	$(E) "[RUN]     Testing mock_test"
 	$(Q) $(BINDIR)/$(CONFIG)/mock_test || ( echo test mock_test failed ; exit 1 )
-	$(E) "[RUN]     Testing qps_openloop_test"
-	$(Q) $(BINDIR)/$(CONFIG)/qps_openloop_test || ( echo test qps_openloop_test failed ; exit 1 )
 	$(E) "[RUN]     Testing qps_test"
 	$(Q) $(BINDIR)/$(CONFIG)/qps_test || ( echo test qps_test failed ; exit 1 )
 	$(E) "[RUN]     Testing secure_auth_context_test"
 	$(Q) $(BINDIR)/$(CONFIG)/secure_auth_context_test || ( echo test secure_auth_context_test failed ; exit 1 )
+	$(E) "[RUN]     Testing secure_sync_unary_ping_pong_test"
+	$(Q) $(BINDIR)/$(CONFIG)/secure_sync_unary_ping_pong_test || ( echo test secure_sync_unary_ping_pong_test failed ; exit 1 )
 	$(E) "[RUN]     Testing server_crash_test"
 	$(Q) $(BINDIR)/$(CONFIG)/server_crash_test || ( echo test server_crash_test failed ; exit 1 )
 	$(E) "[RUN]     Testing shutdown_test"
@@ -3560,8 +3561,6 @@ test_cxx: test_zookeeper buildtests_cxx
 	$(Q) $(BINDIR)/$(CONFIG)/status_test || ( echo test status_test failed ; exit 1 )
 	$(E) "[RUN]     Testing streaming_throughput_test"
 	$(Q) $(BINDIR)/$(CONFIG)/streaming_throughput_test || ( echo test streaming_throughput_test failed ; exit 1 )
-	$(E) "[RUN]     Testing stress_test"
-	$(Q) $(BINDIR)/$(CONFIG)/stress_test || ( echo test stress_test failed ; exit 1 )
 	$(E) "[RUN]     Testing sync_streaming_ping_pong_test"
 	$(Q) $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test || ( echo test sync_streaming_ping_pong_test failed ; exit 1 )
 	$(E) "[RUN]     Testing sync_unary_ping_pong_test"
@@ -3757,45 +3756,90 @@ $(GENDIR)/test/cpp/util/messages.grpc.pb.cc: test/cpp/util/messages.proto $(PROT
 endif
 
 ifeq ($(NO_PROTOC),true)
-$(GENDIR)/test/proto/empty.pb.cc: protoc_dep_error
-$(GENDIR)/test/proto/empty.grpc.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/benchmarks/control.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc: protoc_dep_error
 else
-$(GENDIR)/test/proto/empty.pb.cc: test/proto/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+$(GENDIR)/test/proto/benchmarks/control.pb.cc: test/proto/benchmarks/control.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
 	$(E) "[PROTOC]  Generating protobuf CC file from $<"
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
 
-$(GENDIR)/test/proto/empty.grpc.pb.cc: test/proto/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+$(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc: test/proto/benchmarks/control.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
 endif
 
 ifeq ($(NO_PROTOC),true)
-$(GENDIR)/test/proto/messages.pb.cc: protoc_dep_error
-$(GENDIR)/test/proto/messages.grpc.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/benchmarks/payloads.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc: protoc_dep_error
 else
-$(GENDIR)/test/proto/messages.pb.cc: test/proto/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+$(GENDIR)/test/proto/benchmarks/payloads.pb.cc: test/proto/benchmarks/payloads.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
 	$(E) "[PROTOC]  Generating protobuf CC file from $<"
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
 
-$(GENDIR)/test/proto/messages.grpc.pb.cc: test/proto/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+$(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc: test/proto/benchmarks/payloads.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
 endif
 
 ifeq ($(NO_PROTOC),true)
-$(GENDIR)/test/proto/qpstest.pb.cc: protoc_dep_error
-$(GENDIR)/test/proto/qpstest.grpc.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/benchmarks/services.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc: protoc_dep_error
 else
-$(GENDIR)/test/proto/qpstest.pb.cc: test/proto/qpstest.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+$(GENDIR)/test/proto/benchmarks/services.pb.cc: test/proto/benchmarks/services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
 	$(E) "[PROTOC]  Generating protobuf CC file from $<"
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
 
-$(GENDIR)/test/proto/qpstest.grpc.pb.cc: test/proto/qpstest.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+$(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc: test/proto/benchmarks/services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
+endif
+
+ifeq ($(NO_PROTOC),true)
+$(GENDIR)/test/proto/benchmarks/stats.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc: protoc_dep_error
+else
+$(GENDIR)/test/proto/benchmarks/stats.pb.cc: test/proto/benchmarks/stats.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
+
+$(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc: test/proto/benchmarks/stats.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
+endif
+
+ifeq ($(NO_PROTOC),true)
+$(GENDIR)/test/proto/empty.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/empty.grpc.pb.cc: protoc_dep_error
+else
+$(GENDIR)/test/proto/empty.pb.cc: test/proto/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
+
+$(GENDIR)/test/proto/empty.grpc.pb.cc: test/proto/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
+endif
+
+ifeq ($(NO_PROTOC),true)
+$(GENDIR)/test/proto/messages.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/messages.grpc.pb.cc: protoc_dep_error
+else
+$(GENDIR)/test/proto/messages.pb.cc: test/proto/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
+
+$(GENDIR)/test/proto/messages.grpc.pb.cc: test/proto/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
 	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
@@ -5392,7 +5436,11 @@ $(OBJDIR)/$(CONFIG)/test/cpp/interop/server.o: $(GENDIR)/test/proto/empty.pb.cc
 
 
 LIBQPS_SRC = \
-    $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc \
+    $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc \
+    $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc \
+    $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc \
+    $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc \
+    $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc \
     $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc \
     test/cpp/qps/client_async.cc \
     test/cpp/qps/client_sync.cc \
@@ -5447,16 +5495,16 @@ ifneq ($(NO_DEPS),true)
 -include $(LIBQPS_OBJS:.o=.dep)
 endif
 endif
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/perf_db_client.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/proto/qpstest.pb.cc $(GENDIR)/test/proto/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/perf_db_client.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/control.pb.cc $(GENDIR)/test/proto/benchmarks/control.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.pb.cc $(GENDIR)/test/proto/benchmarks/payloads.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/services.pb.cc $(GENDIR)/test/proto/benchmarks/services.grpc.pb.cc $(GENDIR)/test/proto/benchmarks/stats.pb.cc $(GENDIR)/test/proto/benchmarks/stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
 
 
 LIBGRPC_CSHARP_EXT_SRC = \
@@ -10239,6 +10287,7 @@ ifneq ($(NO_DEPS),true)
 -include $(RECONNECT_INTEROP_CLIENT_OBJS:.o=.dep)
 endif
 endif
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/reconnect_interop_client.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc
 
 
 RECONNECT_INTEROP_SERVER_SRC = \
@@ -10285,6 +10334,7 @@ ifneq ($(NO_DEPS),true)
 -include $(RECONNECT_INTEROP_SERVER_OBJS:.o=.dep)
 endif
 endif
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/reconnect_interop_server.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc
 
 
 SECURE_AUTH_CONTEXT_TEST_SRC = \
@@ -10327,6 +10377,46 @@ endif
 endif
 
 
+SECURE_SYNC_UNARY_PING_PONG_TEST_SRC = \
+    test/cpp/qps/secure_sync_unary_ping_pong_test.cc \
+
+SECURE_SYNC_UNARY_PING_PONG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SECURE_SYNC_UNARY_PING_PONG_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/secure_sync_unary_ping_pong_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)/secure_sync_unary_ping_pong_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/secure_sync_unary_ping_pong_test: $(PROTOBUF_DEP) $(SECURE_SYNC_UNARY_PING_PONG_TEST_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) $(SECURE_SYNC_UNARY_PING_PONG_TEST_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 $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/secure_sync_unary_ping_pong_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/secure_sync_unary_ping_pong_test.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_secure_sync_unary_ping_pong_test: $(SECURE_SYNC_UNARY_PING_PONG_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SECURE_SYNC_UNARY_PING_PONG_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 SERVER_CRASH_TEST_SRC = \
     test/cpp/end2end/server_crash_test.cc \
 
@@ -10575,6 +10665,9 @@ ifneq ($(NO_DEPS),true)
 -include $(STRESS_TEST_OBJS:.o=.dep)
 endif
 endif
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/interop_client.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/stress_interop_client.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/stress_test.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc
 
 
 SYNC_STREAMING_PING_PONG_TEST_SRC = \
diff --git a/build.yaml b/build.yaml
index 4f6e35b0de1c25503687ce23db1e81f101d2ffe0..873606f98064fcb9d8fbbb5cf5c588b7f5e751bb 100644
--- a/build.yaml
+++ b/build.yaml
@@ -751,7 +751,11 @@ libs:
   - test/cpp/qps/timer.h
   - test/cpp/util/benchmark_config.h
   src:
-  - test/proto/qpstest.proto
+  - test/proto/messages.proto
+  - test/proto/benchmarks/control.proto
+  - test/proto/benchmarks/payloads.proto
+  - test/proto/benchmarks/services.proto
+  - test/proto/benchmarks/stats.proto
   - test/cpp/qps/perf_db.proto
   - test/cpp/qps/client_async.cc
   - test/cpp/qps/client_sync.cc
@@ -1937,6 +1941,7 @@ targets:
   - posix
 - name: qps_openloop_test
   build: test
+  run: false
   language: c++
   src:
   - test/cpp/qps/qps_openloop_test.cc
@@ -2037,6 +2042,23 @@ targets:
   - grpc
   - gpr_test_util
   - gpr
+- name: secure_sync_unary_ping_pong_test
+  build: test
+  language: c++
+  src:
+  - test/cpp/qps/secure_sync_unary_ping_pong_test.cc
+  deps:
+  - qps
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
+  platforms:
+  - mac
+  - linux
+  - posix
 - name: server_crash_test
   build: test
   language: c++
@@ -2107,6 +2129,7 @@ targets:
   - posix
 - name: stress_test
   build: test
+  run: false
   language: c++
   headers:
   - test/cpp/interop/client_helper.h
diff --git a/doc/PROTOCOL-HTTP2.md b/doc/PROTOCOL-HTTP2.md
index 02d4f28102b00e1f26c958fa67c47e9f07409f99..7f7055bb4df7b35391a7f25761ab1c28bfe6708f 100644
--- a/doc/PROTOCOL-HTTP2.md
+++ b/doc/PROTOCOL-HTTP2.md
@@ -10,58 +10,64 @@ Production rules are using <a href="http://tools.ietf.org/html/rfc5234">ABNF syn
 
 The following is the general sequence of message atoms in a GRPC request & response message stream
 
-* Request → Request-Headers *Delimited-Message EOS
-* Response → (Response-Headers *Delimited-Message Trailers) / Trailers-Only
+* Request → Request-Headers \*Length-Prefixed-Message EOS
+* Response → (Response-Headers \*Length-Prefixed-Message Trailers) / Trailers-Only
 
 
 ### Requests
 
-* Request → Request-Headers *Delimited-Message EOS
+* Request → Request-Headers \*Length-Prefixed-Message EOS
 
 Request-Headers are delivered as HTTP2 headers in HEADERS + CONTINUATION frames.
 
-* **Request-Headers** → Call-Definition *Custom-Metadata
-* **Call-Definition** → Method Scheme Path TE [Authority] [Timeout] [Content-Type] [Message-Type] [Message-Encoding] [Message-Accept-Encoding] [User-Agent]
-* **Method** →  “:method POST”
-* **Scheme** → “:scheme ”  (“http” / “https”)
-* **Path** → “:path”  {_path identifying method within exposed API_}
-* **Authority** → “:authority” {_virtual host name of authority_}
-* **TE** → “te” “trailers”  # Used to detect incompatible proxies
-* **Timeout** → “grpc-timeout” TimeoutValue TimeoutUnit
+* **Request-Headers** → Call-Definition \*Custom-Metadata
+* **Call-Definition** → Method Scheme Path TE [Authority] [Timeout] Content-Type [Message-Type] [Message-Encoding] [Message-Accept-Encoding] [User-Agent]
+* **Method** →  ":method POST"
+* **Scheme** → ":scheme "  ("http" / "https")
+* **Path** → ":path"  {_path identifying method within exposed API_}
+* **Authority** → ":authority" {_virtual host name of authority_}
+* **TE** → "te" "trailers"  # Used to detect incompatible proxies
+* **Timeout** → "grpc-timeout" TimeoutValue TimeoutUnit
 * **TimeoutValue** → {_positive integer as ASCII string of at most 8 digits_}
 * **TimeoutUnit** → Hour / Minute / Second / Millisecond / Microsecond / Nanosecond
-* **Hour** → “H”
-* **Minute** → “M”
-* **Second** → “S”
-* **Millisecond** → “m”
-* **Microsecond** → “u”
-* **Nanosecond** → “n”
-* **Content-Type** → “content-type” “application/grpc” [(“+proto” / “+json” / {_custom_})]
-* **Content-Coding** → “gzip” / “deflate” / “snappy” / {_custom_}
-* **Message-Encoding** → “grpc-encoding” Content-Coding
-* **Message-Accept-Encoding** → “grpc-accept-encoding” Content-Coding *("," Content-Coding)
-* **User-Agent** → “user-agent” {_structured user-agent string_}
-* **Message-Type** → “grpc-message-type” {_type name for message schema_}
+* **Hour** → "H"
+* **Minute** → "M"
+* **Second** → "S"
+* **Millisecond** → "m"
+* **Microsecond** → "u"
+* **Nanosecond** → "n"
+* **Content-Type** → "content-type" "application/grpc" [("+proto" / "+json" / {_custom_})]
+* **Content-Coding** → "identity" / "gzip" / "deflate" / "snappy" / {_custom_}
+* **Message-Encoding** → "grpc-encoding" Content-Coding
+* **Message-Accept-Encoding** → "grpc-accept-encoding" Content-Coding \*("," Content-Coding)
+* **User-Agent** → "user-agent" {_structured user-agent string_}
+* **Message-Type** → "grpc-message-type" {_type name for message schema_}
 * **Custom-Metadata** → Binary-Header / ASCII-Header
-* **Binary-Header** → {Header-Name “-bin” } {_base64 encoded value_}
+* **Binary-Header** → {Header-Name "-bin" } {_base64 encoded value_}
 * **ASCII-Header** → Header-Name {_value_}
-* **Header-Name** → 1*( %x30-39 / %x61-7A / “_” / “-”) ; 0-9 a-z 
+* **Header-Name** → 1\*( %x30-39 / %x61-7A / "\_" / "-") ; 0-9 a-z \_ -
 
 
-HTTP2 requires that reserved headers, ones starting with “:” appear before all other headers. Additionally implementations should send **Timeout** immediately after the reserved headers and they should send the **Call-Definition** headers before sending **Custom-Metadata**.
+HTTP2 requires that reserved headers, ones starting with ":" appear before all other headers. Additionally implementations should send **Timeout** immediately after the reserved headers and they should send the **Call-Definition** headers before sending **Custom-Metadata**.
 
 If **Timeout** is omitted a server should assume an infinite timeout. Client implementations are free to send a default minimum timeout based on their deployment requirements.
 
-**Custom-Metadata** is an arbitrary set of key-value pairs defined by the application layer. Aside from transport limits on the total length of HTTP2 HEADERS the only other constraint is that header names starting with “grpc-” are reserved for future use.
+**Custom-Metadata** is an arbitrary set of key-value pairs defined by the application layer. Header names starting with "grpc-" but not listed here are reserved for future GRPC use and should not be used by applications as **Custom-Metadata**.
 
-Note that HTTP2 does not allow arbitrary octet sequences for header values so binary header values must be encoded using Base64 as per https://tools.ietf.org/html/rfc4648#section-4. Implementations MUST accept padded and un-padded values and should emit un-padded values. Applications define binary headers by having their names end with “-bin”. Runtime libraries use this suffix to detect binary headers and properly apply base64 encoding & decoding as headers are sent and received.
+Note that HTTP2 does not allow arbitrary octet sequences for header values so binary header values must be encoded using Base64 as per https://tools.ietf.org/html/rfc4648#section-4. Implementations MUST accept padded and un-padded values and should emit un-padded values. Applications define binary headers by having their names end with "-bin". Runtime libraries use this suffix to detect binary headers and properly apply base64 encoding & decoding as headers are sent and received.
 
-The repeated sequence of **Delimited-Message** items is delivered in DATA frames
+Servers may limit the size of **Request-Headers**, with a default of 8 KiB
+suggested.  Implementations are encouraged to compute total header size like
+HTTP/2's `SETTINGS_MAX_HEADER_LIST_SIZE`: the sum of all header fields, for each
+field the sum of the uncompressed field name and value lengths plus 32, with
+binary values' lengths being post-Base64.
 
-* **Delimited-Message** → Compressed-Flag Message-Length Message
+The repeated sequence of **Length-Prefixed-Message** items is delivered in DATA frames
+
+* **Length-Prefixed-Message** → Compressed-Flag Message-Length Message
 * **Compressed-Flag** → 0 / 1   # encoded as 1 byte unsigned integer
 * **Message-Length** → {_length of Message_}  # encoded as 4 byte unsigned integer
-* **Message** → *{binary octet}
+* **Message** → \*{binary octet}
 
 A **Compressed-Flag** value of 1 indicates that the binary octet sequence of **Message** is compressed using the mechanism declared by the **Message-Encoding** header. A value of 0 indicates that no encoding of **Message** bytes has occurred. Compression contexts are NOT maintained over message boundaries, implementations must create a new context for each message in the stream. If the **Message-Encoding** header is omitted then the **Compressed-Flag** must be 0.
 
@@ -69,13 +75,13 @@ For requests, **EOS** (end-of-stream) is indicated by the presence of the END_ST
 
 ###Responses
 
-* **Response** → (Response-Headers *Delimited-Message Trailers) / Trailers-Only
-* **Response-Headers** → HTTP-Status [Message-Encoding] [Message-Accept-Encoding] Content-Type *Custom-Metadata
+* **Response** → (Response-Headers \*Length-Prefixed-Message Trailers) / Trailers-Only
+* **Response-Headers** → HTTP-Status [Message-Encoding] [Message-Accept-Encoding] Content-Type \*Custom-Metadata
 * **Trailers-Only** → HTTP-Status Content-Type Trailers
-* **Trailers** → Status [Status-Message] *Custom-Metadata
-* **HTTP-Status** → “:status 200”
-* **Status** → “grpc-status” <status-code-as-ASCII-string>
-* **Status-Message** → “grpc-message” <descriptive text for status as ASCII string>
+* **Trailers** → Status [Status-Message] \*Custom-Metadata
+* **HTTP-Status** → ":status 200"
+* **Status** → "grpc-status" <status-code-as-ASCII-string>
+* **Status-Message** → "grpc-message" <descriptive text for status as ASCII string>
 
 **Response-Headers** & **Trailers-Only** are each delivered in a single HTTP2 HEADERS frame block. Most responses are expected to have both headers and trailers but **Trailers-Only** is permitted for calls that produce an immediate error. Status must be sent in **Trailers** even if the status code is OK.
 
@@ -83,6 +89,9 @@ For responses end-of-stream is indicated by the presence of the END_STREAM flag
 
 Implementations should expect broken deployments to send non-200 HTTP status codes in responses as well as a variety of non-GRPC content-types and to omit **Status** & **Status-Message**. Implementations must synthesize a **Status** & **Status-Message** to propagate to the application layer when this occurs.
 
+Clients may limit the size of **Response-Headers**, **Trailers**, and
+**Trailers-Only**, with a default of 8 KiB each suggested.
+
 ####Example
 
 Sample unary-call showing HTTP2 framing sequence
@@ -101,7 +110,7 @@ grpc-encoding = gzip
 authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v
 
 DATA (flags = END_STREAM)
-<Delimited Message>
+<Length-Prefixed Message>
 ```
 **Response**
 ```
@@ -110,7 +119,7 @@ HEADERS (flags = END_HEADERS)
 grpc-encoding = gzip
 
 DATA
-<Delimited Message>
+<Length-Prefixed Message>
 
 HEADERS (flags = END_STREAM, END_HEADERS)
 grpc-status = 0 # OK
@@ -120,7 +129,7 @@ trace-proto-bin = jher831yy13JHy3hc
 
 While the protocol does not require a user-agent to function it is recommended that clients provide a structured user-agent string that provides a basic description of the calling library, version & platform to facilitate issue diagnosis in heterogeneous environments. The following structure is recommended to library developers
 ```
-User-Agent → “grpc-” Language ?(“-” Variant) “/” Version ?( “ (“  *(AdditionalProperty “;”) “)” )
+User-Agent → "grpc-" Language ?("-" Variant) "/" Version ?( " ("  *(AdditionalProperty ";") ")" )
 ```
 E.g.
 
@@ -136,7 +145,7 @@ grpc-java-android/0.9.1 (gingerbread/1.2.4; nexus5; tmobile)
 All GRPC calls need to specify an internal ID. We will use HTTP2 stream-ids as call identifiers in this scheme. NOTE: These id’s are contextual to an open HTTP2 session and will not be unique within a given process that is handling more than one HTTP2 session nor can they be used as GUIDs.
 
 #####Data Frames
-DATA frame boundaries have no relation to **Delimited-Message** boundaries and implementations should make no assumptions about their alignment.
+DATA frame boundaries have no relation to **Length-Prefixed-Message** boundaries and implementations should make no assumptions about their alignment.
 
 #####Errors
 
diff --git a/examples/README.md b/examples/README.md
index cc4b239f8e256ba6e71d4352bd1f29c473ebad43..84ec80057e1620eb295503aba2977c5ec073b26b 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -18,7 +18,7 @@ You can find quick start guides for each language, including installation instru
 * [Android Java](https://github.com/grpc/grpc-java/tree/master/examples/android)
 * [Python](python/helloworld)
 * [C#](csharp)
-* [Objective-C](objective-c/route_guide)
+* [Objective-C](objective-c/helloworld)
 * [PHP](php)
 
 ## What's in this repository?
diff --git a/examples/objective-c/auth_sample/AuthTestService.podspec b/examples/objective-c/auth_sample/AuthTestService.podspec
index 9f2a2cc361b1e8b992f2f0dbc532f5cc2f6d9fe4..9521d4971a8a4dd2713f2585b18648304e5f8141 100644
--- a/examples/objective-c/auth_sample/AuthTestService.podspec
+++ b/examples/objective-c/auth_sample/AuthTestService.podspec
@@ -3,13 +3,13 @@ Pod::Spec.new do |s|
   s.version  = "0.0.1"
   s.license  = "New BSD"
 
-  s.ios.deployment_target = "6.0"
-  s.osx.deployment_target = "10.8"
+  s.ios.deployment_target = "7.1"
+  s.osx.deployment_target = "10.9"
 
   # Base directory where the .proto files are.
   src = "../../protos"
 
-  # Directory where the generated files will be place.
+  # Directory where the generated files will be placed.
   dir = "Pods/" + s.name
 
   # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
@@ -22,14 +22,14 @@ Pod::Spec.new do |s|
     ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}"
     ms.header_mappings_dir = dir
     ms.requires_arc = false
-    ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
+    ms.dependency "Protobuf", "~> 3.0.0-alpha-4"
   end
 
   s.subspec "Services" do |ss|
     ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}"
     ss.header_mappings_dir = dir
     ss.requires_arc = true
-    ss.dependency "gRPC", "~> 0.6"
+    ss.dependency "gRPC", "~> 0.11"
     ss.dependency "#{s.name}/Messages"
   end
 end
diff --git a/examples/objective-c/auth_sample/MakeRPCViewController.m b/examples/objective-c/auth_sample/MakeRPCViewController.m
index 366bc9deea9b2409d8b10cd7e36831cafeb814ae..4fd23dc1448c245aa7e578ab15cd2e31e6a34d5d 100644
--- a/examples/objective-c/auth_sample/MakeRPCViewController.m
+++ b/examples/objective-c/auth_sample/MakeRPCViewController.m
@@ -35,7 +35,6 @@
 
 #import <AuthTestService/AuthSample.pbrpc.h>
 #import <Google/SignIn.h>
-#include <grpc/status.h>
 #import <ProtoRPC/ProtoRPC.h>
 
 NSString * const kTestScope = @"https://www.googleapis.com/auth/xapi.zoo";
@@ -49,10 +48,10 @@ static NSString * const kTestHostAddress = @"grpc-test.sandbox.google.com";
 
 @implementation NSError (AuthSample)
 - (NSString *)UIDescription {
-  if (self.code == GRPC_STATUS_UNAUTHENTICATED) {
+  if (self.code == GRPCErrorCodeUnauthenticated) {
     // Authentication error. OAuth2 specifies we'll receive a challenge header.
-    // |userInfo[kGRPCStatusMetadataKey]| is the dictionary of response metadata.
-    NSString *challengeHeader = self.userInfo[kGRPCStatusMetadataKey][@"www-authenticate"] ?: @"";
+    // |userInfo[kGRPCHeadersKey]| is the dictionary of response headers.
+    NSString *challengeHeader = self.userInfo[kGRPCHeadersKey][@"www-authenticate"] ?: @"";
     return [@"Invalid credentials. Server challenge:\n" stringByAppendingString:challengeHeader];
   } else {
     // Any other error.
@@ -89,7 +88,7 @@ static NSString * const kTestHostAddress = @"grpc-test.sandbox.google.com";
 
   // Set the access token to be used.
   NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken;
-  call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
+  call.requestHeaders[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
 
   // Start the RPC.
   [call start];
diff --git a/examples/objective-c/auth_sample/Podfile b/examples/objective-c/auth_sample/Podfile
index dd4fd558c0e37b76469ba95cb58029e32f3e61fa..ea70511dc6caba4bf3452919b6f52defdc371f13 100644
--- a/examples/objective-c/auth_sample/Podfile
+++ b/examples/objective-c/auth_sample/Podfile
@@ -1,6 +1,9 @@
 source 'https://github.com/CocoaPods/Specs.git'
 platform :ios, '8.0'
 
+pod 'Protobuf', :path => "../../../third_party/protobuf"
+pod 'gRPC', :path => "../../.."
+
 target 'AuthSample' do
   # Depend on the generated AuthTestService library.
   pod 'AuthTestService', :path => '.'
diff --git a/examples/objective-c/auth_sample/README.md b/examples/objective-c/auth_sample/README.md
index 5ae64526191d718d8e7d2bfb711e5a60d70e01d0..c560b7af65bc7055dde078842da46d9ba0b52a91 100644
--- a/examples/objective-c/auth_sample/README.md
+++ b/examples/objective-c/auth_sample/README.md
@@ -1,189 +1,3 @@
 #OAuth2 on gRPC: Objective-C
 
-This example application demostrates how to use OAuth2 on gRPC to make authenticated API calls on
-behalf of a user. By walking through it you'll learn how to use the Objective-C gRPC API to:
-
-- Initialize and configure a remote call object before the RPC is started.
-- Set request metadata elements on a call, which are semantically equivalent to HTTP request
-headers.
-- Read response metadata from a call, which is equivalent to HTTP response headers and trailers.
-
-It assumes you know the basics on how to make gRPC API calls using the Objective-C client library,
-as shown in the [Hello World](../helloworld)
-or [Route Guide](../route_guide) tutorials,
-and are familiar with OAuth2 concepts like _access token_.
-
-- [Example code and setup](#setup)
-- [Try it out!](#try)
-- [Create an RPC object and start it later](#rpc-object)
-- [Set request metadata of a call: Authorization header with an access token](#request-metadata)
-- [Get response metadata of a call: Auth challenge header](#response-metadata)
-
-<a name="setup"></a>
-## Example code and setup
-
-The example code for our tutorial is in [examples/objective-c/auth_sample](.).
-To download the example, clone this repository by running the following command:
-```shell
-$ git clone https://github.com/grpc/grpc.git
-```
-
-Then change your current directory to `examples/objective-c/auth_sample`:
-```shell
-$ cd examples/objective-c/auth_sample
-```
-
-Our example is a simple application with two views. The first one lets a user sign in and out using
-the OAuth2 flow of Google's [iOS SignIn library](https://developers.google.com/identity/sign-in/ios/).
-(Google's library is used in this example because the test gRPC service we are going to call expects
-Google account credentials, but neither gRPC nor the Objective-C client library is tied to any
-specific OAuth2 provider). The second view makes a gRPC request to the test server, using the
-access token obtained by the first view.
-
-Note: OAuth2 libraries need the application to register and obtain an ID from the identity provider
-(in the case of this example app, Google). The app's XCode project is configured using that ID, so
-you shouldn't copy this project "as is" for your own app: it would result in your app being
-identified in the consent screen as "gRPC-AuthSample", and not having access to real Google
-services. Instead, configure your own XCode project following the [instructions here](https://developers.google.com/identity/sign-in/ios/).
-
-As with the other examples, you also should have [Cocoapods](https://cocoapods.org/#install)
-installed, as well as the relevant tools to generate the client library code. You can obtain the
-latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc).
-
-
-<a name="try"></a>
-## Try it out!
-
-To try the sample app, first have Cocoapods generate and install the client library for our .proto
-files:
-
-```shell
-$ pod install
-```
-
-(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet
-on your computer's cache).
-
-Finally, open the XCode workspace created by Cocoapods, and run the app.
-
-The first view, `SelectUserViewController.h/m`, asks you to sign in with your Google account, and to
-give the "gRPC-AuthSample" app the following permissions:
-
-- View your email address.
-- View your basic profile info.
-- "Test scope for access to the Zoo service".
-
-This last permission, corresponding to the scope `https://www.googleapis.com/auth/xapi.zoo` doesn't
-grant any real capability: it's only used for testing. You can log out at any time.
-
-The second view, `MakeRPCViewController.h/m`, makes a gRPC request to a test server at
-https://grpc-test.sandbox.google.com, sending the access token along with the request. The test
-service simply validates the token and writes in its response which user it belongs to, and which
-scopes it gives access to. (The client application already knows those two values; it's a way to
-verify that everything went as expected).
-
-The next sections guide you step-by-step through how the gRPC call in `MakeRPCViewController` is
-performed.
-
-<a name="rpc-object"></a>
-## Create an RPC object and start it later
-
-The other basic tutorials show how to invoke an RPC by calling an asynchronous method in a generated
-client object. This shows how to initialize an object that represents the RPC, and configure it
-before starting the network request.
-
-Assume you have a proto service definition like this:
-
-```protobuf
-option objc_class_prefix = "AUTH";
-
-service TestService {
-  rpc UnaryCall(Request) returns (Response);
-}
-```
-
-A `unaryCallWithRequest:handler:` method, with which you're already familiar, is generated for the
-`AUTHTestService` class:
-
-```objective-c
-[client unaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
-  ...
-}];
-```
-
-In addition, an `RPCToUnaryCallWithRequest:handler:` method is generated, which returns a
-not-yet-started RPC object:
-
-```objective-c
-#import <ProtoRPC/ProtoRPC.h>
-
-ProtoRPC *call =
-    [client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
-      ...
-    }];
-```
-
-The RPC represented by this object can be started at any later time like this:
-
-```objective-c
-[call start];
-```
-
-<a name="request-metadata"></a>
-## Set request metadata of a call: Authorization header with an access token
-
-The `ProtoRPC` class has a `requestMetadata` property (inherited from `GRPCCall`) defined like this:
-
-```objective-c
-- (NSMutableDictionary *)requestMetadata; // nonatomic
-- (void)setRequestMetadata:(NSDictionary *)requestMetadata; // nonatomic, copy
-```
-
-Setting it to a dictionary of metadata keys and values will have them sent on the wire when the call
-is started. gRPC metadata are pieces of information about the call sent by the client to the server
-(and vice versa). They take the form of key-value pairs and are essentially opaque to gRPC itself.
-
-```objective-c
-call.requestMetadata = @{@"My-Header": @"Value for this header",
-                         @"Another-Header": @"Its value"};
-```
-
-For convenience, the property is initialized with an empty `NSMutableDictionary`, so that request
-metadata elements can be set like this:
-
-```objective-c
-call.requestMetadata[@"My-Header"] = @"Value for this header";
-```
-
-If you have an access token, OAuth2 specifies it is to be sent in this format:
-
-```objective-c
-call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
-```
-
-<a name="response-metadata"></a>
-## Get response metadata of a call: Auth challenge header
-
-The `ProtoRPC` class also inherits a `responseMetadata` property, analogous to the request metadata
-we just looked at. It's defined like this:
-
-```objective-c
-@property(atomic, readonly) NSDictionary *responseMetadata;
-```
-
-To access OAuth2's authentication challenge header you write:
-
-```objective-c
-call.responseMetadata[@"www-authenticate"]
-```
-
-Note that, as gRPC metadata elements are mapped to HTTP/2 headers (or trailers), the keys of the
-response metadata are always ASCII strings in lowercase.
-
-Many uses cases of response metadata are getting more details about an RPC error. For convenience,
-when a `NSError` instance is passed to an RPC handler block, the response metadata dictionary can
-also be accessed this way:
-
-```objective-c
-error.userInfo[kGRPCStatusMetadataKey]
-```
+This is the supporting code for the tutorial "[OAuth2 on gRPC: Objective-C](http://www.grpc.io/docs/tutorials/auth/oauth2-objective-c.html)."
diff --git a/examples/objective-c/helloworld/README.md b/examples/objective-c/helloworld/README.md
index 75df1a7a26e95fa70c8b50894e4ee53857c7c778..81c5aaa7bcb4e665433d41bc924ab20ce3d52558 100644
--- a/examples/objective-c/helloworld/README.md
+++ b/examples/objective-c/helloworld/README.md
@@ -12,11 +12,13 @@ Here's how to build and run the Objective-C implementation of the [Hello World](
 example used in [Getting started](https://github.com/grpc/grpc/tree/master/examples).
 
 The example code for this and our other examples lives in the `examples` directory. Clone
-this repository to your local machine by running the following command:
+this repository to your local machine by running the following commands:
 
 
 ```sh
 $ git clone https://github.com/grpc/grpc.git
+$ cd grpc
+$ git submodule update --init
 ```
 
 Change your current directory to `examples/objective-c/helloworld`
@@ -53,4 +55,4 @@ responds with a `HLWHelloResponse`, which contains a string that is then output
 
 ## Tutorial
 
-You can find a more detailed tutorial in [gRPC Basics: Objective-C](../route_guide/README.md).
+You can find a more detailed tutorial in [gRPC Basics: Objective-C](http://www.grpc.io/docs/tutorials/basic/objective-c.html).
diff --git a/examples/objective-c/route_guide/Podfile b/examples/objective-c/route_guide/Podfile
index a97f4146858af7c2a39fd003659b0151ac652b85..efa46bba4fa9ed7c52d7c01b2742d4b5365042c4 100644
--- a/examples/objective-c/route_guide/Podfile
+++ b/examples/objective-c/route_guide/Podfile
@@ -2,6 +2,8 @@ source 'https://github.com/CocoaPods/Specs.git'
 platform :ios, '8.0'
 
 target 'RouteGuideClient' do
+  pod 'Protobuf', :path => "../../../third_party/protobuf"
+  pod 'gRPC', :path => "../../.."
   # Depend on the generated RouteGuide library.
   pod 'RouteGuide', :path => '.'
 end
diff --git a/examples/objective-c/route_guide/README.md b/examples/objective-c/route_guide/README.md
index 15864c01f4b8b0b0c3c451ed186f7639edcaed2d..6a6f7c0d3388fbb9f149058774efcf28f82fef2c 100644
--- a/examples/objective-c/route_guide/README.md
+++ b/examples/objective-c/route_guide/README.md
@@ -1,360 +1,4 @@
 #gRPC Basics: Objective-C
 
-This tutorial provides a basic Objective-C programmer's introduction to working with gRPC. By
-walking through this example you'll learn how to:
+This is the supporting code for the tutorial "[gRPC Basics: Objective-C](http://www.grpc.io/docs/tutorials/basic/objective-c.html)."
 
-- Define a service in a .proto file.
-- Generate client code using the protocol buffer compiler.
-- Use the Objective-C gRPC API to write a simple client for your service.
-
-It assumes a passing familiarity with [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview).
-Note that the example in this tutorial uses the proto3 version of the protocol buffers language,
-which is currently in alpha release: you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3)
-and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the
-protocol buffers Github repository.
-
-This isn't a comprehensive guide to using gRPC in Objective-C: more reference documentation is
-coming soon.
-
-- [Why use gRPC?](#why-grpc)
-- [Example code and setup](#setup)
-- [Try it out!](#try)
-- [Defining the service](#proto)
-- [Generating client code](#protoc)
-- [Creating the client](#client)
-
-<a name="why-grpc"></a>
-## Why use gRPC?
-
-With gRPC you can define your service once in a .proto file and implement clients and servers in any
-of gRPC's supported languages, which in turn can be run in environments ranging from servers inside
-Google to your own tablet - all the complexity of communication between different languages and
-environments is handled for you by gRPC. You also get all the advantages of working with protocol
-buffers, including efficient serialization, a simple IDL, and easy interface updating.
-
-gRPC and proto3 are specially suited for mobile clients: gRPC is implemented on top of HTTP/2, which
-results in network bandwidth savings over using HTTP/1.1. Serialization and parsing of the proto
-binary format is more efficient than the equivalent JSON, resulting in CPU and battery savings. And
-proto3 uses a runtime that has been optimized over the years at Google to keep code size to a
-minimum. The latter is important in Objective-C, because the ability of the compiler to strip unused
-code is limited by the dynamic nature of the language.
-
-
-<a name="setup"></a>
-## Example code and setup
-
-The example code for our tutorial is in [examples/objective-c/route_guide](.).
-To download the example, clone this repository by running the following command:
-```shell
-$ git clone https://github.com/grpc/grpc.git
-```
-
-Then change your current directory to `examples/objective-c/route_guide`:
-```shell
-$ cd examples/objective-c/route_guide
-```
-
-Our example is a simple route mapping application that lets clients get information about features
-on their route, create a summary of their route, and exchange route information such as traffic
-updates with the server and other clients.
-
-You also should have [Cocoapods](https://cocoapods.org/#install) installed, as well as the relevant
-tools to generate the client library code (and a server in another language, for testing). You can
-obtain the latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc).
-
-
-<a name="try"></a>
-## Try it out!
-
-To try the sample app, we need a gRPC server running locally. Let's compile and run, for example,
-the C++ server in this repository:
-
-```shell
-$ pushd ../../cpp/route_guide
-$ make
-$ ./route_guide_server &
-$ popd
-```
-
-Now have Cocoapods generate and install the client library for our .proto files:
-
-```shell
-$ pod install
-```
-
-(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet
-on your computer's cache).
-
-Finally, open the XCode workspace created by Cocoapods, and run the app. You can check the calling
-code in `ViewControllers.m` and see the results in XCode's log console.
-
-The next sections guide you step-by-step through how this proto service is defined, how to generate
-a client library from it, and how to create an app that uses that library.
-
-
-<a name="proto"></a>
-## Defining the service
-
-First let's look at how the service we're using is defined. A gRPC *service* and its method
-*request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview).
-You can see the complete .proto file for our example in [`examples/protos/route_guide.proto`](../../protos/route_guide.proto).
-
-To define a service, you specify a named `service` in your .proto file:
-
-```protobuf
-service RouteGuide {
-   ...
-}
-```
-
-Then you define `rpc` methods inside your service definition, specifying their request and response
-types. Protocol buffers let you define four kinds of service method, all of which are used in the
-`RouteGuide` service:
-
-- A *simple RPC* where the client sends a request to the server and receives a response later, just
-like a normal remote procedure call.
-```protobuf
-   // Obtains the feature at a given position.
-   rpc GetFeature(Point) returns (Feature) {}
-```
-
-- A *response-streaming RPC* where the client sends a request to the server and gets back a stream
-of response messages. You specify a response-streaming method by placing the `stream` keyword before
-the *response* type.
-```protobuf
-  // Obtains the Features available within the given Rectangle.  Results are
-  // streamed rather than returned at once (e.g. in a response message with a
-  // repeated field), as the rectangle may cover a large area and contain a
-  // huge number of features.
-  rpc ListFeatures(Rectangle) returns (stream Feature) {}
-```
-
-- A *request-streaming RPC* where the client sends a sequence of messages to the server. Once the
-client has finished writing the messages, it waits for the server to read them all and return its
-response. You specify a request-streaming method by placing the `stream` keyword before the
-*request* type.
-```protobuf
-  // Accepts a stream of Points on a route being traversed, returning a
-  // RouteSummary when traversal is completed.
-  rpc RecordRoute(stream Point) returns (RouteSummary) {}
-```
-
-- A *bidirectional streaming RPC* where both sides send a sequence of messages to the other. The two
-streams operate independently, so clients and servers can read and write in whatever order they
-like: for example, the server could wait to receive all the client messages before writing its
-responses, or it could alternately read a message then write a message, or some other combination of
-reads and writes. The order of messages in each stream is preserved. You specify this type of method
-by placing the `stream` keyword before both the request and the response.
-```protobuf
-  // Accepts a stream of RouteNotes sent while a route is being traversed,
-  // while receiving other RouteNotes (e.g. from other users).
-  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
-```
-
-Our .proto file also contains protocol buffer message type definitions for all the request and
-response types used in our service methods - for example, here's the `Point` message type:
-```protobuf
-// Points are represented as latitude-longitude pairs in the E7 representation
-// (degrees multiplied by 10**7 and rounded to the nearest integer).
-// Latitudes should be in the range +/- 90 degrees and longitude should be in
-// the range +/- 180 degrees (inclusive).
-message Point {
-  int32 latitude = 1;
-  int32 longitude = 2;
-}
-```
-
-You can specify a prefix to be used for your generated classes by adding the `objc_class_prefix`
-option at the top of the file. For example:
-```protobuf
-option objc_class_prefix = "RTG";
-```
-
-
-<a name="protoc"></a>
-## Generating client code
-
-Next we need to generate the gRPC client interfaces from our .proto service definition. We do this
-using the protocol buffer compiler (`protoc`) with a special gRPC Objective-C plugin.
-
-For simplicity, we've provided a [Podspec file](RouteGuide.podspec)
-that runs `protoc` for you with the appropriate plugin, input, and output, and describes how to
-compile the generated files. You just need to run in this directory (`examples/objective-c/route_guide`):
-
-```shell
-$ pod install
-```
-
-which, before installing the generated library in the XCode project of this sample, runs:
-
-```shell
-$ protoc -I ../../protos --objc_out=Pods/RouteGuide --objcgrpc_out=Pods/RouteGuide ../../protos/route_guide.proto
-```
-
-Running this command generates the following files under `Pods/RouteGuide/`:
-- `RouteGuide.pbobjc.h`, the header which declares your generated message classes.
-- `RouteGuide.pbobjc.m`, which contains the implementation of your message classes.
-- `RouteGuide.pbrpc.h`, the header which declares your generated service classes.
-- `RouteGuide.pbrpc.m`, which contains the implementation of your service classes.
-
-These contain:
-- All the protocol buffer code to populate, serialize, and retrieve our request and response message
-types.
-- A class called `RTGRouteGuide` that lets clients call the methods defined in the `RouteGuide`
-service.
-
-You can also use the provided Podspec file to generate client code from any other proto service
-definition; just replace the name (matching the file name), version, and other metadata.
-
-
-<a name="client"></a>
-## Creating the client
-
-In this section, we'll look at creating an Objective-C client for our `RouteGuide` service. You can
-see our complete example client code in [ViewControllers.m](ViewControllers.m).
-(Note: In your apps, for maintainability and readability reasons, you shouldn't put all of your view
-controllers in a single file; it's done here only to simplify the learning process).
-
-### Constructing a client object
-
-To call service methods, we first need to create a client object, an instance of the generated
-`RTGRouteGuide` class. The designated initializer of the class expects a `NSString *` with the
-server address and port we want to connect to:
-
-```objective-c
-#import <RouteGuide/RouteGuide.pbrpc.h>
-
-static NSString * const kHostAddress = @"http://localhost:50051";
-
-...
-
-RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
-```
-
-Notice that we've specified the HTTP scheme in the host address. This is because the server we will
-be using to test our client doesn't use [TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security).
-This is fine because it will be running locally on our development machine. The most common case,
-though, is connecting with a gRPC server on the internet, running gRPC over TLS. For that case, the
-HTTPS scheme can be specified (or no scheme at all, as HTTPS is the default value). The default
-value of the port is that of the scheme selected: 443 for HTTPS and 80 for HTTP.
-
-
-### Calling service methods
-
-Now let's look at how we call our service methods. As you will see, all these methods are
-asynchronous, so you can call them from the main thread of your app without worrying about freezing
-your UI or the OS killing your app.
-
-#### Simple RPC
-
-Calling the simple RPC `GetFeature` is nearly as straightforward as calling any other asynchronous
-method on Cocoa.
-
-```objective-c
-RTGPoint *point = [RTGPoint message];
-point.latitude = 40E7;
-point.longitude = -74E7;
-
-[client getFeatureWithRequest:point handler:^(RTGFeature *response, NSError *error) {
-  if (response) {
-    // Successful response received
-  } else {
-    // RPC error
-  }
-}];
-```
-
-As you can see, we create and populate a request protocol buffer object (in our case `RTGPoint`).
-Then, we call the method on the client object, passing it the request, and a block to handle the
-response (or any RPC error). If the RPC finishes successfully, the handler block is called with a
-`nil` error argument, and we can read the response information from the server from the response
-argument. If, instead, some RPC error happens, the handler block is called with a `nil` response
-argument, and we can read the details of the problem from the error argument.
-
-```objective-c
-NSLog(@"Found feature called %@ at %@.", response.name, response.location);
-```
-
-#### Streaming RPCs
-
-Now let's look at our streaming methods. Here's where we call the response-streaming method
-`ListFeatures`, which results in our client receiving a stream of geographical `RTGFeature`s:
-
-```objective-c
-[client listFeaturesWithRequest:rectangle
-                   eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
-  if (response) {
-    // Element of the stream of responses received
-  } else if (error) {
-    // RPC error; the stream is over.
-  }
-  if (done) {
-    // The stream is over (all the responses were received, or an error occured). Do any cleanup.
-  }
-}];
-```
-
-Notice how the signature of the `eventHandler` block now includes a `BOOL done` parameter. The
-`eventHandler` block can be called any number of times; only on the last call is the `done` argument
-value set to `YES`. If an error occurs, the RPC finishes and the block is called with the arguments
-`(YES, nil, error)`.
-
-The request-streaming method `RecordRoute` expects a stream of `RTGPoint`s from the cient. This
-stream is passed to the method as an object of class `GRXWriter`. The simplest way to create one is
-to initialize one from a `NSArray` object:
-
-
-```objective-c
-#import <RxLibrary/GRXWriter+Immediate.h>
-
-...
-
-RTGPoint *point1 = [RTGPoint message];
-point.latitude = 40E7;
-point.longitude = -74E7;
-
-RTGPoint *point2 = [RTGPoint message];
-point.latitude = 40E7;
-point.longitude = -74E7;
-
-GRXWriter *locationsWriter = [GRXWriter writerWithContainer:@[point1, point2]];
-
-[client recordRouteWithRequestsWriter:locationsWriter
-                              handler:^(RTGRouteSummary *response, NSError *error) {
-  if (response) {
-    NSLog(@"Finished trip with %i points", response.pointCount);
-    NSLog(@"Passed %i features", response.featureCount);
-    NSLog(@"Travelled %i meters", response.distance);
-    NSLog(@"It took %i seconds", response.elapsedTime);
-  } else {
-    NSLog(@"RPC error: %@", error);
-  }
-}];
-
-```
-
-The `GRXWriter` class is generic enough to allow for asynchronous streams, streams of future values,
-or even infinite streams.
-
-Finally, let's look at our bidirectional streaming RPC `RouteChat()`. The way to call a
-bidirectional streaming RPC is just a combination of how to call request-streaming RPCs and
-response-streaming RPCs.
-
-```objective-c
-[client routeChatWithRequestsWriter:notesWriter
-                       eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
-  if (note) {
-    NSLog(@"Got message %@ at %@", note.message, note.location);
-  } else if (error) {
-    NSLog(@"RPC error: %@", error);
-  }
-  if (done) {
-    NSLog(@"Chat ended.");
-  }
-}];
-```
-
-The semantics for the handler block and the `GRXWriter` argument here are exactly the same as for
-our request-streaming and response-streaming methods. Although both client and server will always
-get the other's messages in the order they were written, the two streams operate completely
-independently.
diff --git a/examples/objective-c/route_guide/RouteGuide.podspec b/examples/objective-c/route_guide/RouteGuide.podspec
index 7b99a6c6a77aaed0ff332fedd40b4f5a1914a4d1..e00f827e3a483e8671584356d9b7a2e27e2288ba 100644
--- a/examples/objective-c/route_guide/RouteGuide.podspec
+++ b/examples/objective-c/route_guide/RouteGuide.podspec
@@ -3,13 +3,13 @@ Pod::Spec.new do |s|
   s.version  = "0.0.1"
   s.license  = "New BSD"
 
-  s.ios.deployment_target = "6.0"
-  s.osx.deployment_target = "10.8"
+  s.ios.deployment_target = "7.1"
+  s.osx.deployment_target = "10.9"
 
   # Base directory where the .proto files are.
   src = "../../protos"
 
-  # Directory where the generated files will be place.
+  # Directory where the generated files will be placed.
   dir = "Pods/" + s.name
 
   # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
@@ -22,14 +22,14 @@ Pod::Spec.new do |s|
     ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}"
     ms.header_mappings_dir = dir
     ms.requires_arc = false
-    ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
+    ms.dependency "Protobuf", "~> 3.0.0-alpha-4"
   end
 
   s.subspec "Services" do |ss|
     ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}"
     ss.header_mappings_dir = dir
     ss.requires_arc = true
-    ss.dependency "gRPC", "~> 0.6"
+    ss.dependency "gRPC", "~> 0.11"
     ss.dependency "#{s.name}/Messages"
   end
 end
diff --git a/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj
index 6ab6b27a1b5191c1f99a2b53ab8b480dc061d6eb..f99775562c0630139f58050dad391788d0de4f5a 100644
--- a/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj
+++ b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj
@@ -121,6 +121,7 @@
 				6325277A1B1D0395003073D9 /* Frameworks */,
 				6325277B1B1D0395003073D9 /* Resources */,
 				FFE0BCF30339E7A50A989EAB /* Copy Pods Resources */,
+				B5388EC5A25E89021740B916 /* Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -177,6 +178,21 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
+		B5388EC5A25E89021740B916 /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RouteGuideClient/Pods-RouteGuideClient-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		C6FC30AD2376EC04317237C5 /* Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
diff --git a/examples/objective-c/route_guide/ViewControllers.m b/examples/objective-c/route_guide/ViewControllers.m
index cfc3338bcacc3501cc4462494ff2746bb9ff6504..0b1a1cf48239c19ae4a5b81921bc65c5ae6b11c8 100644
--- a/examples/objective-c/route_guide/ViewControllers.m
+++ b/examples/objective-c/route_guide/ViewControllers.m
@@ -32,13 +32,14 @@
  */
 
 #import <UIKit/UIKit.h>
+#import <GRPCClient/GRPCCall+Tests.h>
 #import <RouteGuide/RouteGuide.pbrpc.h>
 #import <RxLibrary/GRXWriter+Immediate.h>
 #import <RxLibrary/GRXWriter+Transformations.h>
 
-static NSString * const kHostAddress = @"http://localhost:50051";
+static NSString * const kHostAddress = @"localhost:50051";
 
-// Category to override RTGPoint's description.
+/** Category to override RTGPoint's description. */
 @interface RTGPoint (Description)
 - (NSString *)description;
 @end
@@ -53,7 +54,7 @@ static NSString * const kHostAddress = @"http://localhost:50051";
 }
 @end
 
-// Category to give RTGRouteNote a convenience constructor.
+/** Category to give RTGRouteNote a convenience constructor. */
 @interface RTGRouteNote (Constructors)
 + (instancetype)noteWithMessage:(NSString *)message
                        latitude:(float)latitude
@@ -75,9 +76,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
 
 #pragma mark Demo: Get Feature
 
-// Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known
-// not to have a feature.
-
+/**
+ * Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known
+ * not to have a feature.
+ */
 @interface GetFeatureViewController : UIViewController
 @end
 
@@ -86,7 +88,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  // This only needs to be done once per host, before creating service objects for that host.
+  [GRPCCall useInsecureConnectionsForHost:kHostAddress];
+
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
   void (^handler)(RTGFeature *response, NSError *error) = ^(RTGFeature *response, NSError *error) {
     if (response.name.length) {
@@ -102,8 +107,8 @@ static NSString * const kHostAddress = @"http://localhost:50051";
   point.latitude = 409146138;
   point.longitude = -746188906;
 
-  [client getFeatureWithRequest:point handler:handler];
-  [client getFeatureWithRequest:[RTGPoint message] handler:handler];
+  [service getFeatureWithRequest:point handler:handler];
+  [service getFeatureWithRequest:[RTGPoint message] handler:handler];
 }
 
 @end
@@ -111,9 +116,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
 
 #pragma mark Demo: List Features
 
-// Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in
-// the pre-generated database. Prints each response as it comes in.
-
+/**
+ * Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in
+ * the pre-generated database. Prints each response as it comes in.
+ */
 @interface ListFeaturesViewController : UIViewController
 @end
 
@@ -122,7 +128,7 @@ static NSString * const kHostAddress = @"http://localhost:50051";
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
   RTGRectangle *rectangle = [RTGRectangle message];
   rectangle.lo.latitude = 405E6;
@@ -131,8 +137,8 @@ static NSString * const kHostAddress = @"http://localhost:50051";
   rectangle.hi.longitude = -745E6;
 
   NSLog(@"Looking for features between %@ and %@", rectangle.lo, rectangle.hi);
-  [client listFeaturesWithRequest:rectangle
-                     eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
+  [service listFeaturesWithRequest:rectangle
+                      eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
     if (response) {
       NSLog(@"Found feature at %@ called %@.", response.location, response.name);
     } else if (error) {
@@ -146,10 +152,11 @@ static NSString * const kHostAddress = @"http://localhost:50051";
 
 #pragma mark Demo: Record Route
 
-// Run the recordRoute demo. Sends several randomly chosen points from the pre-generated feature
-// database with a variable delay in between. Prints the statistics when they are sent from the
-// server.
-
+/**
+ * Run the recordRoute demo. Sends several randomly chosen points from the pre-generated feature
+ * database with a variable delay in between. Prints the statistics when they are sent from the
+ * server.
+ */
 @interface RecordRouteViewController : UIViewController
 @end
 
@@ -171,9 +178,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
     return location;
   }];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
-  [client recordRouteWithRequestsWriter:locations handler:^(RTGRouteSummary *response, NSError *error) {
+  [service recordRouteWithRequestsWriter:locations
+                                 handler:^(RTGRouteSummary *response, NSError *error) {
     if (response) {
       NSLog(@"Finished trip with %i points", response.pointCount);
       NSLog(@"Passed %i features", response.featureCount);
@@ -190,9 +198,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
 
 #pragma mark Demo: Route Chat
 
-// Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from
-// the server.
-
+/**
+ * Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from
+ * the server.
+ */
 @interface RouteChatViewController : UIViewController
 @end
 
@@ -210,10 +219,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
     return note;
   }];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
-  [client routeChatWithRequestsWriter:notesWriter
-                         eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
+  [service routeChatWithRequestsWriter:notesWriter
+                          eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
     if (note) {
       NSLog(@"Got message %@ at %@", note.message, note.location);
     } else if (error) {
diff --git a/gRPC.podspec b/gRPC.podspec
index d101253bb17523af3b66d2af2e61cd5827fe74bd..2100fc86e7ced9e2f6e2271c98af978df8f0de74 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -566,8 +566,12 @@ Pod::Spec.new do |s|
     ss.header_mappings_dir = '.'
     # This isn't officially supported in Cocoapods. We've asked for an alternative:
     # https://github.com/CocoaPods/CocoaPods/issues/4386
-    ss.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC" ' +
-                                             '"$(PODS_ROOT)/Headers/Private/gRPC/include"' }
+    ss.xcconfig = {
+      'USE_HEADERMAP' => 'NO',
+      'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+      'USER_HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC"',
+      'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC/include"'
+    }
 
     ss.requires_arc = false
     ss.libraries = 'z'
diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h
index c413703970be0647d135c9fd18e3835863118335..9d19b0770897300a8e45321540cb75780a82b9d4 100644
--- a/include/grpc++/support/byte_buffer.h
+++ b/include/grpc++/support/byte_buffer.h
@@ -75,7 +75,6 @@ class ByteBuffer GRPC_FINAL {
   // takes ownership
   void set_buffer(grpc_byte_buffer* buf) {
     if (buffer_) {
-      gpr_log(GPR_ERROR, "Overriding existing buffer");
       Clear();
     }
     buffer_ = buf;
diff --git a/include/grpc++/support/string_ref.h b/include/grpc++/support/string_ref.h
index a17e167d2bf1ed0f2fb3a0d4696314379ec0730b..9d965260ca34b260a7e7ddeff516d7124ddeea82 100644
--- a/include/grpc++/support/string_ref.h
+++ b/include/grpc++/support/string_ref.h
@@ -56,7 +56,7 @@ class string_ref {
   typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
 
   // constants
-  const static size_t npos = size_t(-1);
+  const static size_t npos;
 
   // construct/copy.
   string_ref() : data_(nullptr), length_(0) {}
diff --git a/include/grpc++/support/sync_stream.h b/include/grpc++/support/sync_stream.h
index 514363338d812c5e3ece63b435517d39488a96a6..daf4e367aeb6ffe99c281a5b6359af8dce341c30 100644
--- a/include/grpc++/support/sync_stream.h
+++ b/include/grpc++/support/sync_stream.h
@@ -131,7 +131,7 @@ class ClientReader GRPC_FINAL : public ClientReaderInterface<R> {
     cq_.Pluck(&ops);
   }
 
-  void WaitForInitialMetadata() {
+  void WaitForInitialMetadata() GRPC_OVERRIDE {
     GPR_ASSERT(!context_->initial_metadata_received_);
 
     CallOpSet<CallOpRecvInitialMetadata> ops;
@@ -257,7 +257,7 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
     cq_.Pluck(&ops);
   }
 
-  void WaitForInitialMetadata() {
+  void WaitForInitialMetadata() GRPC_OVERRIDE {
     GPR_ASSERT(!context_->initial_metadata_received_);
 
     CallOpSet<CallOpRecvInitialMetadata> ops;
diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h
index 1433ffdf7e30e0d777147b14d2a90d8ed800f3d8..ffc5982bc034cf2243e52e341ef0364922cc0bba 100644
--- a/include/grpc/byte_buffer.h
+++ b/include/grpc/byte_buffer.h
@@ -106,6 +106,9 @@ void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
 int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
                                  gpr_slice *slice);
 
+/** Merge all data from \a reader into single slice */
+gpr_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader);
+
 /** Returns a RAW byte buffer instance from the output of \a reader. */
 grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
     grpc_byte_buffer_reader *reader);
diff --git a/include/grpc/support/histogram.h b/include/grpc/support/histogram.h
index 2fd1084208fdb9aeb3d42f1ba7488c432e4d51dc..fd56dacc989c6979b34fa3671ceb93a3e4e96206 100644
--- a/include/grpc/support/histogram.h
+++ b/include/grpc/support/histogram.h
@@ -50,7 +50,7 @@ void gpr_histogram_add(gpr_histogram *h, double x);
 /* The following merges the second histogram into the first. It only works
    if they have the same buckets and resolution. Returns 0 on failure, 1
    on success */
-int gpr_histogram_merge(gpr_histogram *dst, gpr_histogram *src);
+int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src);
 
 double gpr_histogram_percentile(gpr_histogram *histogram, double percentile);
 double gpr_histogram_mean(gpr_histogram *histogram);
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index bce1ce971442d2c47e38691904207d694eef859e..6f478ccacb987e85d80724c5c874dd9c6fdbe0d3 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -121,12 +121,14 @@ void grpc_pollset_kick_ext(grpc_pollset *p,
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
         specific_worker->reevaluate_polling_on_wakeup = 1;
       }
+      specific_worker->kicked_specifically = 1;
       grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd);
     } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) {
       GPR_TIMER_MARK("kick_yoself", 0);
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
         specific_worker->reevaluate_polling_on_wakeup = 1;
       }
+      specific_worker->kicked_specifically = 1;
       grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd);
     }
   } else if (gpr_tls_get(&g_current_thread_poller) != (gpr_intptr)p) {
@@ -242,6 +244,7 @@ void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
   /* this must happen before we (potentially) drop pollset->mu */
   worker->next = worker->prev = NULL;
   worker->reevaluate_polling_on_wakeup = 0;
+  worker->kicked_specifically = 0;
   /* TODO(ctiller): pool these */
   grpc_wakeup_fd_init(&worker->wakeup_fd);
   /* If there's work waiting for the pollset to be idle, and the
@@ -308,7 +311,7 @@ void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
     if (worker->reevaluate_polling_on_wakeup) {
       worker->reevaluate_polling_on_wakeup = 0;
       pollset->kicked_without_pollers = 0;
-      if (queued_work) {
+      if (queued_work || worker->kicked_specifically) {
         /* If there's queued work on the list, then set the deadline to be
            immediate so we get back out of the polling loop quickly */
         deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h
index 34f76db2afa93190047d8e827e5a862d180bf690..95ebeab1c26f875c10c443610735ed480986c9ee 100644
--- a/src/core/iomgr/pollset_posix.h
+++ b/src/core/iomgr/pollset_posix.h
@@ -51,6 +51,7 @@ struct grpc_fd;
 typedef struct grpc_pollset_worker {
   grpc_wakeup_fd wakeup_fd;
   int reevaluate_polling_on_wakeup;
+  int kicked_specifically;
   struct grpc_pollset_worker *next;
   struct grpc_pollset_worker *prev;
 } grpc_pollset_worker;
diff --git a/src/core/support/histogram.c b/src/core/support/histogram.c
index 8a1a9d92330d2210d2195dc4adea0c0c8f30b5c7..77b48af9969f20cb6462d95f6a2c43ade950326c 100644
--- a/src/core/support/histogram.c
+++ b/src/core/support/histogram.c
@@ -125,7 +125,7 @@ void gpr_histogram_add(gpr_histogram *h, double x) {
   h->buckets[bucket_for(h, x)]++;
 }
 
-int gpr_histogram_merge(gpr_histogram *dst, gpr_histogram *src) {
+int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src) {
   if ((dst->num_buckets != src->num_buckets) ||
       (dst->multiplier != src->multiplier)) {
     /* Fail because these histograms don't match */
diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c
index bc0586d0691ff6d3e62445c9499a477eef8f5292..623a8d9233746fdb8a3629870d918fbce41267fd 100644
--- a/src/core/support/time_win32.c
+++ b/src/core/support/time_win32.c
@@ -66,14 +66,12 @@ gpr_timespec gpr_now(gpr_clock_type clock) {
       now_tv.tv_nsec = now_tb.millitm * 1000000;
       break;
     case GPR_CLOCK_MONOTONIC:
+    case GPR_CLOCK_PRECISE:
       QueryPerformanceCounter(&timestamp);
       now_dbl = (timestamp.QuadPart - g_start_time.QuadPart) * g_time_scale;
       now_tv.tv_sec = (time_t)now_dbl;
       now_tv.tv_nsec = (int)((now_dbl - (double)now_tv.tv_sec) * 1e9);
       break;
-    case GPR_CLOCK_PRECISE:
-      gpr_precise_clock_now(&now_tv);
-      break;
   }
   return now_tv;
 }
diff --git a/src/core/surface/byte_buffer_reader.c b/src/core/surface/byte_buffer_reader.c
index 283db83833d75aa7d383cc7c917d375d2a0dd2da..9f830df68ce70dd57ad3b41c4ba30dc1f4ac956a 100644
--- a/src/core/surface/byte_buffer_reader.c
+++ b/src/core/surface/byte_buffer_reader.c
@@ -31,6 +31,7 @@
  *
  */
 
+#include <string.h>
 #include <grpc/byte_buffer_reader.h>
 
 #include <grpc/compression.h>
@@ -103,3 +104,21 @@ int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
   }
   return 0;
 }
+
+gpr_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader) {
+  gpr_slice in_slice;
+  size_t bytes_read = 0;
+  const size_t input_size = grpc_byte_buffer_length(reader->buffer_out);
+  gpr_slice out_slice = gpr_slice_malloc(input_size);
+  gpr_uint8 *const outbuf = GPR_SLICE_START_PTR(out_slice); /* just an alias */
+
+  while (grpc_byte_buffer_reader_next(reader, &in_slice) != 0) {
+    const size_t slice_length = GPR_SLICE_LENGTH(in_slice);
+    memcpy(&(outbuf[bytes_read]), GPR_SLICE_START_PTR(in_slice), slice_length);
+    bytes_read += slice_length;
+    gpr_slice_unref(in_slice);
+    GPR_ASSERT(bytes_read <= input_size);
+  }
+  return out_slice;
+}
+
diff --git a/src/cpp/util/string_ref.cc b/src/cpp/util/string_ref.cc
index 604134fa9da9cfca538d61e5d091a25be48e0e49..66c79a1818431daa0b6719778c259dcd678fca80 100644
--- a/src/cpp/util/string_ref.cc
+++ b/src/cpp/util/string_ref.cc
@@ -40,7 +40,7 @@
 
 namespace grpc {
 
-const size_t string_ref::npos;
+const size_t string_ref::npos = size_t(-1);
 
 string_ref& string_ref::operator=(const string_ref& rhs) {
   data_ = rhs.data_;
diff --git a/src/csharp/.gitignore b/src/csharp/.gitignore
index deac55029eeddea0cda72a0446f949a0a48552d9..0f96a4822194b96c319fe9e62982b22b0e2272f4 100644
--- a/src/csharp/.gitignore
+++ b/src/csharp/.gitignore
@@ -7,6 +7,7 @@ Grpc.v12.suo
 Grpc.sdf
 
 TestResult.xml
+coverage_results.xml
 /TestResults
 .vs/
 *.nupkg
diff --git a/src/csharp/.nuget/packages.config b/src/csharp/.nuget/packages.config
index a7df95cf6bd8d02347fc9409c9f57f68a8b4450d..89a310ac5697ed25fe4542a4f74e3fa1cc67dfeb 100644
--- a/src/csharp/.nuget/packages.config
+++ b/src/csharp/.nuget/packages.config
@@ -1,4 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="NUnit.Runners" version="2.6.4" />
+  <package id="OpenCover" version="4.6.166" />
+  <package id="ReportGenerator" version="2.3.2.0" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
index e58528ff50ed45ded6790074c49a91166680866e..25a5a27c8e3f00ac7b689e79825f6bbce5d98e83 100644
--- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
@@ -38,6 +38,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Grpc.Core;
 using Grpc.Core.Internal;
+using Grpc.Core.Profiling;
 using Grpc.Core.Utils;
 using NUnit.Framework;
 
@@ -200,19 +201,6 @@ namespace Grpc.Core.Tests
             Assert.AreEqual(headers[1].Key, trailers[1].Key);
             CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes);
         }
-
-        [Test]
-        public void UnaryCallPerformance()
-        {
-            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
-            {
-                return request;
-            });
-
-            var callDetails = helper.CreateUnaryCall();
-            BenchmarkUtil.RunBenchmark(1, 10,
-                                       () => { Calls.BlockingUnaryCall(callDetails, "ABC"); });
-        }
             
         [Test]
         public void UnknownMethodHandler()
diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
index 91d072ababecdd6360923320f302fd7a086b74a2..e5ffa319895dee48029bf8dbd709855e32c5e7cc 100644
--- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
+++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
@@ -88,6 +88,7 @@
     <Compile Include="CompressionTest.cs" />
     <Compile Include="ContextPropagationTest.cs" />
     <Compile Include="MetadataTest.cs" />
+    <Compile Include="PerformanceTest.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
diff --git a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs
index 874df02baa0c6453f987c0ddfa06a79cc8f5993d..9be5450d8100ae7ba25672db2110c8b6cab26fb8 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs
@@ -34,6 +34,7 @@
 using System;
 using System.Runtime.InteropServices;
 using Grpc.Core.Internal;
+using Grpc.Core.Utils;
 using NUnit.Framework;
 
 namespace Grpc.Core.Internal.Tests
@@ -198,5 +199,23 @@ namespace Grpc.Core.Internal.Tests
                 Console.WriteLine("Test cannot be run on this platform, skipping the test.");
             }
         }
+            
+        // Test attribute commented out to prevent running as part of the default test suite.
+        // [Test]
+        // [Category("Performance")]
+        public void NowBenchmark() 
+        {
+            // approx Timespec.Now latency <33ns
+            BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.Now; });
+        }
+
+        // Test attribute commented out to prevent running as part of the default test suite.
+        // [Test]
+        // [Category("Performance")]
+        public void PreciseNowBenchmark()
+        {
+            // approx Timespec.PreciseNow latency <18ns (when compiled with GRPC_TIMERS_RDTSC)
+            BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.PreciseNow; });
+        }
     }
 }
diff --git a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5516cd33774e14165e2e5a28781a62542fb12ee8
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs
@@ -0,0 +1,99 @@
+#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.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Profiling;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Tests
+{
+    public class PerformanceTest
+    {
+        const string Host = "127.0.0.1";
+
+        MockServiceHelper helper;
+        Server server;
+        Channel channel;
+
+        [SetUp]
+        public void Init()
+        {
+            helper = new MockServiceHelper(Host);
+            server = helper.GetServer();
+            server.Start();
+            channel = helper.GetChannel();
+        }
+
+        [TearDown]
+        public void Cleanup()
+        {
+            channel.ShutdownAsync().Wait();
+            server.ShutdownAsync().Wait();
+        }
+
+        // Test attribute commented out to prevent running as part of the default test suite.
+        //[Test]
+        //[Category("Performance")]
+        public void UnaryCallPerformance()
+        {
+            var profiler = new BasicProfiler();
+            Profilers.SetForCurrentThread(profiler);
+
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
+                return request;
+            });
+
+            var callDetails = helper.CreateUnaryCall();
+            for(int i = 0; i < 3000; i++)
+            {
+                Calls.BlockingUnaryCall(callDetails, "ABC");
+            }
+
+            profiler.Reset();
+
+            for(int i = 0; i < 3000; i++)
+            {
+                Calls.BlockingUnaryCall(callDetails, "ABC");
+            }
+            profiler.Dump("latency_trace_csharp.txt");
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 92d4e19eac25bc10c5d908be065230273a5e8f85..0aab7bdd8ad2a4591bf12ef21bdcfff3523e1aec 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -119,6 +119,10 @@
     <Compile Include="CompressionLevel.cs" />
     <Compile Include="WriteOptions.cs" />
     <Compile Include="ContextPropagationToken.cs" />
+    <Compile Include="Profiling\ProfilerEntry.cs" />
+    <Compile Include="Profiling\ProfilerScope.cs" />
+    <Compile Include="Profiling\IProfiler.cs" />
+    <Compile Include="Profiling\Profilers.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="Grpc.Core.nuspec" />
@@ -150,4 +154,7 @@
   <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" />
   <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" />
   <ItemGroup />
+  <ItemGroup>
+    <Folder Include="Profiling\" />
+  </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index 800462c85407a45a822a4fa5e00aa863255787b3..e3ecc472826d9ac5d8ea1bf700d0b0bf9781ea43 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -39,6 +39,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Grpc.Core.Internal;
 using Grpc.Core.Logging;
+using Grpc.Core.Profiling;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
@@ -87,6 +88,9 @@ namespace Grpc.Core.Internal
         /// </summary>
         public TResponse UnaryCall(TRequest msg)
         {
+            var profiler = Profilers.ForCurrentThread();
+
+            using (profiler.NewScope("AsyncCall.UnaryCall"))
             using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create())
             {
                 byte[] payload = UnsafeSerialize(msg);
@@ -104,24 +108,26 @@ namespace Grpc.Core.Internal
                 }
 
                 using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
+                using (var ctx = BatchContextSafeHandle.Create())
                 {
-                    using (var ctx = BatchContextSafeHandle.Create())
-                    {
-                        call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall());
-                        var ev = cq.Pluck(ctx.Handle);
+                    call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall());
+
+                    var ev = cq.Pluck(ctx.Handle);
 
-                        bool success = (ev.success != 0);
-                        try
+                    bool success = (ev.success != 0);
+                    try
+                    {
+                        using (profiler.NewScope("AsyncCall.UnaryCall.HandleBatch"))
                         {
                             HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata());
                         }
-                        catch (Exception e)
-                        {
-                            Logger.Error(e, "Exception occured while invoking completion delegate.");
-                        }
+                    }
+                    catch (Exception e)
+                    {
+                        Logger.Error(e, "Exception occured while invoking completion delegate.");
                     }
                 }
-
+                    
                 // Once the blocking call returns, the result should be available synchronously.
                 // Note that GetAwaiter().GetResult() doesn't wrap exceptions in AggregateException.
                 return unaryResponseTcs.Task.GetAwaiter().GetResult();
@@ -329,27 +335,35 @@ namespace Grpc.Core.Internal
 
         private void Initialize(CompletionQueueSafeHandle cq)
         {
-            var call = CreateNativeCall(cq);
-            details.Channel.AddCallReference(this);
-            InitializeInternal(call);
-            RegisterCancellationCallback();
+            using (Profilers.ForCurrentThread().NewScope("AsyncCall.Initialize"))
+            { 
+                var call = CreateNativeCall(cq);
+
+                details.Channel.AddCallReference(this);
+                InitializeInternal(call);
+                RegisterCancellationCallback();
+            }
         }
 
         private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq)
         {
-            if (injectedNativeCall != null)
-            {
-                return injectedNativeCall;  // allows injecting a mock INativeCall in tests.
-            }
+            using (Profilers.ForCurrentThread().NewScope("AsyncCall.CreateNativeCall"))
+            { 
+                if (injectedNativeCall != null)
+                {
+                    return injectedNativeCall;  // allows injecting a mock INativeCall in tests.
+                }
 
-            var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance;
+                var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance;
 
-            var credentials = details.Options.Credentials;
-            using (var nativeCredentials = credentials != null ? credentials.ToNativeCredentials() : null)
-            {
-                return details.Channel.Handle.CreateCall(environment.CompletionRegistry,
-                    parentCall, ContextPropagationToken.DefaultMask, cq,
-                    details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value), nativeCredentials);
+                var credentials = details.Options.Credentials;
+                using (var nativeCredentials = credentials != null ? credentials.ToNativeCredentials() : null)
+                {
+                    var result = details.Channel.Handle.CreateCall(environment.CompletionRegistry,
+                                 parentCall, ContextPropagationToken.DefaultMask, cq,
+                                 details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value), nativeCredentials);
+                    return result;
+                }
             }
         }
 
@@ -385,33 +399,37 @@ namespace Grpc.Core.Internal
         /// </summary>
         private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders)
         {
-            TResponse msg = default(TResponse);
-            var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null;
-
-            lock (myLock)
+            using (Profilers.ForCurrentThread().NewScope("AsyncCall.HandleUnaryResponse"))
             {
-                finished = true;
+                TResponse msg = default(TResponse);
+                var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null;
 
-                if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK)
+                lock (myLock)
                 {
-                    receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers);
+                    finished = true;
+
+                    if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK)
+                    {
+                        receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers);
+                    }
+                    finishedStatus = receivedStatus;
+
+                    ReleaseResourcesIfPossible();
+
                 }
-                finishedStatus = receivedStatus;
 
-                ReleaseResourcesIfPossible();
-            }
+                responseHeadersTcs.SetResult(responseHeaders);
 
-            responseHeadersTcs.SetResult(responseHeaders);
+                var status = receivedStatus.Status;
 
-            var status = receivedStatus.Status;
+                if (!success || status.StatusCode != StatusCode.OK)
+                {
+                    unaryResponseTcs.SetException(new RpcException(status));
+                    return;
+                }
 
-            if (!success || status.StatusCode != StatusCode.OK)
-            {
-                unaryResponseTcs.SetException(new RpcException(status));
-                return;
+                unaryResponseTcs.SetResult(msg);
             }
-
-            unaryResponseTcs.SetResult(msg);
         }
 
         /// <summary>
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
index 3e2c57c9b5b0a8dc00440df0a2f2b864211fa214..953f61aa1ea0d5724a70a8a530177e50ba764ccf 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
@@ -41,6 +41,7 @@ using System.Threading.Tasks;
 
 using Grpc.Core.Internal;
 using Grpc.Core.Logging;
+using Grpc.Core.Profiling;
 using Grpc.Core.Utils;
 
 namespace Grpc.Core.Internal
@@ -167,16 +168,19 @@ namespace Grpc.Core.Internal
         /// </summary>
         protected bool ReleaseResourcesIfPossible()
         {
-            if (!disposed && call != null)
+            using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.ReleaseResourcesIfPossible"))
             {
-                bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished);
-                if (noMoreSendCompletions && readingDone && finished)
+                if (!disposed && call != null)
                 {
-                    ReleaseResources();
-                    return true;
+                    bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished);
+                    if (noMoreSendCompletions && readingDone && finished)
+                    {
+                        ReleaseResources();
+                        return true;
+                    }
                 }
+                return false;
             }
-            return false;
         }
 
         protected abstract bool IsClient
@@ -228,7 +232,10 @@ namespace Grpc.Core.Internal
 
         protected byte[] UnsafeSerialize(TWrite msg)
         {
-            return serializer(msg);
+            using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.UnsafeSerialize"))
+            {
+                return serializer(msg);
+            }
         }
 
         protected Exception TrySerialize(TWrite msg, out byte[] payload)
@@ -247,15 +254,20 @@ namespace Grpc.Core.Internal
 
         protected Exception TryDeserialize(byte[] payload, out TRead msg)
         {
-            try
-            {
-                msg = deserializer(payload);
-                return null;
-            } 
-            catch (Exception e)
+            using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.TryDeserialize"))
             {
-                msg = default(TRead);
-                return e;
+                try
+                {
+                
+                    msg = deserializer(payload);
+                    return null;
+             
+                }
+                catch (Exception e)
+                {
+                    msg = default(TRead);
+                    return e;
+                }
             }
         }
 
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index 0be7a4dd3a1f3033321072360457ca7865baef95..ddeedebd117f650f99702c759d22bd5a824110ae 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -34,6 +34,7 @@ using System.Diagnostics;
 using System.Runtime.InteropServices;
 using Grpc.Core;
 using Grpc.Core.Utils;
+using Grpc.Core.Profiling;
 
 namespace Grpc.Core.Internal
 {
@@ -131,8 +132,11 @@ namespace Grpc.Core.Internal
 
         public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
         {
-            grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
-                .CheckOk();
+            using (Profilers.ForCurrentThread().NewScope("CallSafeHandle.StartUnary"))
+            {
+                grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
+                    .CheckOk();
+            }
         }
 
         public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray)
diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
index d270d77526ffd44e69e6da66728bf614deef9852..5f9169bcb2f02b70878a4fd871379c3c7eca6fca 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
@@ -32,6 +32,7 @@ using System;
 using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
+using Grpc.Core.Profiling;
 
 namespace Grpc.Core.Internal
 {
@@ -84,13 +85,16 @@ namespace Grpc.Core.Internal
 
         public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline, CredentialsSafeHandle credentials)
         {
-            var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
-            if (credentials != null)
+            using (Profilers.ForCurrentThread().NewScope("ChannelSafeHandle.CreateCall"))
             {
-                result.SetCredentials(credentials);
+                var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
+                if (credentials != null)
+                {
+                    result.SetCredentials(credentials);
+                }
+                result.SetCompletionRegistry(registry);
+                return result;
             }
-            result.SetCompletionRegistry(registry);
-            return result;
         }
 
         public ChannelState CheckConnectivityState(bool tryToConnect)
diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
index f7a3471bb4b9d16ce7fbd293ab2bce8e57b8f2bc..9de2bc7950bd8678fcf286d505b7e6f064e86781 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
@@ -31,6 +31,7 @@
 using System;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
+using Grpc.Core.Profiling;
 
 namespace Grpc.Core.Internal
 {
@@ -70,7 +71,10 @@ namespace Grpc.Core.Internal
 
         public CompletionQueueEvent Pluck(IntPtr tag)
         {
-            return grpcsharp_completion_queue_pluck(this, tag);
+            using (Profilers.ForCurrentThread().NewScope("CompletionQueueSafeHandle.Pluck"))
+            {
+                return grpcsharp_completion_queue_pluck(this, tag);
+            }
         }
 
         public void Shutdown()
diff --git a/src/csharp/Grpc.Core/Internal/Enums.cs b/src/csharp/Grpc.Core/Internal/Enums.cs
index 185098160b6e5d065ec4fec14cca9ae68c12d726..b0eab2001bc5b1e70dc22d6b2b4de0b88a0e4945 100644
--- a/src/csharp/Grpc.Core/Internal/Enums.cs
+++ b/src/csharp/Grpc.Core/Internal/Enums.cs
@@ -102,6 +102,9 @@ namespace Grpc.Core.Internal
         /* Realtime clock */
         Realtime,
 
+        /* Precise clock good for performance profiling. */
+        Precise,
+
         /* Timespan - the distance between two time points */
         Timespan
     }
diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
index 31b834c979ae38b67177a61ef8b6d7b5bb1dffde..ed1bd24498081ae63e050330329aa08b0d1cddd6 100644
--- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
@@ -31,6 +31,7 @@
 using System;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
+using Grpc.Core.Profiling;
 
 namespace Grpc.Core.Internal
 {
@@ -66,14 +67,17 @@ namespace Grpc.Core.Internal
             
         public static MetadataArraySafeHandle Create(Metadata metadata)
         {
-            // TODO(jtattermusch): we might wanna check that the metadata is readonly 
-            var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
-            for (int i = 0; i < metadata.Count; i++)
+            using (Profilers.ForCurrentThread().NewScope("MetadataArraySafeHandle.Create"))
             {
-                var valueBytes = metadata[i].GetSerializedValueUnsafe();
-                grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length));
+                // TODO(jtattermusch): we might wanna check that the metadata is readonly 
+                var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
+                for (int i = 0; i < metadata.Count; i++)
+                {
+                    var valueBytes = metadata[i].GetSerializedValueUnsafe();
+                    grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length));
+                }
+                return metadataArray;
             }
-            return metadataArray;
         }
 
         /// <summary>
diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs
index daf85d5f61d29d0fc8e5f093b305696fef4bd8f9..38fc067d9f4b61b4d5753ef0ab0ca717967ba945 100644
--- a/src/csharp/Grpc.Core/Internal/Timespec.cs
+++ b/src/csharp/Grpc.Core/Internal/Timespec.cs
@@ -239,6 +239,19 @@ namespace Grpc.Core.Internal
             }
         }
 
+        /// <summary>
+        /// Gets current timestamp using <c>GPRClockType.Precise</c>.
+        /// Only available internally because core needs to be compiled with 
+        /// GRPC_TIMERS_RDTSC support for this to use RDTSC.
+        /// </summary>
+        internal static Timespec PreciseNow
+        {
+            get
+            {
+                return gprsharp_now(GPRClockType.Precise);
+            }
+        }
+
         internal static int NativeSize
         {
             get
diff --git a/src/csharp/Grpc.Core/Profiling/IProfiler.cs b/src/csharp/Grpc.Core/Profiling/IProfiler.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c426c365d2d6a7cc76c216d902f24a24d5264414
--- /dev/null
+++ b/src/csharp/Grpc.Core/Profiling/IProfiler.cs
@@ -0,0 +1,47 @@
+#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.IO;
+using System.Threading;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core.Profiling
+{
+    internal interface IProfiler 
+    {
+        void Begin(string tag);
+        void End(string tag);
+        void Mark(string tag);
+    }
+}
diff --git a/src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs b/src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5cc4c3c05429378b3ecb8bd1ae3d345304bce427
--- /dev/null
+++ b/src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs
@@ -0,0 +1,87 @@
+#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.IO;
+using System.Threading;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core.Profiling
+{
+    internal struct ProfilerEntry
+    {
+        public enum Type {
+            BEGIN,
+            END,
+            MARK
+        }
+
+        public ProfilerEntry(Timespec timespec, Type type, string tag)
+        {
+            this.timespec = timespec;
+            this.type = type;
+            this.tag = tag;
+        }
+
+        public Timespec timespec;
+        public Type type;
+        public string tag;
+
+        public override string ToString()
+        {
+            // mimic the output format used by C core.
+            return string.Format(
+                "{{\"t\": {0}.{1}, \"thd\":\"unknown\", \"type\": \"{2}\", \"tag\": \"{3}\", " +
+                "\"file\": \"unknown\", \"line\": 0, \"imp\": 0}}",
+                timespec.TimevalSeconds, timespec.TimevalNanos.ToString("D9"),
+                GetTypeAbbreviation(type), tag);
+        }
+
+        internal static string GetTypeAbbreviation(Type type)
+        {
+            switch (type)
+            {
+                case Type.BEGIN:
+                    return "{";
+
+                case Type.END:
+                    return "}";
+                
+                case Type.MARK:
+                    return ".";
+                default:
+                    throw new ArgumentException("Unknown type");
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Profiling/ProfilerScope.cs b/src/csharp/Grpc.Core/Profiling/ProfilerScope.cs
new file mode 100644
index 0000000000000000000000000000000000000000..413f3a1a358bba66b42bffca42373c89fcf50bb6
--- /dev/null
+++ b/src/csharp/Grpc.Core/Profiling/ProfilerScope.cs
@@ -0,0 +1,60 @@
+#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.IO;
+using System.Threading;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core.Profiling
+{
+    // Allows declaring Begin and End of a profiler scope with a using statement.
+    // declared as struct for better performance.
+    internal struct ProfilerScope : IDisposable
+    {
+        readonly IProfiler profiler;
+        readonly string tag;
+
+        public ProfilerScope(IProfiler profiler, string tag)
+        {
+            this.profiler = profiler;
+            this.tag = tag;
+            this.profiler.Begin(this.tag);
+        }
+            
+        public void Dispose()
+        {
+            profiler.End(tag);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Profiling/Profilers.cs b/src/csharp/Grpc.Core/Profiling/Profilers.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c8123347f2b954295583c46802f713a15722d31c
--- /dev/null
+++ b/src/csharp/Grpc.Core/Profiling/Profilers.cs
@@ -0,0 +1,131 @@
+#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.IO;
+using System.Threading;
+using Grpc.Core.Internal;
+
+namespace Grpc.Core.Profiling
+{
+    internal static class Profilers
+    {
+        static readonly NopProfiler defaultProfiler = new NopProfiler();
+        static readonly ThreadLocal<IProfiler> profilers = new ThreadLocal<IProfiler>();
+
+        public static IProfiler ForCurrentThread()
+        {
+            return profilers.Value ?? defaultProfiler;
+        }
+
+        public static void SetForCurrentThread(IProfiler profiler)
+        {
+            profilers.Value = profiler;
+        }
+
+        public static ProfilerScope NewScope(this IProfiler profiler, string tag)
+        {
+            return new ProfilerScope(profiler, tag);
+        }
+    }
+
+    internal class NopProfiler : IProfiler
+    {
+        public void Begin(string tag)
+        {
+        }
+
+        public void End(string tag)
+        {
+        }
+
+        public void Mark(string tag)
+        {
+        }
+    }
+
+    // Profiler using Timespec.PreciseNow
+    internal class BasicProfiler : IProfiler
+    {
+        ProfilerEntry[] entries;
+        int count;
+
+        public BasicProfiler() : this(1024*1024)
+        {
+        }
+
+        public BasicProfiler(int capacity)
+        {
+            this.entries = new ProfilerEntry[capacity];
+        }
+
+        public void Begin(string tag) {
+            AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.BEGIN, tag));
+        }
+
+        public void End(string tag) {
+            AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.END, tag));
+        }
+
+        public void Mark(string tag) {
+            AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.MARK, tag));
+        }
+
+        public void Reset()
+        {
+            count = 0;
+        }
+
+        public void Dump(string filepath)
+        {
+            using (var stream = new StreamWriter(filepath))
+            {
+                Dump(stream);
+            }
+        }
+
+        public void Dump(TextWriter stream)
+        {
+            for (int i = 0; i < count; i++)
+            {
+                var entry = entries[i];
+                stream.WriteLine(entry.ToString());
+            }
+        }
+
+        // NOT THREADSAFE!
+        void AddEntry(ProfilerEntry entry) {
+            entries[count++] = entry;
+        }
+    }
+}
diff --git a/src/csharp/README.md b/src/csharp/README.md
index 2083e184af029091bd34fba264a2886f763e3852..65ae0b5efda3ea55d96de1e521a1b42904838129 100644
--- a/src/csharp/README.md
+++ b/src/csharp/README.md
@@ -1,3 +1,4 @@
+[![Nuget](https://img.shields.io/nuget/v/Grpc.svg)](http://www.nuget.org/packages/Grpc/)
 gRPC C#
 =======
 
diff --git a/src/node/README.md b/src/node/README.md
index 5d89e2228db79f1da5c8e38c665916a344030c53..b46b98624327d3a6569fd73fa3b0d0d9306c340c 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -1,3 +1,4 @@
+[![npm](https://img.shields.io/npm/v/grpc.svg)](https://www.npmjs.com/package/grpc)
 # Node.js gRPC Library
 
 ## Status
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 596ea5ebb0b082601010c2e2bde00950726dd4b9..d57826781d391d6872e6f00b757ee380ffa8a134 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -33,6 +33,17 @@
 
 /**
  * Client module
+ *
+ * This module contains the factory method for creating Client classes, and the
+ * method calling code for all types of methods.
+ *
+ * For example, to create a client and call a method on it:
+ *
+ * var proto_obj = grpc.load(proto_file_path);
+ * var Client = proto_obj.package.subpackage.ServiceName;
+ * var client = new Client(server_address, client_credentials);
+ * var call = client.unaryMethod(arguments, callback);
+ *
  * @module
  */
 
@@ -601,7 +612,15 @@ exports.makeClientConstructor = function(methods, serviceName) {
     if (!options) {
       options = {};
     }
-    options['grpc.primary_user_agent'] = 'grpc-node/' + version;
+    /* Append the grpc-node user agent string after the application user agent
+     * string, and put the combination at the beginning of the user agent string
+     */
+    if (options['grpc.primary_user_agent']) {
+      options['grpc.primary_user_agent'] += ' ';
+    } else {
+      options['grpc.primary_user_agent'] = '';
+    }
+    options['grpc.primary_user_agent'] += 'grpc-node/' + version;
     /* Private fields use $ as a prefix instead of _ because it is an invalid
      * prefix of a method name */
     this.$channel = new grpc.Channel(address, credentials, options);
diff --git a/src/node/src/common.js b/src/node/src/common.js
index ebaaa13db0f7c854ea6815bccb404a34f0b0703a..e4fe5a8e0358335305c7c3880f766089c456a499 100644
--- a/src/node/src/common.js
+++ b/src/node/src/common.js
@@ -32,6 +32,8 @@
  */
 
 /**
+ * This module contains functions that are common to client and server
+ * code. None of them should be used directly by gRPC users.
  * @module
  */
 
diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js
index 183c3ad4fcfe313326c980ea1a0fa0e8adf2db97..0a2f1489b6ead9db26642f983d69a2dec7083f6c 100644
--- a/src/node/src/metadata.js
+++ b/src/node/src/metadata.js
@@ -33,6 +33,15 @@
 
 /**
  * Metadata module
+ *
+ * This module defines the Metadata class, which represents header and trailer
+ * metadata for gRPC calls. Here is an example of how to use it:
+ *
+ * var metadata = new metadata_module.Metadata();
+ * metadata.set('key1', 'value1');
+ * metadata.add('key1', 'value2');
+ * metadata.get('key1') // returns ['value1', 'value2']
+ *
  * @module
  */
 
diff --git a/src/node/src/server.js b/src/node/src/server.js
index 89e1090c6c47b2f171e5c4b0cd62a5f3e817302e..d1fb627e6cb3f4be4d404d638fdb21229db423fc 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -33,6 +33,17 @@
 
 /**
  * Server module
+ *
+ * This module contains all the server code for Node gRPC: both the Server
+ * class itself and the method handler code for all types of methods.
+ *
+ * For example, to create a Server, add a service, and start it:
+ *
+ * var server = new server_module.Server();
+ * server.addProtoService(protobuf_service_descriptor, service_implementation);
+ * server.bind('address:port', server_credential);
+ * server.start();
+ *
  * @module
  */
 
@@ -746,8 +757,8 @@ Server.prototype.addProtoService = function(service, implementation) {
  * 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=} creds Server credential object to be used for SSL. Pass
- *     nothing for an insecure port
+ * @param {ServerCredentials=} creds Server credential object to be used for
+ *     SSL. Pass an insecure credentials object for an insecure port.
  */
 Server.prototype.bind = function(port, creds) {
   if (this.started) {
diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h
index 2e379a71572c409a2c3e1d69d9385447095b5aad..6b443877e9b7dc57e97a1144f952a2525619434d 100644
--- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h
+++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h
@@ -33,17 +33,19 @@
 
 #import "GRPCCall.h"
 
-// Helpers for setting and reading headers compatible with OAuth2.
+/** Helpers for setting and reading headers compatible with OAuth2. */
 @interface GRPCCall (OAuth2)
 
-// Setting this property is equivalent to setting "Bearer <passed token>" as the value of the
-// request header with key "authorization" (the authorization header). Setting it to nil removes the
-// authorization header from the request.
-// The value obtained by getting the property is the OAuth2 bearer token if the authorization header
-// of the request has the form "Bearer <token>", or nil otherwise.
+/**
+ * Setting this property is equivalent to setting "Bearer <passed token>" as the value of the
+ * request header with key "authorization" (the authorization header). Setting it to nil removes the
+ * authorization header from the request.
+ * The value obtained by getting the property is the OAuth2 bearer token if the authorization header
+ * of the request has the form "Bearer <token>", or nil otherwise.
+ */
 @property(atomic, copy) NSString *oauth2AccessToken;
 
-// Returns the value (if any) of the "www-authenticate" response header (the challenge header).
+/** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */
 @property(atomic, readonly) NSString *oauth2ChallengeHeader;
 
 @end
diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h
index cca161460632cd694b55851a2c96c8a0a0815bbb..ccc5723ec702e9333aba8201fec04412d3678f85 100644
--- a/src/objective-c/GRPCClient/GRPCCall+Tests.h
+++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h
@@ -33,22 +33,28 @@
 
 #import "GRPCCall.h"
 
-// Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be
-// used in releases, but are sometimes needed for testing.
+/**
+ * Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be
+ * used in releases, but are sometimes needed for testing.
+ */
 @interface GRPCCall (Tests)
 
-// Establish all SSL connections to the provided host using the passed SSL target name and the root
-// certificates found in the file at |certsPath|.
-//
-// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
-// more than one invocation of the methods of this category.
+/**
+ * Establish all SSL connections to the provided host using the passed SSL target name and the root
+ * certificates found in the file at |certsPath|.
+ *
+ * Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
+ * more than one invocation of the methods of this category.
+ */
 + (void)useTestCertsPath:(NSString *)certsPath
                 testName:(NSString *)testName
                  forHost:(NSString *)host;
 
-// Establish all connections to the provided host using cleartext instead of SSL.
-//
-// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
-// more than one invocation of the methods of this category.
+/**
+ * Establish all connections to the provided host using cleartext instead of SSL.
+ *
+ * Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
+ * more than one invocation of the methods of this category.
+ */
 + (void)useInsecureConnectionsForHost:(NSString *)host;
 @end
diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m
index bade0b29208249f043cd58448a94203581f9aac8..c8e81337031a7b3c605c5cd6c7c94f83bc760306 100644
--- a/src/objective-c/GRPCClient/GRPCCall+Tests.m
+++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m
@@ -40,6 +40,9 @@
 + (void)useTestCertsPath:(NSString *)certsPath
                 testName:(NSString *)testName
                  forHost:(NSString *)host {
+  if (!host || !certsPath || !testName) {
+    [NSException raise:NSInvalidArgumentException format:@"host, path and name must be provided."];
+  }
   GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
   hostConfig.pathToCertificates = certsPath;
   hostConfig.hostNameOverride = testName;
diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h
index 35f7e16af70f1a3a01a0d68bce5534df24b6d997..5918f8857a9bccbfa9ec69ed38613f3959cdd702 100644
--- a/src/objective-c/GRPCClient/GRPCCall.h
+++ b/src/objective-c/GRPCClient/GRPCCall.h
@@ -31,117 +31,145 @@
  *
  */
 
-// The gRPC protocol is an RPC protocol on top of HTTP2.
-//
-// While the most common type of RPC receives only one request message and returns only one response
-// message, the protocol also supports RPCs that return multiple individual messages in a streaming
-// fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and
-// responses.
-//
-// Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of
-// the "non-streaming type" sending only one message in the corresponding direction (the protocol
-// doesn't make any distinction).
-//
-// Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed
-// transparently on the same TCP connection.
+/**
+ * The gRPC protocol is an RPC protocol on top of HTTP2.
+ *
+ * While the most common type of RPC receives only one request message and returns only one response
+ * message, the protocol also supports RPCs that return multiple individual messages in a streaming
+ * fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and
+ * responses.
+ *
+ * Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of
+ * the "non-streaming type" sending only one message in the corresponding direction (the protocol
+ * doesn't make any distinction).
+ *
+ * Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed
+ * transparently on the same TCP connection.
+ */
 
 #import <Foundation/Foundation.h>
 #import <RxLibrary/GRXWriter.h>
 
 #pragma mark gRPC errors
 
-// Domain of NSError objects produced by gRPC.
+/** Domain of NSError objects produced by gRPC. */
 extern NSString *const kGRPCErrorDomain;
 
-// gRPC error codes.
-// Note that a few of these are never produced by the gRPC libraries, but are of general utility for
-// server applications to produce.
+/**
+ * gRPC error codes.
+ * Note that a few of these are never produced by the gRPC libraries, but are of general utility for
+ * server applications to produce.
+ */
 typedef NS_ENUM(NSUInteger, GRPCErrorCode) {
-  // The operation was cancelled (typically by the caller).
+  /** The operation was cancelled (typically by the caller). */
   GRPCErrorCodeCancelled = 1,
 
-  // Unknown error. Errors raised by APIs that do not return enough error information may be
-  // converted to this error.
+  /**
+   * Unknown error. Errors raised by APIs that do not return enough error information may be
+   * converted to this error.
+   */
   GRPCErrorCodeUnknown = 2,
 
-  // The client specified an invalid argument. Note that this differs from FAILED_PRECONDITION.
-  // INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the
-  // server (e.g., a malformed file name).
+  /**
+   * The client specified an invalid argument. Note that this differs from FAILED_PRECONDITION.
+   * INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the
+   * server (e.g., a malformed file name).
+   */
   GRPCErrorCodeInvalidArgument = 3,
 
-  // Deadline expired before operation could complete. For operations that change the state of the
-  // server, this error may be returned even if the operation has completed successfully. For
-  // example, a successful response from the server could have been delayed long enough for the
-  // deadline to expire.
+  /**
+   * Deadline expired before operation could complete. For operations that change the state of the
+   * server, this error may be returned even if the operation has completed successfully. For
+   * example, a successful response from the server could have been delayed long enough for the
+   * deadline to expire.
+   */
   GRPCErrorCodeDeadlineExceeded = 4,
 
-  // Some requested entity (e.g., file or directory) was not found.
+  /** Some requested entity (e.g., file or directory) was not found. */
   GRPCErrorCodeNotFound = 5,
 
-  // Some entity that we attempted to create (e.g., file or directory) already exists.
+  /** Some entity that we attempted to create (e.g., file or directory) already exists. */
   GRPCErrorCodeAlreadyExists = 6,
 
-  // The caller does not have permission to execute the specified operation. PERMISSION_DENIED isn't
-  // used for rejections caused by exhausting some resource (RESOURCE_EXHAUSTED is used instead for
-  // those errors). PERMISSION_DENIED doesn't indicate a failure to identify the caller
-  // (UNAUTHENTICATED is used instead for those errors).
+  /**
+   * The caller does not have permission to execute the specified operation. PERMISSION_DENIED isn't
+   * used for rejections caused by exhausting some resource (RESOURCE_EXHAUSTED is used instead for
+   * those errors). PERMISSION_DENIED doesn't indicate a failure to identify the caller
+   * (UNAUTHENTICATED is used instead for those errors).
+   */
   GRPCErrorCodePermissionDenied = 7,
 
-  // The request does not have valid authentication credentials for the operation (e.g. the caller's
-  // identity can't be verified).
+  /**
+   * The request does not have valid authentication credentials for the operation (e.g. the caller's
+   * identity can't be verified).
+   */
   GRPCErrorCodeUnauthenticated = 16,
 
-  // Some resource has been exhausted, perhaps a per-user quota.
+  /** Some resource has been exhausted, perhaps a per-user quota. */
   GRPCErrorCodeResourceExhausted = 8,
 
-  // The RPC was rejected because the server is not in a state required for the procedure's
-  // execution. For example, a directory to be deleted may be non-empty, etc.
-  // The client should not retry until the server state has been explicitly fixed (e.g. by
-  // performing another RPC). The details depend on the service being called, and should be found in
-  // the NSError's userInfo.
+  /**
+   * The RPC was rejected because the server is not in a state required for the procedure's
+   * execution. For example, a directory to be deleted may be non-empty, etc.
+   * The client should not retry until the server state has been explicitly fixed (e.g. by
+   * performing another RPC). The details depend on the service being called, and should be found in
+   * the NSError's userInfo.
+   */
   GRPCErrorCodeFailedPrecondition = 9,
 
-  // The RPC was aborted, typically due to a concurrency issue like sequencer check failures,
-  // transaction aborts, etc. The client should retry at a higher-level (e.g., restarting a read-
-  // modify-write sequence).
+  /**
+   * The RPC was aborted, typically due to a concurrency issue like sequencer check failures,
+   * transaction aborts, etc. The client should retry at a higher-level (e.g., restarting a read-
+   * modify-write sequence).
+   */
   GRPCErrorCodeAborted = 10,
 
-  // The RPC was attempted past the valid range. E.g., enumerating past the end of a list.
-  // Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system state
-  // changes. For example, an RPC to get elements of a list will generate INVALID_ARGUMENT if asked
-  // to return the element at a negative index, but it will generate OUT_OF_RANGE if asked to return
-  // the element at an index past the current size of the list.
+  /**
+   * The RPC was attempted past the valid range. E.g., enumerating past the end of a list.
+   * Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system state
+   * changes. For example, an RPC to get elements of a list will generate INVALID_ARGUMENT if asked
+   * to return the element at a negative index, but it will generate OUT_OF_RANGE if asked to return
+   * the element at an index past the current size of the list.
+   */
   GRPCErrorCodeOutOfRange = 11,
 
-  // The procedure is not implemented or not supported/enabled in this server.
+  /** The procedure is not implemented or not supported/enabled in this server. */
   GRPCErrorCodeUnimplemented = 12,
 
-  // Internal error. Means some invariant expected by the server application or the gRPC library has
-  // been broken.
+  /**
+   * Internal error. Means some invariant expected by the server application or the gRPC library has
+   * been broken.
+   */
   GRPCErrorCodeInternal = 13,
 
-  // The server is currently unavailable. This is most likely a transient condition and may be
-  // corrected by retrying with a backoff.
+  /**
+   * The server is currently unavailable. This is most likely a transient condition and may be
+   * corrected by retrying with a backoff.
+   */
   GRPCErrorCodeUnavailable = 14,
 
-  // Unrecoverable data loss or corruption.
+  /** Unrecoverable data loss or corruption. */
   GRPCErrorCodeDataLoss = 15,
 };
 
-// Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by
-// the server.
+/**
+ * Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by
+ * the server.
+ */
 extern id const kGRPCHeadersKey;
 extern id const kGRPCTrailersKey;
 
 #pragma mark GRPCCall
 
-// The container of the request headers of an RPC conforms to this protocol, which is a subset of
-// NSMutableDictionary's interface. It will become a NSMutableDictionary later on.
-// The keys of this container are the header names, which per the HTTP standard are case-
-// insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and
-// can only consist of ASCII characters.
-// A header value is a NSString object (with only ASCII characters), unless the header name has the
-// suffix "-bin", in which case the value has to be a NSData object.
+/**
+ * The container of the request headers of an RPC conforms to this protocol, which is a subset of
+ * NSMutableDictionary's interface. It will become a NSMutableDictionary later on.
+ * The keys of this container are the header names, which per the HTTP standard are case-
+ * insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and
+ * can only consist of ASCII characters.
+ * A header value is a NSString object (with only ASCII characters), unless the header name has the
+ * suffix "-bin", in which case the value has to be a NSData object.
+ */
 @protocol GRPCRequestHeaders <NSObject>
 
 @property(nonatomic, readonly) NSUInteger count;
@@ -154,53 +182,63 @@ extern id const kGRPCTrailersKey;
 
 @end
 
-// Represents a single gRPC remote call.
+/** Represents a single gRPC remote call. */
 @interface GRPCCall : GRXWriter
 
-// These HTTP headers will be passed to the server as part of this call. Each HTTP header is a
-// name-value pair with string names and either string or binary values.
-//
-// The passed dictionary has to use NSString keys, corresponding to the header names. The value
-// associated to each can be a NSString object or a NSData object. E.g.:
-//
-// call.requestHeaders = @{@"authorization": @"Bearer ..."};
-//
-// call.requestHeaders[@"my-header-bin"] = someData;
-//
-// After the call is started, trying to modify this property is an error.
-//
-// The property is initialized to an empty NSMutableDictionary.
+/**
+ * These HTTP headers will be passed to the server as part of this call. Each HTTP header is a
+ * name-value pair with string names and either string or binary values.
+ *
+ * The passed dictionary has to use NSString keys, corresponding to the header names. The value
+ * associated to each can be a NSString object or a NSData object. E.g.:
+ *
+ * call.requestHeaders = @{@"authorization": @"Bearer ..."};
+ *
+ * call.requestHeaders[@"my-header-bin"] = someData;
+ *
+ * After the call is started, trying to modify this property is an error.
+ *
+ * The property is initialized to an empty NSMutableDictionary.
+ */
 @property(atomic, readonly) id<GRPCRequestHeaders> requestHeaders;
 
-// This dictionary is populated with the HTTP headers received from the server. This happens before
-// any response message is received from the server. It has the same structure as the request
-// headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a
-// NSData value; the others have a NSString value.
-//
-// The value of this property is nil until all response headers are received, and will change before
-// any of -writeValue: or -writesFinishedWithError: are sent to the writeable.
+/**
+ * This dictionary is populated with the HTTP headers received from the server. This happens before
+ * any response message is received from the server. It has the same structure as the request
+ * headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a
+ * NSData value; the others have a NSString value.
+ *
+ * The value of this property is nil until all response headers are received, and will change before
+ * any of -writeValue: or -writesFinishedWithError: are sent to the writeable.
+ */
 @property(atomic, readonly) NSDictionary *responseHeaders;
 
-// Same as responseHeaders, but populated with the HTTP trailers received from the server before the
-// call finishes.
-//
-// The value of this property is nil until all response trailers are received, and will change
-// before -writesFinishedWithError: is sent to the writeable.
+/**
+ * Same as responseHeaders, but populated with the HTTP trailers received from the server before the
+ * call finishes.
+ *
+ * The value of this property is nil until all response trailers are received, and will change
+ * before -writesFinishedWithError: is sent to the writeable.
+ */
 @property(atomic, readonly) NSDictionary *responseTrailers;
 
-// The request writer has to write NSData objects into the provided Writeable. The server will
-// receive each of those separately and in order as distinct messages.
-// A gRPC call might not complete until the request writer finishes. On the other hand, the request
-// finishing doesn't necessarily make the call to finish, as the server might continue sending
-// messages to the response side of the call indefinitely (depending on the semantics of the
-// specific remote method called).
-// To finish a call right away, invoke cancel.
+/**
+ * The request writer has to write NSData objects into the provided Writeable. The server will
+ * receive each of those separately and in order as distinct messages.
+ * A gRPC call might not complete until the request writer finishes. On the other hand, the request
+ * finishing doesn't necessarily make the call to finish, as the server might continue sending
+ * messages to the response side of the call indefinitely (depending on the semantics of the
+ * specific remote method called).
+ * To finish a call right away, invoke cancel.
+ */
 - (instancetype)initWithHost:(NSString *)host
                         path:(NSString *)path
               requestsWriter:(GRXWriter *)requestsWriter NS_DESIGNATED_INITIALIZER;
 
-// Finishes the request side of this call, notifies the server that the RPC should be cancelled, and
-// finishes the response side of the call with an error of code CANCELED.
+/**
+ * Finishes the request side of this call, notifies the server that the RPC should be cancelled, and
+ * finishes the response side of the call with an error of code CANCELED.
+ */
 - (void)cancel;
 
 // TODO(jcanizales): Let specify a deadline. As a category of GRXWriter?
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h
index 2a7b701576a8c60a0c41b8a296ef2af15301cf07..e2d19d506ad772d283307ef01d09fd109e0d3a22 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.h
@@ -35,12 +35,16 @@
 
 struct grpc_channel;
 
-// Each separate instance of this class represents at least one TCP connection to the provided host.
-// Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|.
+/**
+ * Each separate instance of this class represents at least one TCP connection to the provided host.
+ * Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|.
+ */
 @interface GRPCChannel : NSObject
 @property(nonatomic, readonly) struct grpc_channel *unmanagedChannel;
 
-// This initializer takes ownership of the passed channel, and will destroy it when this object is
-// deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects.
+/**
+ * This initializer takes ownership of the passed channel, and will destroy it when this object is
+ * deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects.
+ */
 - (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER;
 @end
diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h
index ab8d714d2206d808948772c5eb704b280d7a14c9..fe3b8f39d1273da12f90c11f5937b1ef72df70ec 100644
--- a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h
+++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h
@@ -36,15 +36,17 @@
 
 typedef void(^GRPCQueueCompletionHandler)(bool success);
 
-// This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the
-// |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for
-// every |grpc_call_*| method that accepts a tag, you can pass a block of type
-// |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is
-// guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is
-// passed a |bool| that tells if the operation was successful.
-//
-// Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to
-// the |grpc_call| that's using it.
+/**
+ * This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the
+ * |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for
+ * every |grpc_call_*| method that accepts a tag, you can pass a block of type
+ * |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is
+ * guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is
+ * passed a |bool| that tells if the operation was successful.
+ *
+ * Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to
+ * the |grpc_call| that's using it.
+ */
 @interface GRPCCompletionQueue : NSObject
 @property(nonatomic, readonly) grpc_completion_queue *unmanagedQueue;
 
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h
index f0bbd5302342eda6fe4a22ff33582dde533cc938..6b4f98746d0caa4382d89885ac6194029d151e60 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.h
+++ b/src/objective-c/GRPCClient/private/GRPCHost.h
@@ -40,18 +40,18 @@ struct grpc_call;
 
 @property(nonatomic, readonly) NSString *address;
 
-// The following properties should only be modified for testing:
+/** The following properties should only be modified for testing: */
 
 @property(nonatomic, getter=isSecure) BOOL secure;
 
 @property(nonatomic, copy) NSString *pathToCertificates;
 @property(nonatomic, copy) NSString *hostNameOverride;
 
-// Host objects initialized with the same address are the same.
+/** Host objects initialized with the same address are the same. */
 + (instancetype)hostWithAddress:(NSString *)address;
 - (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
 
-// Create a grpc_call object to the provided path on this host.
+/** Create a grpc_call object to the provided path on this host. */
 - (struct grpc_call *)unmanagedCallWithPath:(NSString *)path
                             completionQueue:(GRPCCompletionQueue *)queue;
 
diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
index 74257eb0584d30361348addeae352bea09d7379b..4e0881e5a2a32b3c2a3787743026103e823f3dca 100644
--- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
@@ -40,13 +40,15 @@ struct grpc_credentials;
 @interface GRPCSecureChannel : GRPCChannel
 - (instancetype)initWithHost:(NSString *)host;
 
-// Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for
-// pathToCertificates results in using the default root certificates distributed with the library.
+/**
+ * Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for
+ * pathToCertificates results in using the default root certificates distributed with the library.
+ */
 - (instancetype)initWithHost:(NSString *)host
           pathToCertificates:(NSString *)path
             hostNameOverride:(NSString *)hostNameOverride;
 
-// The passed arguments aren't required to be valid beyond the invocation of this initializer.
+/** The passed arguments aren't required to be valid beyond the invocation of this initializer. */
 - (instancetype)initWithHost:(NSString *)host
                  credentials:(struct grpc_credentials *)credentials
                         args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER;
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
index 4ca2766147e61450ca6d3f7aeccae127952ab9af..7747aa53ef07fdf25fb605e05984d116db659c14 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
@@ -39,7 +39,7 @@
 
 @interface GRPCOperation : NSObject
 @property(nonatomic, readonly) grpc_op op;
-// Guaranteed to be called when the operation has finished.
+/** Guaranteed to be called when the operation has finished. */
 - (void)finish;
 @end
 
diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/NSError+GRPC.h
index f4729dc8a137c4402c748df0fa419825e7097882..e0c1efc1f91b84135f84d8da74f70b4320166131 100644
--- a/src/objective-c/GRPCClient/private/NSError+GRPC.h
+++ b/src/objective-c/GRPCClient/private/NSError+GRPC.h
@@ -35,7 +35,9 @@
 #include <grpc/grpc.h>
 
 @interface NSError (GRPC)
-// Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode|
-// and whose domain is |kGRPCErrorDomain|.
+/**
+ * Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode|
+ * and whose domain is |kGRPCErrorDomain|.
+ */
 + (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details;
 @end
diff --git a/src/objective-c/ProtoRPC/ProtoMethod.h b/src/objective-c/ProtoRPC/ProtoMethod.h
index 8f554a04832d9298aaf9380064e61e5ecde1efd0..a0ed2cf98a5e2a5203f0d2f0ce78125e1ac5a175 100644
--- a/src/objective-c/ProtoRPC/ProtoMethod.h
+++ b/src/objective-c/ProtoRPC/ProtoMethod.h
@@ -33,8 +33,10 @@
 
 #import <Foundation/Foundation.h>
 
-// A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint
-// can implement multiple services.
+/**
+ * A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint
+ * can implement multiple services.
+ */
 @interface ProtoMethod : NSObject
 @property(nonatomic, readonly) NSString *package;
 @property(nonatomic, readonly) NSString *service;
diff --git a/src/objective-c/README.md b/src/objective-c/README.md
index a861a9f6f93cba29640887c0508912e5cff413ed..c1d25b96f5056ca8faf4b573173c7b1e03619585 100644
--- a/src/objective-c/README.md
+++ b/src/objective-c/README.md
@@ -1,3 +1,4 @@
+[![Cocoapods](https://img.shields.io/cocoapods/v/gRPC.svg)](https://cocoapods.org/pods/gRPC)
 # gRPC for Objective-C
 
 - [Install protoc with the gRPC plugin](#install)
diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h
index ca94ce275f704bd004c60812ba984dbcf0dcff22..03b0359278a24fbf7b3578166240fb4796102477 100644
--- a/src/objective-c/RxLibrary/GRXBufferedPipe.h
+++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h
@@ -36,25 +36,27 @@
 #import "GRXWriteable.h"
 #import "GRXWriter.h"
 
-// A buffered pipe is a Writer that also acts as a Writeable.
-// Once it is started, whatever values are written into it (via -writeValue:) will be propagated
-// immediately, unless flow control prevents it.
-// If it is throttled and keeps receiving values, as well as if it receives values before being
-// started, it will buffer them and propagate them in order as soon as its state becomes Started.
-// If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and
-// propagate the error immediately.
-//
-// Beware that a pipe of this type can't prevent receiving more values when it is paused (for
-// example if used to write data to a congested network connection). Because in such situations the
-// pipe will keep buffering all data written to it, your application could run out of memory and
-// crash. If you want to react to flow control signals to prevent that, instead of using this class
-// you can implement an object that conforms to GRXWriter.
-//
-// Thread-safety:
-// The methods of an object of this class should not be called concurrently from different threads.
+/**
+ * A buffered pipe is a Writer that also acts as a Writeable.
+ * Once it is started, whatever values are written into it (via -writeValue:) will be propagated
+ * immediately, unless flow control prevents it.
+ * If it is throttled and keeps receiving values, as well as if it receives values before being
+ * started, it will buffer them and propagate them in order as soon as its state becomes Started.
+ * If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and
+ * propagate the error immediately.
+ *
+ * Beware that a pipe of this type can't prevent receiving more values when it is paused (for
+ * example if used to write data to a congested network connection). Because in such situations the
+ * pipe will keep buffering all data written to it, your application could run out of memory and
+ * crash. If you want to react to flow control signals to prevent that, instead of using this class
+ * you can implement an object that conforms to GRXWriter.
+ *
+ * Thread-safety:
+ * The methods of an object of this class should not be called concurrently from different threads.
+ */
 @interface GRXBufferedPipe : GRXWriter<GRXWriteable>
 
-// Convenience constructor.
+/** Convenience constructor. */
 + (instancetype)pipe;
 
 @end
diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h
index 1080001905e6cf39ca6b7240cf5da1321e59accd..b2775f98b56e5c19039d4eeb075e6dbf7b0a0562 100644
--- a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h
+++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h
@@ -36,36 +36,48 @@
 #import "GRXWriter.h"
 #import "GRXWriteable.h"
 
-// This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a
-// GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last
-// message sent to it (no matter what messages are sent to the wrapper, in what order, nor from
-// which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g.
-// by the app cancelling the writes), no further messages are sent to the writeable except
-// writesFinishedWithError:.
-//
-// TODO(jcanizales): Let the user specify another queue for the writeable callbacks.
+/**
+ * This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a
+ * GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last
+ * message sent to it (no matter what messages are sent to the wrapper, in what order, nor from
+ * which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g.
+ * by the app cancelling the writes), no further messages are sent to the writeable except
+ * writesFinishedWithError:.
+ *
+ * TODO(jcanizales): Let the user specify another queue for the writeable callbacks.
+ */
 @interface GRXConcurrentWriteable : NSObject
 
-// The GRXWriteable passed is the wrapped writeable.
-// The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released
-// after that.
+/**
+ * The GRXWriteable passed is the wrapped writeable.
+ * The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released
+ * after that.
+ */
 - (instancetype)initWithWriteable:(id<GRXWriteable>)writeable NS_DESIGNATED_INITIALIZER;
 
-// Enqueues writeValue: to be sent to the writeable in the main thread.
-// The passed handler is invoked from the main thread after writeValue: returns.
+/**
+ * Enqueues writeValue: to be sent to the writeable in the main thread.
+ * The passed handler is invoked from the main thread after writeValue: returns.
+ */
 - (void)enqueueValue:(id)value completionHandler:(void (^)())handler;
 
-// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that
-// message is sent to the writeable, all other methods of this object are effectively noops.
+/**
+ * Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that
+ * message is sent to the writeable, all other methods of this object are effectively noops.
+ */
 - (void)enqueueSuccessfulCompletion;
 
-// If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one
-// to be sent to it in the main thread, and cancel all other pending messages to the writeable
-// enqueued by this object (both past and future).
-// The error argument cannot be nil.
+/**
+ * If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one
+ * to be sent to it in the main thread, and cancel all other pending messages to the writeable
+ * enqueued by this object (both past and future).
+ * The error argument cannot be nil.
+ */
 - (void)cancelWithError:(NSError *)error;
 
-// Cancels all pending messages to the writeable enqueued by this object (both past and future).
-// Because the writeable won't receive writesFinishedWithError:, this also releases the writeable.
+/**
+ * Cancels all pending messages to the writeable enqueued by this object (both past and future).
+ * Because the writeable won't receive writesFinishedWithError:, this also releases the writeable.
+ */
 - (void)cancelSilently;
 @end
diff --git a/src/objective-c/RxLibrary/GRXForwardingWriter.h b/src/objective-c/RxLibrary/GRXForwardingWriter.h
index f310832284a3eb22bf42a90cf68af2082a47d702..8d45b8ed8d3ed54465bf239e5e8cd752d2c9c571 100644
--- a/src/objective-c/RxLibrary/GRXForwardingWriter.h
+++ b/src/objective-c/RxLibrary/GRXForwardingWriter.h
@@ -33,17 +33,19 @@
 
 #import "GRXWriter.h"
 
-// A "proxy" class that simply forwards values, completion, and errors from its input writer to its
-// writeable.
-// It is useful as a superclass for pipes that act as a transformation of their
-// input writer, and for classes that represent objects with input and
-// output sequences of values, like an RPC.
-//
-// Thread-safety:
-// All messages sent to this object need to be serialized. When it is started, the writer it wraps
-// is started in the same thread. Manual state changes are propagated to the wrapped writer in the
-// same thread too. Importantly, all messages the wrapped writer sends to its writeable need to be
-// serialized with any message sent to this object.
+/**
+ * A "proxy" class that simply forwards values, completion, and errors from its input writer to its
+ * writeable.
+ * It is useful as a superclass for pipes that act as a transformation of their
+ * input writer, and for classes that represent objects with input and
+ * output sequences of values, like an RPC.
+ *
+ * Thread-safety:
+ * All messages sent to this object need to be serialized. When it is started, the writer it wraps
+ * is started in the same thread. Manual state changes are propagated to the wrapped writer in the
+ * same thread too. Importantly, all messages the wrapped writer sends to its writeable need to be
+ * serialized with any message sent to this object.
+ */
 @interface GRXForwardingWriter : GRXWriter
 - (instancetype)initWithWriter:(GRXWriter *)writer NS_DESIGNATED_INITIALIZER;
 @end
diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.h b/src/objective-c/RxLibrary/GRXImmediateWriter.h
index 3fcc25943424b05a1992dabd91f7ff5ced5bda46..e22b056ff565a493264e2a303a01ef24d57834ae 100644
--- a/src/objective-c/RxLibrary/GRXImmediateWriter.h
+++ b/src/objective-c/RxLibrary/GRXImmediateWriter.h
@@ -35,46 +35,60 @@
 
 #import "GRXWriter.h"
 
-// Utility to construct GRXWriter instances from values that are immediately available when
-// required.
-//
-// Thread-safety:
-//
-// An object of this class shouldn't be messaged concurrently by more than one thread. It will start
-// messaging the writeable before |startWithWriteable:| returns, in the same thread. That is the
-// only place where the writer can be paused or stopped prematurely.
-//
-// If a paused writer of this class is resumed, it will start messaging the writeable, in the same
-// thread, before |setState:| returns. Because the object can't be legally accessed concurrently,
-// that's the only place where it can be paused again (or stopped).
+/**
+ * Utility to construct GRXWriter instances from values that are immediately available when
+ * required.
+ *
+ * Thread-safety:
+ *
+ * An object of this class shouldn't be messaged concurrently by more than one thread. It will start
+ * messaging the writeable before |startWithWriteable:| returns, in the same thread. That is the
+ * only place where the writer can be paused or stopped prematurely.
+ *
+ * If a paused writer of this class is resumed, it will start messaging the writeable, in the same
+ * thread, before |setState:| returns. Because the object can't be legally accessed concurrently,
+ * that's the only place where it can be paused again (or stopped).
+ */
 @interface GRXImmediateWriter : GRXWriter
 
-// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
-// its writeable. The NSEnumerator is released when it finishes.
+/**
+ * Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
+ * its writeable. The NSEnumerator is released when it finishes.
+ */
 + (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator;
 
-// Returns a writer that pushes to its writeable the successive values returned by the passed
-// block. When the block first returns nil, it is released.
+/**
+ * Returns a writer that pushes to its writeable the successive values returned by the passed
+ * block. When the block first returns nil, it is released.
+ */
 + (GRXWriter *)writerWithValueSupplier:(id (^)())block;
 
-// Returns a writer that iterates over the values of the passed container and pushes them to
-// its writeable. The container is released when the iteration is over.
-//
-// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
-// call one method per element. Because GRXWriteable instances accept values one by one, that speed
-// gain doesn't happen here.
+/**
+ * Returns a writer that iterates over the values of the passed container and pushes them to
+ * its writeable. The container is released when the iteration is over.
+ *
+ * Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
+ * call one method per element. Because GRXWriteable instances accept values one by one, that speed
+ * gain doesn't happen here.
+ */
 + (GRXWriter *)writerWithContainer:(id<NSFastEnumeration>)container;
 
-// Returns a writer that sends the passed value to its writeable and then finishes (releasing the
-// value).
+/**
+ * Returns a writer that sends the passed value to its writeable and then finishes (releasing the
+ * value).
+ */
 + (GRXWriter *)writerWithValue:(id)value;
 
-// Returns a writer that, as part of its start method, sends the passed error to the writeable
-// (then releasing the error).
+/**
+ * Returns a writer that, as part of its start method, sends the passed error to the writeable
+ * (then releasing the error).
+ */
 + (GRXWriter *)writerWithError:(NSError *)error;
 
-// Returns a writer that, as part of its start method, finishes immediately without sending any
-// values to its writeable.
+/**
+ * Returns a writer that, as part of its start method, finishes immediately without sending any
+ * values to its writeable.
+ */
 + (GRXWriter *)emptyWriter;
 
 @end
diff --git a/src/objective-c/RxLibrary/GRXWriteable.h b/src/objective-c/RxLibrary/GRXWriteable.h
index 45613d6dd09408c84d6e2ef4cc3946f371371f94..7fe805c6638978051a37ac3aae96a77bc7c5ab31 100644
--- a/src/objective-c/RxLibrary/GRXWriteable.h
+++ b/src/objective-c/RxLibrary/GRXWriteable.h
@@ -33,16 +33,20 @@
 
 #import <Foundation/Foundation.h>
 
-// A GRXWriteable is an object to which a sequence of values can be sent. The
-// sequence finishes with an optional error.
+/**
+ * A GRXWriteable is an object to which a sequence of values can be sent. The
+ * sequence finishes with an optional error.
+ */
 @protocol GRXWriteable <NSObject>
 
-// Push the next value of the sequence to the receiving object.
+/** Push the next value of the sequence to the receiving object. */
 - (void)writeValue:(id)value;
 
-// Signal that the sequence is completed, or that an error ocurred. After this
-// message is sent to the instance, neither it nor writeValue: may be
-// called again.
+/**
+ * Signal that the sequence is completed, or that an error ocurred. After this
+ * message is sent to the instance, neither it nor writeValue: may be
+ * called again.
+ */
 - (void)writesFinishedWithError:(NSError *)errorOrNil;
 @end
 
@@ -51,8 +55,10 @@ typedef void (^GRXCompletionHandler)(NSError *errorOrNil);
 typedef void (^GRXSingleHandler)(id value, NSError *errorOrNil);
 typedef void (^GRXEventHandler)(BOOL done, id value, NSError *error);
 
-// Utility to create objects that conform to the GRXWriteable protocol, from
-// blocks that handle each of the two methods of the protocol.
+/**
+ * Utility to create objects that conform to the GRXWriteable protocol, from
+ * blocks that handle each of the two methods of the protocol.
+ */
 @interface GRXWriteable : NSObject<GRXWriteable>
 
 + (instancetype)writeableWithSingleHandler:(GRXSingleHandler)handler;
diff --git a/src/objective-c/RxLibrary/GRXWriter+Immediate.h b/src/objective-c/RxLibrary/GRXWriter+Immediate.h
index b75c0a5a64c30cad01a5fd3ebd4da2e628f1499e..be880151f4f79ff49f5ad50a38d76ac5bc7d2fa6 100644
--- a/src/objective-c/RxLibrary/GRXWriter+Immediate.h
+++ b/src/objective-c/RxLibrary/GRXWriter+Immediate.h
@@ -35,32 +35,44 @@
 
 @interface GRXWriter (Immediate)
 
-// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
-// its writeable. The NSEnumerator is released when it finishes.
+/**
+ * Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
+ * its writeable. The NSEnumerator is released when it finishes.
+ */
 + (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator;
 
-// Returns a writer that pushes to its writeable the successive values returned by the passed
-// block. When the block first returns nil, it is released.
+/**
+ * Returns a writer that pushes to its writeable the successive values returned by the passed
+ * block. When the block first returns nil, it is released.
+ */
 + (instancetype)writerWithValueSupplier:(id (^)())block;
 
-// Returns a writer that iterates over the values of the passed container and pushes them to
-// its writeable. The container is released when the iteration is over.
-//
-// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
-// call one method per element. Because GRXWriteable instances accept values one by one, that speed
-// gain doesn't happen here.
+/**
+ * Returns a writer that iterates over the values of the passed container and pushes them to
+ * its writeable. The container is released when the iteration is over.
+ *
+ * Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
+ * call one method per element. Because GRXWriteable instances accept values one by one, that speed
+ * gain doesn't happen here.
+ */
 + (instancetype)writerWithContainer:(id<NSFastEnumeration>)container;
 
-// Returns a writer that sends the passed value to its writeable and then finishes (releasing the
-// value).
+/**
+ * Returns a writer that sends the passed value to its writeable and then finishes (releasing the
+ * value).
+ */
 + (instancetype)writerWithValue:(id)value;
 
-// Returns a writer that, as part of its start method, sends the passed error to the writeable
-// (then releasing the error).
+/**
+ * Returns a writer that, as part of its start method, sends the passed error to the writeable
+ * (then releasing the error).
+ */
 + (instancetype)writerWithError:(NSError *)error;
 
-// Returns a writer that, as part of its start method, finishes immediately without sending any
-// values to its writeable.
+/**
+ * Returns a writer that, as part of its start method, finishes immediately without sending any
+ * values to its writeable.
+ */
 + (instancetype)emptyWriter;
 
 @end
diff --git a/src/objective-c/RxLibrary/GRXWriter+Transformations.h b/src/objective-c/RxLibrary/GRXWriter+Transformations.h
index 60c4da37d694f721e74176495e0e7e7f887e15bb..17d61e754101bda6f7b2b645c4af598303d7fd78 100644
--- a/src/objective-c/RxLibrary/GRXWriter+Transformations.h
+++ b/src/objective-c/RxLibrary/GRXWriter+Transformations.h
@@ -35,8 +35,10 @@
 
 @interface GRXWriter (Transformations)
 
-// Returns a writer that wraps the receiver, and has all the values the receiver would write
-// transformed by the provided mapping function.
+/**
+ * Returns a writer that wraps the receiver, and has all the values the receiver would write
+ * transformed by the provided mapping function.
+ */
 - (GRXWriter *)map:(id (^)(id value))map;
 
 @end
diff --git a/src/objective-c/RxLibrary/GRXWriter.h b/src/objective-c/RxLibrary/GRXWriter.h
index b1c994aa382c05780743619fb614271bafa47735..ff81268446a91728cd3665158d4bda814e62029e 100644
--- a/src/objective-c/RxLibrary/GRXWriter.h
+++ b/src/objective-c/RxLibrary/GRXWriter.h
@@ -35,73 +35,87 @@
 
 #import "GRXWriteable.h"
 
-// States of a writer.
+/** States of a writer. */
 typedef NS_ENUM(NSInteger, GRXWriterState) {
 
-  // The writer has not yet been given a writeable to which it can push its values. To have a writer
-  // transition to the Started state, send it a startWithWriteable: message.
-  //
-  // A writer's state cannot be manually set to this value.
+  /**
+   * The writer has not yet been given a writeable to which it can push its values. To have a writer
+   * transition to the Started state, send it a startWithWriteable: message.
+   *
+   * A writer's state cannot be manually set to this value.
+   */
   GRXWriterStateNotStarted,
 
-  // The writer might push values to the writeable at any moment.
+  /** The writer might push values to the writeable at any moment. */
   GRXWriterStateStarted,
 
-  // The writer is temporarily paused, and won't send any more values to the writeable unless its
-  // state is set back to Started. The writer might still transition to the Finished state at any
-  // moment, and is allowed to send writesFinishedWithError: to its writeable.
+  /**
+   * The writer is temporarily paused, and won't send any more values to the writeable unless its
+   * state is set back to Started. The writer might still transition to the Finished state at any
+   * moment, and is allowed to send writesFinishedWithError: to its writeable.
+   */
   GRXWriterStatePaused,
 
-  // The writer has released its writeable and won't interact with it anymore.
-  //
-  // One seldomly wants to set a writer's state to this value, as its writeable isn't notified with
-  // a writesFinishedWithError: message. Instead, sending finishWithError: to the writer will make
-  // it notify the writeable and then transition to this state.
+  /**
+   * The writer has released its writeable and won't interact with it anymore.
+   *
+   * One seldomly wants to set a writer's state to this value, as its writeable isn't notified with
+   * a writesFinishedWithError: message. Instead, sending finishWithError: to the writer will make
+   * it notify the writeable and then transition to this state.
+   */
   GRXWriterStateFinished
 };
 
-// An GRXWriter object can produce, on demand, a sequence of values. The sequence may be produced
-// asynchronously, and it may consist of any number of elements, including none or an infinite
-// number.
-//
-// GRXWriter is the active dual of NSEnumerator. The difference between them is thus whether the
-// object plays an active or passive role during usage: A user of NSEnumerator pulls values off it,
-// and passes the values to a writeable. A user of GRXWriter, though, just gives it a writeable, and
-// the GRXWriter instance pushes values to the writeable. This makes this protocol suitable to
-// represent a sequence of future values, as well as collections with internal iteration.
-//
-// An instance of GRXWriter can start producing values after a writeable is passed to it. It can
-// also be commanded to finish the sequence immediately (with an optional error). Finally, it can be
-// asked to pause, and resumed later. All GRXWriter objects support pausing and early termination.
-//
-// Thread-safety:
-//
-// State transitions take immediate effect if the object is used from a single thread. Subclasses
-// might offer stronger guarantees.
-//
-// Unless otherwise indicated by a conforming subclass, no messages should be sent concurrently to a
-// GRXWriter. I.e., conforming classes aren't required to be thread-safe.
+/**
+ * An GRXWriter object can produce, on demand, a sequence of values. The sequence may be produced
+ * asynchronously, and it may consist of any number of elements, including none or an infinite
+ * number.
+ *
+ * GRXWriter is the active dual of NSEnumerator. The difference between them is thus whether the
+ * object plays an active or passive role during usage: A user of NSEnumerator pulls values off it,
+ * and passes the values to a writeable. A user of GRXWriter, though, just gives it a writeable, and
+ * the GRXWriter instance pushes values to the writeable. This makes this protocol suitable to
+ * represent a sequence of future values, as well as collections with internal iteration.
+ *
+ * An instance of GRXWriter can start producing values after a writeable is passed to it. It can
+ * also be commanded to finish the sequence immediately (with an optional error). Finally, it can be
+ * asked to pause, and resumed later. All GRXWriter objects support pausing and early termination.
+ *
+ * Thread-safety:
+ *
+ * State transitions take immediate effect if the object is used from a single thread. Subclasses
+ * might offer stronger guarantees.
+ *
+ * Unless otherwise indicated by a conforming subclass, no messages should be sent concurrently to a
+ * GRXWriter. I.e., conforming classes aren't required to be thread-safe.
+ */
 @interface GRXWriter : NSObject
 
-// This property can be used to query the current state of the writer, which determines how it might
-// currently use its writeable. Some state transitions can be triggered by setting this property to
-// the corresponding value, and that's useful for advanced use cases like pausing an writer. For
-// more details, see the documentation of the enum further down.
+/**
+ * This property can be used to query the current state of the writer, which determines how it might
+ * currently use its writeable. Some state transitions can be triggered by setting this property to
+ * the corresponding value, and that's useful for advanced use cases like pausing an writer. For
+ * more details, see the documentation of the enum further down.
+ */
 @property(nonatomic) GRXWriterState state;
 
-// Transition to the Started state, and start sending messages to the writeable (a reference to it
-// is retained). Messages to the writeable may be sent before the method returns, or they may be
-// sent later in the future. See GRXWriteable.h for the different messages a writeable can receive.
-//
-// If this writer draws its values from an external source (e.g. from the filesystem or from a
-// server), calling this method will commonly trigger side effects (like network connections).
-//
-// This method might only be called on writers in the NotStarted state.
+/**
+ * Transition to the Started state, and start sending messages to the writeable (a reference to it
+ * is retained). Messages to the writeable may be sent before the method returns, or they may be
+ * sent later in the future. See GRXWriteable.h for the different messages a writeable can receive.
+ *
+ * If this writer draws its values from an external source (e.g. from the filesystem or from a
+ * server), calling this method will commonly trigger side effects (like network connections).
+ *
+ * This method might only be called on writers in the NotStarted state.
+ */
 - (void)startWithWriteable:(id<GRXWriteable>)writeable;
 
-// Send writesFinishedWithError:errorOrNil to the writeable. Then release the reference to it and
-// transition to the Finished state.
-//
-// This method might only be called on writers in the Started or Paused state.
+/**
+ * Send writesFinishedWithError:errorOrNil to the writeable. Then release the reference to it and
+ * transition to the Finished state.
+ *
+ * This method might only be called on writers in the Started or Paused state.
+ */
 - (void)finishWithError:(NSError *)errorOrNil;
 @end
diff --git a/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h
index 400e834c6ae08bb9a6b5a870d59e6ab92c14df40..0622f7067c02b5989a5853813982e41b92aaea03 100644
--- a/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h
+++ b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h
@@ -35,17 +35,23 @@
 
 @interface NSEnumerator (GRXUtil)
 
-// Returns a NSEnumerator instance that iterates through the elements of the passed container that
-// supports fast enumeration. Note that this negates the speed benefits of fast enumeration over
-// NSEnumerator. It's only intended for the rare cases when one needs the latter and only has the
-// former, e.g. for iteration that needs to be paused and resumed later.
+/**
+ * Returns a NSEnumerator instance that iterates through the elements of the passed container that
+ * supports fast enumeration. Note that this negates the speed benefits of fast enumeration over
+ * NSEnumerator. It's only intended for the rare cases when one needs the latter and only has the
+ * former, e.g. for iteration that needs to be paused and resumed later.
+ */
 + (NSEnumerator *)grx_enumeratorWithContainer:(id<NSFastEnumeration>)container;
 
-// Returns a NSEnumerator instance that provides a single object before finishing. The value is then
-// released.
+/**
+ * Returns a NSEnumerator instance that provides a single object before finishing. The value is then
+ * released.
+ */
 + (NSEnumerator *)grx_enumeratorWithSingleValue:(id)value;
 
-// Returns a NSEnumerator instance that delegates the invocations of nextObject to the passed block.
-// When the block first returns nil, it is released.
+/**
+ * Returns a NSEnumerator instance that delegates the invocations of nextObject to the passed block.
+ * When the block first returns nil, it is released.
+ */
 + (NSEnumerator *)grx_enumeratorWithValueSupplier:(id (^)())block;
 @end
diff --git a/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h
index 34cfc4d8a77090a1a554219641778fef90fcf461..fb50c37863a54d65b017dd57c61bed2b3c526f11 100644
--- a/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h
+++ b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h
@@ -33,10 +33,14 @@
 
 #import <Foundation/Foundation.h>
 
-// Concrete subclass of NSEnumerator that delegates the invocations of nextObject to a block passed
-// on initialization.
+/**
+ * Concrete subclass of NSEnumerator that delegates the invocations of nextObject to a block passed
+ * on initialization.
+ */
 @interface GRXNSBlockEnumerator : NSEnumerator
-// The first time the passed block returns nil, the enumeration will end and the block will be
-// released.
+/**
+ * The first time the passed block returns nil, the enumeration will end and the block will be
+ * released.
+ */
 - (instancetype)initWithValueSupplier:(id (^)())block;
 @end
diff --git a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h
index 156502920556c8728d6f7fb70389536ecdb43117..62c27dbc7f3192ec0ce32a07fed428727b622530 100644
--- a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h
+++ b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h
@@ -33,11 +33,15 @@
 
 #import <Foundation/Foundation.h>
 
-// This is a bridge to interact through NSEnumerator's interface with objects that only conform to
-// NSFastEnumeration. (There's nothing specifically fast about it - you certainly don't win any
-// speed by using this instead of a NSEnumerator provided by your container).
+/**
+ * This is a bridge to interact through NSEnumerator's interface with objects that only conform to
+ * NSFastEnumeration. (There's nothing specifically fast about it - you certainly don't win any
+ * speed by using this instead of a NSEnumerator provided by your container).
+ */
 @interface GRXNSFastEnumerator : NSEnumerator
-// After the iteration of the container (via the NSFastEnumeration protocol) is over, the container
-// is released. If the container is modified during enumeration, an exception is thrown.
+/**
+ * After the iteration of the container (via the NSFastEnumeration protocol) is over, the container
+ * is released. If the container is modified during enumeration, an exception is thrown.
+ */
 - (instancetype)initWithContainer:(id<NSFastEnumeration>)container;
 @end
diff --git a/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h
index 12aa51e213903716fbe43e3779fd7efee6e8bb83..24a21a1b22cbb2ab9ba27047f38398cc73cca440 100644
--- a/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h
+++ b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h
@@ -33,9 +33,11 @@
 
 #import <Foundation/Foundation.h>
 
-// Concrete subclass of NSEnumerator whose instances return a single object before finishing.
+/** Concrete subclass of NSEnumerator whose instances return a single object before finishing. */
 @interface GRXNSScalarEnumerator : NSEnumerator
-// Param value: the single object this instance will produce. After the first invocation of
-// nextObject, the value is released.
+/**
+ * Param value: the single object this instance will produce. After the first invocation of
+ * nextObject, the value is released.
+ */
 - (instancetype)initWithValue:(id)value;
 @end
diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h
index 43b87068647d45ef8bc1c4cf5475e37204a337e1..01a15e2a430ff756379283f3140988629174925e 100644
--- a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h
+++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h
@@ -33,7 +33,7 @@
 
 #import "RxLibrary/GRXForwardingWriter.h"
 
-// A "proxy" writer that transforms all the values of its input writer by using a mapping function.
+/** A "proxy" writer that transforms all the values of its input writer by using a mapping function. */
 @interface GRXMappingWriter : GRXForwardingWriter
 - (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map
     NS_DESIGNATED_INITIALIZER;
diff --git a/src/objective-c/change-comments.py b/src/objective-c/change-comments.py
new file mode 100755
index 0000000000000000000000000000000000000000..9aa0e0c9f5a196ff66f0269a907528d2a2244d43
--- /dev/null
+++ b/src/objective-c/change-comments.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python2.7
+# 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.
+
+"""Change comments style of source files from // to /** */"""
+
+import re
+import sys
+
+
+if len(sys.argv) < 2:
+  print("Please provide at least one source file name as argument.")
+  sys.exit()
+
+for file_name in sys.argv[1:]:
+
+  print("Modifying format of {file} comments in place...".format(
+      file=file_name,
+  ))
+
+
+  # Input
+
+  with open(file_name, "r") as input_file:
+    lines = input_file.readlines()
+
+  def peek():
+    return lines[0]
+
+  def read_line():
+    return lines.pop(0)
+
+  def more_input_available():
+    return lines
+
+
+  # Output
+
+  output_lines = []
+
+  def write(line):
+    output_lines.append(line)
+
+  def flush_output():
+    with open(file_name, "w") as output_file:
+      for line in output_lines:
+        output_file.write(line)
+
+
+  # Pattern matching
+
+  comment_regex = r'^(\s*)//\s(.*)$'
+
+  def is_comment(line):
+    return re.search(comment_regex, line)
+
+  def isnt_comment(line):
+    return not is_comment(line)
+
+  def next_line(predicate):
+    return more_input_available() and predicate(peek())
+
+
+  # Transformation
+
+  def indentation_of(line):
+    match = re.search(comment_regex, line)
+    return match.group(1)
+
+  def content(line):
+    match = re.search(comment_regex, line)
+    return match.group(2)
+
+  def format_as_block(comment_block):
+    if len(comment_block) == 0:
+      return []
+
+    indent = indentation_of(comment_block[0])
+
+    if len(comment_block) == 1:
+      return [indent + "/** " + content(comment_block[0]) + " */\n"]
+
+    block = ["/**"] + [" * " + content(line) for line in comment_block] + [" */"]
+    return [indent + line.rstrip() + "\n" for line in block]
+
+
+  # Main algorithm
+
+  while more_input_available():
+    while next_line(isnt_comment):
+      write(read_line())
+
+    comment_block = []
+    # Get all lines in the same comment block. We could restrict the indentation
+    # to be the same as the first line of the block, but it's probably ok.
+    while (next_line(is_comment)):
+      comment_block.append(read_line())
+
+    for line in format_as_block(comment_block):
+      write(line)
+
+  flush_output()
diff --git a/src/objective-c/format-all-comments.sh b/src/objective-c/format-all-comments.sh
new file mode 100644
index 0000000000000000000000000000000000000000..e6b6b5a0b4968840a211242d4e7af442d738abad
--- /dev/null
+++ b/src/objective-c/format-all-comments.sh
@@ -0,0 +1,31 @@
+#!/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.
+
+find . -type f -name "*.h" ! -path "*/Pods/*" ! -path "./generated_libraries/*" ! -path "./examples/*" ! -path "./tests/*" | xargs ./change-comments.py
diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m
index 09a55e07045e8a39a3a3bea52361bf3b7a3e77ec..00c4b8830d344104648115f2180d4757235ef47e 100644
--- a/src/objective-c/tests/GRPCClientTests.m
+++ b/src/objective-c/tests/GRPCClientTests.m
@@ -42,9 +42,6 @@
 #import <RxLibrary/GRXWriteable.h>
 #import <RxLibrary/GRXWriter+Immediate.h>
 
-// These are a few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall)
-// rather than a generated proto library on top of it.
-
 static NSString * const kHostAddress = @"localhost:5050";
 static NSString * const kPackage = @"grpc.testing";
 static NSString * const kService = @"TestService";
@@ -53,11 +50,10 @@ static ProtoMethod *kInexistentMethod;
 static ProtoMethod *kEmptyCallMethod;
 static ProtoMethod *kUnaryCallMethod;
 
-// This is an observer class for testing that responseMetadata is KVO-compliant
-
+/** Observer class for testing that responseMetadata is KVO-compliant */
 @interface PassthroughObserver : NSObject
-
-- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback;
+- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback
+    NS_DESIGNATED_INITIALIZER;
 
 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
                        context:(void *)context;
@@ -67,23 +63,38 @@ static ProtoMethod *kUnaryCallMethod;
   void (^_callback)(NSString*, id, NSDictionary*);
 }
 
+- (instancetype)init {
+  return [self initWithCallback:nil];
+}
+
 - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
-  self = [super init];
-  if (self) {
+  if (!callback) {
+    return nil;
+  }
+  if ((self = [super init])) {
     _callback = callback;
   }
   return self;
-  
 }
 
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
-{
+- (void)observeValueForKeyPath:(NSString *)keyPath
+                      ofObject:(id)object
+                        change:(NSDictionary *)change
+                       context:(void *)context {
   _callback(keyPath, object, change);
   [object removeObserver:self forKeyPath:keyPath];
 }
 
 @end
 
+# pragma mark Tests
+
+/**
+ * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
+ * a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
+ *
+ * TODO(jcanizales): Run them also against a local SSL server and against a remote server.
+ */
 @interface GRPCClientTests : XCTestCase
 @end
 
@@ -180,6 +191,7 @@ static ProtoMethod *kUnaryCallMethod;
   [self waitForExpectationsWithTimeout:8 handler:nil];
 }
 
+// TODO(jcanizales): Activate this test against the remote server.
 - (void)testMetadata {
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
 
diff --git a/src/objective-c/tests/InteropTests.h b/src/objective-c/tests/InteropTests.h
index 1045c3d1248da37320d2bdab29cc044b8f92057e..6d54343b1355c6b19faac5fad18287c59aa921f1 100644
--- a/src/objective-c/tests/InteropTests.h
+++ b/src/objective-c/tests/InteropTests.h
@@ -33,11 +33,17 @@
 
 #import <XCTest/XCTest.h>
 
-// Implements tests as described here:
-// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
-
+/**
+ * Implements tests as described here:
+ * https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
+ *
+ * This is an abstract class that needs to be subclassed. See |+host|.
+ */
 @interface InteropTests : XCTestCase
-// Returns @"grpc-test.sandbox.google.com".
-// Override in a subclass to perform the same tests against a different address.
+/**
+ * Host to send the RPCs to. The base implementation returns nil, which would make all tests to
+ * fail.
+ * Override in a subclass to perform these tests against a specific address.
+ */
 + (NSString *)host;
 @end
diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m
index af58e2bd0489bdba3dd5979d748c0a30adf2ffce..26877b1ae845238a78e5d2a11995f188272bc2a9 100644
--- a/src/objective-c/tests/InteropTests.m
+++ b/src/objective-c/tests/InteropTests.m
@@ -78,21 +78,20 @@
 
 #pragma mark Tests
 
-static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
-
 @implementation InteropTests {
   RMTTestService *_service;
 }
 
 + (NSString *)host {
-  return kRemoteSSLHost;
+  return nil;
 }
 
 - (void)setUp {
-  _service = [RMTTestService serviceWithHost:self.class.host];
+  _service = self.class.host ? [RMTTestService serviceWithHost:self.class.host] : nil;
 }
 
 - (void)testEmptyUnaryRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyUnary"];
 
   RMTEmpty *request = [RMTEmpty message];
@@ -110,6 +109,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
 }
 
 - (void)testLargeUnaryRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"];
 
   RMTSimpleRequest *request = [RMTSimpleRequest message];
@@ -132,6 +132,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
 }
 
 - (void)testClientStreamingRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ClientStreaming"];
 
   RMTStreamingInputCallRequest *request1 = [RMTStreamingInputCallRequest message];
@@ -164,6 +165,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
 }
 
 - (void)testServerStreamingRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ServerStreaming"];
 
   NSArray *expectedSizes = @[@31415, @9, @2653, @58979];
@@ -200,6 +202,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
 }
 
 - (void)testPingPongRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"];
 
   NSArray *requests = @[@27182, @8, @1828, @45904];
@@ -243,6 +246,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
 }
 
 - (void)testEmptyStreamRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"];
   [_service fullDuplexCallWithRequestsWriter:[GRXWriter emptyWriter]
                                 eventHandler:^(BOOL done,
@@ -256,6 +260,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
 }
 
 - (void)testCancelAfterBeginRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterBegin"];
 
   // A buffered pipe to which we never write any value acts as a writer that just hangs.
@@ -273,6 +278,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
 }
 
 - (void)testCancelAfterFirstResponseRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterFirstResponse"];
 
   // A buffered pipe to which we write a single value but never close
diff --git a/src/objective-c/tests/InteropTestsLocalCleartext.m b/src/objective-c/tests/InteropTestsLocalCleartext.m
index 2d7d3c4b2c08f8abb690c96382713e1070fd2a2d..56927a8af6d1906c8865590486d58a38844a56e9 100644
--- a/src/objective-c/tests/InteropTestsLocalCleartext.m
+++ b/src/objective-c/tests/InteropTestsLocalCleartext.m
@@ -31,15 +31,13 @@
  *
  */
 
-// Repeat of the tests in InteropTests.m, but sending the RPCs to a local cleartext server instead
-// of the remote SSL one.
-
 #import <GRPCClient/GRPCCall+Tests.h>
 
 #import "InteropTests.h"
 
 static NSString * const kLocalCleartextHost = @"localhost:5050";
 
+/** Tests in InteropTests.m, sending the RPCs to a local cleartext server. */
 @interface InteropTestsLocalCleartext : InteropTests
 @end
 
diff --git a/src/objective-c/tests/InteropTestsLocalSSL.m b/src/objective-c/tests/InteropTestsLocalSSL.m
index f69f806dcf5a6371e63f30abd0960e45ea6d956b..9d7afefbfe3cd37e4fa4f2940fbcab940375d1a0 100644
--- a/src/objective-c/tests/InteropTestsLocalSSL.m
+++ b/src/objective-c/tests/InteropTestsLocalSSL.m
@@ -31,15 +31,13 @@
  *
  */
 
-// Repeat of the tests in InteropTests.m, but sending the RPCs to a local SSL server instead of the
-// remote one.
-
 #import <GRPCClient/GRPCCall+Tests.h>
 
 #import "InteropTests.h"
 
 static NSString * const kLocalSSLHost = @"localhost:5051";
 
+/** Tests in InteropTests.m, sending the RPCs to a local SSL server. */
 @interface InteropTestsLocalSSL : InteropTests
 @end
 
diff --git a/src/objective-c/tests/InteropTestsRemote.m b/src/objective-c/tests/InteropTestsRemote.m
new file mode 100644
index 0000000000000000000000000000000000000000..a67be98431e2ea551eaf440c8176df8da14e6973
--- /dev/null
+++ b/src/objective-c/tests/InteropTestsRemote.m
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#import <GRPCClient/GRPCCall+Tests.h>
+
+#import "InteropTests.h"
+
+static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
+
+/** Tests in InteropTests.m, sending the RPCs to a remote SSL server. */
+@interface InteropTestsRemote : InteropTests
+@end
+
+@implementation InteropTestsRemote
+
++ (NSString *)host {
+  return kRemoteSSLHost;
+}
+
+@end
diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m
deleted file mode 100644
index 976fff55bcc496f65ba4cd15296be26d38602cf1..0000000000000000000000000000000000000000
--- a/src/objective-c/tests/LocalClearTextTests.m
+++ /dev/null
@@ -1,164 +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.
- *
- */
-
-#import <UIKit/UIKit.h>
-#import <XCTest/XCTest.h>
-
-#import <GRPCClient/GRPCCall.h>
-#import <ProtoRPC/ProtoMethod.h>
-#import <RouteGuide/RouteGuide.pbobjc.h>
-#import <RouteGuide/RouteGuide.pbrpc.h>
-#import <RxLibrary/GRXWriteable.h>
-#import <RxLibrary/GRXWriter+Immediate.h>
-
-// These tests require a gRPC "RouteGuide" sample server to be running locally. You can compile and
-// run one by following the instructions here: https://github.com/grpc/grpc/blob/master/examples/cpp/cpptutorial.md#try-it-out
-// Be sure to have the C gRPC library installed in your system (for example, by having followed the
-// instructions at https://github.com/grpc/homebrew-grpc
-
-static NSString * const kRouteGuideHost = @"http://localhost:50051";
-static NSString * const kPackage = @"routeguide";
-static NSString * const kService = @"RouteGuide";
-
-@interface LocalClearTextTests : XCTestCase
-@end
-
-@implementation LocalClearTextTests
-
-// This test currently fails: see Issue #1907.
-//- (void)testConnectionToLocalServer {
-//  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
-//
-//  // This method isn't implemented by the local server.
-//  GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage
-//                                                         interface:kService
-//                                                            method:@"EmptyCall"];
-//
-//  GRXWriter *requestsWriter = [GRXWriter writerWithValue:[NSData data]];
-//
-//  GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-//                                           method:method
-//                                   requestsWriter:requestsWriter];
-//
-//  id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
-//    XCTFail(@"Received unexpected response: %@", value);
-//  } completionHandler:^(NSError *errorOrNil) {
-//    XCTAssertNotNil(errorOrNil, @"Finished without error!");
-//    XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil);
-//    [expectation fulfill];
-//  }];
-//
-//  [call startWithWriteable:responsesWriteable];
-//
-//  [self waitForExpectationsWithTimeout:8.0 handler:nil];
-//}
-
-- (void)testEmptyRPC {
-  __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
-  __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
-
-  ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
-                                                     service:kService
-                                                      method:@"RecordRoute"];
-
-  GRXWriter *requestsWriter = [GRXWriter emptyWriter];
-
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-                                             path:method.HTTPPath
-                                   requestsWriter:requestsWriter];
-
-  id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
-    XCTAssertNotNil(value, @"nil value received as response.");
-    XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
-    [response fulfill];
-  } completionHandler:^(NSError *errorOrNil) {
-    XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
-    [completion fulfill];
-  }];
-
-  [call startWithWriteable:responsesWriteable];
-
-  [self waitForExpectationsWithTimeout:2.0 handler:nil];
-}
-
-- (void)testSimpleProtoRPC {
-  __weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."];
-  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
-
-  ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
-                                                     service:kService
-                                                      method:@"GetFeature"];
-
-  RGDPoint *point = [RGDPoint message];
-  point.latitude = 28E7;
-  point.longitude = -15E7;
-  GRXWriter *requestsWriter = [GRXWriter writerWithValue:[point data]];
-
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-                                             path:method.HTTPPath
-                                   requestsWriter:requestsWriter];
-
-  id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
-    XCTAssertNotNil(value, @"nil value received as response.");
-    RGDFeature *feature = [RGDFeature parseFromData:value error:NULL];
-    XCTAssertEqualObjects(point, feature.location);
-    XCTAssertNotNil(feature.name, @"Response's name is nil.");
-    [response fulfill];
-  } completionHandler:^(NSError *errorOrNil) {
-    XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
-    [completion fulfill];
-  }];
-
-  [call startWithWriteable:responsesWriteable];
-
-  [self waitForExpectationsWithTimeout:2.0 handler:nil];
-}
-
-- (void)testSimpleProtoRPCUsingGeneratedService {
-  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
-
-  RGDPoint *point = [RGDPoint message];
-  point.latitude = 28E7;
-  point.longitude = -15E7;
-
-  RGDRouteGuide *service = [[RGDRouteGuide alloc] initWithHost:kRouteGuideHost];
-  [service getFeatureWithRequest:point handler:^(RGDFeature *response, NSError *error) {
-    XCTAssertNil(error, @"Finished with unexpected error: %@", error);
-    XCTAssertEqualObjects(point, response.location);
-    XCTAssertNotNil(response.name, @"Response's name is nil.");
-    [completion fulfill];
-  }];
-
-  [self waitForExpectationsWithTimeout:2.0 handler:nil];
-}
-@end
diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile
index 2aa837f764e963dd9c13e3895f27f016101a1532..2a9b894cf631c8a46b19543e69ae8809ab7b522b 100644
--- a/src/objective-c/tests/Podfile
+++ b/src/objective-c/tests/Podfile
@@ -6,10 +6,26 @@ pod 'gRPC', :path => "../../.."
 pod 'RemoteTest', :path => "../generated_libraries/RemoteTestClient"
 pod 'RouteGuide', :path => "../generated_libraries/RouteGuideClient"
 
-link_with 'AllTests'
+link_with 'AllTests',
+          'RxLibraryUnitTests',
+          'InteropTests',
+          'InteropTestsLocalSSL',
+          'InteropTestsLocalCleartext'
 
 target 'Tests' do
 end
 
 target 'AllTests' do
 end
+
+target 'RxLibraryUnitTests' do
+end
+
+target 'InteropTestsRemote' do
+end
+
+target 'InteropTestsLocalSSL' do
+end
+
+target 'InteropTestsLocalCleartext' do
+end
diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
index 3a1c3d940a928c2b41d06e57e78628cbc11198a7..b0429617c0157e0de80ebf0965d78e789edad079 100644
--- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
+++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
@@ -7,16 +7,33 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		036D953EE34B1FD523647ACD /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
+		08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
+		50267643BA114A2A724D4FDF /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
 		6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; };
-		63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */; };
 		63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; };
-		63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; };
 		635697CD1B14FC11007A7283 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635697CC1B14FC11007A7283 /* Tests.m */; };
 		635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; };
 		63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; };
+		6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; settings = {ASSET_TAGS = (); }; };
+		63DC84181BE15179000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; };
+		63DC84281BE15267000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; };
+		63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
+		63DC84391BE15294000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC84481BE152B5000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; settings = {ASSET_TAGS = (); }; };
+		63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; settings = {ASSET_TAGS = (); }; };
+		63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; settings = {ASSET_TAGS = (); }; };
 		63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; };
 		63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; };
 		7D8A186224D39101F90230F6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
+		DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -27,6 +44,34 @@
 			remoteGlobalIDString = 635697C61B14FC11007A7283;
 			remoteInfo = Tests;
 		};
+		63DC84191BE15179000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
+		63DC84291BE15267000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
+		63DC843A1BE15294000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
+		63DC84491BE152B5000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -45,7 +90,6 @@
 		0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
 		35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = "<group>"; };
-		63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalClearTextTests.m; sourceTree = "<group>"; };
 		63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = "<group>"; };
 		635697C71B14FC11007A7283 /* libTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTests.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -53,6 +97,11 @@
 		635697D81B14FC11007A7283 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTests.m; sourceTree = "<group>"; };
 		63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalCleartext.m; sourceTree = "<group>"; };
+		6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsRemote.m; sourceTree = "<group>"; };
+		63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxLibraryUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsRemote.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalSSL.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalCleartext.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		63E240CC1B6C4D3A005F3B0E /* InteropTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteropTests.h; sourceTree = "<group>"; };
 		63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalSSL.m; sourceTree = "<group>"; };
 		63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TestCertificates.bundle; sourceTree = "<group>"; };
@@ -76,6 +125,42 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		63DC84101BE15179000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84181BE15179000708E8 /* libTests.a in Frameworks */,
+				036D953EE34B1FD523647ACD /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84201BE15267000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84281BE15267000708E8 /* libTests.a in Frameworks */,
+				DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84311BE15294000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84391BE15294000708E8 /* libTests.a in Frameworks */,
+				08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84401BE152B5000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84481BE152B5000708E8 /* libTests.a in Frameworks */,
+				50267643BA114A2A724D4FDF /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -112,6 +197,10 @@
 			children = (
 				635697C71B14FC11007A7283 /* libTests.a */,
 				63423F441B150A5F006CF63C /* AllTests.xctest */,
+				63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */,
+				63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */,
+				63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */,
+				63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -122,10 +211,10 @@
 				6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */,
 				63E240CC1B6C4D3A005F3B0E /* InteropTests.h */,
 				635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */,
+				6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */,
 				63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */,
 				63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */,
 				63423F501B151B77006CF63C /* RxLibraryUnitTests.m */,
-				63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */,
 				635697CC1B14FC11007A7283 /* Tests.m */,
 				635697D71B14FC11007A7283 /* Supporting Files */,
 			);
@@ -152,6 +241,7 @@
 				63423F411B150A5F006CF63C /* Frameworks */,
 				63423F421B150A5F006CF63C /* Resources */,
 				A441F71824DCB9D0CA297748 /* Copy Pods Resources */,
+				5F14F59509E10C2852014F9E /* Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -180,6 +270,90 @@
 			productReference = 635697C71B14FC11007A7283 /* libTests.a */;
 			productType = "com.apple.product-type.library.static";
 		};
+		63DC84121BE15179000708E8 /* RxLibraryUnitTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */;
+			buildPhases = (
+				B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */,
+				63DC840F1BE15179000708E8 /* Sources */,
+				63DC84101BE15179000708E8 /* Frameworks */,
+				63DC84111BE15179000708E8 /* Resources */,
+				4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */,
+				C977426A8727267BBAC7D48E /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC841A1BE15179000708E8 /* PBXTargetDependency */,
+			);
+			name = RxLibraryUnitTests;
+			productName = RxLibraryUnitTests;
+			productReference = 63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		63DC84221BE15267000708E8 /* InteropTestsRemote */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */;
+			buildPhases = (
+				4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */,
+				63DC841F1BE15267000708E8 /* Sources */,
+				63DC84201BE15267000708E8 /* Frameworks */,
+				63DC84211BE15267000708E8 /* Resources */,
+				900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */,
+				C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC842A1BE15267000708E8 /* PBXTargetDependency */,
+			);
+			name = InteropTestsRemote;
+			productName = InteropTests;
+			productReference = 63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		63DC84331BE15294000708E8 /* InteropTestsLocalSSL */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */;
+			buildPhases = (
+				5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */,
+				63DC84301BE15294000708E8 /* Sources */,
+				63DC84311BE15294000708E8 /* Frameworks */,
+				63DC84321BE15294000708E8 /* Resources */,
+				C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */,
+				693DD0B453431D64EA24FD66 /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC843B1BE15294000708E8 /* PBXTargetDependency */,
+			);
+			name = InteropTestsLocalSSL;
+			productName = InteropTestsLocalSSL;
+			productReference = 63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */;
+			buildPhases = (
+				7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */,
+				63DC843F1BE152B5000708E8 /* Sources */,
+				63DC84401BE152B5000708E8 /* Frameworks */,
+				63DC84411BE152B5000708E8 /* Resources */,
+				A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */,
+				8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC844A1BE152B5000708E8 /* PBXTargetDependency */,
+			);
+			name = InteropTestsLocalCleartext;
+			productName = InteropTestsLocalCleartext;
+			productReference = 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -195,6 +369,18 @@
 					635697C61B14FC11007A7283 = {
 						CreatedOnToolsVersion = 6.3.1;
 					};
+					63DC84121BE15179000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
+					63DC84221BE15267000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
+					63DC84331BE15294000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
+					63DC84421BE152B5000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
 				};
 			};
 			buildConfigurationList = 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */;
@@ -211,6 +397,10 @@
 			targets = (
 				635697C61B14FC11007A7283 /* Tests */,
 				63423F431B150A5F006CF63C /* AllTests */,
+				63DC84121BE15179000708E8 /* RxLibraryUnitTests */,
+				63DC84221BE15267000708E8 /* InteropTestsRemote */,
+				63DC84331BE15294000708E8 /* InteropTestsLocalSSL */,
+				63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */,
 			);
 		};
 /* End PBXProject section */
@@ -224,9 +414,158 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		63DC84111BE15179000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84211BE15267000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84321BE15294000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84411BE152B5000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
+		4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		5F14F59509E10C2852014F9E /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		693DD0B453431D64EA24FD66 /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		914ADDD7106BA9BB8A7E569F /* Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -257,6 +596,81 @@
 			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
+		A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C977426A8727267BBAC7D48E /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -265,11 +679,11 @@
 			buildActionMask = 2147483647;
 			files = (
 				63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */,
-				63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */,
-				63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */,
+				6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */,
 				63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */,
 				6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */,
 				635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */,
+				63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -281,6 +695,42 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		63DC840F1BE15179000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC841F1BE15267000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */,
+				6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84301BE15294000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */,
+				6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC843F1BE152B5000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */,
+				63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */,
+				6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -289,6 +739,26 @@
 			target = 635697C61B14FC11007A7283 /* Tests */;
 			targetProxy = 63423F4B1B150A5F006CF63C /* PBXContainerItemProxy */;
 		};
+		63DC841A1BE15179000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC84191BE15179000708E8 /* PBXContainerItemProxy */;
+		};
+		63DC842A1BE15267000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC84291BE15267000708E8 /* PBXContainerItemProxy */;
+		};
+		63DC843B1BE15294000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC843A1BE15294000708E8 /* PBXContainerItemProxy */;
+		};
+		63DC844A1BE152B5000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC84491BE152B5000708E8 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -418,6 +888,110 @@
 			};
 			name = Release;
 		};
+		63DC841C1BE15179000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC841D1BE15179000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		63DC842C1BE15267000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC842D1BE15267000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		63DC843D1BE15294000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC843E1BE15294000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		63DC844C1BE152B5000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC844D1BE152B5000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -448,6 +1022,42 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC841C1BE15179000708E8 /* Debug */,
+				63DC841D1BE15179000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC842C1BE15267000708E8 /* Debug */,
+				63DC842D1BE15267000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC843D1BE15294000708E8 /* Debug */,
+				63DC843E1BE15294000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC844C1BE152B5000708E8 /* Debug */,
+				63DC844D1BE152B5000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 635697BF1B14FC11007A7283 /* Project object */;
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
index a7e0ed110ecf1239ff58966c7e07b553b9c1f5cb..e6a052a8ce19f3985b2b21dc583feb53951ade0b 100644
--- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
@@ -23,10 +23,10 @@
       </BuildActionEntries>
    </BuildAction>
    <TestAction
+      buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      buildConfiguration = "Debug">
+      shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
             skipped = "NO">
@@ -44,6 +44,9 @@
                <Test
                   Identifier = "GRPCClientTests/testMetadata">
                </Test>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
                <Test
                   Identifier = "LocalClearTextTests">
                </Test>
@@ -62,15 +65,18 @@
             ReferencedContainer = "container:Tests.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
    </TestAction>
    <LaunchAction
+      buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Debug"
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
       <MacroExpansion>
          <BuildableReference
@@ -85,10 +91,10 @@
       </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
+      buildConfiguration = "Release"
       shouldUseLaunchSchemeArgsEnv = "YES"
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Release"
       debugDocumentVersioning = "YES">
       <MacroExpansion>
          <BuildableReference
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..ce358bf69f5945e6da83b11eb4c47af4b7a75004
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84421BE152B5000708E8"
+               BuildableName = "InteropTestsLocalCleartext.xctest"
+               BlueprintName = "InteropTestsLocalCleartext"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84421BE152B5000708E8"
+               BuildableName = "InteropTestsLocalCleartext.xctest"
+               BlueprintName = "InteropTestsLocalCleartext"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "GRPCClientTests/testConnectionToRemoteServer">
+               </Test>
+               <Test
+                  Identifier = "GRPCClientTests/testMetadata">
+               </Test>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84421BE152B5000708E8"
+            BuildableName = "InteropTestsLocalCleartext.xctest"
+            BlueprintName = "InteropTestsLocalCleartext"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84421BE152B5000708E8"
+            BuildableName = "InteropTestsLocalCleartext.xctest"
+            BlueprintName = "InteropTestsLocalCleartext"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..f268da1fb0c1abdcdb3316385f1b284d16d00906
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84331BE15294000708E8"
+               BuildableName = "InteropTestsLocalSSL.xctest"
+               BlueprintName = "InteropTestsLocalSSL"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84331BE15294000708E8"
+               BuildableName = "InteropTestsLocalSSL.xctest"
+               BlueprintName = "InteropTestsLocalSSL"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84331BE15294000708E8"
+            BuildableName = "InteropTestsLocalSSL.xctest"
+            BlueprintName = "InteropTestsLocalSSL"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84331BE15294000708E8"
+            BuildableName = "InteropTestsLocalSSL.xctest"
+            BlueprintName = "InteropTestsLocalSSL"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..186d7208e04acc9c1bc9705d37d4e53ead431915
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84221BE15267000708E8"
+               BuildableName = "InteropTestsRemote.xctest"
+               BlueprintName = "InteropTestsRemote"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84221BE15267000708E8"
+               BuildableName = "InteropTestsRemote.xctest"
+               BlueprintName = "InteropTestsRemote"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84221BE15267000708E8"
+            BuildableName = "InteropTestsRemote.xctest"
+            BlueprintName = "InteropTestsRemote"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84221BE15267000708E8"
+            BuildableName = "InteropTestsRemote.xctest"
+            BlueprintName = "InteropTestsRemote"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..3abc1d42e4d8f578d2f59f2e53ecc70685a1393f
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84121BE15179000708E8"
+               BuildableName = "RxLibraryUnitTests.xctest"
+               BlueprintName = "RxLibraryUnitTests"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84121BE15179000708E8"
+               BuildableName = "RxLibraryUnitTests.xctest"
+               BlueprintName = "RxLibraryUnitTests"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84121BE15179000708E8"
+            BuildableName = "RxLibraryUnitTests.xctest"
+            BlueprintName = "RxLibraryUnitTests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84121BE15179000708E8"
+            BuildableName = "RxLibraryUnitTests.xctest"
+            BlueprintName = "RxLibraryUnitTests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/php/bin/run_php_cs_fixer.sh b/src/php/bin/run_php_cs_fixer.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3e11a12bc1fec26144d61fcc06f99e13e0de71f7
--- /dev/null
+++ b/src/php/bin/run_php_cs_fixer.sh
@@ -0,0 +1,40 @@
+#!/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 -e
+command -v php-cs-fixer > /dev/null || {
+  echo "Cannot find php-cs-fixer. Exiting..."
+  exit 1
+}
+cd $(dirname $0)/..
+php-cs-fixer fix lib/Grpc || true
+php-cs-fixer fix tests/generated_code || true
+php-cs-fixer fix tests/interop || true
+php-cs-fixer fix tests/unit_tests || true
diff --git a/src/php/lib/Grpc/AbstractCall.php b/src/php/lib/Grpc/AbstractCall.php
index a3c7a9e017a1f21737556fd6c2f647cf9bf33b30..53849d51fca2c52758f23ff8beaef1701e47b1c0 100644
--- a/src/php/lib/Grpc/AbstractCall.php
+++ b/src/php/lib/Grpc/AbstractCall.php
@@ -31,65 +31,79 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
+
 namespace Grpc;
 
-abstract class AbstractCall {
+abstract class AbstractCall
+{
+    protected $call;
+    protected $deserialize;
+    protected $metadata;
 
-  protected $call;
-  protected $deserialize;
-  protected $metadata;
+    /**
+     * Create a new Call wrapper object.
+     *
+     * @param Channel         $channel     The channel to communicate on
+     * @param string          $method      The method to call on the
+     *                                     remote server
+     * @param callback        $deserialize A callback function to deserialize
+     *                                     the response
+     * @param (optional) long $timeout     Timeout in microseconds
+     */
+    public function __construct(Channel $channel,
+                                $method,
+                                $deserialize,
+                                $timeout = false)
+    {
+        if ($timeout) {
+            $now = Timeval::now();
+            $delta = new Timeval($timeout);
+            $deadline = $now->add($delta);
+        } else {
+            $deadline = Timeval::infFuture();
+        }
+        $this->call = new Call($channel, $method, $deadline);
+        $this->deserialize = $deserialize;
+        $this->metadata = null;
+    }
 
-  /**
-   * Create a new Call wrapper object.
-   * @param Channel $channel The channel to communicate on
-   * @param string $method The method to call on the remote server
-   * @param callback $deserialize A callback function to deserialize
-   * the response
-   * @param (optional) long $timeout Timeout in microseconds
-   */
-  public function __construct(Channel $channel, $method, $deserialize, $timeout = false) {
-    if ($timeout) {
-      $now = Timeval::now();
-      $delta = new Timeval($timeout);
-      $deadline = $now->add($delta);
-    } else {
-      $deadline = Timeval::infFuture();
+    /**
+     * @return The metadata sent by the server.
+     */
+    public function getMetadata()
+    {
+        return $this->metadata;
     }
-    $this->call = new Call($channel, $method, $deadline);
-    $this->deserialize = $deserialize;
-    $this->metadata = null;
-  }
 
-  /**
-   * @return The metadata sent by the server.
-   */
-  public function getMetadata() {
-    return $this->metadata;
-  }
+    /**
+     * @return string The URI of the endpoint.
+     */
+    public function getPeer()
+    {
+        return $this->call->getPeer();
+    }
 
-  /**
-   * @return string The URI of the endpoint.
-   */
-  public function getPeer() {
-    return $this->call->getPeer();
-  }
+    /**
+     * Cancels the call.
+     */
+    public function cancel()
+    {
+        $this->call->cancel();
+    }
 
-  /**
-   * Cancels the call
-   */
-  public function cancel() {
-    $this->call->cancel();
-  }
+    /**
+     * Deserialize a response value to an object.
+     *
+     * @param string $value The binary value to deserialize
+     *
+     * @return The deserialized value
+     */
+    protected function deserializeResponse($value)
+    {
+        if ($value === null) {
+            return;
+        }
 
-  /**
-   * Deserialize a response value to an object.
-   * @param string $value The binary value to deserialize
-   * @return The deserialized value
-   */
-  protected function deserializeResponse($value) {
-    if ($value === null) {
-      return null;
+        return call_user_func($this->deserialize, $value);
     }
-    return call_user_func($this->deserialize, $value);
-  }
 }
diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php
index 5614314598086f4e381ddd7076da6f1ac6fb5180..c26be607ffc98bb618fbdfac6e2b9e7cfd2a39c4 100755
--- a/src/php/lib/Grpc/BaseStub.php
+++ b/src/php/lib/Grpc/BaseStub.php
@@ -31,255 +31,308 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
+
 namespace Grpc;
 
 /**
  * Base class for generated client stubs. Stub methods are expected to call
  * _simpleRequest or _streamRequest and return the result.
  */
-class BaseStub {
+class BaseStub
+{
+    private $hostname;
+    private $channel;
 
-  private $hostname;
-  private $channel;
+    // a callback function
+    private $update_metadata;
 
-  // a callback function
-  private $update_metadata;
+    /**
+     * @param $hostname string
+     * @param $opts array
+     *  - 'update_metadata': (optional) a callback function which takes in a
+     * metadata array, and returns an updated metadata array
+     */
+    public function __construct($hostname, $opts)
+    {
+        $this->hostname = $hostname;
+        $this->update_metadata = null;
+        if (isset($opts['update_metadata'])) {
+            if (is_callable($opts['update_metadata'])) {
+                $this->update_metadata = $opts['update_metadata'];
+            }
+            unset($opts['update_metadata']);
+        }
+        $package_config = json_decode(
+            file_get_contents(dirname(__FILE__).'/../../composer.json'), true);
+        $opts['grpc.primary_user_agent'] =
+            'grpc-php/'.$package_config['version'];
+        $this->channel = new Channel($hostname, $opts);
+    }
 
-  /**
-   * @param $hostname string
-   * @param $opts array
-   *  - 'update_metadata': (optional) a callback function which takes in a
-   * metadata array, and returns an updated metadata array
-   */
-  public function __construct($hostname, $opts) {
-    $this->hostname = $hostname;
-    $this->update_metadata = null;
-    if (isset($opts['update_metadata'])) {
-      if (is_callable($opts['update_metadata'])) {
-        $this->update_metadata = $opts['update_metadata'];
-      }
-      unset($opts['update_metadata']);
+    /**
+     * @return string The URI of the endpoint.
+     */
+    public function getTarget()
+    {
+        return $this->channel->getTarget();
     }
-    $package_config = json_decode(
-        file_get_contents(dirname(__FILE__) . '/../../composer.json'), true);
-    $opts['grpc.primary_user_agent'] =
-        'grpc-php/' . $package_config['version'];
-    $this->channel = new Channel($hostname, $opts);
-  }
 
-  /**
-   * @return string The URI of the endpoint.
-   */
-  public function getTarget() {
-    return $this->channel->getTarget();
-  }
+    /**
+     * @param $try_to_connect bool
+     *
+     * @return int The grpc connectivity state
+     */
+    public function getConnectivityState($try_to_connect = false)
+    {
+        return $this->channel->getConnectivityState($try_to_connect);
+    }
 
-  /**
-   * @param $try_to_connect bool
-   * @return int The grpc connectivity state
-   */
-  public function getConnectivityState($try_to_connect = false) {
-    return $this->channel->getConnectivityState($try_to_connect);
-  }
+    /**
+     * @param $timeout in microseconds
+     *
+     * @return bool true if channel is ready
+     * @throw Exception if channel is in FATAL_ERROR state
+     */
+    public function waitForReady($timeout)
+    {
+        $new_state = $this->getConnectivityState(true);
+        if ($this->_checkConnectivityState($new_state)) {
+            return true;
+        }
 
-  /**
-   * @param $timeout in microseconds
-   * @return bool true if channel is ready
-   * @throw Exception if channel is in FATAL_ERROR state
-   */
-  public function waitForReady($timeout) {
-    $new_state = $this->getConnectivityState(true);
-    if ($this->_checkConnectivityState($new_state)) {
-      return true;
-    }
+        $now = Timeval::now();
+        $delta = new Timeval($timeout);
+        $deadline = $now->add($delta);
 
-    $now = Timeval::now();
-    $delta = new Timeval($timeout);
-    $deadline = $now->add($delta);
+        while ($this->channel->watchConnectivityState($new_state, $deadline)) {
+            // state has changed before deadline
+            $new_state = $this->getConnectivityState();
+            if ($this->_checkConnectivityState($new_state)) {
+                return true;
+            }
+        }
+        // deadline has passed
+        $new_state = $this->getConnectivityState();
 
-    while ($this->channel->watchConnectivityState($new_state, $deadline)) {
-      // state has changed before deadline
-      $new_state = $this->getConnectivityState();
-      if ($this->_checkConnectivityState($new_state)) {
-        return true;
-      }
+        return $this->_checkConnectivityState($new_state);
     }
-    // deadline has passed
-    $new_state = $this->getConnectivityState();
-    return $this->_checkConnectivityState($new_state);
-  }
 
-  private function _checkConnectivityState($new_state) {
-    if ($new_state == \Grpc\CHANNEL_READY) {
-      return true;
+    private function _checkConnectivityState($new_state)
+    {
+        if ($new_state == \Grpc\CHANNEL_READY) {
+            return true;
+        }
+        if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) {
+            throw new \Exception('Failed to connect to server');
+        }
+
+        return false;
     }
-    if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) {
-      // @codeCoverageIgnoreStart
-      throw new \Exception('Failed to connect to server');
-      // @codeCoverageIgnoreEnd
+
+    /**
+     * Close the communication channel associated with this stub.
+     */
+    public function close()
+    {
+        $this->channel->close();
     }
-    return false;
-  }
 
-  /**
-   * Close the communication channel associated with this stub
-   */
-  public function close() {
-    $this->channel->close();
-  }
+    /**
+     * constructs the auth uri for the jwt.
+     */
+    private function _get_jwt_aud_uri($method)
+    {
+        $last_slash_idx = strrpos($method, '/');
+        if ($last_slash_idx === false) {
+            throw new \InvalidArgumentException(
+                'service name must have a slash');
+        }
+        $service_name = substr($method, 0, $last_slash_idx);
 
-  /**
-   * constructs the auth uri for the jwt
-   */
-  private function _get_jwt_aud_uri($method) {
-    $last_slash_idx = strrpos($method, '/');
-    if ($last_slash_idx === false) {
-      throw new \InvalidArgumentException('service name must have a slash');
+        return 'https://'.$this->hostname.$service_name;
     }
-    $service_name = substr($method, 0, $last_slash_idx);
-    return "https://" . $this->hostname . $service_name;
-  }
 
-  /**
-   * extract $timeout from $metadata
-   * @param $metadata The metadata map
-   * @return list($metadata_copy, $timeout)
-   */
-  private function _extract_timeout_from_metadata($metadata) {
-    $timeout = false;
-    $metadata_copy = $metadata;
-    if (isset($metadata['timeout'])) {
-      $timeout = $metadata['timeout'];
-      unset($metadata_copy['timeout']);
+    /**
+     * extract $timeout from $metadata.
+     *
+     * @param $metadata The metadata map
+     *
+     * @return list($metadata_copy, $timeout)
+     */
+    private function _extract_timeout_from_metadata($metadata)
+    {
+        $timeout = false;
+        $metadata_copy = $metadata;
+        if (isset($metadata['timeout'])) {
+            $timeout = $metadata['timeout'];
+            unset($metadata_copy['timeout']);
+        }
+
+        return [$metadata_copy, $timeout];
     }
-    return array($metadata_copy, $timeout);
-  }
 
-  /**
-   * validate and normalize the metadata array
-   * @param $metadata The metadata map
-   * @return $metadata Validated and key-normalized metadata map
-   * @throw InvalidArgumentException if key contains invalid characters
-   */
-  private function _validate_and_normalize_metadata($metadata) {
-    $metadata_copy = array();
-    foreach ($metadata as $key => $value) {
-      if (!preg_match('/^[A-Za-z\d_-]+$/', $key)) {
-        throw new \InvalidArgumentException(
-            'Metadata keys must be nonempty strings containing only '.
-            'alphanumeric characters, hyphens and underscores');
-      }
-      $metadata_copy[strtolower($key)] = $value;
+    /**
+     * validate and normalize the metadata array.
+     *
+     * @param $metadata The metadata map
+     *
+     * @return $metadata Validated and key-normalized metadata map
+     * @throw InvalidArgumentException if key contains invalid characters
+     */
+    private function _validate_and_normalize_metadata($metadata)
+    {
+        $metadata_copy = [];
+        foreach ($metadata as $key => $value) {
+            if (!preg_match('/^[A-Za-z\d_-]+$/', $key)) {
+                throw new \InvalidArgumentException(
+                    'Metadata keys must be nonempty strings containing only '.
+                    'alphanumeric characters, hyphens and underscores');
+            }
+            $metadata_copy[strtolower($key)] = $value;
+        }
+
+        return $metadata_copy;
     }
-    return $metadata_copy;
-  }
 
-  /* This class is intended to be subclassed by generated code, so all functions
-     begin with "_" to avoid name collisions. */
+    /* This class is intended to be subclassed by generated code, so
+     * all functions begin with "_" to avoid name collisions. */
 
-  /**
-   * Call a remote method that takes a single argument and has a single output
-   *
-   * @param string $method The name of the method to call
-   * @param $argument The argument to the method
-   * @param callable $deserialize A function that deserializes the response
-   * @param array $metadata A metadata map to send to the server
-   * @return SimpleSurfaceActiveCall The active call object
-   */
-  public function _simpleRequest($method,
-                                 $argument,
-                                 callable $deserialize,
-                                 $metadata = array(),
-                                 $options = array()) {
-    list($actual_metadata, $timeout)  = $this->_extract_timeout_from_metadata($metadata);
-    $call = new UnaryCall($this->channel, $method, $deserialize, $timeout);
-    $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
-    if (is_callable($this->update_metadata)) {
-      $actual_metadata = call_user_func($this->update_metadata,
+    /**
+     * Call a remote method that takes a single argument and has a
+     * single output.
+     *
+     * @param string $method The name of the method to call
+     * @param $argument The argument to the method
+     * @param callable $deserialize A function that deserializes the response
+     * @param array    $metadata    A metadata map to send to the server
+     *
+     * @return SimpleSurfaceActiveCall The active call object
+     */
+    public function _simpleRequest($method,
+                                   $argument,
+                                   callable $deserialize,
+                                   $metadata = [],
+                                   $options = [])
+    {
+        list($actual_metadata, $timeout) =
+            $this->_extract_timeout_from_metadata($metadata);
+        $call = new UnaryCall($this->channel,
+                              $method,
+                              $deserialize,
+                              $timeout);
+        $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
+        if (is_callable($this->update_metadata)) {
+            $actual_metadata = call_user_func($this->update_metadata,
                                         $actual_metadata,
                                         $jwt_aud_uri);
+        }
+        $actual_metadata = $this->_validate_and_normalize_metadata(
+            $actual_metadata);
+        $call->start($argument, $actual_metadata, $options);
+
+        return $call;
     }
-    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
-    $call->start($argument, $actual_metadata, $options);
-    return $call;
-  }
 
-  /**
-   * Call a remote method that takes a stream of arguments and has a single
-   * output
-   *
-   * @param string $method The name of the method to call
-   * @param $arguments An array or Traversable of arguments to stream to the
-   *     server
-   * @param callable $deserialize A function that deserializes the response
-   * @param array $metadata A metadata map to send to the server
-   * @return ClientStreamingSurfaceActiveCall The active call object
-   */
-  public function _clientStreamRequest($method,
-                                       callable $deserialize,
-                                       $metadata = array()) {
-    list($actual_metadata, $timeout)  = $this->_extract_timeout_from_metadata($metadata);
-    $call = new ClientStreamingCall($this->channel, $method, $deserialize, $timeout);
-    $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
-    if (is_callable($this->update_metadata)) {
-      $actual_metadata = call_user_func($this->update_metadata,
+    /**
+     * Call a remote method that takes a stream of arguments and has a single
+     * output.
+     *
+     * @param string $method The name of the method to call
+     * @param $arguments An array or Traversable of arguments to stream to the
+     *     server
+     * @param callable $deserialize A function that deserializes the response
+     * @param array    $metadata    A metadata map to send to the server
+     *
+     * @return ClientStreamingSurfaceActiveCall The active call object
+     */
+    public function _clientStreamRequest($method,
+                                         callable $deserialize,
+                                         $metadata = [])
+    {
+        list($actual_metadata, $timeout) =
+            $this->_extract_timeout_from_metadata($metadata);
+        $call = new ClientStreamingCall($this->channel,
+                                        $method,
+                                        $deserialize,
+                                        $timeout);
+        $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
+        if (is_callable($this->update_metadata)) {
+            $actual_metadata = call_user_func($this->update_metadata,
                                         $actual_metadata,
                                         $jwt_aud_uri);
+        }
+        $actual_metadata = $this->_validate_and_normalize_metadata(
+            $actual_metadata);
+        $call->start($actual_metadata);
+
+        return $call;
     }
-    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
-    $call->start($actual_metadata);
-    return $call;
-  }
 
-  /**
-   * Call a remote method that takes a single argument and returns a stream of
-   * responses
-   *
-   * @param string $method The name of the method to call
-   * @param $argument The argument to the method
-   * @param callable $deserialize A function that deserializes the responses
-   * @param array $metadata A metadata map to send to the server
-   * @return ServerStreamingSurfaceActiveCall The active call object
-   */
-  public function _serverStreamRequest($method,
-                                       $argument,
-                                       callable $deserialize,
-                                       $metadata = array(),
-                                       $options = array()) {
-    list($actual_metadata, $timeout)  = $this->_extract_timeout_from_metadata($metadata);
-    $call = new ServerStreamingCall($this->channel, $method, $deserialize, $timeout);
-    $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
-    if (is_callable($this->update_metadata)) {
-      $actual_metadata = call_user_func($this->update_metadata,
+    /**
+     * Call a remote method that takes a single argument and returns a stream of
+     * responses.
+     *
+     * @param string $method The name of the method to call
+     * @param $argument The argument to the method
+     * @param callable $deserialize A function that deserializes the responses
+     * @param array    $metadata    A metadata map to send to the server
+     *
+     * @return ServerStreamingSurfaceActiveCall The active call object
+     */
+    public function _serverStreamRequest($method,
+                                         $argument,
+                                         callable $deserialize,
+                                         $metadata = [],
+                                         $options = [])
+    {
+        list($actual_metadata, $timeout) =
+            $this->_extract_timeout_from_metadata($metadata);
+        $call = new ServerStreamingCall($this->channel,
+                                        $method,
+                                        $deserialize,
+                                        $timeout);
+        $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
+        if (is_callable($this->update_metadata)) {
+            $actual_metadata = call_user_func($this->update_metadata,
                                         $actual_metadata,
                                         $jwt_aud_uri);
+        }
+        $actual_metadata = $this->_validate_and_normalize_metadata(
+            $actual_metadata);
+        $call->start($argument, $actual_metadata, $options);
+
+        return $call;
     }
-    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
-    $call->start($argument, $actual_metadata, $options);
-    return $call;
-  }
 
-  /**
-   * Call a remote method with messages streaming in both directions
-   *
-   * @param string $method The name of the method to call
-   * @param callable $deserialize A function that deserializes the responses
-   * @param array $metadata A metadata map to send to the server
-   * @return BidiStreamingSurfaceActiveCall The active call object
-   */
-  public function _bidiRequest($method,
-                               callable $deserialize,
-                               $metadata = array()) {
-    list($actual_metadata, $timeout)  = $this->_extract_timeout_from_metadata($metadata);
-    $call = new BidiStreamingCall($this->channel, $method, $deserialize, $timeout);
-    $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
-    if (is_callable($this->update_metadata)) {
-      $actual_metadata = call_user_func($this->update_metadata,
+    /**
+     * Call a remote method with messages streaming in both directions.
+     *
+     * @param string   $method      The name of the method to call
+     * @param callable $deserialize A function that deserializes the responses
+     * @param array    $metadata    A metadata map to send to the server
+     *
+     * @return BidiStreamingSurfaceActiveCall The active call object
+     */
+    public function _bidiRequest($method,
+                                 callable $deserialize,
+                                 $metadata = [])
+    {
+        list($actual_metadata, $timeout) =
+            $this->_extract_timeout_from_metadata($metadata);
+        $call = new BidiStreamingCall($this->channel,
+                                      $method,
+                                      $deserialize,
+                                      $timeout);
+        $jwt_aud_uri = $this->_get_jwt_aud_uri($method);
+        if (is_callable($this->update_metadata)) {
+            $actual_metadata = call_user_func($this->update_metadata,
                                         $actual_metadata,
                                         $jwt_aud_uri);
+        }
+        $actual_metadata = $this->_validate_and_normalize_metadata(
+            $actual_metadata);
+        $call->start($actual_metadata);
+
+        return $call;
     }
-    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
-    $call->start($actual_metadata);
-    return $call;
-  }
 }
diff --git a/src/php/lib/Grpc/BidiStreamingCall.php b/src/php/lib/Grpc/BidiStreamingCall.php
index c432fd52d8d9e6f1d307dbe52fc95a73a148ec06..bf813c12e761b73212e94847910e788acc5049de 100644
--- a/src/php/lib/Grpc/BidiStreamingCall.php
+++ b/src/php/lib/Grpc/BidiStreamingCall.php
@@ -31,68 +31,87 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
+
 namespace Grpc;
 
 /**
  * Represents an active call that allows for sending and recieving messages in
  * streams in any order.
  */
-class BidiStreamingCall extends AbstractCall {
-  /**
-   * Start the call
-   * @param array $metadata Metadata to send with the call, if applicable
-   */
-  public function start($metadata = array()) {
-    $this->call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]);
-  }
+class BidiStreamingCall extends AbstractCall
+{
+    /**
+     * Start the call.
+     *
+     * @param array $metadata Metadata to send with the call, if applicable
+     */
+    public function start($metadata = [])
+    {
+        $this->call->startBatch([
+            OP_SEND_INITIAL_METADATA => $metadata,
+        ]);
+    }
+
+    /**
+     * Reads the next value from the server.
+     *
+     * @return The next value from the server, or null if there is none
+     */
+    public function read()
+    {
+        $batch = [OP_RECV_MESSAGE => true];
+        if ($this->metadata === null) {
+            $batch[OP_RECV_INITIAL_METADATA] = true;
+        }
+        $read_event = $this->call->startBatch($batch);
+        if ($this->metadata === null) {
+            $this->metadata = $read_event->metadata;
+        }
 
-  /**
-   * Reads the next value from the server.
-   * @return The next value from the server, or null if there is none
-   */
-  public function read() {
-    $batch = [OP_RECV_MESSAGE => true];
-    if ($this->metadata === null) {
-      $batch[OP_RECV_INITIAL_METADATA] = true;
+        return $this->deserializeResponse($read_event->message);
     }
-    $read_event = $this->call->startBatch($batch);
-    if ($this->metadata === null) {
-      $this->metadata = $read_event->metadata;
+
+    /**
+     * Write a single message to the server. This cannot be called after
+     * writesDone is called.
+     *
+     * @param ByteBuffer $data    The data to write
+     * @param array      $options an array of options, possible keys:
+     *                            'flags' => a number
+     */
+    public function write($data, $options = [])
+    {
+        $message_array = ['message' => $data->serialize()];
+        if (isset($options['flags'])) {
+            $message_array['flags'] = $options['flags'];
+        }
+        $this->call->startBatch([
+            OP_SEND_MESSAGE => $message_array,
+        ]);
     }
-    return $this->deserializeResponse($read_event->message);
-  }
 
-  /**
-   * Write a single message to the server. This cannot be called after
-   * writesDone is called.
-   * @param ByteBuffer $data The data to write
-   * @param array $options an array of options, possible keys:
-   *              'flags' => a number
-   */
-  public function write($data, $options = array()) {
-    $message_array = ['message' => $data->serialize()];
-    if (isset($options['flags'])) {
-      $message_array['flags'] = $options['flags'];
+    /**
+     * Indicate that no more writes will be sent.
+     */
+    public function writesDone()
+    {
+        $this->call->startBatch([
+            OP_SEND_CLOSE_FROM_CLIENT => true,
+        ]);
     }
-    $this->call->startBatch([OP_SEND_MESSAGE => $message_array]);
-  }
 
-  /**
-   * Indicate that no more writes will be sent.
-   */
-  public function writesDone() {
-    $this->call->startBatch([OP_SEND_CLOSE_FROM_CLIENT => true]);
-  }
+    /**
+     * Wait for the server to send the status, and return it.
+     *
+     * @return object The status object, with integer $code, string $details,
+     *                and array $metadata members
+     */
+    public function getStatus()
+    {
+        $status_event = $this->call->startBatch([
+            OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
 
-  /**
-   * Wait for the server to send the status, and return it.
-   * @return object The status object, with integer $code, string $details,
-   *     and array $metadata members
-   */
-  public function getStatus() {
-    $status_event = $this->call->startBatch([
-        OP_RECV_STATUS_ON_CLIENT => true
-    ]);
-    return $status_event->status;
-  }
-}
\ No newline at end of file
+        return $status_event->status;
+    }
+}
diff --git a/src/php/lib/Grpc/ClientStreamingCall.php b/src/php/lib/Grpc/ClientStreamingCall.php
index b96c17e751d4ba95b1182b5770361daac6ede86d..500cfe0d7a0a3e6421ad43cb817c4d5ea6aeeb12 100644
--- a/src/php/lib/Grpc/ClientStreamingCall.php
+++ b/src/php/lib/Grpc/ClientStreamingCall.php
@@ -31,47 +31,61 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
+
 namespace Grpc;
 
 /**
  * Represents an active call that sends a stream of messages and then gets a
  * single response.
  */
-class ClientStreamingCall extends AbstractCall {
-  /**
-   * Start the call.
-   * @param array $metadata Metadata to send with the call, if applicable
-   */
-  public function start($metadata = array()) {
-    $this->call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]);
-  }
+class ClientStreamingCall extends AbstractCall
+{
+    /**
+     * Start the call.
+     *
+     * @param array $metadata Metadata to send with the call, if applicable
+     */
+    public function start($metadata = [])
+    {
+        $this->call->startBatch([
+            OP_SEND_INITIAL_METADATA => $metadata,
+        ]);
+    }
 
-  /**
-   * Write a single message to the server. This cannot be called after
-   * wait is called.
-   * @param ByteBuffer $data The data to write
-   * @param array $options an array of options, possible keys:
-   *              'flags' => a number
-   */
-  public function write($data, $options = array()) {
-    $message_array = ['message' => $data->serialize()];
-    if (isset($options['flags'])) {
-      $message_array['flags'] = $options['flags'];
+    /**
+     * Write a single message to the server. This cannot be called after
+     * wait is called.
+     *
+     * @param ByteBuffer $data    The data to write
+     * @param array      $options an array of options, possible keys:
+     *                            'flags' => a number
+     */
+    public function write($data, $options = [])
+    {
+        $message_array = ['message' => $data->serialize()];
+        if (isset($options['flags'])) {
+            $message_array['flags'] = $options['flags'];
+        }
+        $this->call->startBatch([
+            OP_SEND_MESSAGE => $message_array,
+        ]);
     }
-    $this->call->startBatch([OP_SEND_MESSAGE => $message_array]);
-  }
 
-  /**
-   * Wait for the server to respond with data and a status
-   * @return [response data, status]
-   */
-  public function wait() {
-    $event = $this->call->startBatch([
-        OP_SEND_CLOSE_FROM_CLIENT => true,
-        OP_RECV_INITIAL_METADATA => true,
-        OP_RECV_MESSAGE => true,
-        OP_RECV_STATUS_ON_CLIENT => true]);
-    $this->metadata = $event->metadata;
-    return array($this->deserializeResponse($event->message), $event->status);
-  }
-}
\ No newline at end of file
+    /**
+     * Wait for the server to respond with data and a status.
+     *
+     * @return [response data, status]
+     */
+    public function wait()
+    {
+        $event = $this->call->startBatch([
+            OP_SEND_CLOSE_FROM_CLIENT => true,
+            OP_RECV_INITIAL_METADATA => true,
+            OP_RECV_MESSAGE => true,
+            OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+        $this->metadata = $event->metadata;
+
+        return [$this->deserializeResponse($event->message), $event->status];
+    }
+}
diff --git a/src/php/lib/Grpc/ServerStreamingCall.php b/src/php/lib/Grpc/ServerStreamingCall.php
index a93c1a5d5e0a60bce6e630bbe716806af055eb74..da48523717e218d04a5d886c0dbc2cb2722b0d4c 100644
--- a/src/php/lib/Grpc/ServerStreamingCall.php
+++ b/src/php/lib/Grpc/ServerStreamingCall.php
@@ -31,53 +31,66 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
+
 namespace Grpc;
 
 /**
  * Represents an active call that sends a single message and then gets a stream
- * of reponses
+ * of reponses.
  */
-class ServerStreamingCall extends AbstractCall {
-  /**
-   * Start the call
-   * @param $data The data to send
-   * @param array $metadata Metadata to send with the call, if applicable
-   * @param array $options an array of options, possible keys:
-   *              'flags' => a number
-   */
-  public function start($data, $metadata = array(), $options = array()) {
-    $message_array = ['message' => $data->serialize()];
-    if (isset($options['flags'])) {
-      $message_array['flags'] = $options['flags'];
+class ServerStreamingCall extends AbstractCall
+{
+    /**
+     * Start the call.
+     *
+     * @param $data The data to send
+     * @param array $metadata Metadata to send with the call, if applicable
+     * @param array $options  an array of options, possible keys:
+     *                        'flags' => a number
+     */
+    public function start($data, $metadata = [], $options = [])
+    {
+        $message_array = ['message' => $data->serialize()];
+        if (isset($options['flags'])) {
+            $message_array['flags'] = $options['flags'];
+        }
+        $event = $this->call->startBatch([
+            OP_SEND_INITIAL_METADATA => $metadata,
+            OP_RECV_INITIAL_METADATA => true,
+            OP_SEND_MESSAGE => $message_array,
+            OP_SEND_CLOSE_FROM_CLIENT => true,
+        ]);
+        $this->metadata = $event->metadata;
     }
-    $event = $this->call->startBatch([
-        OP_SEND_INITIAL_METADATA => $metadata,
-        OP_RECV_INITIAL_METADATA => true,
-        OP_SEND_MESSAGE => $message_array,
-        OP_SEND_CLOSE_FROM_CLIENT => true]);
-    $this->metadata = $event->metadata;
-  }
 
-  /**
-   * @return An iterator of response values
-   */
-  public function responses() {
-    $response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message;
-    while($response !== null) {
-      yield $this->deserializeResponse($response);
-      $response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message;
+    /**
+     * @return An iterator of response values
+     */
+    public function responses()
+    {
+        $response = $this->call->startBatch([
+            OP_RECV_MESSAGE => true,
+        ])->message;
+        while ($response !== null) {
+            yield $this->deserializeResponse($response);
+            $response = $this->call->startBatch([
+                OP_RECV_MESSAGE => true,
+            ])->message;
+        }
     }
-  }
 
-  /**
-   * Wait for the server to send the status, and return it.
-   * @return object The status object, with integer $code, string $details,
-   *     and array $metadata members
-   */
-  public function getStatus() {
-    $status_event = $this->call->startBatch([
-        OP_RECV_STATUS_ON_CLIENT => true
-    ]);
-    return $status_event->status;
-  }
+    /**
+     * Wait for the server to send the status, and return it.
+     *
+     * @return object The status object, with integer $code, string $details,
+     *                and array $metadata members
+     */
+    public function getStatus()
+    {
+        $status_event = $this->call->startBatch([
+            OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        return $status_event->status;
+    }
 }
diff --git a/src/php/lib/Grpc/UnaryCall.php b/src/php/lib/Grpc/UnaryCall.php
index 38af6b1d745650afb778634d1a6b7a193760480b..b57903d6d07ac595ac98a9c6c04075137c699764 100644
--- a/src/php/lib/Grpc/UnaryCall.php
+++ b/src/php/lib/Grpc/UnaryCall.php
@@ -31,41 +31,50 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
+
 namespace Grpc;
 
 /**
  * Represents an active call that sends a single message and then gets a single
  * response.
  */
-class UnaryCall extends AbstractCall {
-  /**
-   * Start the call
-   * @param $data The data to send
-   * @param array $metadata Metadata to send with the call, if applicable
-   * @param array $options an array of options, possible keys:
-   *              'flags' => a number
-   */
-  public function start($data, $metadata = array(), $options = array()) {
-    $message_array = ['message' => $data->serialize()];
-    if (isset($options['flags'])) {
-      $message_array['flags'] = $options['flags'];
+class UnaryCall extends AbstractCall
+{
+    /**
+     * Start the call.
+     *
+     * @param $data The data to send
+     * @param array $metadata Metadata to send with the call, if applicable
+     * @param array $options  an array of options, possible keys:
+     *                        'flags' => a number
+     */
+    public function start($data, $metadata = [], $options = [])
+    {
+        $message_array = ['message' => $data->serialize()];
+        if (isset($options['flags'])) {
+            $message_array['flags'] = $options['flags'];
+        }
+        $event = $this->call->startBatch([
+            OP_SEND_INITIAL_METADATA => $metadata,
+            OP_RECV_INITIAL_METADATA => true,
+            OP_SEND_MESSAGE => $message_array,
+            OP_SEND_CLOSE_FROM_CLIENT => true,
+        ]);
+        $this->metadata = $event->metadata;
     }
-    $event = $this->call->startBatch([
-        OP_SEND_INITIAL_METADATA => $metadata,
-        OP_RECV_INITIAL_METADATA => true,
-        OP_SEND_MESSAGE => $message_array,
-        OP_SEND_CLOSE_FROM_CLIENT => true]);
-    $this->metadata = $event->metadata;
-  }
 
-  /**
-   * Wait for the server to respond with data and a status
-   * @return [response data, status]
-   */
-  public function wait() {
-    $event = $this->call->startBatch([
-        OP_RECV_MESSAGE => true,
-        OP_RECV_STATUS_ON_CLIENT => true]);
-    return array($this->deserializeResponse($event->message), $event->status);
-  }
+    /**
+     * Wait for the server to respond with data and a status.
+     *
+     * @return [response data, status]
+     */
+    public function wait()
+    {
+        $event = $this->call->startBatch([
+            OP_RECV_MESSAGE => true,
+            OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        return [$this->deserializeResponse($event->message), $event->status];
+    }
 }
diff --git a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
index 1819e401f6fc1192018e89621738b88560be43e4..4a0bd6a1f1dcff249a047d3911b598626f3875b7 100644
--- a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
+++ b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
@@ -31,184 +31,212 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php');
-require_once dirname(__FILE__) . '/math.php';
-
-abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase {
-  /* These tests require that a server exporting the math service must be
-   * running on $GRPC_TEST_HOST */
-  protected static $client;
-  protected static $timeout;
-
-  public function testWaitForNotReady() {
-    $this->assertFalse(self::$client->waitForReady(1));
-  }
-
-  public function testWaitForReady() {
-    $this->assertTrue(self::$client->waitForReady(250000));
-  }
-
-  public function testAlreadyReady() {
-    $this->assertTrue(self::$client->waitForReady(250000));
-    $this->assertTrue(self::$client->waitForReady(100));
-  }
-
-  public function testGetTarget() {
-    $this->assertTrue(is_string(self::$client->getTarget()));
-  }
-
-  /**
-   * @expectedException InvalidArgumentException
-   */
-  public function testClose() {
-    self::$client->close();
-    $div_arg = new math\DivArgs();
-    $call = self::$client->Div($div_arg);
-  }
-
-  /**
-   * @expectedException InvalidArgumentException
-   */
-  public function testInvalidMetadata() {
-    $div_arg = new math\DivArgs();
-    $call = self::$client->Div($div_arg, array(' ' => 'abc123'));
-  }
-
-  public function testGetCallMetadata() {
-    $div_arg = new math\DivArgs();
-    $call = self::$client->Div($div_arg);
-    $this->assertTrue(is_array($call->getMetadata()));
-  }
-
-  public function testTimeout() {
-    $div_arg = new math\DivArgs();
-    $call = self::$client->Div($div_arg, array('timeout' => 100));
-    list($response, $status) = $call->wait();
-    $this->assertSame(\Grpc\STATUS_DEADLINE_EXCEEDED, $status->code);
-  }
-
-  public function testCancel() {
-    $div_arg = new math\DivArgs();
-    $call = self::$client->Div($div_arg);
-    $call->cancel();
-    list($response, $status) = $call->wait();
-    $this->assertSame(\Grpc\STATUS_CANCELLED, $status->code);
-  }
-
-  /**
-   * @expectedException InvalidArgumentException
-   */
-  public function testInvalidMethodName() {
-    $invalid_client = new DummyInvalidClient('host', array());
-    $div_arg = new math\DivArgs();
-    $invalid_client->InvalidUnaryCall($div_arg);
-  }
-
-  public function testWriteFlags() {
-    $div_arg = new math\DivArgs();
-    $div_arg->setDividend(7);
-    $div_arg->setDivisor(4);
-    $call = self::$client->Div($div_arg, array(), array('flags' => Grpc\WRITE_NO_COMPRESS));
-    $this->assertTrue(is_string($call->getPeer()));
-    list($response, $status) = $call->wait();
-    $this->assertSame(1, $response->getQuotient());
-    $this->assertSame(3, $response->getRemainder());
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
-
-  public function testWriteFlagsServerStreaming() {
-    $fib_arg = new math\FibArgs();
-    $fib_arg->setLimit(7);
-    $call = self::$client->Fib($fib_arg, array(), array('flags' => Grpc\WRITE_NO_COMPRESS));
-    $result_array = iterator_to_array($call->responses());
-    $status = $call->getStatus();
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
-
-  public function testWriteFlagsClientStreaming() {
-    $call = self::$client->Sum();
-    $num = new math\Num();
-    $num->setNum(1);
-    $call->write($num, array('flags' => Grpc\WRITE_NO_COMPRESS));
-    list($response, $status) = $call->wait();
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
-
-  public function testWriteFlagsBidiStreaming() {
-    $call = self::$client->DivMany();
-    $div_arg = new math\DivArgs();
-    $div_arg->setDividend(7);
-    $div_arg->setDivisor(4);
-    $call->write($div_arg, array('flags' => Grpc\WRITE_NO_COMPRESS));
-    $response = $call->read();
-    $call->writesDone();
-    $status = $call->getStatus();
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
-
-  public function testSimpleRequest() {
-    $div_arg = new math\DivArgs();
-    $div_arg->setDividend(7);
-    $div_arg->setDivisor(4);
-    $call = self::$client->Div($div_arg);
-    $this->assertTrue(is_string($call->getPeer()));
-    list($response, $status) = $call->wait();
-    $this->assertSame(1, $response->getQuotient());
-    $this->assertSame(3, $response->getRemainder());
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
-
-  public function testServerStreaming() {
-    $fib_arg = new math\FibArgs();
-    $fib_arg->setLimit(7);
-    $call = self::$client->Fib($fib_arg);
-    $this->assertTrue(is_string($call->getPeer()));
-    $result_array = iterator_to_array($call->responses());
-    $extract_num = function($num){
-      return $num->getNum();
-    };
-    $values = array_map($extract_num, $result_array);
-    $this->assertSame([1, 1, 2, 3, 5, 8, 13], $values);
-    $status = $call->getStatus();
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
-
-  public function testClientStreaming() {
-    $call = self::$client->Sum();
-    $this->assertTrue(is_string($call->getPeer()));
-    for ($i = 0; $i < 7; $i++) {
-      $num = new math\Num();
-      $num->setNum($i);
-      $call->write($num);
-    }
-    list($response, $status) = $call->wait();
-    $this->assertSame(21, $response->getNum());
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
-
-  public function testBidiStreaming() {
-    $call = self::$client->DivMany();
-    $this->assertTrue(is_string($call->getPeer()));
-    for ($i = 0; $i < 7; $i++) {
-      $div_arg = new math\DivArgs();
-      $div_arg->setDividend(2 * $i + 1);
-      $div_arg->setDivisor(2);
-      $call->write($div_arg);
-      $response = $call->read();
-      $this->assertSame($i, $response->getQuotient());
-      $this->assertSame(1, $response->getRemainder());
-    }
-    $call->writesDone();
-    $status = $call->getStatus();
-    $this->assertSame(\Grpc\STATUS_OK, $status->code);
-  }
+require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php');
+require_once dirname(__FILE__).'/math.php';
+
+abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * These tests require that a server exporting the math service must be
+     * running on $GRPC_TEST_HOST.
+     */
+    protected static $client;
+    protected static $timeout;
+
+    public function testWaitForNotReady()
+    {
+        $this->assertFalse(self::$client->waitForReady(1));
+    }
+
+    public function testWaitForReady()
+    {
+        $this->assertTrue(self::$client->waitForReady(250000));
+    }
+
+    public function testAlreadyReady()
+    {
+        $this->assertTrue(self::$client->waitForReady(250000));
+        $this->assertTrue(self::$client->waitForReady(100));
+    }
+
+    public function testGetTarget()
+    {
+        $this->assertTrue(is_string(self::$client->getTarget()));
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    public function testClose()
+    {
+        self::$client->close();
+        $div_arg = new math\DivArgs();
+        $call = self::$client->Div($div_arg);
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    public function testInvalidMetadata()
+    {
+        $div_arg = new math\DivArgs();
+        $call = self::$client->Div($div_arg, [' ' => 'abc123']);
+    }
+
+    public function testGetCallMetadata()
+    {
+        $div_arg = new math\DivArgs();
+        $call = self::$client->Div($div_arg);
+        $this->assertTrue(is_array($call->getMetadata()));
+    }
+
+    public function testTimeout()
+    {
+        $div_arg = new math\DivArgs();
+        $call = self::$client->Div($div_arg, ['timeout' => 100]);
+        list($response, $status) = $call->wait();
+        $this->assertSame(\Grpc\STATUS_DEADLINE_EXCEEDED, $status->code);
+    }
+
+    public function testCancel()
+    {
+        $div_arg = new math\DivArgs();
+        $call = self::$client->Div($div_arg);
+        $call->cancel();
+        list($response, $status) = $call->wait();
+        $this->assertSame(\Grpc\STATUS_CANCELLED, $status->code);
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    public function testInvalidMethodName()
+    {
+        $invalid_client = new DummyInvalidClient('host', []);
+        $div_arg = new math\DivArgs();
+        $invalid_client->InvalidUnaryCall($div_arg);
+    }
+
+    public function testWriteFlags()
+    {
+        $div_arg = new math\DivArgs();
+        $div_arg->setDividend(7);
+        $div_arg->setDivisor(4);
+        $call = self::$client->Div($div_arg, [],
+                                   ['flags' => Grpc\WRITE_NO_COMPRESS]);
+        $this->assertTrue(is_string($call->getPeer()));
+        list($response, $status) = $call->wait();
+        $this->assertSame(1, $response->getQuotient());
+        $this->assertSame(3, $response->getRemainder());
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
+
+    public function testWriteFlagsServerStreaming()
+    {
+        $fib_arg = new math\FibArgs();
+        $fib_arg->setLimit(7);
+        $call = self::$client->Fib($fib_arg, [],
+                                   ['flags' => Grpc\WRITE_NO_COMPRESS]);
+        $result_array = iterator_to_array($call->responses());
+        $status = $call->getStatus();
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
+
+    public function testWriteFlagsClientStreaming()
+    {
+        $call = self::$client->Sum();
+        $num = new math\Num();
+        $num->setNum(1);
+        $call->write($num, ['flags' => Grpc\WRITE_NO_COMPRESS]);
+        list($response, $status) = $call->wait();
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
+
+    public function testWriteFlagsBidiStreaming()
+    {
+        $call = self::$client->DivMany();
+        $div_arg = new math\DivArgs();
+        $div_arg->setDividend(7);
+        $div_arg->setDivisor(4);
+        $call->write($div_arg, ['flags' => Grpc\WRITE_NO_COMPRESS]);
+        $response = $call->read();
+        $call->writesDone();
+        $status = $call->getStatus();
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
+
+    public function testSimpleRequest()
+    {
+        $div_arg = new math\DivArgs();
+        $div_arg->setDividend(7);
+        $div_arg->setDivisor(4);
+        $call = self::$client->Div($div_arg);
+        $this->assertTrue(is_string($call->getPeer()));
+        list($response, $status) = $call->wait();
+        $this->assertSame(1, $response->getQuotient());
+        $this->assertSame(3, $response->getRemainder());
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
+
+    public function testServerStreaming()
+    {
+        $fib_arg = new math\FibArgs();
+        $fib_arg->setLimit(7);
+        $call = self::$client->Fib($fib_arg);
+        $this->assertTrue(is_string($call->getPeer()));
+        $result_array = iterator_to_array($call->responses());
+        $extract_num = function ($num) {
+                         return $num->getNum();
+                       };
+        $values = array_map($extract_num, $result_array);
+        $this->assertSame([1, 1, 2, 3, 5, 8, 13], $values);
+        $status = $call->getStatus();
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
+
+    public function testClientStreaming()
+    {
+        $call = self::$client->Sum();
+        $this->assertTrue(is_string($call->getPeer()));
+        for ($i = 0; $i < 7; ++$i) {
+            $num = new math\Num();
+            $num->setNum($i);
+            $call->write($num);
+        }
+        list($response, $status) = $call->wait();
+        $this->assertSame(21, $response->getNum());
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
+
+    public function testBidiStreaming()
+    {
+        $call = self::$client->DivMany();
+        $this->assertTrue(is_string($call->getPeer()));
+        for ($i = 0; $i < 7; ++$i) {
+            $div_arg = new math\DivArgs();
+            $div_arg->setDividend(2 * $i + 1);
+            $div_arg->setDivisor(2);
+            $call->write($div_arg);
+            $response = $call->read();
+            $this->assertSame($i, $response->getQuotient());
+            $this->assertSame(1, $response->getRemainder());
+        }
+        $call->writesDone();
+        $status = $call->getStatus();
+        $this->assertSame(\Grpc\STATUS_OK, $status->code);
+    }
 }
 
-class DummyInvalidClient extends \Grpc\BaseStub {
-  public function InvalidUnaryCall(\math\DivArgs $argument,
-                                   $metadata = array(),
-                                   $options = array()) {
-    return $this->_simpleRequest('invalidMethodName', $argument,
-                                 function() {}, $metadata, $options);
-  }
+class DummyInvalidClient extends \Grpc\BaseStub
+{
+    public function InvalidUnaryCall(\math\DivArgs $argument,
+                                     $metadata = [],
+                                     $options = [])
+    {
+        return $this->_simpleRequest('invalidMethodName',
+                                     $argument,
+                                     function () {},
+                                     $metadata,
+                                     $options);
+    }
 }
diff --git a/src/php/tests/generated_code/GeneratedCodeTest.php b/src/php/tests/generated_code/GeneratedCodeTest.php
index 64bcf45b4a0174dc7c5f48abcc29ea57a47f1b68..7043e8e1d1bc1232e7abb00e232e243247897e1b 100755
--- a/src/php/tests/generated_code/GeneratedCodeTest.php
+++ b/src/php/tests/generated_code/GeneratedCodeTest.php
@@ -31,15 +31,18 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-require_once dirname(__FILE__) . '/AbstractGeneratedCodeTest.php';
+require_once dirname(__FILE__).'/AbstractGeneratedCodeTest.php';
 
-class GeneratedCodeTest extends AbstractGeneratedCodeTest {
-  public function setUp() {
-    self::$client = new math\MathClient(
+class GeneratedCodeTest extends AbstractGeneratedCodeTest
+{
+    public function setUp()
+    {
+        self::$client = new math\MathClient(
         getenv('GRPC_TEST_HOST'), []);
-  }
+    }
 
-  public static function tearDownAfterClass() {
-    self::$client->close();
-  }
+    public static function tearDownAfterClass()
+    {
+        self::$client->close();
+    }
 }
diff --git a/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php b/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php
index 09c09cf35359c5a86ad1991b7b55fd36e0622d9a..5a20e684c73d88790c4b6303274161590113fb47 100644
--- a/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php
+++ b/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php
@@ -31,21 +31,25 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-require_once dirname(__FILE__) . '/AbstractGeneratedCodeTest.php';
+require_once dirname(__FILE__).'/AbstractGeneratedCodeTest.php';
 
-class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest {
-  public function setUp() {
-    self::$client = new math\MathClient(
-        getenv('GRPC_TEST_HOST'), ['update_metadata' =>
-                                   function($a_hash,
-                                            $client = array()) {
-                                     $a_copy = $a_hash;
-                                     $a_copy['foo'] = ['bar'];
-                                     return $a_copy;
-                                   }]);
-  }
+class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest
+{
+    public function setUp()
+    {
+        self::$client = new math\MathClient(
+        getenv('GRPC_TEST_HOST'),
+        ['update_metadata' => function ($a_hash,
+                                        $client = []) {
+                                $a_copy = $a_hash;
+                                $a_copy['foo'] = ['bar'];
 
-  public static function tearDownAfterClass() {
-    self::$client->close();
-  }
+                                return $a_copy;
+                              }]);
+    }
+
+    public static function tearDownAfterClass()
+    {
+        self::$client->close();
+    }
 }
diff --git a/src/php/tests/generated_code/math_client.php b/src/php/tests/generated_code/math_client.php
index 7bc78287be80c527c991e50dc69b6483d4d1345a..76ccabc0684972d5878b8d8924dc8c7d234f7588 100644
--- a/src/php/tests/generated_code/math_client.php
+++ b/src/php/tests/generated_code/math_client.php
@@ -36,31 +36,32 @@
 include 'vendor/autoload.php';
 include 'tests/generated_code/math.php';
 
-function p($line) {
-  print("$line<br/>\n");
+function p($line)
+{
+    print("$line<br/>\n");
 }
 
-$host = "localhost:50051";
+$host = 'localhost:50051';
 p("Connecting to host: $host");
 $client = new math\MathClient($host, []);
-p("Client class: ".get_class($client));
+p('Client class: '.get_class($client));
 p('');
 
-p("Running unary call test:");
+p('Running unary call test:');
 $dividend = 7;
 $divisor = 4;
 $div_arg = new math\DivArgs();
 $div_arg->setDividend($dividend);
 $div_arg->setDivisor($divisor);
 $call = $client->Div($div_arg);
-p("Call peer: ".$call->getPeer());
+p('Call peer: '.$call->getPeer());
 p("Dividing $dividend by $divisor");
 list($response, $status) = $call->wait();
-p("quotient = ".$response->getQuotient());
-p("remainder = ".$response->getRemainder());
+p('quotient = '.$response->getQuotient());
+p('remainder = '.$response->getRemainder());
 p('');
 
-p("Running server streaming test:");
+p('Running server streaming test:');
 $limit = 7;
 $fib_arg = new math\FibArgs();
 $fib_arg->setLimit($limit);
@@ -68,35 +69,35 @@ $call = $client->Fib($fib_arg);
 $result_array = iterator_to_array($call->responses());
 $result = '';
 foreach ($result_array as $num) {
-  $result .= ' '.$num->getNum();
+    $result .= ' '.$num->getNum();
 }
 p("The first $limit Fibonacci numbers are:".$result);
 p('');
 
-p("Running client streaming test:");
+p('Running client streaming test:');
 $call = $client->Sum();
-for ($i = 0; $i <= $limit; $i++) {
-  $num = new math\Num();
-  $num->setNum($i);
-  $call->write($num);
+for ($i = 0; $i <= $limit; ++$i) {
+    $num = new math\Num();
+    $num->setNum($i);
+    $call->write($num);
 }
 list($response, $status) = $call->wait();
-p(sprintf("The first %d positive integers sum to: %d",
+p(sprintf('The first %d positive integers sum to: %d',
           $limit, $response->getNum()));
 p('');
 
-p("Running bidi-streaming test:");
+p('Running bidi-streaming test:');
 $call = $client->DivMany();
-for ($i = 0; $i < 7; $i++) {
-  $div_arg = new math\DivArgs();
-  $dividend = 2 * $i + 1;
-  $divisor = 3;
-  $div_arg->setDividend($dividend);
-  $div_arg->setDivisor($divisor);
-  $call->write($div_arg);
-  p("client writing: $dividend / $divisor");
-  $response = $call->read();
-  p(sprintf("server writing: quotient = %d, remainder = %d",
+for ($i = 0; $i < 7; ++$i) {
+    $div_arg = new math\DivArgs();
+    $dividend = 2 * $i + 1;
+    $divisor = 3;
+    $div_arg->setDividend($dividend);
+    $div_arg->setDivisor($divisor);
+    $call->write($div_arg);
+    p("client writing: $dividend / $divisor");
+    $response = $call->read();
+    p(sprintf('server writing: quotient = %d, remainder = %d',
             $response->getQuotient(), $response->getRemainder()));
 }
 $call->writesDone();
diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php
index 03ce2ac70017ed26d9c86f29f198ccdaebf849c6..3019866561e3fa11f9ec58636b66d2a7b433029e 100755
--- a/src/php/tests/interop/interop_client.php
+++ b/src/php/tests/interop/interop_client.php
@@ -31,7 +31,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php');
+require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php');
 require 'empty.php';
 require 'messages.php';
 require 'test.php';
@@ -41,394 +41,427 @@ use GuzzleHttp\ClientInterface;
 
 /**
  * Assertion function that always exits with an error code if the assertion is
- * falsy
+ * falsy.
+ *
  * @param $value Assertion value. Should be true.
  * @param $error_message Message to display if the assertion is false
  */
-function hardAssert($value, $error_message) {
-  if (!$value) {
-    echo $error_message . "\n";
-    exit(1);
-  }
+function hardAssert($value, $error_message)
+{
+    if (!$value) {
+        echo $error_message."\n";
+        exit(1);
+    }
 }
 
 /**
  * Run the empty_unary test.
+ *
  * @param $stub Stub object that has service methods
  */
-function emptyUnary($stub) {
-  list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
-  hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
-  hardAssert($result !== null, 'Call completed with a null response');
+function emptyUnary($stub)
+{
+    list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
+    hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+    hardAssert($result !== null, 'Call completed with a null response');
 }
 
 /**
  * Run the large_unary test.
+ *
  * @param $stub Stub object that has service methods
  */
-function largeUnary($stub) {
-  performLargeUnary($stub);
+function largeUnary($stub)
+{
+    performLargeUnary($stub);
 }
 
 /**
- * Shared code between large unary test and auth test
+ * Shared code between large unary test and auth test.
+ *
  * @param $stub Stub object that has service methods
  * @param $fillUsername boolean whether to fill result with username
  * @param $fillOauthScope boolean whether to fill result with oauth scope
  */
 function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false,
-                           $metadata = array()) {
-  $request_len = 271828;
-  $response_len = 314159;
-
-  $request = new grpc\testing\SimpleRequest();
-  $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
-  $request->setResponseSize($response_len);
-  $payload = new grpc\testing\Payload();
-  $payload->setType(grpc\testing\PayloadType::COMPRESSABLE);
-  $payload->setBody(str_repeat("\0", $request_len));
-  $request->setPayload($payload);
-  $request->setFillUsername($fillUsername);
-  $request->setFillOauthScope($fillOauthScope);
-
-  list($result, $status) = $stub->UnaryCall($request, $metadata)->wait();
-  hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
-  hardAssert($result !== null, 'Call returned a null response');
-  $payload = $result->getPayload();
-  hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
+                           $metadata = [])
+{
+    $request_len = 271828;
+    $response_len = 314159;
+
+    $request = new grpc\testing\SimpleRequest();
+    $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
+    $request->setResponseSize($response_len);
+    $payload = new grpc\testing\Payload();
+    $payload->setType(grpc\testing\PayloadType::COMPRESSABLE);
+    $payload->setBody(str_repeat("\0", $request_len));
+    $request->setPayload($payload);
+    $request->setFillUsername($fillUsername);
+    $request->setFillOauthScope($fillOauthScope);
+
+    list($result, $status) = $stub->UnaryCall($request, $metadata)->wait();
+    hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+    hardAssert($result !== null, 'Call returned a null response');
+    $payload = $result->getPayload();
+    hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
          'Payload had the wrong type');
-  hardAssert(strlen($payload->getBody()) === $response_len,
+    hardAssert(strlen($payload->getBody()) === $response_len,
          'Payload had the wrong length');
-  hardAssert($payload->getBody() === str_repeat("\0", $response_len),
+    hardAssert($payload->getBody() === str_repeat("\0", $response_len),
          'Payload had the wrong content');
-  return $result;
+
+    return $result;
 }
 
 /**
  * Run the service account credentials auth test.
+ *
  * @param $stub Stub object that has service methods
  * @param $args array command line args
  */
-function serviceAccountCreds($stub, $args) {
-  if (!array_key_exists('oauth_scope', $args)) {
-    throw new Exception('Missing oauth scope');
-  }
-  $jsonKey = json_decode(
-      file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
-      true);
-  $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
-  hardAssert($result->getUsername() == $jsonKey['client_email'],
+function serviceAccountCreds($stub, $args)
+{
+    if (!array_key_exists('oauth_scope', $args)) {
+        throw new Exception('Missing oauth scope');
+    }
+    $jsonKey = json_decode(
+        file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
+        true);
+    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
+    hardAssert($result->getUsername() == $jsonKey['client_email'],
              'invalid email returned');
-  hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false,
+    hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false,
              'invalid oauth scope returned');
 }
 
 /**
  * Run the compute engine credentials auth test.
- * Has not been run from gcloud as of 2015-05-05
+ * Has not been run from gcloud as of 2015-05-05.
+ *
  * @param $stub Stub object that has service methods
  * @param $args array command line args
  */
-function computeEngineCreds($stub, $args) {
-  if (!array_key_exists('oauth_scope', $args)) {
-    throw new Exception('Missing oauth scope');
-  }
-  if (!array_key_exists('default_service_account', $args)) {
-    throw new Exception('Missing default_service_account');
-  }
-  $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
-  hardAssert($args['default_service_account'] == $result->getUsername(),
+function computeEngineCreds($stub, $args)
+{
+    if (!array_key_exists('oauth_scope', $args)) {
+        throw new Exception('Missing oauth scope');
+    }
+    if (!array_key_exists('default_service_account', $args)) {
+        throw new Exception('Missing default_service_account');
+    }
+    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
+    hardAssert($args['default_service_account'] == $result->getUsername(),
              'invalid email returned');
 }
 
 /**
  * Run the jwt token credentials auth test.
+ *
  * @param $stub Stub object that has service methods
  * @param $args array command line args
  */
-function jwtTokenCreds($stub, $args) {
-  $jsonKey = json_decode(
-      file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
-      true);
-  $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
-  hardAssert($result->getUsername() == $jsonKey['client_email'],
+function jwtTokenCreds($stub, $args)
+{
+    $jsonKey = json_decode(
+        file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
+        true);
+    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
+    hardAssert($result->getUsername() == $jsonKey['client_email'],
              'invalid email returned');
 }
 
 /**
  * Run the oauth2_auth_token auth test.
+ *
  * @param $stub Stub object that has service methods
  * @param $args array command line args
  */
-function oauth2AuthToken($stub, $args) {
-  $jsonKey = json_decode(
-      file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
-      true);
-  $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
-  hardAssert($result->getUsername() == $jsonKey['client_email'],
+function oauth2AuthToken($stub, $args)
+{
+    $jsonKey = json_decode(
+        file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
+        true);
+    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
+    hardAssert($result->getUsername() == $jsonKey['client_email'],
              'invalid email returned');
 }
 
 /**
  * Run the per_rpc_creds auth test.
+ *
  * @param $stub Stub object that has service methods
  * @param $args array command line args
  */
-function perRpcCreds($stub, $args) {
-  $jsonKey = json_decode(
-      file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
-      true);
-  $auth_credentials = ApplicationDefaultCredentials::getCredentials(
-      $args['oauth_scope']
-  );
-  $token = $auth_credentials->fetchAuthToken();
-  $metadata = array(CredentialsLoader::AUTH_METADATA_KEY =>
-                    array(sprintf("%s %s",
-                                  $token['token_type'],
-                                  $token['access_token'])));
-  $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true,
+function perRpcCreds($stub, $args)
+{
+    $jsonKey = json_decode(
+        file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
+        true);
+    $auth_credentials = ApplicationDefaultCredentials::getCredentials(
+        $args['oauth_scope']
+    );
+    $token = $auth_credentials->fetchAuthToken();
+    $metadata = [CredentialsLoader::AUTH_METADATA_KEY => [sprintf('%s %s',
+                          $token['token_type'],
+                          $token['access_token'])]];
+    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true,
                               $metadata);
-  hardAssert($result->getUsername() == $jsonKey['client_email'],
+    hardAssert($result->getUsername() == $jsonKey['client_email'],
              'invalid email returned');
 }
 
 /**
  * Run the client_streaming test.
+ *
  * @param $stub Stub object that has service methods
  */
-function clientStreaming($stub) {
-  $request_lengths = array(27182, 8, 1828, 45904);
-
-  $requests = array_map(
-      function($length) {
-        $request = new grpc\testing\StreamingInputCallRequest();
-        $payload = new grpc\testing\Payload();
-        $payload->setBody(str_repeat("\0", $length));
-        $request->setPayload($payload);
-        return $request;
-      }, $request_lengths);
-
-  $call = $stub->StreamingInputCall();
-  foreach ($requests as $request) {
-    $call->write($request);
-  }
-  list($result, $status) = $call->wait();
-  hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
-  hardAssert($result->getAggregatedPayloadSize() === 74922,
+function clientStreaming($stub)
+{
+    $request_lengths = [27182, 8, 1828, 45904];
+
+    $requests = array_map(
+        function ($length) {
+            $request = new grpc\testing\StreamingInputCallRequest();
+            $payload = new grpc\testing\Payload();
+            $payload->setBody(str_repeat("\0", $length));
+            $request->setPayload($payload);
+
+            return $request;
+        }, $request_lengths);
+
+    $call = $stub->StreamingInputCall();
+    foreach ($requests as $request) {
+        $call->write($request);
+    }
+    list($result, $status) = $call->wait();
+    hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+    hardAssert($result->getAggregatedPayloadSize() === 74922,
               'aggregated_payload_size was incorrect');
 }
 
 /**
  * Run the server_streaming test.
+ *
  * @param $stub Stub object that has service methods.
  */
-function serverStreaming($stub) {
-  $sizes = array(31415, 9, 2653, 58979);
-
-  $request = new grpc\testing\StreamingOutputCallRequest();
-  $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
-  foreach($sizes as $size) {
-    $response_parameters = new grpc\testing\ResponseParameters();
-    $response_parameters->setSize($size);
-    $request->addResponseParameters($response_parameters);
-  }
+function serverStreaming($stub)
+{
+    $sizes = [31415, 9, 2653, 58979];
 
-  $call = $stub->StreamingOutputCall($request);
-  $i = 0;
-  foreach($call->responses() as $value) {
-    hardAssert($i < 4, 'Too many responses');
-    $payload = $value->getPayload();
-    hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
-                'Payload ' . $i . ' had the wrong type');
-    hardAssert(strlen($payload->getBody()) === $sizes[$i],
-                'Response ' . $i . ' had the wrong length');
-    $i += 1;
-  }
-  hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
+    $request = new grpc\testing\StreamingOutputCallRequest();
+    $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
+    foreach ($sizes as $size) {
+        $response_parameters = new grpc\testing\ResponseParameters();
+        $response_parameters->setSize($size);
+        $request->addResponseParameters($response_parameters);
+    }
+
+    $call = $stub->StreamingOutputCall($request);
+    $i = 0;
+    foreach ($call->responses() as $value) {
+        hardAssert($i < 4, 'Too many responses');
+        $payload = $value->getPayload();
+        hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
+                'Payload '.$i.' had the wrong type');
+        hardAssert(strlen($payload->getBody()) === $sizes[$i],
+                'Response '.$i.' had the wrong length');
+        $i += 1;
+    }
+    hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
              'Call did not complete successfully');
 }
 
 /**
  * Run the ping_pong test.
+ *
  * @param $stub Stub object that has service methods.
  */
-function pingPong($stub) {
-  $request_lengths = array(27182, 8, 1828, 45904);
-  $response_lengths = array(31415, 9, 2653, 58979);
-
-  $call = $stub->FullDuplexCall();
-  for($i = 0; $i < 4; $i++) {
-    $request = new grpc\testing\StreamingOutputCallRequest();
-    $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
-    $response_parameters = new grpc\testing\ResponseParameters();
-    $response_parameters->setSize($response_lengths[$i]);
-    $request->addResponseParameters($response_parameters);
-    $payload = new grpc\testing\Payload();
-    $payload->setBody(str_repeat("\0", $request_lengths[$i]));
-    $request->setPayload($payload);
-
-    $call->write($request);
-    $response = $call->read();
+function pingPong($stub)
+{
+    $request_lengths = [27182, 8, 1828, 45904];
+    $response_lengths = [31415, 9, 2653, 58979];
+
+    $call = $stub->FullDuplexCall();
+    for ($i = 0; $i < 4; ++$i) {
+        $request = new grpc\testing\StreamingOutputCallRequest();
+        $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
+        $response_parameters = new grpc\testing\ResponseParameters();
+        $response_parameters->setSize($response_lengths[$i]);
+        $request->addResponseParameters($response_parameters);
+        $payload = new grpc\testing\Payload();
+        $payload->setBody(str_repeat("\0", $request_lengths[$i]));
+        $request->setPayload($payload);
 
-    hardAssert($response !== null, 'Server returned too few responses');
-    $payload = $response->getPayload();
-    hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
-                'Payload ' . $i . ' had the wrong type');
-    hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
-                'Payload ' . $i . ' had the wrong length');
-  }
-  $call->writesDone();
-  hardAssert($call->read() === null, 'Server returned too many responses');
-  hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
+        $call->write($request);
+        $response = $call->read();
+
+        hardAssert($response !== null, 'Server returned too few responses');
+        $payload = $response->getPayload();
+        hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
+                'Payload '.$i.' had the wrong type');
+        hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
+                'Payload '.$i.' had the wrong length');
+    }
+    $call->writesDone();
+    hardAssert($call->read() === null, 'Server returned too many responses');
+    hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
               'Call did not complete successfully');
 }
 
 /**
  * Run the empty_stream test.
+ *
  * @param $stub Stub object that has service methods.
  */
-function emptyStream($stub) {
-  $call = $stub->FullDuplexCall();
-  $call->writesDone();
-  hardAssert($call->read() === null, 'Server returned too many responses');
-  hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
+function emptyStream($stub)
+{
+    $call = $stub->FullDuplexCall();
+    $call->writesDone();
+    hardAssert($call->read() === null, 'Server returned too many responses');
+    hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
              'Call did not complete successfully');
 }
 
 /**
  * Run the cancel_after_begin test.
+ *
  * @param $stub Stub object that has service methods.
  */
-function cancelAfterBegin($stub) {
-  $call = $stub->StreamingInputCall();
-  $call->cancel();
-  list($result, $status) = $call->wait();
-  hardAssert($status->code === Grpc\STATUS_CANCELLED,
+function cancelAfterBegin($stub)
+{
+    $call = $stub->StreamingInputCall();
+    $call->cancel();
+    list($result, $status) = $call->wait();
+    hardAssert($status->code === Grpc\STATUS_CANCELLED,
              'Call status was not CANCELLED');
 }
 
 /**
  * Run the cancel_after_first_response test.
+ *
  * @param $stub Stub object that has service methods.
  */
-function cancelAfterFirstResponse($stub) {
-  $call = $stub->FullDuplexCall();
-  $request = new grpc\testing\StreamingOutputCallRequest();
-  $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
-  $response_parameters = new grpc\testing\ResponseParameters();
-  $response_parameters->setSize(31415);
-  $request->addResponseParameters($response_parameters);
-  $payload = new grpc\testing\Payload();
-  $payload->setBody(str_repeat("\0", 27182));
-  $request->setPayload($payload);
-
-  $call->write($request);
-  $response = $call->read();
-
-  $call->cancel();
-  hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED,
+function cancelAfterFirstResponse($stub)
+{
+    $call = $stub->FullDuplexCall();
+    $request = new grpc\testing\StreamingOutputCallRequest();
+    $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
+    $response_parameters = new grpc\testing\ResponseParameters();
+    $response_parameters->setSize(31415);
+    $request->addResponseParameters($response_parameters);
+    $payload = new grpc\testing\Payload();
+    $payload->setBody(str_repeat("\0", 27182));
+    $request->setPayload($payload);
+
+    $call->write($request);
+    $response = $call->read();
+
+    $call->cancel();
+    hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED,
              'Call status was not CANCELLED');
 }
 
-function timeoutOnSleepingServer($stub) {
-  $call = $stub->FullDuplexCall(array('timeout' => 1000));
-  $request = new grpc\testing\StreamingOutputCallRequest();
-  $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
-  $response_parameters = new grpc\testing\ResponseParameters();
-  $response_parameters->setSize(8);
-  $request->addResponseParameters($response_parameters);
-  $payload = new grpc\testing\Payload();
-  $payload->setBody(str_repeat("\0", 9));
-  $request->setPayload($payload);
-
-  $call->write($request);
-  $response = $call->read();
-
-  hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED,
+function timeoutOnSleepingServer($stub)
+{
+    $call = $stub->FullDuplexCall(['timeout' => 1000]);
+    $request = new grpc\testing\StreamingOutputCallRequest();
+    $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
+    $response_parameters = new grpc\testing\ResponseParameters();
+    $response_parameters->setSize(8);
+    $request->addResponseParameters($response_parameters);
+    $payload = new grpc\testing\Payload();
+    $payload->setBody(str_repeat("\0", 9));
+    $request->setPayload($payload);
+
+    $call->write($request);
+    $response = $call->read();
+
+    hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED,
              'Call status was not DEADLINE_EXCEEDED');
 }
 
-$args = getopt('', array('server_host:', 'server_port:', 'test_case:',
-                         'use_tls::', 'use_test_ca::',
-                         'server_host_override:', 'oauth_scope:',
-                         'default_service_account:'));
+$args = getopt('', ['server_host:', 'server_port:', 'test_case:',
+                    'use_tls::', 'use_test_ca::',
+                    'server_host_override:', 'oauth_scope:',
+                    'default_service_account:', ]);
 if (!array_key_exists('server_host', $args)) {
-  throw new Exception('Missing argument: --server_host is required');
+    throw new Exception('Missing argument: --server_host is required');
 }
 if (!array_key_exists('server_port', $args)) {
-  throw new Exception('Missing argument: --server_port is required');
+    throw new Exception('Missing argument: --server_port is required');
 }
 if (!array_key_exists('test_case', $args)) {
-  throw new Exception('Missing argument: --test_case is required');
+    throw new Exception('Missing argument: --test_case is required');
 }
 
 if ($args['server_port'] == 443) {
-  $server_address = $args['server_host'];
+    $server_address = $args['server_host'];
 } else {
-  $server_address = $args['server_host'] . ':' . $args['server_port'];
+    $server_address = $args['server_host'].':'.$args['server_port'];
 }
 
 $test_case = $args['test_case'];
 
 $host_override = 'foo.test.google.fr';
 if (array_key_exists('server_host_override', $args)) {
-  $host_override = $args['server_host_override'];
+    $host_override = $args['server_host_override'];
 }
 
 $use_tls = false;
 if (array_key_exists('use_tls', $args) &&
     $args['use_tls'] != 'false') {
-  $use_tls = true;
+    $use_tls = true;
 }
 
 $use_test_ca = false;
 if (array_key_exists('use_test_ca', $args) &&
     $args['use_test_ca'] != 'false') {
-  $use_test_ca = true;
+    $use_test_ca = true;
 }
 
 $opts = [];
 
 if ($use_tls) {
-  if ($use_test_ca) {
-    $ssl_credentials = Grpc\Credentials::createSsl(
-        file_get_contents(dirname(__FILE__) . '/../data/ca.pem'));
-  } else {
-    $ssl_credentials = Grpc\Credentials::createSsl();
-  }
-  $opts['credentials'] = $ssl_credentials;
-  $opts['grpc.ssl_target_name_override'] = $host_override;
+    if ($use_test_ca) {
+        $ssl_credentials = Grpc\Credentials::createSsl(
+        file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
+    } else {
+        $ssl_credentials = Grpc\Credentials::createSsl();
+    }
+    $opts['credentials'] = $ssl_credentials;
+    $opts['grpc.ssl_target_name_override'] = $host_override;
 }
 
-if (in_array($test_case, array('service_account_creds',
-    'compute_engine_creds', 'jwt_token_creds'))) {
-  if ($test_case == 'jwt_token_creds') {
-    $auth_credentials = ApplicationDefaultCredentials::getCredentials();
-  } else {
-    $auth_credentials = ApplicationDefaultCredentials::getCredentials(
-      $args['oauth_scope']
-    );
-  }
-  $opts['update_metadata'] = $auth_credentials->getUpdateMetadataFunc();
+if (in_array($test_case, ['service_account_creds',
+    'compute_engine_creds', 'jwt_token_creds', ])) {
+    if ($test_case == 'jwt_token_creds') {
+        $auth_credentials = ApplicationDefaultCredentials::getCredentials();
+    } else {
+        $auth_credentials = ApplicationDefaultCredentials::getCredentials(
+            $args['oauth_scope']
+        );
+    }
+    $opts['update_metadata'] = $auth_credentials->getUpdateMetadataFunc();
 }
 
 if ($test_case == 'oauth2_auth_token') {
-  $auth_credentials = ApplicationDefaultCredentials::getCredentials(
-      $args['oauth_scope']
-  );
-  $token = $auth_credentials->fetchAuthToken();
-  $update_metadata =
-      function($metadata,
-               $authUri = null,
-               ClientInterface $client = null) use ($token) {
-        $metadata_copy = $metadata;
-        $metadata_copy[CredentialsLoader::AUTH_METADATA_KEY] =
-            array(sprintf("%s %s",
-                          $token['token_type'],
-                          $token['access_token']));
-        return $metadata_copy;
-      };
-  $opts['update_metadata'] = $update_metadata;
+    $auth_credentials = ApplicationDefaultCredentials::getCredentials(
+        $args['oauth_scope']
+    );
+    $token = $auth_credentials->fetchAuthToken();
+    $update_metadata =
+        function ($metadata,
+                  $authUri = null,
+                  ClientInterface $client = null) use ($token) {
+            $metadata_copy = $metadata;
+            $metadata_copy[CredentialsLoader::AUTH_METADATA_KEY] =
+                [sprintf('%s %s',
+                         $token['token_type'],
+                         $token['access_token'])];
+
+            return $metadata_copy;
+        };
+    $opts['update_metadata'] = $update_metadata;
 }
 
 $stub = new grpc\testing\TestServiceClient($server_address, $opts);
@@ -437,49 +470,49 @@ echo "Connecting to $server_address\n";
 echo "Running test case $test_case\n";
 
 switch ($test_case) {
-  case 'empty_unary':
-    emptyUnary($stub);
-    break;
-  case 'large_unary':
-    largeUnary($stub);
-    break;
-  case 'client_streaming':
-    clientStreaming($stub);
-    break;
-  case 'server_streaming':
-    serverStreaming($stub);
-    break;
-  case 'ping_pong':
-    pingPong($stub);
-    break;
-  case 'empty_stream':
-    emptyStream($stub);
-    break;
-  case 'cancel_after_begin':
-    cancelAfterBegin($stub);
-    break;
-  case 'cancel_after_first_response':
-    cancelAfterFirstResponse($stub);
-    break;
-  case 'timeout_on_sleeping_server':
-    timeoutOnSleepingServer($stub);
-    break;
-  case 'service_account_creds':
-    serviceAccountCreds($stub, $args);
-    break;
-  case 'compute_engine_creds':
-    computeEngineCreds($stub, $args);
-    break;
-  case 'jwt_token_creds':
-    jwtTokenCreds($stub, $args);
-    break;
-  case 'oauth2_auth_token':
-    oauth2AuthToken($stub, $args);
-    break;
-  case 'per_rpc_creds':
-    perRpcCreds($stub, $args);
-    break;
-  default:
-    echo "Unsupported test case $test_case\n";
-    exit(1);
+    case 'empty_unary':
+        emptyUnary($stub);
+        break;
+    case 'large_unary':
+        largeUnary($stub);
+        break;
+    case 'client_streaming':
+        clientStreaming($stub);
+        break;
+    case 'server_streaming':
+        serverStreaming($stub);
+        break;
+    case 'ping_pong':
+        pingPong($stub);
+        break;
+    case 'empty_stream':
+        emptyStream($stub);
+        break;
+    case 'cancel_after_begin':
+        cancelAfterBegin($stub);
+        break;
+    case 'cancel_after_first_response':
+        cancelAfterFirstResponse($stub);
+        break;
+    case 'timeout_on_sleeping_server':
+        timeoutOnSleepingServer($stub);
+        break;
+    case 'service_account_creds':
+        serviceAccountCreds($stub, $args);
+        break;
+    case 'compute_engine_creds':
+        computeEngineCreds($stub, $args);
+        break;
+    case 'jwt_token_creds':
+        jwtTokenCreds($stub, $args);
+        break;
+    case 'oauth2_auth_token':
+        oauth2AuthToken($stub, $args);
+        break;
+    case 'per_rpc_creds':
+        perRpcCreds($stub, $args);
+        break;
+    default:
+        echo "Unsupported test case $test_case\n";
+        exit(1);
 }
diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php
index caff15ee110f6ac2a0fb076d1e48af5e8de4d109..3b697b50c3bc84415acbe18c173a85d68911306a 100755
--- a/src/php/tests/unit_tests/CallTest.php
+++ b/src/php/tests/unit_tests/CallTest.php
@@ -31,56 +31,64 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-class CallTest extends PHPUnit_Framework_TestCase{
-  static $server;
-  static $port;
+class CallTest extends PHPUnit_Framework_TestCase
+{
+    public static $server;
+    public static $port;
 
-  public static function setUpBeforeClass() {
-    self::$server = new Grpc\Server([]);
-    self::$port = self::$server->addHttp2Port('0.0.0.0:0');
-  }
+    public static function setUpBeforeClass()
+    {
+        self::$server = new Grpc\Server([]);
+        self::$port = self::$server->addHttp2Port('0.0.0.0:0');
+    }
 
-  public function setUp() {
-    $this->channel = new Grpc\Channel('localhost:' . self::$port, []);
-    $this->call = new Grpc\Call($this->channel,
-                                '/foo',
-                                Grpc\Timeval::infFuture());
-  }
+    public function setUp()
+    {
+        $this->channel = new Grpc\Channel('localhost:'.self::$port, []);
+        $this->call = new Grpc\Call($this->channel,
+                                    '/foo',
+                                    Grpc\Timeval::infFuture());
+    }
 
-  public function testAddEmptyMetadata() {
-    $batch = [
-        Grpc\OP_SEND_INITIAL_METADATA => []
-              ];
-    $result = $this->call->startBatch($batch);
-    $this->assertTrue($result->send_metadata);
-  }
+    public function testAddEmptyMetadata()
+    {
+        $batch = [
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+        ];
+        $result = $this->call->startBatch($batch);
+        $this->assertTrue($result->send_metadata);
+    }
 
-  public function testAddSingleMetadata() {
-    $batch = [
-        Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value']]
-              ];
-    $result = $this->call->startBatch($batch);
-    $this->assertTrue($result->send_metadata);
-  }
+    public function testAddSingleMetadata()
+    {
+        $batch = [
+            Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value']],
+        ];
+        $result = $this->call->startBatch($batch);
+        $this->assertTrue($result->send_metadata);
+    }
 
-  public function testAddMultiValueMetadata() {
-    $batch = [
-        Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value1', 'value2']]
-              ];
-    $result = $this->call->startBatch($batch);
-    $this->assertTrue($result->send_metadata);
-  }
+    public function testAddMultiValueMetadata()
+    {
+        $batch = [
+            Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value1', 'value2']],
+        ];
+        $result = $this->call->startBatch($batch);
+        $this->assertTrue($result->send_metadata);
+    }
 
-  public function testAddSingleAndMultiValueMetadata() {
-    $batch = [
-        Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'],
-                                          'key2' => ['value2', 'value3']]
-              ];
-    $result = $this->call->startBatch($batch);
-    $this->assertTrue($result->send_metadata);
-  }
+    public function testAddSingleAndMultiValueMetadata()
+    {
+        $batch = [
+            Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'],
+                                              'key2' => ['value2', 'value3'], ],
+        ];
+        $result = $this->call->startBatch($batch);
+        $this->assertTrue($result->send_metadata);
+    }
 
-  public function testGetPeer() {
-    $this->assertTrue(is_string($this->call->getPeer()));
-  }
+    public function testGetPeer()
+    {
+        $this->assertTrue(is_string($this->call->getPeer()));
+    }
 }
diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php
index b65366233ad2ad6d83509921b0f3201aef60fd8e..5a38262451bec477021d19c1fe54079bf29d7ae5 100755
--- a/src/php/tests/unit_tests/EndToEndTest.php
+++ b/src/php/tests/unit_tests/EndToEndTest.php
@@ -31,217 +31,228 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-class EndToEndTest extends PHPUnit_Framework_TestCase{
-  public function setUp() {
-    $this->server = new Grpc\Server([]);
-    $this->port = $this->server->addHttp2Port('0.0.0.0:0');
-    $this->channel = new Grpc\Channel('localhost:' . $this->port, []);
-    $this->server->start();
-  }
-
-  public function tearDown() {
-    unset($this->channel);
-    unset($this->server);
-  }
-
-  public function testSimpleRequestBody() {
-    $deadline = Grpc\Timeval::infFuture();
-    $status_text = 'xyz';
-    $call = new Grpc\Call($this->channel,
-                          'dummy_method',
-                          $deadline);
-
-    $event = $call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
-                                       ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_close);
-
-    $event = $this->server->requestCall();
-    $this->assertSame('dummy_method', $event->method);
-    $server_call = $event->call;
-
-    $event = $server_call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_STATUS_FROM_SERVER => [
-            'metadata' => [],
-            'code' => Grpc\STATUS_OK,
-            'details' => $status_text
-                                            ],
-        Grpc\OP_RECV_CLOSE_ON_SERVER => true
-                                        ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_status);
-    $this->assertFalse($event->cancelled);
-
-    $event = $call->startBatch([
-        Grpc\OP_RECV_INITIAL_METADATA => true,
-        Grpc\OP_RECV_STATUS_ON_CLIENT => true
-                                 ]);
-
-    $status = $event->status;
-    $this->assertSame([], $status->metadata);
-    $this->assertSame(Grpc\STATUS_OK, $status->code);
-    $this->assertSame($status_text, $status->details);
-
-    unset($call);
-    unset($server_call);
-  }
-
-  public function testMessageWriteFlags() {
-    $deadline = Grpc\Timeval::infFuture();
-    $req_text = 'message_write_flags_test';
-    $status_text = 'xyz';
-    $call = new Grpc\Call($this->channel,
-                          'dummy_method',
-                          $deadline);
-
-    $event = $call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
-                                 'flags' => Grpc\WRITE_NO_COMPRESS],
-        Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
-                                       ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_close);
-
-    $event = $this->server->requestCall();
-    $this->assertSame('dummy_method', $event->method);
-    $server_call = $event->call;
-
-    $event = $server_call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_STATUS_FROM_SERVER => [
-            'metadata' => [],
-            'code' => Grpc\STATUS_OK,
-            'details' => $status_text
-        ],
-    ]);
-
-    $event = $call->startBatch([
-        Grpc\OP_RECV_INITIAL_METADATA => true,
-        Grpc\OP_RECV_STATUS_ON_CLIENT => true
-                                 ]);
-
-    $status = $event->status;
-    $this->assertSame([], $status->metadata);
-    $this->assertSame(Grpc\STATUS_OK, $status->code);
-    $this->assertSame($status_text, $status->details);
-
-    unset($call);
-    unset($server_call);
-  }
-
-  public function testClientServerFullRequestResponse() {
-    $deadline = Grpc\Timeval::infFuture();
-    $req_text = 'client_server_full_request_response';
-    $reply_text = 'reply:client_server_full_request_response';
-    $status_text = 'status:client_server_full_response_text';
-
-    $call = new Grpc\Call($this->channel,
-                          'dummy_method',
-                          $deadline);
-
-    $event = $call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
-        Grpc\OP_SEND_MESSAGE => ['message' => $req_text],
-                                       ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_close);
-    $this->assertTrue($event->send_message);
-
-    $event = $this->server->requestCall();
-    $this->assertSame('dummy_method', $event->method);
-    $server_call = $event->call;
-
-    $event = $server_call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
-        Grpc\OP_SEND_STATUS_FROM_SERVER => [
-            'metadata' => [],
-            'code' => Grpc\STATUS_OK,
-            'details' => $status_text
-                                            ],
-        Grpc\OP_RECV_MESSAGE => true,
-        Grpc\OP_RECV_CLOSE_ON_SERVER => true,
-                                        ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_status);
-    $this->assertTrue($event->send_message);
-    $this->assertFalse($event->cancelled);
-    $this->assertSame($req_text, $event->message);
-
-    $event = $call->startBatch([
-        Grpc\OP_RECV_INITIAL_METADATA => true,
-        Grpc\OP_RECV_MESSAGE => true,
-        Grpc\OP_RECV_STATUS_ON_CLIENT => true,
-                                       ]);
-
-    $this->assertSame([], $event->metadata);
-    $this->assertSame($reply_text, $event->message);
-    $status = $event->status;
-    $this->assertSame([], $status->metadata);
-    $this->assertSame(Grpc\STATUS_OK, $status->code);
-    $this->assertSame($status_text, $status->details);
-
-    unset($call);
-    unset($server_call);
-  }
-
-  public function testGetTarget() {
-    $this->assertTrue(is_string($this->channel->getTarget()));
-  }
-
-  public function testGetConnectivityState() {
-    $this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE);
-  }
-
-  public function testWatchConnectivityStateFailed() {
-    $idle_state = $this->channel->getConnectivityState();
-    $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
-
-    $now = Grpc\Timeval::now();
-    $delta = new Grpc\Timeval(500000); // should timeout
-    $deadline = $now->add($delta);
-
-    $this->assertFalse($this->channel->watchConnectivityState(
+class EndToEndTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $this->server = new Grpc\Server([]);
+        $this->port = $this->server->addHttp2Port('0.0.0.0:0');
+        $this->channel = new Grpc\Channel('localhost:'.$this->port, []);
+        $this->server->start();
+    }
+
+    public function tearDown()
+    {
+        unset($this->channel);
+        unset($this->server);
+    }
+
+    public function testSimpleRequestBody()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $status_text = 'xyz';
+        $call = new Grpc\Call($this->channel,
+                              'dummy_method',
+                              $deadline);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+
+        $event = $this->server->requestCall();
+        $this->assertSame('dummy_method', $event->method);
+        $server_call = $event->call;
+
+        $event = $server_call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_STATUS_FROM_SERVER => [
+                'metadata' => [],
+                'code' => Grpc\STATUS_OK,
+                'details' => $status_text,
+            ],
+            Grpc\OP_RECV_CLOSE_ON_SERVER => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_status);
+        $this->assertFalse($event->cancelled);
+
+        $event = $call->startBatch([
+            Grpc\OP_RECV_INITIAL_METADATA => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $status = $event->status;
+        $this->assertSame([], $status->metadata);
+        $this->assertSame(Grpc\STATUS_OK, $status->code);
+        $this->assertSame($status_text, $status->details);
+
+        unset($call);
+        unset($server_call);
+    }
+
+    public function testMessageWriteFlags()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $req_text = 'message_write_flags_test';
+        $status_text = 'xyz';
+        $call = new Grpc\Call($this->channel,
+                              'dummy_method',
+                              $deadline);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
+                                     'flags' => Grpc\WRITE_NO_COMPRESS, ],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+
+        $event = $this->server->requestCall();
+        $this->assertSame('dummy_method', $event->method);
+        $server_call = $event->call;
+
+        $event = $server_call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_STATUS_FROM_SERVER => [
+                'metadata' => [],
+                'code' => Grpc\STATUS_OK,
+                'details' => $status_text,
+            ],
+        ]);
+
+        $event = $call->startBatch([
+            Grpc\OP_RECV_INITIAL_METADATA => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $status = $event->status;
+        $this->assertSame([], $status->metadata);
+        $this->assertSame(Grpc\STATUS_OK, $status->code);
+        $this->assertSame($status_text, $status->details);
+
+        unset($call);
+        unset($server_call);
+    }
+
+    public function testClientServerFullRequestResponse()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $req_text = 'client_server_full_request_response';
+        $reply_text = 'reply:client_server_full_request_response';
+        $status_text = 'status:client_server_full_response_text';
+
+        $call = new Grpc\Call($this->channel,
+                              'dummy_method',
+                              $deadline);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+            Grpc\OP_SEND_MESSAGE => ['message' => $req_text],
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+        $this->assertTrue($event->send_message);
+
+        $event = $this->server->requestCall();
+        $this->assertSame('dummy_method', $event->method);
+        $server_call = $event->call;
+
+        $event = $server_call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
+            Grpc\OP_SEND_STATUS_FROM_SERVER => [
+                'metadata' => [],
+                'code' => Grpc\STATUS_OK,
+                'details' => $status_text,
+            ],
+            Grpc\OP_RECV_MESSAGE => true,
+            Grpc\OP_RECV_CLOSE_ON_SERVER => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_status);
+        $this->assertTrue($event->send_message);
+        $this->assertFalse($event->cancelled);
+        $this->assertSame($req_text, $event->message);
+
+        $event = $call->startBatch([
+            Grpc\OP_RECV_INITIAL_METADATA => true,
+            Grpc\OP_RECV_MESSAGE => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $this->assertSame([], $event->metadata);
+        $this->assertSame($reply_text, $event->message);
+        $status = $event->status;
+        $this->assertSame([], $status->metadata);
+        $this->assertSame(Grpc\STATUS_OK, $status->code);
+        $this->assertSame($status_text, $status->details);
+
+        unset($call);
+        unset($server_call);
+    }
+
+    public function testGetTarget()
+    {
+        $this->assertTrue(is_string($this->channel->getTarget()));
+    }
+
+    public function testGetConnectivityState()
+    {
+        $this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE);
+    }
+
+    public function testWatchConnectivityStateFailed()
+    {
+        $idle_state = $this->channel->getConnectivityState();
+        $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
+
+        $now = Grpc\Timeval::now();
+        $delta = new Grpc\Timeval(500000); // should timeout
+        $deadline = $now->add($delta);
+
+        $this->assertFalse($this->channel->watchConnectivityState(
         $idle_state, $deadline));
-  }
+    }
 
-  public function testWatchConnectivityStateSuccess() {
-    $idle_state = $this->channel->getConnectivityState(true);
-    $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
+    public function testWatchConnectivityStateSuccess()
+    {
+        $idle_state = $this->channel->getConnectivityState(true);
+        $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
 
-    $now = Grpc\Timeval::now();
-    $delta = new Grpc\Timeval(3000000); // should finish well before
-    $deadline = $now->add($delta);
+        $now = Grpc\Timeval::now();
+        $delta = new Grpc\Timeval(3000000); // should finish well before
+        $deadline = $now->add($delta);
 
-    $this->assertTrue($this->channel->watchConnectivityState(
+        $this->assertTrue($this->channel->watchConnectivityState(
         $idle_state, $deadline));
 
-    $new_state = $this->channel->getConnectivityState();
-    $this->assertTrue($idle_state != $new_state);
-  }
+        $new_state = $this->channel->getConnectivityState();
+        $this->assertTrue($idle_state != $new_state);
+    }
 
-  public function testWatchConnectivityStateDoNothing() {
-    $idle_state = $this->channel->getConnectivityState();
-    $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
+    public function testWatchConnectivityStateDoNothing()
+    {
+        $idle_state = $this->channel->getConnectivityState();
+        $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
 
-    $now = Grpc\Timeval::now();
-    $delta = new Grpc\Timeval(100000);
-    $deadline = $now->add($delta);
+        $now = Grpc\Timeval::now();
+        $delta = new Grpc\Timeval(100000);
+        $deadline = $now->add($delta);
 
-    $this->assertFalse($this->channel->watchConnectivityState(
+        $this->assertFalse($this->channel->watchConnectivityState(
         $idle_state, $deadline));
 
-    $new_state = $this->channel->getConnectivityState();
-    $this->assertTrue($new_state == Grpc\CHANNEL_IDLE);
-  }
+        $new_state = $this->channel->getConnectivityState();
+        $this->assertTrue($new_state == Grpc\CHANNEL_IDLE);
+    }
 }
diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php
index d7fca14a0d5dbccace4ad9d4abcec36b7b556982..e66bde376c7a8d45fe17ca469bce7c1a73610c4c 100755
--- a/src/php/tests/unit_tests/SecureEndToEndTest.php
+++ b/src/php/tests/unit_tests/SecureEndToEndTest.php
@@ -31,186 +31,193 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
-  public function setUp() {
-    $credentials = Grpc\Credentials::createSsl(
-        file_get_contents(dirname(__FILE__) . '/../data/ca.pem'));
-    $server_credentials = Grpc\ServerCredentials::createSsl(
-        null,
-        file_get_contents(dirname(__FILE__) . '/../data/server1.key'),
-        file_get_contents(dirname(__FILE__) . '/../data/server1.pem'));
-    $this->server = new Grpc\Server();
-    $this->port = $this->server->addSecureHttp2Port('0.0.0.0:0',
+class SecureEndToEndTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $credentials = Grpc\Credentials::createSsl(
+            file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
+        $server_credentials = Grpc\ServerCredentials::createSsl(
+            null,
+            file_get_contents(dirname(__FILE__).'/../data/server1.key'),
+            file_get_contents(dirname(__FILE__).'/../data/server1.pem'));
+        $this->server = new Grpc\Server();
+        $this->port = $this->server->addSecureHttp2Port('0.0.0.0:0',
                                               $server_credentials);
-    $this->server->start();
-    $this->host_override = 'foo.test.google.fr';
-    $this->channel = new Grpc\Channel(
-        'localhost:' . $this->port,
-        [
+        $this->server->start();
+        $this->host_override = 'foo.test.google.fr';
+        $this->channel = new Grpc\Channel(
+            'localhost:'.$this->port,
+            [
             'grpc.ssl_target_name_override' => $this->host_override,
             'grpc.default_authority' => $this->host_override,
-            'credentials' => $credentials
-         ]);
-  }
-
-  public function tearDown() {
-    unset($this->channel);
-    unset($this->server);
-  }
-
-  public function testSimpleRequestBody() {
-    $deadline = Grpc\Timeval::infFuture();
-    $status_text = 'xyz';
-    $call = new Grpc\Call($this->channel,
-                          'dummy_method',
-                          $deadline,
-                          $this->host_override);
-
-    $event = $call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
-                                       ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_close);
-
-    $event = $this->server->requestCall();
-    $this->assertSame('dummy_method', $event->method);
-    $server_call = $event->call;
-
-    $event = $server_call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_STATUS_FROM_SERVER => [
-            'metadata' => [],
-            'code' => Grpc\STATUS_OK,
-            'details' => $status_text
-                                            ],
-        Grpc\OP_RECV_CLOSE_ON_SERVER => true
-                                        ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_status);
-    $this->assertFalse($event->cancelled);
-
-    $event = $call->startBatch([
-        Grpc\OP_RECV_INITIAL_METADATA => true,
-        Grpc\OP_RECV_STATUS_ON_CLIENT => true
-                                 ]);
-
-    $this->assertSame([], $event->metadata);
-    $status = $event->status;
-    $this->assertSame([], $status->metadata);
-    $this->assertSame(Grpc\STATUS_OK, $status->code);
-    $this->assertSame($status_text, $status->details);
-
-    unset($call);
-    unset($server_call);
-  }
-
-  public function testMessageWriteFlags() {
-    $deadline = Grpc\Timeval::infFuture();
-    $req_text = 'message_write_flags_test';
-    $status_text = 'xyz';
-    $call = new Grpc\Call($this->channel,
-                          'dummy_method',
-                          $deadline,
-                          $this->host_override);
-
-    $event = $call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
-                                 'flags' => Grpc\WRITE_NO_COMPRESS],
-        Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
-                                       ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_close);
-
-    $event = $this->server->requestCall();
-    $this->assertSame('dummy_method', $event->method);
-    $server_call = $event->call;
-
-    $event = $server_call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_STATUS_FROM_SERVER => [
-            'metadata' => [],
-            'code' => Grpc\STATUS_OK,
-            'details' => $status_text
-        ],
-    ]);
-
-    $event = $call->startBatch([
-        Grpc\OP_RECV_INITIAL_METADATA => true,
-        Grpc\OP_RECV_STATUS_ON_CLIENT => true
-    ]);
-
-    $this->assertSame([], $event->metadata);
-    $status = $event->status;
-    $this->assertSame([], $status->metadata);
-    $this->assertSame(Grpc\STATUS_OK, $status->code);
-    $this->assertSame($status_text, $status->details);
-
-    unset($call);
-    unset($server_call);
-  }
-
-  public function testClientServerFullRequestResponse() {
-    $deadline = Grpc\Timeval::infFuture();
-    $req_text = 'client_server_full_request_response';
-    $reply_text = 'reply:client_server_full_request_response';
-    $status_text = 'status:client_server_full_response_text';
-
-    $call = new Grpc\Call($this->channel,
-                          'dummy_method',
-                          $deadline,
-                          $this->host_override);
-
-    $event = $call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
-        Grpc\OP_SEND_MESSAGE => ['message' => $req_text]
-                                       ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_close);
-    $this->assertTrue($event->send_message);
-
-    $event = $this->server->requestCall();
-    $this->assertSame('dummy_method', $event->method);
-    $server_call = $event->call;
-
-    $event = $server_call->startBatch([
-        Grpc\OP_SEND_INITIAL_METADATA => [],
-        Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
-        Grpc\OP_SEND_STATUS_FROM_SERVER => [
-            'metadata' => [],
-            'code' => Grpc\STATUS_OK,
-            'details' => $status_text
-                                            ],
-        Grpc\OP_RECV_MESSAGE => true,
-        Grpc\OP_RECV_CLOSE_ON_SERVER => true,
-                                        ]);
-
-    $this->assertTrue($event->send_metadata);
-    $this->assertTrue($event->send_status);
-    $this->assertTrue($event->send_message);
-    $this->assertFalse($event->cancelled);
-    $this->assertSame($req_text, $event->message);
-
-    $event = $call->startBatch([
-        Grpc\OP_RECV_INITIAL_METADATA => true,
-        Grpc\OP_RECV_MESSAGE => true,
-        Grpc\OP_RECV_STATUS_ON_CLIENT => true,
-                                       ]);
-
-    $this->assertSame([], $event->metadata);
-    $this->assertSame($reply_text, $event->message);
-    $status = $event->status;
-    $this->assertSame([], $status->metadata);
-    $this->assertSame(Grpc\STATUS_OK, $status->code);
-    $this->assertSame($status_text, $status->details);
-
-    unset($call);
-    unset($server_call);
-  }
+            'credentials' => $credentials,
+            ]
+        );
+    }
+
+    public function tearDown()
+    {
+        unset($this->channel);
+        unset($this->server);
+    }
+
+    public function testSimpleRequestBody()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $status_text = 'xyz';
+        $call = new Grpc\Call($this->channel,
+                              'dummy_method',
+                              $deadline,
+                              $this->host_override);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+
+        $event = $this->server->requestCall();
+        $this->assertSame('dummy_method', $event->method);
+        $server_call = $event->call;
+
+        $event = $server_call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_STATUS_FROM_SERVER => [
+                'metadata' => [],
+                'code' => Grpc\STATUS_OK,
+                'details' => $status_text,
+            ],
+            Grpc\OP_RECV_CLOSE_ON_SERVER => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_status);
+        $this->assertFalse($event->cancelled);
+
+        $event = $call->startBatch([
+            Grpc\OP_RECV_INITIAL_METADATA => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $this->assertSame([], $event->metadata);
+        $status = $event->status;
+        $this->assertSame([], $status->metadata);
+        $this->assertSame(Grpc\STATUS_OK, $status->code);
+        $this->assertSame($status_text, $status->details);
+
+        unset($call);
+        unset($server_call);
+    }
+
+    public function testMessageWriteFlags()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $req_text = 'message_write_flags_test';
+        $status_text = 'xyz';
+        $call = new Grpc\Call($this->channel,
+                              'dummy_method',
+                              $deadline,
+                              $this->host_override);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
+                                     'flags' => Grpc\WRITE_NO_COMPRESS, ],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+
+        $event = $this->server->requestCall();
+        $this->assertSame('dummy_method', $event->method);
+        $server_call = $event->call;
+
+        $event = $server_call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_STATUS_FROM_SERVER => [
+                'metadata' => [],
+                'code' => Grpc\STATUS_OK,
+                'details' => $status_text,
+            ],
+        ]);
+
+        $event = $call->startBatch([
+            Grpc\OP_RECV_INITIAL_METADATA => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $this->assertSame([], $event->metadata);
+        $status = $event->status;
+        $this->assertSame([], $status->metadata);
+        $this->assertSame(Grpc\STATUS_OK, $status->code);
+        $this->assertSame($status_text, $status->details);
+
+        unset($call);
+        unset($server_call);
+    }
+
+    public function testClientServerFullRequestResponse()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $req_text = 'client_server_full_request_response';
+        $reply_text = 'reply:client_server_full_request_response';
+        $status_text = 'status:client_server_full_response_text';
+
+        $call = new Grpc\Call($this->channel,
+                              'dummy_method',
+                              $deadline,
+                              $this->host_override);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+            Grpc\OP_SEND_MESSAGE => ['message' => $req_text],
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+        $this->assertTrue($event->send_message);
+
+        $event = $this->server->requestCall();
+        $this->assertSame('dummy_method', $event->method);
+        $server_call = $event->call;
+
+        $event = $server_call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
+            Grpc\OP_SEND_STATUS_FROM_SERVER => [
+                'metadata' => [],
+                'code' => Grpc\STATUS_OK,
+                'details' => $status_text,
+            ],
+            Grpc\OP_RECV_MESSAGE => true,
+            Grpc\OP_RECV_CLOSE_ON_SERVER => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_status);
+        $this->assertTrue($event->send_message);
+        $this->assertFalse($event->cancelled);
+        $this->assertSame($req_text, $event->message);
+
+        $event = $call->startBatch([
+            Grpc\OP_RECV_INITIAL_METADATA => true,
+            Grpc\OP_RECV_MESSAGE => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $this->assertSame([], $event->metadata);
+        $this->assertSame($reply_text, $event->message);
+        $status = $event->status;
+        $this->assertSame([], $status->metadata);
+        $this->assertSame(Grpc\STATUS_OK, $status->code);
+        $this->assertSame($status_text, $status->details);
+
+        unset($call);
+        unset($server_call);
+    }
 }
diff --git a/src/php/tests/unit_tests/TimevalTest.php b/src/php/tests/unit_tests/TimevalTest.php
index 7b4925cad607e9e7989d43df39dbae0185fa6610..1d2a8d303e737f304fd57db6a0cf8bd44e936f93 100755
--- a/src/php/tests/unit_tests/TimevalTest.php
+++ b/src/php/tests/unit_tests/TimevalTest.php
@@ -31,56 +31,64 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
-class TimevalTest extends PHPUnit_Framework_TestCase{
-  public function testCompareSame() {
-    $zero = Grpc\Timeval::zero();
-    $this->assertSame(0, Grpc\Timeval::compare($zero, $zero));
-  }
+class TimevalTest extends PHPUnit_Framework_TestCase
+{
+    public function testCompareSame()
+    {
+        $zero = Grpc\Timeval::zero();
+        $this->assertSame(0, Grpc\Timeval::compare($zero, $zero));
+    }
 
-  public function testPastIsLessThanZero() {
-    $zero = Grpc\Timeval::zero();
-    $past = Grpc\Timeval::infPast();
-    $this->assertLessThan(0, Grpc\Timeval::compare($past, $zero));
-    $this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past));
-  }
+    public function testPastIsLessThanZero()
+    {
+        $zero = Grpc\Timeval::zero();
+        $past = Grpc\Timeval::infPast();
+        $this->assertLessThan(0, Grpc\Timeval::compare($past, $zero));
+        $this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past));
+    }
 
-  public function testFutureIsGreaterThanZero() {
-    $zero = Grpc\Timeval::zero();
-    $future = Grpc\Timeval::infFuture();
-    $this->assertLessThan(0, Grpc\Timeval::compare($zero, $future));
-    $this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero));
-  }
+    public function testFutureIsGreaterThanZero()
+    {
+        $zero = Grpc\Timeval::zero();
+        $future = Grpc\Timeval::infFuture();
+        $this->assertLessThan(0, Grpc\Timeval::compare($zero, $future));
+        $this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero));
+    }
 
-  /**
-   * @depends testFutureIsGreaterThanZero
-   */
-  public function testNowIsBetweenZeroAndFuture() {
-    $zero = Grpc\Timeval::zero();
-    $future = Grpc\Timeval::infFuture();
-    $now = Grpc\Timeval::now();
-    $this->assertLessThan(0, Grpc\Timeval::compare($zero, $now));
-    $this->assertLessThan(0, Grpc\Timeval::compare($now, $future));
-  }
+    /**
+     * @depends testFutureIsGreaterThanZero
+     */
+    public function testNowIsBetweenZeroAndFuture()
+    {
+        $zero = Grpc\Timeval::zero();
+        $future = Grpc\Timeval::infFuture();
+        $now = Grpc\Timeval::now();
+        $this->assertLessThan(0, Grpc\Timeval::compare($zero, $now));
+        $this->assertLessThan(0, Grpc\Timeval::compare($now, $future));
+    }
 
-  public function testNowAndAdd() {
-    $now = Grpc\Timeval::now();
-    $delta = new Grpc\Timeval(1000);
-    $deadline = $now->add($delta);
-    $this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now));
-  }
+    public function testNowAndAdd()
+    {
+        $now = Grpc\Timeval::now();
+        $delta = new Grpc\Timeval(1000);
+        $deadline = $now->add($delta);
+        $this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now));
+    }
 
-  public function testNowAndSubtract() {
-    $now = Grpc\Timeval::now();
-    $delta = new Grpc\Timeval(1000);
-    $deadline = $now->subtract($delta);
-    $this->assertLessThan(0, Grpc\Timeval::compare($deadline, $now));
-  }
+    public function testNowAndSubtract()
+    {
+        $now = Grpc\Timeval::now();
+        $delta = new Grpc\Timeval(1000);
+        $deadline = $now->subtract($delta);
+        $this->assertLessThan(0, Grpc\Timeval::compare($deadline, $now));
+    }
 
-  public function testAddAndSubtract() {
-    $now = Grpc\Timeval::now();
-    $delta = new Grpc\Timeval(1000);
-    $deadline = $now->add($delta);
-    $back_to_now = $deadline->subtract($delta);
-    $this->assertSame(0, Grpc\Timeval::compare($back_to_now, $now));
-  }
+    public function testAddAndSubtract()
+    {
+        $now = Grpc\Timeval::now();
+        $delta = new Grpc\Timeval(1000);
+        $deadline = $now->add($delta);
+        $back_to_now = $deadline->subtract($delta);
+        $this->assertSame(0, Grpc\Timeval::compare($back_to_now, $now));
+    }
 }
diff --git a/src/ruby/README.md b/src/ruby/README.md
index e6ffadcaa93df4c690a1076595852df4445741c5..76d9f7663a0f0efb36dfe2655567d59dddbca516 100644
--- a/src/ruby/README.md
+++ b/src/ruby/README.md
@@ -1,3 +1,4 @@
+[![Gem](https://img.shields.io/gem/v/grpc.svg)](https://rubygems.org/gems/grpc/)
 gRPC Ruby
 =========
 
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 115d8136b53f12f1fc551651ae78056c2dcd2d6a..20d14c797f99600c7696bf0e89780f8061aab38e 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -1851,6 +1851,11 @@
   endif
   % endif
   % endif
+  % for src in tgt.src:
+  % if not proto_re.match(src) and any(proto_re.match(src2) for src2 in tgt.src):
+  $(OBJDIR)/$(CONFIG)/${os.path.splitext(src)[0]}.o: ${' '.join(proto_to_cc(src2) for src2 in tgt.src if proto_re.match(src2))}
+  % endif
+  % endfor
   </%def>
 
   ifneq ($(OPENSSL_DEP),)
diff --git a/templates/gRPC.podspec.template b/templates/gRPC.podspec.template
index 3b96fe28857a56194d41c0bbc4841ab037fbe063..3885cb337b5ec815a450e98ddd9a8294e2324279 100644
--- a/templates/gRPC.podspec.template
+++ b/templates/gRPC.podspec.template
@@ -88,8 +88,12 @@
       ss.header_mappings_dir = '.'
       # This isn't officially supported in Cocoapods. We've asked for an alternative:
       # https://github.com/CocoaPods/CocoaPods/issues/4386
-      ss.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC" ' +
-                                               '"$(PODS_ROOT)/Headers/Private/gRPC/include"' }
+      ss.xcconfig = {
+        'USE_HEADERMAP' => 'NO',
+        'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+        'USER_HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC"',
+        'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC/include"'
+      }
 
       ss.requires_arc = false
       ss.libraries = 'z'
diff --git a/test/core/client_config/lb_policies_test.c b/test/core/client_config/lb_policies_test.c
index a9edf381575e23660d59516406472000828a2dfb..3eb6f11bf74a8a1cb1a3420ca5056234cf6c126c 100644
--- a/test/core/client_config/lb_policies_test.c
+++ b/test/core/client_config/lb_policies_test.c
@@ -211,29 +211,32 @@ static void teardown_servers(servers_fixture *f) {
   gpr_free(f);
 }
 
+typedef struct request_data {
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  char *details;
+  size_t details_capacity;
+  grpc_status_code status;
+  grpc_call_details *call_details;
+} request_data;
+
 /** Returns connection sequence (server indices), which must be freed */
 int *perform_request(servers_fixture *f, grpc_channel *client,
-                     const test_spec *spec) {
+                     request_data *rdata, const test_spec *spec) {
   grpc_call *c;
   int s_idx;
   int *s_valid;
   gpr_timespec deadline;
   grpc_op ops[6];
   grpc_op *op;
-  grpc_status_code status;
-  char *details;
-  size_t details_capacity;
   int was_cancelled;
-  grpc_call_details *call_details;
   size_t i, iter_num;
   grpc_event ev;
   int read_tag;
   int *connection_sequence;
-  grpc_metadata_array initial_metadata_recv;
-  grpc_metadata_array trailing_metadata_recv;
 
   s_valid = gpr_malloc(sizeof(int) * f->num_servers);
-  call_details = gpr_malloc(sizeof(grpc_call_details) * f->num_servers);
+  rdata->call_details = gpr_malloc(sizeof(grpc_call_details) * f->num_servers);
   connection_sequence = gpr_malloc(sizeof(int) * spec->num_iters);
 
   /* Send a trivial request. */
@@ -241,8 +244,8 @@ int *perform_request(servers_fixture *f, grpc_channel *client,
 
   for (iter_num = 0; iter_num < spec->num_iters; iter_num++) {
     cq_verifier *cqv = cq_verifier_create(f->cq);
-    details = NULL;
-    details_capacity = 0;
+    rdata->details = NULL;
+    rdata->details_capacity = 0;
     was_cancelled = 2;
 
     for (i = 0; i < f->num_servers; i++) {
@@ -255,11 +258,11 @@ int *perform_request(servers_fixture *f, grpc_channel *client,
     }
 
     connection_sequence[iter_num] = -1;
-    grpc_metadata_array_init(&initial_metadata_recv);
-    grpc_metadata_array_init(&trailing_metadata_recv);
+    grpc_metadata_array_init(&rdata->initial_metadata_recv);
+    grpc_metadata_array_init(&rdata->trailing_metadata_recv);
 
     for (i = 0; i < f->num_servers; i++) {
-      grpc_call_details_init(&call_details[i]);
+      grpc_call_details_init(&rdata->call_details[i]);
     }
     memset(s_valid, 0, f->num_servers * sizeof(int));
 
@@ -278,15 +281,15 @@ int *perform_request(servers_fixture *f, grpc_channel *client,
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_INITIAL_METADATA;
-    op->data.recv_initial_metadata = &initial_metadata_recv;
+    op->data.recv_initial_metadata = &rdata->initial_metadata_recv;
     op->flags = 0;
     op->reserved = NULL;
     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->data.recv_status_on_client.trailing_metadata = &rdata->trailing_metadata_recv;
+    op->data.recv_status_on_client.status = &rdata->status;
+    op->data.recv_status_on_client.status_details = &rdata->details;
+    op->data.recv_status_on_client.status_details_capacity = &rdata->details_capacity;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -299,7 +302,7 @@ int *perform_request(servers_fixture *f, grpc_channel *client,
       if (f->servers[i] != NULL) {
         GPR_ASSERT(GRPC_CALL_OK ==
                    grpc_server_request_call(f->servers[i], &f->server_calls[i],
-                                            &call_details[i],
+                                            &rdata->call_details[i],
                                             &f->request_metadata_recv[i], f->cq,
                                             f->cq, tag(1000 + (int)i)));
       }
@@ -348,11 +351,12 @@ int *perform_request(servers_fixture *f, grpc_channel *client,
       cq_expect_completion(cqv, tag(1), 1);
       cq_verify(cqv);
 
-      GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-      GPR_ASSERT(0 == strcmp(details, "xyz"));
-      GPR_ASSERT(0 == strcmp(call_details[s_idx].method, "/foo"));
-      GPR_ASSERT(0 == strcmp(call_details[s_idx].host, "foo.test.google.fr"));
+      GPR_ASSERT(rdata->status == GRPC_STATUS_UNIMPLEMENTED);
+      GPR_ASSERT(0 == strcmp(rdata->details, "xyz"));
+      GPR_ASSERT(0 == strcmp(rdata->call_details[s_idx].method, "/foo"));
+      GPR_ASSERT(0 == strcmp(rdata->call_details[s_idx].host, "foo.test.google.fr"));
       GPR_ASSERT(was_cancelled == 1);
+    } else {
     }
 
     for (i = 0; i < f->num_servers; i++) {
@@ -361,20 +365,20 @@ int *perform_request(servers_fixture *f, grpc_channel *client,
       }
       grpc_metadata_array_destroy(&f->request_metadata_recv[i]);
     }
-    grpc_metadata_array_destroy(&initial_metadata_recv);
-    grpc_metadata_array_destroy(&trailing_metadata_recv);
+    grpc_metadata_array_destroy(&rdata->initial_metadata_recv);
+    grpc_metadata_array_destroy(&rdata->trailing_metadata_recv);
 
     cq_verifier_destroy(cqv);
 
     grpc_call_destroy(c);
 
     for (i = 0; i < f->num_servers; i++) {
-      grpc_call_details_destroy(&call_details[i]);
+      grpc_call_details_destroy(&rdata->call_details[i]);
     }
-    gpr_free(details);
+    gpr_free(rdata->details);
   }
 
-  gpr_free(call_details);
+  gpr_free(rdata->call_details);
   gpr_free(s_valid);
 
   return connection_sequence;
@@ -436,6 +440,7 @@ void run_spec(const test_spec *spec) {
   char *client_hostport;
   char *servers_hostports_str;
   int *actual_connection_sequence;
+  request_data rdata;
   servers_fixture *f = setup_servers("127.0.0.1", spec->num_servers);
 
   /* Create client. */
@@ -448,7 +453,7 @@ void run_spec(const test_spec *spec) {
   gpr_log(GPR_INFO, "Testing '%s' with servers=%s client=%s", spec->description,
           servers_hostports_str, client_hostport);
 
-  actual_connection_sequence = perform_request(f, client, spec);
+  actual_connection_sequence = perform_request(f, client, &rdata, spec);
 
   spec->verifier(f, client, actual_connection_sequence, spec->num_iters);
 
diff --git a/test/core/network_benchmarks/low_level_ping_pong.c b/test/core/network_benchmarks/low_level_ping_pong.c
index 0ce4bd4b25013f4217f5550d7ae6774e6522f50b..7a2d894481b16d5e375de883a33ff33c8ba2d360 100644
--- a/test/core/network_benchmarks/low_level_ping_pong.c
+++ b/test/core/network_benchmarks/low_level_ping_pong.c
@@ -82,7 +82,7 @@ typedef struct thread_args {
 /* Basic call to read() */
 static int read_bytes(int fd, char *buf, size_t read_size, int spin) {
   size_t bytes_read = 0;
-  int err;
+  ssize_t err;
   do {
     err = read(fd, buf + bytes_read, read_size - bytes_read);
     if (err < 0) {
@@ -96,7 +96,7 @@ static int read_bytes(int fd, char *buf, size_t read_size, int spin) {
         return -1;
       }
     } else {
-      bytes_read += err;
+      bytes_read += (size_t)err;
     }
   } while (bytes_read < read_size);
   return 0;
@@ -115,6 +115,7 @@ static int poll_read_bytes(int fd, char *buf, size_t read_size, int spin) {
   struct pollfd pfd;
   size_t bytes_read = 0;
   int err;
+  ssize_t err2;
 
   pfd.fd = fd;
   pfd.events = POLLIN;
@@ -132,13 +133,13 @@ static int poll_read_bytes(int fd, char *buf, size_t read_size, int spin) {
     GPR_ASSERT(err == 1);
     GPR_ASSERT(pfd.revents == POLLIN);
     do {
-      err = read(fd, buf + bytes_read, read_size - bytes_read);
-    } while (err < 0 && errno == EINTR);
-    if (err < 0 && errno != EAGAIN) {
+      err2 = read(fd, buf + bytes_read, read_size - bytes_read);
+    } while (err2 < 0 && errno == EINTR);
+    if (err2 < 0 && errno != EAGAIN) {
       gpr_log(GPR_ERROR, "Read failed: %s", strerror(errno));
       return -1;
     }
-    bytes_read += err;
+    bytes_read += (size_t) err2;
   } while (bytes_read < read_size);
   return 0;
 }
@@ -157,6 +158,7 @@ static int epoll_read_bytes(struct thread_args *args, char *buf, int spin) {
   struct epoll_event ev;
   size_t bytes_read = 0;
   int err;
+  ssize_t err2;
   size_t read_size = args->msg_size;
 
   do {
@@ -172,10 +174,11 @@ static int epoll_read_bytes(struct thread_args *args, char *buf, int spin) {
     GPR_ASSERT(ev.data.fd == args->fds.read_fd);
     do {
       do {
-        err = read(args->fds.read_fd, buf + bytes_read, read_size - bytes_read);
-      } while (err < 0 && errno == EINTR);
+        err2 = read(args->fds.read_fd, buf + bytes_read,
+		    read_size - bytes_read);
+      } while (err2 < 0 && errno == EINTR);
       if (errno == EAGAIN) break;
-      bytes_read += err;
+      bytes_read += (size_t) err2;
       /* TODO(klempner): This should really be doing an extra call after we are
          done to ensure we see an EAGAIN */
     } while (bytes_read < read_size);
@@ -199,7 +202,7 @@ static int epoll_read_bytes_spin(struct thread_args *args, char *buf) {
  */
 static int blocking_write_bytes(struct thread_args *args, char *buf) {
   size_t bytes_written = 0;
-  int err;
+  ssize_t err;
   size_t write_size = args->msg_size;
   do {
     err = write(args->fds.write_fd, buf + bytes_written,
@@ -212,7 +215,7 @@ static int blocking_write_bytes(struct thread_args *args, char *buf) {
         return -1;
       }
     } else {
-      bytes_written += err;
+      bytes_written += (size_t)err;
     }
   } while (bytes_written < write_size);
   return 0;
@@ -297,7 +300,7 @@ static void print_histogram(gpr_histogram *histogram) {
 
 static double now(void) {
   gpr_timespec tv = gpr_now(GPR_CLOCK_REALTIME);
-  return 1e9 * tv.tv_sec + tv.tv_nsec;
+  return 1e9 * (double)tv.tv_sec + (double)tv.tv_nsec;
 }
 
 static void client_thread(thread_args *args) {
@@ -373,7 +376,7 @@ error:
   return -1;
 }
 
-static int connect_client(struct sockaddr *addr, int len) {
+static int connect_client(struct sockaddr *addr, socklen_t len) {
   int fd = socket(addr->sa_family, SOCK_STREAM, 0);
   int err;
   if (fd < 0) {
@@ -586,27 +589,27 @@ static int run_benchmark(char *socket_type, thread_args *client_args,
   return 0;
 }
 
-static int run_all_benchmarks(int msg_size) {
+static int run_all_benchmarks(size_t msg_size) {
   int error = 0;
   size_t i;
   for (i = 0; i < GPR_ARRAY_SIZE(test_strategies); ++i) {
-    test_strategy *test_strategy = &test_strategies[i];
+    test_strategy *strategy = &test_strategies[i];
     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));
       char *socket_type = socket_types[j];
 
-      client_args->read_bytes = test_strategy->read_strategy;
+      client_args->read_bytes = strategy->read_strategy;
       client_args->write_bytes = blocking_write_bytes;
-      client_args->setup = test_strategy->setup;
+      client_args->setup = strategy->setup;
       client_args->msg_size = msg_size;
-      client_args->strategy_name = test_strategy->name;
-      server_args->read_bytes = test_strategy->read_strategy;
+      client_args->strategy_name = strategy->name;
+      server_args->read_bytes = strategy->read_strategy;
       server_args->write_bytes = blocking_write_bytes;
-      server_args->setup = test_strategy->setup;
+      server_args->setup = strategy->setup;
       server_args->msg_size = msg_size;
-      server_args->strategy_name = test_strategy->name;
+      server_args->strategy_name = strategy->name;
       error = run_benchmark(socket_type, client_args, server_args);
       if (error < 0) {
         return error;
@@ -623,7 +626,7 @@ int main(int argc, char **argv) {
   char *read_strategy = NULL;
   char *socket_type = NULL;
   size_t i;
-  const test_strategy *test_strategy = NULL;
+  const test_strategy *strategy = NULL;
   int error = 0;
 
   gpr_cmdline *cmdline =
@@ -643,7 +646,7 @@ int main(int argc, char **argv) {
 
   if (read_strategy == NULL) {
     gpr_log(GPR_INFO, "No strategy specified, running all benchmarks");
-    return run_all_benchmarks(msg_size);
+    return run_all_benchmarks((size_t)msg_size);
   }
 
   if (socket_type == NULL) {
@@ -657,23 +660,23 @@ int main(int argc, char **argv) {
 
   for (i = 0; i < GPR_ARRAY_SIZE(test_strategies); ++i) {
     if (strcmp(test_strategies[i].name, read_strategy) == 0) {
-      test_strategy = &test_strategies[i];
+      strategy = &test_strategies[i];
     }
   }
-  if (test_strategy == NULL) {
+  if (strategy == NULL) {
     fprintf(stderr, "Invalid read strategy %s\n", read_strategy);
     return -1;
   }
 
-  client_args->read_bytes = test_strategy->read_strategy;
+  client_args->read_bytes = strategy->read_strategy;
   client_args->write_bytes = blocking_write_bytes;
-  client_args->setup = test_strategy->setup;
-  client_args->msg_size = msg_size;
+  client_args->setup = strategy->setup;
+  client_args->msg_size = (size_t)msg_size;
   client_args->strategy_name = read_strategy;
-  server_args->read_bytes = test_strategy->read_strategy;
+  server_args->read_bytes = strategy->read_strategy;
   server_args->write_bytes = blocking_write_bytes;
-  server_args->setup = test_strategy->setup;
-  server_args->msg_size = msg_size;
+  server_args->setup = strategy->setup;
+  server_args->msg_size = (size_t)msg_size;
   server_args->strategy_name = read_strategy;
 
   error = run_benchmark(socket_type, client_args, server_args);
diff --git a/test/core/support/cpu_test.c b/test/core/support/cpu_test.c
index 6559c1b57e24f440fb7d55e7e4a58126e281fa88..fa83878a1521a4dfac0e0798d04fecb9bd6121ba 100644
--- a/test/core/support/cpu_test.c
+++ b/test/core/support/cpu_test.c
@@ -81,9 +81,9 @@ static void worker_thread(void *arg) {
   gpr_uint32 cpu;
   int r = 12345678;
   int i, j;
-  for (i = 0; i < 1000; i++) {
+  for (i = 0; i < 1000 / GRPC_TEST_SLOWDOWN_FACTOR; i++) {
     /* run for a bit - just calculate something random. */
-    for (j = 0; j < 1000000; j++) {
+    for (j = 0; j < 1000000 / GRPC_TEST_SLOWDOWN_FACTOR; j++) {
       r = (r * 17) & ((r - i) | (r * i));
     }
     cpu = gpr_cpu_current_cpu();
diff --git a/test/core/surface/byte_buffer_reader_test.c b/test/core/surface/byte_buffer_reader_test.c
index 560e0ac7b23a476da832d5915a2fc5ec61ddd204..7f9cd6b62b02cb1302618202f1d01a963e9c6b66 100644
--- a/test/core/surface/byte_buffer_reader_test.c
+++ b/test/core/surface/byte_buffer_reader_test.c
@@ -184,6 +184,39 @@ static void test_byte_buffer_from_reader(void) {
   grpc_byte_buffer_destroy(buffer_from_reader);
 }
 
+static void test_readall(void) {
+  char* lotsa_as[512];
+  char* lotsa_bs[1024];
+  gpr_slice slices[2];
+  grpc_byte_buffer *buffer;
+  grpc_byte_buffer_reader reader;
+  gpr_slice slice_out;
+
+  LOG_TEST("test_readall");
+
+  memset(lotsa_as, 'a', 512);
+  memset(lotsa_bs, 'b', 1024);
+  /* use slices large enough to overflow inlining */
+  slices[0] = gpr_slice_malloc(512);
+  memcpy(GPR_SLICE_START_PTR(slices[0]), lotsa_as, 512);
+  slices[1] = gpr_slice_malloc(1024);
+  memcpy(GPR_SLICE_START_PTR(slices[1]), lotsa_bs, 1024);
+
+  buffer = grpc_raw_byte_buffer_create(slices, 2);
+  gpr_slice_unref(slices[0]);
+  gpr_slice_unref(slices[1]);
+
+  grpc_byte_buffer_reader_init(&reader, buffer);
+  slice_out = grpc_byte_buffer_reader_readall(&reader);
+
+  GPR_ASSERT(GPR_SLICE_LENGTH(slice_out) == 512 + 1024);
+  GPR_ASSERT(memcmp(GPR_SLICE_START_PTR(slice_out), lotsa_as, 512) == 0);
+  GPR_ASSERT(memcmp(&(GPR_SLICE_START_PTR(slice_out)[512]), lotsa_bs, 1024) ==
+             0);
+  gpr_slice_unref(slice_out);
+  grpc_byte_buffer_destroy(buffer);
+}
+
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   test_read_one_slice();
@@ -192,6 +225,6 @@ int main(int argc, char **argv) {
   test_read_gzip_compressed_slice();
   test_read_deflate_compressed_slice();
   test_byte_buffer_from_reader();
-
+  test_readall();
   return 0;
 }
diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc
index 9c35fede8f68b564b9e2403527a8cb5165651d06..80057d893e10071e9bccbeeaf722db1c2ea35daf 100644
--- a/test/cpp/end2end/mock_test.cc
+++ b/test/cpp/end2end/mock_test.cc
@@ -62,7 +62,7 @@ template <class W, class R>
 class MockClientReaderWriter GRPC_FINAL
     : public ClientReaderWriterInterface<W, R> {
  public:
-  void WaitForInitialMetadata() {}
+  void WaitForInitialMetadata() GRPC_OVERRIDE {}
   bool Read(R* msg) GRPC_OVERRIDE { return true; }
   bool Write(const W& msg) GRPC_OVERRIDE { return true; }
   bool WritesDone() GRPC_OVERRIDE { return true; }
@@ -73,7 +73,7 @@ class MockClientReaderWriter<EchoRequest, EchoResponse> GRPC_FINAL
     : public ClientReaderWriterInterface<EchoRequest, EchoResponse> {
  public:
   MockClientReaderWriter() : writes_done_(false) {}
-  void WaitForInitialMetadata() {}
+  void WaitForInitialMetadata() GRPC_OVERRIDE {}
   bool Read(EchoResponse* msg) GRPC_OVERRIDE {
     if (writes_done_) return false;
     msg->set_message(last_message_);
diff --git a/test/cpp/interop/stress_test.cc b/test/cpp/interop/stress_test.cc
index 49f3fbcc14c57264e2ebd400e142156a0f4d68dc..5d1419728e4ca7d46ddc809a7d828a12e1f2860d 100644
--- a/test/cpp/interop/stress_test.cc
+++ b/test/cpp/interop/stress_test.cc
@@ -186,14 +186,7 @@ int main(int argc, char** argv) {
   // Parse test cases and weights
   if (FLAGS_test_cases.length() == 0) {
     gpr_log(GPR_INFO, "Not running tests. The 'test_cases' string is empty");
-
-    // TODO(sreek): stress_tests is currently being run by run_tests.py in
-    // jenkins. There does not seem to be a straightforward way to skip this.
-    // So, for now, return 0 (i.e success) to keep jenkins build happy. Moreover
-    // we don't want to run stress_tests (for now) in jenkins anyway.
-    // Once we figure out a good way to skip this tests in run_tests.py, I will
-    // change this to 'return 1'
-    return 0;
+    return 1;
   }
 
   vector<pair<TestCaseType, int>> tests;
diff --git a/test/cpp/qps/async_streaming_ping_pong_test.cc b/test/cpp/qps/async_streaming_ping_pong_test.cc
index 411df4d32af684019a4d334bc257c8c6690b399a..9fef93a70fd8e136589d9f888c8fa7433de65c11 100644
--- a/test/cpp/qps/async_streaming_ping_pong_test.cc
+++ b/test/cpp/qps/async_streaming_ping_pong_test.cc
@@ -35,8 +35,6 @@
 
 #include <grpc/support/log.h>
 
-#include <signal.h>
-
 #include "test/cpp/qps/driver.h"
 #include "test/cpp/qps/report.h"
 #include "test/cpp/util/benchmark_config.h"
@@ -52,17 +50,15 @@ static void RunAsyncStreamingPingPong() {
 
   ClientConfig client_config;
   client_config.set_client_type(ASYNC_CLIENT);
-  client_config.set_enable_ssl(false);
   client_config.set_outstanding_rpcs_per_channel(1);
   client_config.set_client_channels(1);
-  client_config.set_payload_size(1);
   client_config.set_async_client_threads(1);
   client_config.set_rpc_type(STREAMING);
+  client_config.mutable_load_params()->mutable_closed_loop();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
-  server_config.set_enable_ssl(false);
-  server_config.set_threads(1);
+  server_config.set_async_server_threads(1);
 
   const auto result =
       RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
@@ -77,7 +73,6 @@ static void RunAsyncStreamingPingPong() {
 int main(int argc, char** argv) {
   grpc::testing::InitBenchmark(&argc, &argv, true);
 
-  signal(SIGPIPE, SIG_IGN);
   grpc::testing::RunAsyncStreamingPingPong();
   return 0;
 }
diff --git a/test/cpp/qps/async_unary_ping_pong_test.cc b/test/cpp/qps/async_unary_ping_pong_test.cc
index eda31b57442a0b9f614c673a359cca03875bfcea..b4ab0e5d5958c5396a6cf56a67684f537b65eea8 100644
--- a/test/cpp/qps/async_unary_ping_pong_test.cc
+++ b/test/cpp/qps/async_unary_ping_pong_test.cc
@@ -35,8 +35,6 @@
 
 #include <grpc/support/log.h>
 
-#include <signal.h>
-
 #include "test/cpp/qps/driver.h"
 #include "test/cpp/qps/report.h"
 #include "test/cpp/util/benchmark_config.h"
@@ -52,17 +50,15 @@ static void RunAsyncUnaryPingPong() {
 
   ClientConfig client_config;
   client_config.set_client_type(ASYNC_CLIENT);
-  client_config.set_enable_ssl(false);
   client_config.set_outstanding_rpcs_per_channel(1);
   client_config.set_client_channels(1);
-  client_config.set_payload_size(1);
   client_config.set_async_client_threads(1);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed_loop();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
-  server_config.set_enable_ssl(false);
-  server_config.set_threads(1);
+  server_config.set_async_server_threads(1);
 
   const auto result =
       RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
@@ -75,7 +71,6 @@ static void RunAsyncUnaryPingPong() {
 
 int main(int argc, char** argv) {
   grpc::testing::InitBenchmark(&argc, &argv, true);
-  signal(SIGPIPE, SIG_IGN);
 
   grpc::testing::RunAsyncUnaryPingPong();
   return 0;
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index cd8b34f65b3216238fc4714f3883f00b7b2497e6..f4400692feae915b58b09f1223614e0d6799582d 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -40,8 +40,9 @@
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/interarrival.h"
 #include "test/cpp/qps/timer.h"
-#include "test/proto/qpstest.grpc.pb.h"
 #include "test/cpp/util/create_test_channel.h"
+#include "test/proto/benchmarks/payloads.grpc.pb.h"
+#include "test/proto/benchmarks/services.grpc.pb.h"
 
 namespace grpc {
 
@@ -75,27 +76,54 @@ class Client {
       channels_[i].init(config.server_targets(i % config.server_targets_size()),
                         config);
     }
-    request_.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
-    request_.set_response_size(config.payload_size());
+    if (config.payload_config().has_bytebuf_params()) {
+      GPR_ASSERT(false);  // not yet implemented
+    } else if (config.payload_config().has_simple_params()) {
+      request_.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
+      request_.set_response_size(
+          config.payload_config().simple_params().resp_size());
+      request_.mutable_payload()->set_type(
+          grpc::testing::PayloadType::COMPRESSABLE);
+      int size = config.payload_config().simple_params().req_size();
+      std::unique_ptr<char[]> body(new char[size]);
+      request_.mutable_payload()->set_body(body.get(), size);
+    } else if (config.payload_config().has_complex_params()) {
+      GPR_ASSERT(false);  // not yet implemented
+    } else {
+      // default should be simple proto without payloads
+      request_.set_response_type(grpc::testing::PayloadType::COMPRESSABLE);
+      request_.set_response_size(0);
+      request_.mutable_payload()->set_type(
+          grpc::testing::PayloadType::COMPRESSABLE);
+    }
   }
   virtual ~Client() {}
 
-  ClientStats Mark() {
+  ClientStats Mark(bool reset) {
     Histogram latencies;
+    Timer::Result timer_result;
+
     // avoid std::vector for old compilers that expect a copy constructor
-    Histogram* to_merge = new Histogram[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]);
+    if (reset) {
+      Histogram* to_merge = new Histogram[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]);
+      }
+      delete[] to_merge;
+      timer_result = timer->Mark();
+    } else {
+      // merge snapshots of each thread histogram
+      for (size_t i = 0; i < threads_.size(); i++) {
+        threads_[i]->MergeStatsInto(&latencies);
+      }
+      timer_result = timer_->Mark();
     }
-    delete[] to_merge;
-
-    auto timer_result = timer->Mark();
 
     ClientStats stats;
     latencies.FillProto(stats.mutable_latencies());
@@ -122,15 +150,18 @@ class Client {
       // We have to use a 2-phase init like this with a default
       // constructor followed by an initializer function to make
       // old compilers happy with using this in std::vector
-      channel_ = CreateTestChannel(target, config.enable_ssl());
-      stub_ = TestService::NewStub(channel_);
+      channel_ = CreateTestChannel(
+          target, config.security_params().server_host_override(),
+          config.has_security_params(),
+          !config.security_params().use_test_ca());
+      stub_ = BenchmarkService::NewStub(channel_);
     }
     Channel* get_channel() { return channel_.get(); }
-    TestService::Stub* get_stub() { return stub_.get(); }
+    BenchmarkService::Stub* get_stub() { return stub_.get(); }
 
    private:
     std::shared_ptr<Channel> channel_;
-    std::unique_ptr<TestService::Stub> stub_;
+    std::unique_ptr<BenchmarkService::Stub> stub_;
   };
   std::vector<ClientChannelInfo> channels_;
 
@@ -146,37 +177,41 @@ class Client {
 
   void SetupLoadTest(const ClientConfig& config, size_t num_threads) {
     // Set up the load distribution based on the number of threads
-    if (config.load_type() == CLOSED_LOOP) {
+    const auto& load = config.load_params();
+
+    std::unique_ptr<RandomDist> random_dist;
+    switch (load.load_case()) {
+    case LoadParams::kClosedLoop:
+      // Closed-loop doesn't use random dist at all
+      break;
+    case LoadParams::kPoisson:
+      random_dist.reset(
+          new ExpDist(load.poisson().offered_load() / num_threads));
+      break;
+    case LoadParams::kUniform:
+      random_dist.reset(
+          new UniformDist(load.uniform().interarrival_lo() * num_threads,
+                          load.uniform().interarrival_hi() * num_threads));
+      break;
+    case LoadParams::kDeterm:
+      random_dist.reset(
+          new DetDist(num_threads / load.determ().offered_load()));
+      break;
+    case LoadParams::kPareto:
+      random_dist.reset(
+          new ParetoDist(load.pareto().interarrival_base() * num_threads,
+                         load.pareto().alpha()));
+      break;
+    default:
+      GPR_ASSERT(false);
+    }
+
+    // Set closed_loop_ based on whether or not random_dist is set
+    if (!random_dist) {
       closed_loop_ = true;
     } else {
       closed_loop_ = false;
-
-      std::unique_ptr<RandomDist> random_dist;
-      const auto& load = config.load_params();
-      switch (config.load_type()) {
-        case POISSON:
-          random_dist.reset(
-              new ExpDist(load.poisson().offered_load() / num_threads));
-          break;
-        case UNIFORM:
-          random_dist.reset(
-              new UniformDist(load.uniform().interarrival_lo() * num_threads,
-                              load.uniform().interarrival_hi() * num_threads));
-          break;
-        case DETERMINISTIC:
-          random_dist.reset(
-              new DetDist(num_threads / load.determ().offered_load()));
-          break;
-        case PARETO:
-          random_dist.reset(
-              new ParetoDist(load.pareto().interarrival_base() * num_threads,
-                             load.pareto().alpha()));
-          break;
-        default:
-          GPR_ASSERT(false);
-          break;
-      }
-
+      // set up interarrival timer according to random dist
       interarrival_timer_.init(*random_dist, num_threads);
       for (size_t i = 0; i < num_threads; i++) {
         next_time_.push_back(
@@ -204,7 +239,7 @@ class Client {
    public:
     Thread(Client* client, size_t idx)
         : done_(false),
-          new_(nullptr),
+          new_stats_(nullptr),
           client_(client),
           idx_(idx),
           impl_(&Thread::ThreadFunc, this) {}
@@ -219,16 +254,21 @@ class Client {
 
     void BeginSwap(Histogram* n) {
       std::lock_guard<std::mutex> g(mu_);
-      new_ = n;
+      new_stats_ = n;
     }
 
     void EndSwap() {
       std::unique_lock<std::mutex> g(mu_);
-      while (new_ != nullptr) {
+      while (new_stats_ != nullptr) {
         cv_.wait(g);
       };
     }
 
+    void MergeStatsInto(Histogram* hist) {
+      std::unique_lock<std::mutex> g(mu_);
+      hist->Merge(histogram_);
+    }
+
    private:
     Thread(const Thread&);
     Thread& operator=(const Thread&);
@@ -246,21 +286,21 @@ class Client {
         if (done_) {
           return;
         }
-        // check if we're marking, swap out the histogram if so
-        if (new_) {
-          new_->Swap(&histogram_);
-          new_ = nullptr;
+        // check if we're resetting stats, swap out the histogram if so
+        if (new_stats_) {
+          new_stats_->Swap(&histogram_);
+          new_stats_ = nullptr;
           cv_.notify_one();
         }
       }
     }
 
-    TestService::Stub* stub_;
+    BenchmarkService::Stub* stub_;
     ClientConfig config_;
     std::mutex mu_;
     std::condition_variable cv_;
     bool done_;
-    Histogram* new_;
+    Histogram* new_stats_;
     Histogram histogram_;
     Client* client_;
     size_t idx_;
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 9ed42b7db633a43ae0f84ce45f261c9a8cf52d97..9594179822ded7a74ca1542e9c671ea45dbdcfaf 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -48,10 +48,10 @@
 #include <gflags/gflags.h>
 #include <grpc++/client_context.h>
 
-#include "test/proto/qpstest.grpc.pb.h"
 #include "test/cpp/qps/timer.h"
 #include "test/cpp/qps/client.h"
 #include "test/cpp/util/create_test_channel.h"
+#include "test/proto/benchmarks/services.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
@@ -88,10 +88,10 @@ template <class RequestType, class ResponseType>
 class ClientRpcContextUnaryImpl : public ClientRpcContext {
  public:
   ClientRpcContextUnaryImpl(
-      int channel_id, TestService::Stub* stub, const RequestType& req,
+      int channel_id, BenchmarkService::Stub* stub, const RequestType& req,
       std::function<
           std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
-              TestService::Stub*, grpc::ClientContext*, const RequestType&,
+              BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
               CompletionQueue*)> start_req,
       std::function<void(grpc::Status, ResponseType*)> on_done)
       : ClientRpcContext(channel_id),
@@ -131,13 +131,13 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
     return true;  // we're done, this'll be ignored
   }
   grpc::ClientContext context_;
-  TestService::Stub* stub_;
+  BenchmarkService::Stub* stub_;
   RequestType req_;
   ResponseType response_;
   bool (ClientRpcContextUnaryImpl::*next_state_)(bool);
   std::function<void(grpc::Status, ResponseType*)> callback_;
   std::function<std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
-      TestService::Stub*, grpc::ClientContext*, const RequestType&,
+      BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
       CompletionQueue*)> start_req_;
   grpc::Status status_;
   double start_;
@@ -151,7 +151,7 @@ class AsyncClient : public Client {
  public:
   explicit AsyncClient(
       const ClientConfig& config,
-      std::function<ClientRpcContext*(int, TestService::Stub*,
+      std::function<ClientRpcContext*(int, BenchmarkService::Stub*,
                                       const SimpleRequest&)> setup_ctx)
       : Client(config),
         channel_lock_(new std::mutex[config.client_channels()]),
@@ -354,11 +354,12 @@ class AsyncUnaryClient GRPC_FINAL : public AsyncClient {
  private:
   static void CheckDone(grpc::Status s, SimpleResponse* response) {}
   static std::unique_ptr<grpc::ClientAsyncResponseReader<SimpleResponse>>
-  StartReq(TestService::Stub* stub, grpc::ClientContext* ctx,
+  StartReq(BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
            const SimpleRequest& request, CompletionQueue* cq) {
     return stub->AsyncUnaryCall(ctx, request, cq);
   };
-  static ClientRpcContext* SetupCtx(int channel_id, TestService::Stub* stub,
+  static ClientRpcContext* SetupCtx(int channel_id,
+                                    BenchmarkService::Stub* stub,
                                     const SimpleRequest& req) {
     return new ClientRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(
         channel_id, stub, req, AsyncUnaryClient::StartReq,
@@ -370,10 +371,11 @@ template <class RequestType, class ResponseType>
 class ClientRpcContextStreamingImpl : public ClientRpcContext {
  public:
   ClientRpcContextStreamingImpl(
-      int channel_id, TestService::Stub* stub, const RequestType& req,
-      std::function<std::unique_ptr<grpc::ClientAsyncReaderWriter<
-          RequestType, ResponseType>>(TestService::Stub*, grpc::ClientContext*,
-                                      CompletionQueue*, void*)> start_req,
+      int channel_id, BenchmarkService::Stub* stub, const RequestType& req,
+      std::function<std::unique_ptr<
+          grpc::ClientAsyncReaderWriter<RequestType, ResponseType>>(
+          BenchmarkService::Stub*, grpc::ClientContext*, CompletionQueue*,
+          void*)> start_req,
       std::function<void(grpc::Status, ResponseType*)> on_done)
       : ClientRpcContext(channel_id),
         context_(),
@@ -420,15 +422,15 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
     return StartWrite(ok);
   }
   grpc::ClientContext context_;
-  TestService::Stub* stub_;
+  BenchmarkService::Stub* stub_;
   RequestType req_;
   ResponseType response_;
   bool (ClientRpcContextStreamingImpl::*next_state_)(bool, Histogram*);
   std::function<void(grpc::Status, ResponseType*)> callback_;
   std::function<
       std::unique_ptr<grpc::ClientAsyncReaderWriter<RequestType, ResponseType>>(
-          TestService::Stub*, grpc::ClientContext*, CompletionQueue*, void*)>
-      start_req_;
+          BenchmarkService::Stub*, grpc::ClientContext*, CompletionQueue*,
+          void*)> start_req_;
   grpc::Status status_;
   double start_;
   std::unique_ptr<grpc::ClientAsyncReaderWriter<RequestType, ResponseType>>
@@ -439,8 +441,8 @@ class AsyncStreamingClient GRPC_FINAL : public AsyncClient {
  public:
   explicit AsyncStreamingClient(const ClientConfig& config)
       : AsyncClient(config, SetupCtx) {
-    // async streaming currently only supported closed loop
-    GPR_ASSERT(config.load_type() == CLOSED_LOOP);
+    // async streaming currently only supports closed loop
+    GPR_ASSERT(closed_loop_);
 
     StartThreads(config.async_client_threads());
   }
@@ -451,12 +453,13 @@ class AsyncStreamingClient GRPC_FINAL : public AsyncClient {
   static void CheckDone(grpc::Status s, SimpleResponse* response) {}
   static std::unique_ptr<
       grpc::ClientAsyncReaderWriter<SimpleRequest, SimpleResponse>>
-  StartReq(TestService::Stub* stub, grpc::ClientContext* ctx,
+  StartReq(BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
            CompletionQueue* cq, void* tag) {
     auto stream = stub->AsyncStreamingCall(ctx, cq, tag);
     return stream;
   };
-  static ClientRpcContext* SetupCtx(int channel_id, TestService::Stub* stub,
+  static ClientRpcContext* SetupCtx(int channel_id,
+                                    BenchmarkService::Stub* stub,
                                     const SimpleRequest& req) {
     return new ClientRpcContextStreamingImpl<SimpleRequest, SimpleResponse>(
         channel_id, stub, req, AsyncStreamingClient::StartReq,
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index ed4134c74363a6e7afc3b351f3c3d7e3d3382077..10d680860a930ab477f38251a7bc05c5480170cd 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -54,10 +54,10 @@
 
 #include "test/cpp/util/create_test_channel.h"
 #include "test/cpp/qps/client.h"
-#include "test/proto/qpstest.grpc.pb.h"
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/interarrival.h"
 #include "test/cpp/qps/timer.h"
+#include "test/proto/benchmarks/services.grpc.pb.h"
 
 #include "src/core/profiling/timers.h"
 
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index dd5c4f4f73fe5083d0bc0c549213107b5930c1aa..2c6247deea16e28b6e88d49b05eef929ea4b1926 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -48,6 +48,7 @@
 #include "test/cpp/qps/driver.h"
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/qps_worker.h"
+#include "test/proto/benchmarks/services.grpc.pb.h"
 
 using std::list;
 using std::thread;
@@ -91,12 +92,12 @@ static ClientContext* AllocContext(list<ClientContext>* contexts, T deadline) {
 }
 
 struct ServerData {
-  unique_ptr<Worker::Stub> stub;
+  unique_ptr<WorkerService::Stub> stub;
   unique_ptr<ClientReaderWriter<ServerArgs, ServerStatus>> stream;
 };
 
 struct ClientData {
-  unique_ptr<Worker::Stub> stub;
+  unique_ptr<WorkerService::Stub> stub;
   unique_ptr<ClientReaderWriter<ClientArgs, ClientStatus>> stream;
 };
 }  // namespace runsc
@@ -131,8 +132,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
     }
 
     int driver_port = grpc_pick_unused_port_or_die();
-    int benchmark_port = grpc_pick_unused_port_or_die();
-    local_workers.emplace_back(new QpsWorker(driver_port, benchmark_port));
+    local_workers.emplace_back(new QpsWorker(driver_port));
     char addr[256];
     sprintf(addr, "localhost:%d", driver_port);
     if (spawn_local_worker_count < 0) {
@@ -161,11 +161,10 @@ std::unique_ptr<ScenarioResult> RunScenario(
   // where class contained in std::vector must have a copy constructor
   auto* servers = new ServerData[num_servers];
   for (size_t i = 0; i < num_servers; i++) {
-    servers[i].stub =
-        Worker::NewStub(CreateChannel(workers[i], InsecureCredentials()));
+    servers[i].stub = WorkerService::NewStub(
+        CreateChannel(workers[i], InsecureCredentials()));
     ServerArgs args;
     result_server_config = server_config;
-    result_server_config.set_host(workers[i]);
     *args.mutable_setup() = server_config;
     servers[i].stream =
         servers[i].stub->RunServer(runsc::AllocContext(&contexts, deadline));
@@ -189,14 +188,13 @@ std::unique_ptr<ScenarioResult> RunScenario(
   // where class contained in std::vector must have a copy constructor
   auto* clients = new ClientData[num_clients];
   for (size_t i = 0; i < num_clients; i++) {
-    clients[i].stub = Worker::NewStub(
+    clients[i].stub = WorkerService::NewStub(
         CreateChannel(workers[i + num_servers], InsecureCredentials()));
     ClientArgs args;
     result_client_config = client_config;
-    result_client_config.set_host(workers[i + num_servers]);
     *args.mutable_setup() = client_config;
     clients[i].stream =
-        clients[i].stub->RunTest(runsc::AllocContext(&contexts, deadline));
+        clients[i].stub->RunClient(runsc::AllocContext(&contexts, deadline));
     GPR_ASSERT(clients[i].stream->Write(args));
     ClientStatus init_status;
     GPR_ASSERT(clients[i].stream->Read(&init_status));
@@ -211,9 +209,9 @@ std::unique_ptr<ScenarioResult> RunScenario(
   // Start a run
   gpr_log(GPR_INFO, "Starting");
   ServerArgs server_mark;
-  server_mark.mutable_mark();
+  server_mark.mutable_mark()->set_reset(true);
   ClientArgs client_mark;
-  client_mark.mutable_mark();
+  client_mark.mutable_mark()->set_reset(true);
   for (auto server = &servers[0]; server != &servers[num_servers]; server++) {
     GPR_ASSERT(server->stream->Write(server_mark));
   }
@@ -251,14 +249,15 @@ std::unique_ptr<ScenarioResult> RunScenario(
     GPR_ASSERT(server->stream->Read(&server_status));
     const auto& stats = server_status.stats();
     result->server_resources.emplace_back(
-        stats.time_elapsed(), stats.time_user(), stats.time_system());
+        stats.time_elapsed(), stats.time_user(), stats.time_system(),
+        server_status.cores());
   }
   for (auto client = &clients[0]; client != &clients[num_clients]; client++) {
     GPR_ASSERT(client->stream->Read(&client_status));
     const auto& stats = client_status.stats();
     result->latencies.MergeProto(stats.latencies());
     result->client_resources.emplace_back(
-        stats.time_elapsed(), stats.time_user(), stats.time_system());
+        stats.time_elapsed(), stats.time_user(), stats.time_system(), -1);
   }
 
   for (auto client = &clients[0]; client != &clients[num_clients]; client++) {
diff --git a/test/cpp/qps/driver.h b/test/cpp/qps/driver.h
index 6116aa656a9524c3f8cad73f069d31e5b60d925b..50bf17ceab2ebf902e195f3b10d9735232d724c5 100644
--- a/test/cpp/qps/driver.h
+++ b/test/cpp/qps/driver.h
@@ -37,22 +37,24 @@
 #include <memory>
 
 #include "test/cpp/qps/histogram.h"
-#include "test/proto/qpstest.grpc.pb.h"
+#include "test/proto/benchmarks/control.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
 class ResourceUsage {
  public:
-  ResourceUsage(double w, double u, double s)
-      : wall_time_(w), user_time_(u), system_time_(s) {}
+  ResourceUsage(double w, double u, double s, int c)
+      : wall_time_(w), user_time_(u), system_time_(s), cores_(c) {}
   double wall_time() const { return wall_time_; }
   double user_time() const { return user_time_; }
   double system_time() const { return system_time_; }
+  int cores() const { return cores_; }
 
  private:
   double wall_time_;
   double user_time_;
   double system_time_;
+  int cores_;
 };
 
 struct ScenarioResult {
diff --git a/test/cpp/qps/histogram.h b/test/cpp/qps/histogram.h
index 1151cca87c5b35d93f44bee6dc389c07821da3d3..35527d2a2cb2a1b08400d8190c98fbc6dcf2a83d 100644
--- a/test/cpp/qps/histogram.h
+++ b/test/cpp/qps/histogram.h
@@ -35,7 +35,7 @@
 #define TEST_QPS_HISTOGRAM_H
 
 #include <grpc/support/histogram.h>
-#include "test/proto/qpstest.grpc.pb.h"
+#include "test/proto/benchmarks/stats.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
@@ -48,7 +48,7 @@ class Histogram {
   }
   Histogram(Histogram&& other) : impl_(other.impl_) { other.impl_ = nullptr; }
 
-  void Merge(Histogram* h) { gpr_histogram_merge(impl_, h->impl_); }
+  void Merge(const Histogram& h) { gpr_histogram_merge(impl_, h.impl_); }
   void Add(double value) { gpr_histogram_add(impl_, value); }
   double Percentile(double pctile) const {
     return gpr_histogram_percentile(impl_, pctile);
diff --git a/test/cpp/qps/perf_db.proto b/test/cpp/qps/perf_db.proto
index 7ae5cfe86e2d1bd4eb1aecec489e08a198cd549b..8a691ddded4952d55f73af347649ce6fdf4822c2 100644
--- a/test/cpp/qps/perf_db.proto
+++ b/test/cpp/qps/perf_db.proto
@@ -29,7 +29,7 @@
 
 syntax = "proto3";
 
-import "test/proto/qpstest.proto";
+import "test/proto/benchmarks/control.proto";
 
 package grpc.testing;
 
diff --git a/test/cpp/qps/qps-sweep.sh b/test/cpp/qps/qps-sweep.sh
index cb93201933595cb27653f8ec67bbcc99fcc0d5d2..36ea974812a56fd5a3038fa8c71da251574946f0 100755
--- a/test/cpp/qps/qps-sweep.sh
+++ b/test/cpp/qps/qps-sweep.sh
@@ -37,17 +37,21 @@ fi
 
 bins=`find . .. ../.. ../../.. -name bins | head -1`
 
-for channels in 1 2 4 8
+for secure in true false
 do
-  for client in SYNCHRONOUS_CLIENT ASYNC_CLIENT
+  for channels in 1 2 4 8
   do
-    for server in SYNCHRONOUS_SERVER ASYNC_SERVER
+    for client in SYNC_CLIENT ASYNC_CLIENT
     do
-      for rpc in UNARY STREAMING
+      for server in SYNC_SERVER ASYNC_SERVER
       do
-        echo "Test $rpc $client $server , $channels channels"
-        "$bins"/opt/qps_driver --rpc_type=$rpc \
-          --client_type=$client --server_type=$server
+        for rpc in UNARY STREAMING
+        do
+          echo "Test $rpc $client $server, $channels channels, secure=$secure"
+          "$bins"/opt/qps_driver --rpc_type=$rpc \
+              --client_type=$client --server_type=$server \
+	      --secure_test=$secure
+	done
       done
     done
   done
diff --git a/test/cpp/qps/qps_driver.cc b/test/cpp/qps/qps_driver.cc
index b1463be8f620b14a9e119db3aca90d9701df369d..4c93a042cf5b1ff0e2b609f2d6f6cf359fe1ab9b 100644
--- a/test/cpp/qps/qps_driver.cc
+++ b/test/cpp/qps/qps_driver.cc
@@ -33,7 +33,6 @@
 
 #include <memory>
 #include <set>
-#include <signal.h>
 
 #include <gflags/gflags.h>
 #include <grpc/support/log.h>
@@ -50,31 +49,39 @@ DEFINE_int32(benchmark_seconds, 30, "Benchmark time (in seconds)");
 DEFINE_int32(local_workers, 0, "Number of local workers to start");
 
 // Common config
-DEFINE_bool(enable_ssl, false, "Use SSL");
 DEFINE_string(rpc_type, "UNARY", "Type of RPC: UNARY or STREAMING");
 
 // Server config
-DEFINE_int32(server_threads, 1, "Number of server threads");
-DEFINE_string(server_type, "SYNCHRONOUS_SERVER", "Server type");
+DEFINE_int32(async_server_threads, 1, "Number of threads for async servers");
+DEFINE_string(server_type, "SYNC_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(simple_req_size, -1, "Simple proto request payload size");
+DEFINE_int32(simple_resp_size, -1, "Simple proto response payload size");
+
+DEFINE_string(client_type, "SYNC_CLIENT", "Client type");
 DEFINE_int32(async_client_threads, 1, "Async client threads");
-DEFINE_string(load_type, "CLOSED_LOOP", "Load type");
-DEFINE_double(load_param_1, 0.0, "Load parameter 1");
-DEFINE_double(load_param_2, 0.0, "Load parameter 2");
+
+DEFINE_double(poisson_load, -1.0, "Poisson offered load (qps)");
+DEFINE_double(uniform_lo, -1.0, "Uniform low interarrival time (us)");
+DEFINE_double(uniform_hi, -1.0, "Uniform high interarrival time (us)");
+DEFINE_double(determ_load, -1.0, "Deterministic offered load (qps)");
+DEFINE_double(pareto_base, -1.0, "Pareto base interarrival time (us)");
+DEFINE_double(pareto_alpha, -1.0, "Pareto alpha value");
+
+DEFINE_bool(secure_test, false, "Run a secure test");
 
 using grpc::testing::ClientConfig;
 using grpc::testing::ServerConfig;
 using grpc::testing::ClientType;
 using grpc::testing::ServerType;
-using grpc::testing::LoadType;
 using grpc::testing::RpcType;
 using grpc::testing::ResourceUsage;
+using grpc::testing::SecurityParams;
 
 namespace grpc {
 namespace testing {
@@ -85,72 +92,63 @@ static void QpsDriver() {
 
   ClientType client_type;
   ServerType server_type;
-  LoadType load_type;
   GPR_ASSERT(ClientType_Parse(FLAGS_client_type, &client_type));
   GPR_ASSERT(ServerType_Parse(FLAGS_server_type, &server_type));
-  GPR_ASSERT(LoadType_Parse(FLAGS_load_type, &load_type));
 
   ClientConfig client_config;
   client_config.set_client_type(client_type);
-  client_config.set_load_type(load_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);
+
+  // Decide which type to use based on the response type
+  if (FLAGS_simple_resp_size >= 0) {
+    auto params =
+        client_config.mutable_payload_config()->mutable_simple_params();
+    params->set_resp_size(FLAGS_simple_resp_size);
+    if (FLAGS_simple_req_size >= 0) {
+      params->set_req_size(FLAGS_simple_req_size);
+    }
+  } else {
+    // set a reasonable default: proto but no payload
+    client_config.mutable_payload_config()->mutable_simple_params();
+  }
+
   client_config.set_async_client_threads(FLAGS_async_client_threads);
   client_config.set_rpc_type(rpc_type);
 
   // set up the load parameters
-  switch (load_type) {
-    case grpc::testing::CLOSED_LOOP:
-      break;
-    case grpc::testing::POISSON: {
-      auto poisson = client_config.mutable_load_params()->mutable_poisson();
-      GPR_ASSERT(FLAGS_load_param_1 != 0.0);
-      poisson->set_offered_load(FLAGS_load_param_1);
-      break;
-    }
-    case grpc::testing::UNIFORM: {
-      auto uniform = client_config.mutable_load_params()->mutable_uniform();
-      GPR_ASSERT(FLAGS_load_param_1 != 0.0);
-      GPR_ASSERT(FLAGS_load_param_2 != 0.0);
-      uniform->set_interarrival_lo(FLAGS_load_param_1 / 1e6);
-      uniform->set_interarrival_hi(FLAGS_load_param_2 / 1e6);
-      break;
-    }
-    case grpc::testing::DETERMINISTIC: {
-      auto determ = client_config.mutable_load_params()->mutable_determ();
-      GPR_ASSERT(FLAGS_load_param_1 != 0.0);
-      determ->set_offered_load(FLAGS_load_param_1);
-      break;
-    }
-    case grpc::testing::PARETO: {
-      auto pareto = client_config.mutable_load_params()->mutable_pareto();
-      GPR_ASSERT(FLAGS_load_param_1 != 0.0);
-      GPR_ASSERT(FLAGS_load_param_2 != 0.0);
-      pareto->set_interarrival_base(FLAGS_load_param_1 / 1e6);
-      pareto->set_alpha(FLAGS_load_param_2);
-      break;
-    }
-    default:
-      GPR_ASSERT(false);
-      break;
+  if (FLAGS_poisson_load > 0.0) {
+    auto poisson = client_config.mutable_load_params()->mutable_poisson();
+    poisson->set_offered_load(FLAGS_poisson_load);
+  } else if (FLAGS_uniform_lo > 0.0) {
+    auto uniform = client_config.mutable_load_params()->mutable_uniform();
+    uniform->set_interarrival_lo(FLAGS_uniform_lo / 1e6);
+    uniform->set_interarrival_hi(FLAGS_uniform_hi / 1e6);
+  } else if (FLAGS_determ_load > 0.0) {
+    auto determ = client_config.mutable_load_params()->mutable_determ();
+    determ->set_offered_load(FLAGS_determ_load);
+  } else if (FLAGS_pareto_base > 0.0) {
+    auto pareto = client_config.mutable_load_params()->mutable_pareto();
+    pareto->set_interarrival_base(FLAGS_pareto_base / 1e6);
+    pareto->set_alpha(FLAGS_pareto_alpha);
+  } else {
+    client_config.mutable_load_params()->mutable_closed_loop();
+    // No further load parameters to set up for closed loop
   }
 
   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);
-
-  // If we're running a sync-server streaming test, make sure
-  // that we have at least as many threads as the active streams
-  // or else threads will be blocked from forward progress and the
-  // client will deadlock on a timer.
-  GPR_ASSERT(!(server_type == grpc::testing::SYNCHRONOUS_SERVER &&
-               rpc_type == grpc::testing::STREAMING &&
-               FLAGS_server_threads <
-                   FLAGS_client_channels * FLAGS_outstanding_rpcs_per_channel));
+  server_config.set_async_server_threads(FLAGS_async_server_threads);
+
+  if (FLAGS_secure_test) {
+    // Set up security params
+    SecurityParams security;
+    security.set_use_test_ca(true);
+    security.set_server_host_override("foo.test.google.fr");
+    client_config.mutable_security_params()->CopyFrom(security);
+    server_config.mutable_security_params()->CopyFrom(security);
+  }
 
   const auto result = RunScenario(
       client_config, FLAGS_num_clients, server_config, FLAGS_num_servers,
@@ -168,7 +166,6 @@ static void QpsDriver() {
 int main(int argc, char** argv) {
   grpc::testing::InitBenchmark(&argc, &argv, true);
 
-  signal(SIGPIPE, SIG_IGN);
   grpc::testing::QpsDriver();
 
   return 0;
diff --git a/test/cpp/qps/qps_interarrival_test.cc b/test/cpp/qps/qps_interarrival_test.cc
index a7979e6187813041c15661ca06234835b99a8f86..ccda28f09ae90c9aa220a4bb9fdca8ebdf03e267 100644
--- a/test/cpp/qps/qps_interarrival_test.cc
+++ b/test/cpp/qps/qps_interarrival_test.cc
@@ -42,7 +42,7 @@
 using grpc::testing::RandomDist;
 using grpc::testing::InterarrivalTimer;
 
-void RunTest(RandomDist &&r, int threads, std::string title) {
+static void RunTest(RandomDist &&r, int threads, std::string title) {
   InterarrivalTimer timer;
   timer.init(r, threads);
   gpr_histogram *h(gpr_histogram_create(0.01, 60e9));
diff --git a/test/cpp/qps/qps_openloop_test.cc b/test/cpp/qps/qps_openloop_test.cc
index 5a6a9249a93e558c76be4783efba2d6b69df082d..dc88c893bb9f1f5d2cb9ef5d43d9f81956d93afb 100644
--- a/test/cpp/qps/qps_openloop_test.cc
+++ b/test/cpp/qps/qps_openloop_test.cc
@@ -31,8 +31,6 @@
  *
  */
 
-#include <signal.h>
-
 #include <set>
 
 #include <grpc/support/log.h>
@@ -52,20 +50,16 @@ static void RunQPS() {
 
   ClientConfig client_config;
   client_config.set_client_type(ASYNC_CLIENT);
-  client_config.set_enable_ssl(false);
   client_config.set_outstanding_rpcs_per_channel(1000);
   client_config.set_client_channels(8);
-  client_config.set_payload_size(1);
   client_config.set_async_client_threads(8);
   client_config.set_rpc_type(UNARY);
-  client_config.set_load_type(POISSON);
   client_config.mutable_load_params()->mutable_poisson()->set_offered_load(
       1000.0);
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
-  server_config.set_enable_ssl(false);
-  server_config.set_threads(4);
+  server_config.set_async_server_threads(4);
 
   const auto result =
       RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
@@ -80,7 +74,6 @@ static void RunQPS() {
 int main(int argc, char** argv) {
   grpc::testing::InitBenchmark(&argc, &argv, true);
 
-  signal(SIGPIPE, SIG_IGN);
   grpc::testing::RunQPS();
 
   return 0;
diff --git a/test/cpp/qps/qps_test.cc b/test/cpp/qps/qps_test.cc
index d0c4a79cd92ae94504842dab72a26763287d88ac..89b35cfb0523c3dcd729d8ef9546897520830be5 100644
--- a/test/cpp/qps/qps_test.cc
+++ b/test/cpp/qps/qps_test.cc
@@ -31,8 +31,6 @@
  *
  */
 
-#include <signal.h>
-
 #include <set>
 
 #include <grpc/support/log.h>
@@ -52,17 +50,15 @@ static void RunQPS() {
 
   ClientConfig client_config;
   client_config.set_client_type(ASYNC_CLIENT);
-  client_config.set_enable_ssl(false);
   client_config.set_outstanding_rpcs_per_channel(1000);
   client_config.set_client_channels(8);
-  client_config.set_payload_size(1);
   client_config.set_async_client_threads(8);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed_loop();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
-  server_config.set_enable_ssl(false);
-  server_config.set_threads(8);
+  server_config.set_async_server_threads(8);
 
   const auto result =
       RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
@@ -77,7 +73,6 @@ static void RunQPS() {
 int main(int argc, char** argv) {
   grpc::testing::InitBenchmark(&argc, &argv, true);
 
-  signal(SIGPIPE, SIG_IGN);
   grpc::testing::RunQPS();
 
   return 0;
diff --git a/test/cpp/qps/qps_test_with_poll.cc b/test/cpp/qps/qps_test_with_poll.cc
index 31d2c1bf7b49b55fa44815f678f379813dfae73b..97da4096ed3253145eafc47f75d8c87baee0e2b0 100644
--- a/test/cpp/qps/qps_test_with_poll.cc
+++ b/test/cpp/qps/qps_test_with_poll.cc
@@ -31,8 +31,6 @@
  *
  */
 
-#include <signal.h>
-
 #include <set>
 
 #include <grpc/support/log.h>
@@ -56,17 +54,15 @@ static void RunQPS() {
 
   ClientConfig client_config;
   client_config.set_client_type(ASYNC_CLIENT);
-  client_config.set_enable_ssl(false);
   client_config.set_outstanding_rpcs_per_channel(1000);
   client_config.set_client_channels(8);
-  client_config.set_payload_size(1);
   client_config.set_async_client_threads(8);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed_loop();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
-  server_config.set_enable_ssl(false);
-  server_config.set_threads(4);
+  server_config.set_async_server_threads(4);
 
   const auto result =
       RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
@@ -83,7 +79,6 @@ int main(int argc, char** argv) {
 
   grpc_platform_become_multipoller = grpc_poll_become_multipoller;
 
-  signal(SIGPIPE, SIG_IGN);
   grpc::testing::RunQPS();
 
   return 0;
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index 4ce77f366dec0baba0985881185aed6fee501f4a..dc59eab7ef6b52fd068fd3fba178b153db6306dc 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -52,17 +52,17 @@
 #include <grpc++/security/server_credentials.h>
 
 #include "test/core/util/grpc_profiler.h"
-#include "test/proto/qpstest.pb.h"
 #include "test/cpp/qps/client.h"
 #include "test/cpp/qps/server.h"
 #include "test/cpp/util/create_test_channel.h"
+#include "test/proto/benchmarks/services.pb.h"
 
 namespace grpc {
 namespace testing {
 
-std::unique_ptr<Client> CreateClient(const ClientConfig& config) {
+static std::unique_ptr<Client> CreateClient(const ClientConfig& config) {
   switch (config.client_type()) {
-    case ClientType::SYNCHRONOUS_CLIENT:
+    case ClientType::SYNC_CLIENT:
       return (config.rpc_type() == RpcType::UNARY)
                  ? CreateSynchronousUnaryClient(config)
                  : CreateSynchronousStreamingClient(config);
@@ -76,26 +76,29 @@ std::unique_ptr<Client> CreateClient(const ClientConfig& config) {
   abort();
 }
 
-std::unique_ptr<Server> CreateServer(const ServerConfig& config,
-                                     int server_port) {
+static void LimitCores(int cores) {}
+
+static std::unique_ptr<Server> CreateServer(const ServerConfig& config) {
+  if (config.core_limit() > 0) {
+    LimitCores(config.core_limit());
+  }
   switch (config.server_type()) {
-    case ServerType::SYNCHRONOUS_SERVER:
-      return CreateSynchronousServer(config, server_port);
+    case ServerType::SYNC_SERVER:
+      return CreateSynchronousServer(config);
     case ServerType::ASYNC_SERVER:
-      return CreateAsyncServer(config, server_port);
+      return CreateAsyncServer(config);
     default:
       abort();
   }
   abort();
 }
 
-class WorkerImpl GRPC_FINAL : public Worker::Service {
+class WorkerServiceImpl GRPC_FINAL : public WorkerService::Service {
  public:
-  explicit WorkerImpl(int server_port)
-      : server_port_(server_port), acquired_(false) {}
+  explicit WorkerServiceImpl() : acquired_(false) {}
 
-  Status RunTest(ServerContext* ctx,
-                 ServerReaderWriter<ClientStatus, ClientArgs>* stream)
+  Status RunClient(ServerContext* ctx,
+                   ServerReaderWriter<ClientStatus, ClientArgs>* stream)
       GRPC_OVERRIDE {
     InstanceGuard g(this);
     if (!g.Acquired()) {
@@ -103,7 +106,7 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
     }
 
     grpc_profiler_start("qps_client.prof");
-    Status ret = RunTestBody(ctx, stream);
+    Status ret = RunClientBody(ctx, stream);
     grpc_profiler_stop();
     return ret;
   }
@@ -126,7 +129,7 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
   // Protect against multiple clients using this worker at once.
   class InstanceGuard {
    public:
-    InstanceGuard(WorkerImpl* impl)
+    InstanceGuard(WorkerServiceImpl* impl)
         : impl_(impl), acquired_(impl->TryAcquireInstance()) {}
     ~InstanceGuard() {
       if (acquired_) {
@@ -137,7 +140,7 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
     bool Acquired() const { return acquired_; }
 
    private:
-    WorkerImpl* const impl_;
+    WorkerServiceImpl* const impl_;
     const bool acquired_;
   };
 
@@ -154,8 +157,8 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
     acquired_ = false;
   }
 
-  Status RunTestBody(ServerContext* ctx,
-                     ServerReaderWriter<ClientStatus, ClientArgs>* stream) {
+  Status RunClientBody(ServerContext* ctx,
+                       ServerReaderWriter<ClientStatus, ClientArgs>* stream) {
     ClientArgs args;
     if (!stream->Read(&args)) {
       return Status(StatusCode::INVALID_ARGUMENT, "");
@@ -175,7 +178,7 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
       if (!args.has_mark()) {
         return Status(StatusCode::INVALID_ARGUMENT, "");
       }
-      *status.mutable_stats() = client->Mark();
+      *status.mutable_stats() = client->Mark(args.mark().reset());
       stream->Write(status);
     }
 
@@ -191,12 +194,13 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
     if (!args.has_setup()) {
       return Status(StatusCode::INVALID_ARGUMENT, "");
     }
-    auto server = CreateServer(args.setup(), server_port_);
+    auto server = CreateServer(args.setup());
     if (!server) {
       return Status(StatusCode::INVALID_ARGUMENT, "");
     }
     ServerStatus status;
-    status.set_port(server_port_);
+    status.set_port(server->port());
+    status.set_cores(server->cores());
     if (!stream->Write(status)) {
       return Status(StatusCode::UNKNOWN, "");
     }
@@ -204,21 +208,19 @@ class WorkerImpl GRPC_FINAL : public Worker::Service {
       if (!args.has_mark()) {
         return Status(StatusCode::INVALID_ARGUMENT, "");
       }
-      *status.mutable_stats() = server->Mark();
+      *status.mutable_stats() = server->Mark(args.mark().reset());
       stream->Write(status);
     }
 
     return Status::OK;
   }
 
-  const int server_port_;
-
   std::mutex mu_;
   bool acquired_;
 };
 
-QpsWorker::QpsWorker(int driver_port, int server_port) {
-  impl_.reset(new WorkerImpl(server_port));
+QpsWorker::QpsWorker(int driver_port) {
+  impl_.reset(new WorkerServiceImpl());
 
   char* server_address = NULL;
   gpr_join_host_port(&server_address, "::", driver_port);
diff --git a/test/cpp/qps/qps_worker.h b/test/cpp/qps/qps_worker.h
index 861588907ecbc390010a4b946889446dd87a3564..0db88ad3d13ea9dbe2a0b9672156cf0504a8e52b 100644
--- a/test/cpp/qps/qps_worker.h
+++ b/test/cpp/qps/qps_worker.h
@@ -42,15 +42,15 @@ class Server;
 
 namespace testing {
 
-class WorkerImpl;
+class WorkerServiceImpl;
 
 class QpsWorker {
  public:
-  QpsWorker(int driver_port, int server_port);
+  explicit QpsWorker(int driver_port);
   ~QpsWorker();
 
  private:
-  std::unique_ptr<WorkerImpl> impl_;
+  std::unique_ptr<WorkerServiceImpl> impl_;
   std::unique_ptr<Server> server_;
 };
 
diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc
index e03e8e1fb08c6e893be0f5fd69c09eb47c8cce3d..b230eb441e81f84b1d32032e13b582c95e630761 100644
--- a/test/cpp/qps/report.cc
+++ b/test/cpp/qps/report.cc
@@ -43,6 +43,7 @@ namespace testing {
 static double WallTime(ResourceUsage u) { return u.wall_time(); }
 static double UserTime(ResourceUsage u) { return u.user_time(); }
 static double SystemTime(ResourceUsage u) { return u.system_time(); }
+static int Cores(ResourceUsage u) { return u.cores(); }
 
 void CompositeReporter::add(std::unique_ptr<Reporter> reporter) {
   reporters_.emplace_back(std::move(reporter));
@@ -83,7 +84,7 @@ void GprLogReporter::ReportQPSPerCore(const ScenarioResult& result) {
       result.latencies.Count() / average(result.client_resources, WallTime);
 
   gpr_log(GPR_INFO, "QPS: %.1f (%.1f/server core)", qps,
-          qps / result.server_config.threads());
+          qps / sum(result.server_resources, Cores));
 }
 
 void GprLogReporter::ReportLatency(const ScenarioResult& result) {
@@ -123,10 +124,10 @@ void PerfDbReporter::ReportQPSPerCore(const ScenarioResult& result) {
   auto qps =
       result.latencies.Count() / average(result.client_resources, WallTime);
 
-  auto qpsPerCore = qps / result.server_config.threads();
+  auto qps_per_core = qps / sum(result.server_resources, Cores);
 
   perf_db_client_.setQps(qps);
-  perf_db_client_.setQpsPerCore(qpsPerCore);
+  perf_db_client_.setQpsPerCore(qps_per_core);
   perf_db_client_.setConfigs(result.client_config, result.server_config);
 }
 
diff --git a/test/cpp/qps/report.h b/test/cpp/qps/report.h
index 00d12369d54002fbdfbaac4a03665d5fea455c37..78779231d38af3fbcfb90cc2b25a35f1fbbed104 100644
--- a/test/cpp/qps/report.h
+++ b/test/cpp/qps/report.h
@@ -41,7 +41,6 @@
 #include <grpc++/support/config.h>
 
 #include "test/cpp/qps/driver.h"
-#include "test/proto/qpstest.grpc.pb.h"
 #include "test/cpp/qps/perf_db_client.h"
 
 namespace grpc {
diff --git a/test/cpp/qps/secure_sync_unary_ping_pong_test.cc b/test/cpp/qps/secure_sync_unary_ping_pong_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..df06f7e471f27249bd011d78f737827850905c20
--- /dev/null
+++ b/test/cpp/qps/secure_sync_unary_ping_pong_test.cc
@@ -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.
+ *
+ */
+
+#include <set>
+
+#include <grpc/support/log.h>
+
+#include "test/cpp/qps/driver.h"
+#include "test/cpp/qps/report.h"
+#include "test/cpp/util/benchmark_config.h"
+
+namespace grpc {
+namespace testing {
+
+static const int WARMUP = 5;
+static const int BENCHMARK = 10;
+
+static void RunSynchronousUnaryPingPong() {
+  gpr_log(GPR_INFO, "Running Synchronous Unary Ping Pong");
+
+  ClientConfig client_config;
+  client_config.set_client_type(SYNC_CLIENT);
+  client_config.set_outstanding_rpcs_per_channel(1);
+  client_config.set_client_channels(1);
+  client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed_loop();
+
+  ServerConfig server_config;
+  server_config.set_server_type(SYNC_SERVER);
+
+  // Set up security params
+  SecurityParams security;
+  security.set_use_test_ca(true);
+  security.set_server_host_override("foo.test.google.fr");
+  client_config.mutable_security_params()->CopyFrom(security);
+  server_config.mutable_security_params()->CopyFrom(security);
+
+  const auto result =
+      RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
+
+  GetReporter()->ReportQPS(*result);
+  GetReporter()->ReportLatency(*result);
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc::testing::InitBenchmark(&argc, &argv, true);
+
+  grpc::testing::RunSynchronousUnaryPingPong();
+
+  return 0;
+}
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
index e48e873dc33d307c1f7ddab2132a5bf648b28d1d..6e81edc8ffe40e9ba612ccd1a01ffcb562ab7fd0 100644
--- a/test/cpp/qps/server.h
+++ b/test/cpp/qps/server.h
@@ -34,22 +34,38 @@
 #ifndef TEST_QPS_SERVER_H
 #define TEST_QPS_SERVER_H
 
+#include <grpc/support/cpu.h>
+#include <grpc++/security/server_credentials.h>
+
+#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/core/util/port.h"
 #include "test/cpp/qps/timer.h"
-#include "test/proto/qpstest.grpc.pb.h"
+#include "test/proto/messages.grpc.pb.h"
+#include "test/proto/benchmarks/control.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
 
 class Server {
  public:
-  Server() : timer_(new Timer) {}
+  explicit Server(const ServerConfig& config) : timer_(new Timer) {
+    if (config.port()) {
+      port_ = config.port();
+    } else {
+      port_ = grpc_pick_unused_port_or_die();
+    }
+  }
   virtual ~Server() {}
 
-  ServerStats Mark() {
-    std::unique_ptr<Timer> timer(new Timer);
-    timer.swap(timer_);
-
-    auto timer_result = timer->Mark();
+  ServerStats Mark(bool reset) {
+    Timer::Result timer_result;
+    if (reset) {
+      std::unique_ptr<Timer> timer(new Timer);
+      timer.swap(timer_);
+      timer_result = timer->Mark();
+    } else {
+      timer_result = timer_->Mark();
+    }
 
     ServerStats stats;
     stats.set_time_elapsed(timer_result.wall);
@@ -70,13 +86,29 @@ class Server {
     return true;
   }
 
+  int port() const { return port_; }
+  int cores() const { return gpr_cpu_num_cores(); }
+  static std::shared_ptr<ServerCredentials> CreateServerCredentials(
+      const ServerConfig& config) {
+    if (config.has_security_params()) {
+      SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
+                                                          test_server1_cert};
+      SslServerCredentialsOptions ssl_opts;
+      ssl_opts.pem_root_certs = "";
+      ssl_opts.pem_key_cert_pairs.push_back(pkcp);
+      return SslServerCredentials(ssl_opts);
+    } else {
+      return InsecureServerCredentials();
+    }
+  }
+
  private:
+  int port_;
   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);
+std::unique_ptr<Server> CreateSynchronousServer(const ServerConfig& config);
+std::unique_ptr<Server> CreateAsyncServer(const ServerConfig& config);
 
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index 98fa9c53e2526fade754fa0f208ea9ba576ee53f..2d922fa6150dac51779099af60758bf559b932d9 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -49,38 +49,40 @@
 #include <grpc++/security/server_credentials.h>
 #include <gtest/gtest.h>
 
-#include "test/proto/qpstest.grpc.pb.h"
 #include "test/cpp/qps/server.h"
+#include "test/proto/benchmarks/services.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
 
 class AsyncQpsServerTest : public Server {
  public:
-  AsyncQpsServerTest(const ServerConfig &config, int port) {
+  explicit AsyncQpsServerTest(const ServerConfig &config) : Server(config) {
     char *server_address = NULL;
-    gpr_join_host_port(&server_address, "::", port);
+
+    gpr_join_host_port(&server_address, "::", port());
 
     ServerBuilder builder;
-    builder.AddListeningPort(server_address, InsecureServerCredentials());
+    builder.AddListeningPort(server_address,
+                             Server::CreateServerCredentials(config));
     gpr_free(server_address);
 
     builder.RegisterAsyncService(&async_service_);
-    for (int i = 0; i < config.threads(); i++) {
+    for (int i = 0; i < config.async_server_threads(); i++) {
       srv_cqs_.emplace_back(builder.AddCompletionQueue());
     }
 
     server_ = builder.BuildAndStart();
 
     using namespace std::placeholders;
-    for (int i = 0; i < 10000 / config.threads(); i++) {
-      for (int j = 0; j < config.threads(); j++) {
+    for (int i = 0; i < 10000 / config.async_server_threads(); i++) {
+      for (int j = 0; j < config.async_server_threads(); j++) {
         auto request_unary = std::bind(
-            &TestService::AsyncService::RequestUnaryCall, &async_service_, _1,
-            _2, _3, srv_cqs_[j].get(), srv_cqs_[j].get(), _4);
+            &BenchmarkService::AsyncService::RequestUnaryCall, &async_service_,
+            _1, _2, _3, srv_cqs_[j].get(), srv_cqs_[j].get(), _4);
         auto request_streaming = std::bind(
-            &TestService::AsyncService::RequestStreamingCall, &async_service_,
-            _1, _2, srv_cqs_[j].get(), srv_cqs_[j].get(), _3);
+            &BenchmarkService::AsyncService::RequestStreamingCall,
+            &async_service_, _1, _2, srv_cqs_[j].get(), srv_cqs_[j].get(), _3);
         contexts_.push_front(
             new ServerRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(
                 request_unary, ProcessRPC));
@@ -89,10 +91,10 @@ class AsyncQpsServerTest : public Server {
                 request_streaming, ProcessRPC));
       }
     }
-    for (int i = 0; i < config.threads(); i++) {
+    for (int i = 0; i < config.async_server_threads(); i++) {
       shutdown_state_.emplace_back(new PerThreadShutdownState());
     }
-    for (int i = 0; i < config.threads(); i++) {
+    for (int i = 0; i < config.async_server_threads(); i++) {
       threads_.emplace_back(&AsyncQpsServerTest::ThreadFunc, this, i);
     }
   }
@@ -309,7 +311,7 @@ class AsyncQpsServerTest : public Server {
   std::vector<std::thread> threads_;
   std::unique_ptr<grpc::Server> server_;
   std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_;
-  TestService::AsyncService async_service_;
+  BenchmarkService::AsyncService async_service_;
   std::forward_list<ServerRpcContext *> contexts_;
 
   class PerThreadShutdownState {
@@ -333,9 +335,8 @@ class AsyncQpsServerTest : public Server {
   std::vector<std::unique_ptr<PerThreadShutdownState>> shutdown_state_;
 };
 
-std::unique_ptr<Server> CreateAsyncServer(const ServerConfig &config,
-                                          int port) {
-  return std::unique_ptr<Server>(new AsyncQpsServerTest(config, port));
+std::unique_ptr<Server> CreateAsyncServer(const ServerConfig &config) {
+  return std::unique_ptr<Server>(new AsyncQpsServerTest(config));
 }
 
 }  // namespace testing
diff --git a/test/cpp/qps/server_sync.cc b/test/cpp/qps/server_sync.cc
index b760ef63ec514a634caa75556d5ae0fd72d5d011..a09b174b7e0448c680c392a55a1d8c040029b6a6 100644
--- a/test/cpp/qps/server_sync.cc
+++ b/test/cpp/qps/server_sync.cc
@@ -43,14 +43,14 @@
 #include <grpc++/server_context.h>
 #include <grpc++/security/server_credentials.h>
 
-#include "test/proto/qpstest.grpc.pb.h"
 #include "test/cpp/qps/server.h"
 #include "test/cpp/qps/timer.h"
+#include "test/proto/benchmarks/services.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
 
-class TestServiceImpl GRPC_FINAL : public TestService::Service {
+class BenchmarkServiceImpl GRPC_FINAL : public BenchmarkService::Service {
  public:
   Status UnaryCall(ServerContext* context, const SimpleRequest* request,
                    SimpleResponse* response) GRPC_OVERRIDE {
@@ -84,30 +84,29 @@ class TestServiceImpl GRPC_FINAL : public TestService::Service {
 
 class SynchronousServer GRPC_FINAL : public grpc::testing::Server {
  public:
-  SynchronousServer(const ServerConfig& config, int port)
-      : impl_(MakeImpl(port)) {}
-
- private:
-  std::unique_ptr<grpc::Server> MakeImpl(int port) {
+  explicit SynchronousServer(const ServerConfig& config) : Server(config) {
     ServerBuilder builder;
 
     char* server_address = NULL;
-    gpr_join_host_port(&server_address, "::", port);
-    builder.AddListeningPort(server_address, InsecureServerCredentials());
+
+    gpr_join_host_port(&server_address, "::", port());
+    builder.AddListeningPort(server_address,
+                             Server::CreateServerCredentials(config));
     gpr_free(server_address);
 
     builder.RegisterService(&service_);
 
-    return builder.BuildAndStart();
+    impl_ = builder.BuildAndStart();
   }
 
-  TestServiceImpl service_;
+ private:
+  BenchmarkServiceImpl service_;
   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));
+    const ServerConfig& config) {
+  return std::unique_ptr<Server>(new SynchronousServer(config));
 }
 
 }  // namespace testing
diff --git a/test/cpp/qps/single_run_localhost.sh b/test/cpp/qps/single_run_localhost.sh
index 9d76f08f80032b740bc0e2133fda45d268bf450b..f5356f18343efc0447d82d23d73d415c52cb01df 100755
--- a/test/cpp/qps/single_run_localhost.sh
+++ b/test/cpp/qps/single_run_localhost.sh
@@ -42,9 +42,9 @@ 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 &
+bins/$config/qps_worker -driver_port 10000 &
 PID1=$!
-bins/$config/qps_worker -driver_port 10010 -server_port 10011 &
+bins/$config/qps_worker -driver_port 10010 &
 PID2=$!
 
 export QPS_WORKERS="localhost:10000,localhost:10010"
diff --git a/test/cpp/qps/sync_streaming_ping_pong_test.cc b/test/cpp/qps/sync_streaming_ping_pong_test.cc
index 52e43939a814c7151425d9e0befdd83706620217..186afc03f7cece0ea8af3513ab63abc5a8b08ee2 100644
--- a/test/cpp/qps/sync_streaming_ping_pong_test.cc
+++ b/test/cpp/qps/sync_streaming_ping_pong_test.cc
@@ -31,8 +31,6 @@
  *
  */
 
-#include <signal.h>
-
 #include <set>
 
 #include <grpc/support/log.h>
@@ -51,17 +49,14 @@ static void RunSynchronousStreamingPingPong() {
   gpr_log(GPR_INFO, "Running Synchronous Streaming Ping Pong");
 
   ClientConfig client_config;
-  client_config.set_client_type(SYNCHRONOUS_CLIENT);
-  client_config.set_enable_ssl(false);
+  client_config.set_client_type(SYNC_CLIENT);
   client_config.set_outstanding_rpcs_per_channel(1);
   client_config.set_client_channels(1);
-  client_config.set_payload_size(1);
   client_config.set_rpc_type(STREAMING);
+  client_config.mutable_load_params()->mutable_closed_loop();
 
   ServerConfig server_config;
-  server_config.set_server_type(SYNCHRONOUS_SERVER);
-  server_config.set_enable_ssl(false);
-  server_config.set_threads(1);
+  server_config.set_server_type(SYNC_SERVER);
 
   const auto result =
       RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
@@ -75,7 +70,6 @@ static void RunSynchronousStreamingPingPong() {
 int main(int argc, char** argv) {
   grpc::testing::InitBenchmark(&argc, &argv, true);
 
-  signal(SIGPIPE, SIG_IGN);
   grpc::testing::RunSynchronousStreamingPingPong();
 
   return 0;
diff --git a/test/cpp/qps/sync_unary_ping_pong_test.cc b/test/cpp/qps/sync_unary_ping_pong_test.cc
index fbd21357aa593dd9358effbbf1d1b5ca6eadd3eb..25851833a6d6f316bc240e7c598be9fca627b9a1 100644
--- a/test/cpp/qps/sync_unary_ping_pong_test.cc
+++ b/test/cpp/qps/sync_unary_ping_pong_test.cc
@@ -31,8 +31,6 @@
  *
  */
 
-#include <signal.h>
-
 #include <set>
 
 #include <grpc/support/log.h>
@@ -51,17 +49,14 @@ static void RunSynchronousUnaryPingPong() {
   gpr_log(GPR_INFO, "Running Synchronous Unary Ping Pong");
 
   ClientConfig client_config;
-  client_config.set_client_type(SYNCHRONOUS_CLIENT);
-  client_config.set_enable_ssl(false);
+  client_config.set_client_type(SYNC_CLIENT);
   client_config.set_outstanding_rpcs_per_channel(1);
   client_config.set_client_channels(1);
-  client_config.set_payload_size(1);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed_loop();
 
   ServerConfig server_config;
-  server_config.set_server_type(SYNCHRONOUS_SERVER);
-  server_config.set_enable_ssl(false);
-  server_config.set_threads(1);
+  server_config.set_server_type(SYNC_SERVER);
 
   const auto result =
       RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
@@ -76,7 +71,6 @@ static void RunSynchronousUnaryPingPong() {
 int main(int argc, char** argv) {
   grpc::testing::InitBenchmark(&argc, &argv, true);
 
-  signal(SIGPIPE, SIG_IGN);
   grpc::testing::RunSynchronousUnaryPingPong();
 
   return 0;
diff --git a/test/cpp/qps/timer.cc b/test/cpp/qps/timer.cc
index 8edb838da37dff75d5e64141660010ad509931d8..3ec7f49f8321d80b35b7195f7f3b4b12f06aa867 100644
--- a/test/cpp/qps/timer.cc
+++ b/test/cpp/qps/timer.cc
@@ -61,7 +61,7 @@ Timer::Result Timer::Sample() {
   return r;
 }
 
-Timer::Result Timer::Mark() {
+Timer::Result Timer::Mark() const {
   Result s = Sample();
   Result r;
   r.wall = s.wall - start_.wall;
diff --git a/test/cpp/qps/timer.h b/test/cpp/qps/timer.h
index 30dbd7e7d50dd6c5cdeefafc15f4f45c08696857..d1aee1a9d199ecb897eb91fae6657cbee6fdbcec 100644
--- a/test/cpp/qps/timer.h
+++ b/test/cpp/qps/timer.h
@@ -44,7 +44,7 @@ class Timer {
     double system;
   };
 
-  Result Mark();
+  Result Mark() const;
 
   static double Now();
 
diff --git a/test/cpp/qps/worker.cc b/test/cpp/qps/worker.cc
index 935e4853a62a11239f75924f15d2a73d10025ce5..430ffb7cdc2a51ef07a6170d2d37c574017dc0a1 100644
--- a/test/cpp/qps/worker.cc
+++ b/test/cpp/qps/worker.cc
@@ -43,8 +43,7 @@
 #include "test/cpp/qps/qps_worker.h"
 #include "test/cpp/util/test_config.h"
 
-DEFINE_int32(driver_port, 0, "Driver server port.");
-DEFINE_int32(server_port, 0, "Spawned server port.");
+DEFINE_int32(driver_port, 0, "Port for communication with driver");
 
 static bool got_sigint = false;
 
@@ -54,7 +53,7 @@ namespace grpc {
 namespace testing {
 
 static void RunServer() {
-  QpsWorker worker(FLAGS_driver_port, FLAGS_server_port);
+  QpsWorker worker(FLAGS_driver_port);
 
   while (!got_sigint) {
     gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
diff --git a/test/proto/qpstest.proto b/test/proto/benchmarks/control.proto
similarity index 51%
rename from test/proto/qpstest.proto
rename to test/proto/benchmarks/control.proto
index ef1f9451e9a6186c028acbf75d55cc62ee386894..966ab78baae32d57130883d39e42ccebca4b6a09 100644
--- a/test/proto/qpstest.proto
+++ b/test/proto/benchmarks/control.proto
@@ -1,4 +1,3 @@
-
 // Copyright 2015, Google Inc.
 // All rights reserved.
 //
@@ -28,62 +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.
 
-// An integration test service that covers all the method signature permutations
-// of unary/streaming requests/responses.
 syntax = "proto3";
 
-package grpc.testing;
-
-enum PayloadType {
-  // Compressable text format.
-  COMPRESSABLE = 0;
-
-  // Uncompressable binary format.
-  UNCOMPRESSABLE = 1;
-
-  // Randomly chosen from all other formats defined in this enum.
-  RANDOM = 2;
-}
-
-message StatsRequest {
-  // run number
-  int32 test_num = 1;
-}
-
-message ServerStats {
-  // wall clock time
-  double time_elapsed = 1;
-
-  // user time used by the server process and threads
-  double time_user = 2;
-
-  // server time used by the server process and all threads
-  double time_system = 3;
-}
-
-message Payload {
-  // The type of data in body.
-  PayloadType type = 1;
-  // Primary contents of payload.
-  bytes body = 2;
-}
+import "test/proto/benchmarks/payloads.proto";
+import "test/proto/benchmarks/stats.proto";
 
-message HistogramData {
-  repeated uint32 bucket = 1;
-  double min_seen = 2;
-  double max_seen = 3;
-  double sum = 4;
-  double sum_of_squares = 5;
-  double count = 6;
-}
+package grpc.testing;
 
 enum ClientType {
-  SYNCHRONOUS_CLIENT = 0;
+  SYNC_CLIENT = 0;
   ASYNC_CLIENT = 1;
 }
 
 enum ServerType {
-  SYNCHRONOUS_SERVER = 0;
+  SYNC_SERVER = 0;
   ASYNC_SERVER = 1;
 }
 
@@ -92,14 +49,6 @@ enum RpcType {
   STREAMING = 1;
 }
 
-enum LoadType {
-  CLOSED_LOOP = 0;
-  POISSON = 1;
-  UNIFORM = 2;
-  DETERMINISTIC = 3;
-  PARETO = 4;
-}
-
 message PoissonParams {
   double offered_load = 1;
 }
@@ -118,32 +67,45 @@ message ParetoParams {
   double alpha = 2;
 }
 
+message ClosedLoopParams {
+}
+
 message LoadParams {
   oneof load {
-    PoissonParams poisson = 1;
-    UniformParams uniform = 2;
-    DeterministicParams determ = 3;
-    ParetoParams pareto = 4;
+    ClosedLoopParams closed_loop = 1;
+    PoissonParams poisson = 2;
+    UniformParams uniform = 3;
+    DeterministicParams determ = 4;
+    ParetoParams pareto = 5;
   };
 }
 
+// presence of SecurityParams implies use of TLS
+message SecurityParams {
+  bool use_test_ca = 1;
+  string server_host_override = 2;
+}
+
 message ClientConfig {
   repeated string server_targets = 1;
   ClientType client_type = 2;
-  bool enable_ssl = 3;
+  SecurityParams security_params = 3;
   int32 outstanding_rpcs_per_channel = 4;
   int32 client_channels = 5;
-  int32 payload_size = 6;
   // only for async client:
   int32 async_client_threads = 7;
   RpcType rpc_type = 8;
-  string host = 9;
-  LoadType load_type = 10;
-  LoadParams load_params = 11;
+  LoadParams load_params = 10;
+  PayloadConfig payload_config = 11;
+}
+
+message ClientStatus {
+  ClientStats stats = 1;
 }
 
 // Request current stats
 message Mark {
+  bool reset = 1;
 }
 
 message ClientArgs {
@@ -153,22 +115,15 @@ message ClientArgs {
   }
 }
 
-message ClientStats {
-  HistogramData latencies = 1;
-  double time_elapsed = 2;
-  double time_user = 3;
-  double time_system = 4;
-}
-
-message ClientStatus {
-  ClientStats stats = 1;
-}
-
 message ServerConfig {
   ServerType server_type = 1;
-  int32 threads = 2;
-  bool enable_ssl = 3;
-  string host = 4;
+  SecurityParams security_params = 2;
+  int32 port = 4;
+  // only for async server
+  int32 async_server_threads = 7;
+  // restrict core usage
+  int32 core_limit = 8;
+  PayloadConfig payload_config = 9;
 }
 
 message ServerArgs {
@@ -181,38 +136,5 @@ message ServerArgs {
 message ServerStatus {
   ServerStats stats = 1;
   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.
-  PayloadType response_type = 1;
-
-  // Desired payload size in the response from the server.
-  // If response_type is COMPRESSABLE, this denotes the size before compression.
-  int32 response_size = 2;
-
-  // Optional input payload sent along with the request.
-  Payload payload = 3;
-}
-
-message SimpleResponse {
-  Payload payload = 1;
-}
-
-service TestService {
-  // One request followed by one response.
-  // The server returns the client payload as-is.
-  rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
-
-  // One request followed by one response.
-  // The server returns the client payload as-is.
-  rpc StreamingCall(stream SimpleRequest) returns (stream SimpleResponse);
-}
-
-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);
+  int32 cores = 3;
 }
diff --git a/test/proto/benchmarks/payloads.proto b/test/proto/benchmarks/payloads.proto
new file mode 100644
index 0000000000000000000000000000000000000000..7e5b2c61ff79ae6a5cb00b6cb22975349e947f31
--- /dev/null
+++ b/test/proto/benchmarks/payloads.proto
@@ -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.
+
+syntax = "proto3";
+
+package grpc.testing;
+
+message ByteBufferParams {
+  int32 req_size = 1;
+  int32 resp_size = 2;
+}
+
+message SimpleProtoParams {
+  int32 req_size = 1;
+  int32 resp_size = 2;
+}
+
+message ComplexProtoParams {
+  // TODO (vpai): Fill this in once the details of complex, representative
+  //              protos are decided
+}
+
+message PayloadConfig {
+  oneof payload {
+    ByteBufferParams bytebuf_params = 1;
+    SimpleProtoParams simple_params = 2;
+    ComplexProtoParams complex_params = 3;
+  }
+}
diff --git a/test/proto/benchmarks/services.proto b/test/proto/benchmarks/services.proto
new file mode 100644
index 0000000000000000000000000000000000000000..4c2cbabdf8710a34abe94c3658c19a15b3c8b8c3
--- /dev/null
+++ b/test/proto/benchmarks/services.proto
@@ -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.
+
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+syntax = "proto3";
+
+import "test/proto/messages.proto";
+import "test/proto/benchmarks/control.proto";
+
+package grpc.testing;
+
+service BenchmarkService {
+  // One request followed by one response.
+  // The server returns the client payload as-is.
+  rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+  // One request followed by one response.
+  // The server returns the client payload as-is.
+  rpc StreamingCall(stream SimpleRequest) returns (stream SimpleResponse);
+}
+
+service WorkerService {
+  // Start server with specified workload
+  rpc RunServer(stream ServerArgs) returns (stream ServerStatus);
+
+  // Start client with specified workload
+  rpc RunClient(stream ClientArgs) returns (stream ClientStatus);
+}
diff --git a/test/proto/benchmarks/stats.proto b/test/proto/benchmarks/stats.proto
new file mode 100644
index 0000000000000000000000000000000000000000..d52144f321497ab462b088ec54b87e68637f1d4d
--- /dev/null
+++ b/test/proto/benchmarks/stats.proto
@@ -0,0 +1,59 @@
+// 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.
+
+syntax = "proto3";
+
+package grpc.testing;
+
+message ServerStats {
+  // wall clock time
+  double time_elapsed = 1;
+
+  // user time used by the server process and threads
+  double time_user = 2;
+
+  // server time used by the server process and all threads
+  double time_system = 3;
+}
+
+message HistogramData {
+  repeated uint32 bucket = 1;
+  double min_seen = 2;
+  double max_seen = 3;
+  double sum = 4;
+  double sum_of_squares = 5;
+  double count = 6;
+}
+
+message ClientStats {
+  HistogramData latencies = 1;
+  double time_elapsed = 2;
+  double time_user = 3;
+  double time_system = 4;
+}
diff --git a/tools/http2_interop/http2interop.go b/tools/http2_interop/http2interop.go
index f1bca7fe13dfdf52879a6c685be8ac03bac7bbc6..8585a044e538d6471b615ba40b3490f85ddba35c 100644
--- a/tools/http2_interop/http2interop.go
+++ b/tools/http2_interop/http2interop.go
@@ -2,15 +2,38 @@ package http2interop
 
 import (
 	"crypto/tls"
+	"crypto/x509"
 	"fmt"
 	"io"
-	"log"
+	"net"
+	"testing"
+	"time"
 )
 
 const (
 	Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
 )
 
+var (
+	defaultTimeout = 1 * time.Second
+)
+
+type HTTP2InteropCtx struct {
+	// Inputs
+	ServerHost             string
+	ServerPort             int
+	UseTLS                 bool
+	UseTestCa              bool
+	ServerHostnameOverride string
+
+	T *testing.T
+
+	// Derived
+	serverSpec string
+	authority  string
+	rootCAs    *x509.CertPool
+}
+
 func parseFrame(r io.Reader) (Frame, error) {
 	fh := FrameHeader{}
 	if err := fh.Parse(r); err != nil {
@@ -49,22 +72,8 @@ func streamFrame(w io.Writer, f Frame) error {
 	return nil
 }
 
-func getHttp2Conn(addr string) (*tls.Conn, error) {
-	config := &tls.Config{
-		InsecureSkipVerify: true,
-		NextProtos:         []string{"h2"},
-	}
-
-	conn, err := tls.Dial("tcp", addr, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return conn, nil
-}
-
-func testClientShortSettings(addr string, length int) error {
-	c, err := getHttp2Conn(addr)
+func testClientShortSettings(ctx *HTTP2InteropCtx, length int) error {
+	c, err := connect(ctx)
 	if err != nil {
 		return err
 	}
@@ -82,22 +91,22 @@ func testClientShortSettings(addr string, length int) error {
 		Data: make([]byte, length),
 	}
 	if err := streamFrame(c, sf); err != nil {
+		ctx.T.Log("Unable to stream frame", sf)
 		return err
 	}
 
 	for {
-		frame, err := parseFrame(c)
-		if err != nil {
+		if _, err := parseFrame(c); err != nil {
+			ctx.T.Log("Unable to parse frame")
 			return err
 		}
-		log.Println(frame)
 	}
 
 	return nil
 }
 
-func testClientPrefaceWithStreamId(addr string) error {
-	c, err := getHttp2Conn(addr)
+func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error {
+	c, err := connect(ctx)
 	if err != nil {
 		return err
 	}
@@ -119,18 +128,16 @@ func testClientPrefaceWithStreamId(addr string) error {
 	}
 
 	for {
-		frame, err := parseFrame(c)
-		if err != nil {
+		if _, err := parseFrame(c); err != nil {
 			return err
 		}
-		log.Println(frame)
 	}
 
 	return nil
 }
 
-func testUnknownFrameType(addr string) error {
-	c, err := getHttp2Conn(addr)
+func testUnknownFrameType(ctx *HTTP2InteropCtx) error {
+	c, err := connect(ctx)
 	if err != nil {
 		return err
 	}
@@ -143,6 +150,7 @@ func testUnknownFrameType(addr string) error {
 	// Send some settings, which are part of the client preface
 	sf := &SettingsFrame{}
 	if err := streamFrame(c, sf); err != nil {
+		ctx.T.Log("Unable to stream frame", sf)
 		return err
 	}
 
@@ -154,6 +162,7 @@ func testUnknownFrameType(addr string) error {
 			},
 		}
 		if err := streamFrame(c, fh); err != nil {
+			ctx.T.Log("Unable to stream frame", fh)
 			return err
 		}
 	}
@@ -162,12 +171,14 @@ func testUnknownFrameType(addr string) error {
 		Data: []byte("01234567"),
 	}
 	if err := streamFrame(c, pf); err != nil {
+		ctx.T.Log("Unable to stream frame", sf)
 		return err
 	}
 
 	for {
 		frame, err := parseFrame(c)
 		if err != nil {
+			ctx.T.Log("Unable to parse frame")
 			return err
 		}
 		if npf, ok := frame.(*PingFrame); !ok {
@@ -183,8 +194,8 @@ func testUnknownFrameType(addr string) error {
 	return nil
 }
 
-func testShortPreface(addr string, prefacePrefix string) error {
-	c, err := getHttp2Conn(addr)
+func testShortPreface(ctx *HTTP2InteropCtx, prefacePrefix string) error {
+	c, err := connect(ctx)
 	if err != nil {
 		return err
 	}
@@ -201,17 +212,15 @@ func testShortPreface(addr string, prefacePrefix string) error {
 	return err
 }
 
-func testTLSMaxVersion(addr string, version uint16) error {
-	config := &tls.Config{
-		InsecureSkipVerify: true,
-		NextProtos:         []string{"h2"},
-		MaxVersion:         version,
-	}
-	conn, err := tls.Dial("tcp", addr, config)
+func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error {
+	config := buildTlsConfig(ctx)
+	config.MaxVersion = version
+	conn, err := connectWithTls(ctx, config)
 	if err != nil {
 		return err
 	}
 	defer conn.Close()
+	conn.SetDeadline(time.Now().Add(defaultTimeout))
 
 	buf := make([]byte, 256)
 	if n, err := conn.Read(buf); err != nil {
@@ -223,16 +232,15 @@ func testTLSMaxVersion(addr string, version uint16) error {
 	return nil
 }
 
-func testTLSApplicationProtocol(addr string) error {
-	config := &tls.Config{
-		InsecureSkipVerify: true,
-		NextProtos:         []string{"h2c"},
-	}
-	conn, err := tls.Dial("tcp", addr, config)
+func testTLSApplicationProtocol(ctx *HTTP2InteropCtx) error {
+	config := buildTlsConfig(ctx)
+	config.NextProtos = []string{"h2c"}
+	conn, err := connectWithTls(ctx, config)
 	if err != nil {
 		return err
 	}
 	defer conn.Close()
+	conn.SetDeadline(time.Now().Add(defaultTimeout))
 
 	buf := make([]byte, 256)
 	if n, err := conn.Read(buf); err != nil {
@@ -243,3 +251,48 @@ func testTLSApplicationProtocol(addr string) error {
 	}
 	return nil
 }
+
+func connect(ctx *HTTP2InteropCtx) (net.Conn, error) {
+	var conn net.Conn
+	var err error
+	if !ctx.UseTLS {
+		conn, err = connectWithoutTls(ctx)
+	} else {
+		config := buildTlsConfig(ctx)
+		conn, err = connectWithTls(ctx, config)
+	}
+	if err != nil {
+		return nil, err
+	}
+	conn.SetDeadline(time.Now().Add(defaultTimeout))
+
+	return conn, nil
+}
+
+func buildTlsConfig(ctx *HTTP2InteropCtx) *tls.Config {
+	return &tls.Config{
+		RootCAs:    ctx.rootCAs,
+		NextProtos: []string{"h2"},
+		ServerName: ctx.authority,
+		MinVersion: tls.VersionTLS12,
+		// TODO(carl-mastrangelo): remove this once all test certificates have been updated.
+		InsecureSkipVerify: true,
+	}
+}
+
+func connectWithoutTls(ctx *HTTP2InteropCtx) (net.Conn, error) {
+	conn, err := net.DialTimeout("tcp", ctx.serverSpec, defaultTimeout)
+	if err != nil {
+		return nil, err
+	}
+	return conn, nil
+}
+
+func connectWithTls(ctx *HTTP2InteropCtx, config *tls.Config) (*tls.Conn, error) {
+	conn, err := connectWithoutTls(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	return tls.Client(conn, config), nil
+}
diff --git a/tools/http2_interop/http2interop_test.go b/tools/http2_interop/http2interop_test.go
index 3b687c035e8748bcba20952138d3b02f0cd2b2d2..dc2960048f13e58e3d2a5d79a88d39b2c3834cc1 100644
--- a/tools/http2_interop/http2interop_test.go
+++ b/tools/http2_interop/http2interop_test.go
@@ -2,46 +2,117 @@ package http2interop
 
 import (
 	"crypto/tls"
+	"crypto/x509"
+	"strings"
 	"flag"
+	"fmt"
 	"io"
+	"io/ioutil"
 	"os"
+	"strconv"
 	"testing"
 )
 
 var (
-	serverSpec = flag.String("spec", ":50051", "The server spec to test")
+	serverHost = flag.String("server_host", "", "The host to test")
+	serverPort = flag.Int("server_port", 443, "The port to test")
+	useTls     = flag.Bool("use_tls", true, "Should TLS tests be run")
+	// TODO: implement
+	testCase              = flag.String("test_case", "", "What test cases to run")
+
+	// The rest of these are unused, but present to fulfill the client interface
+	serverHostOverride    = flag.String("server_host_override", "", "Unused")
+	useTestCa             = flag.Bool("use_test_ca", false, "Unused")
+	defaultServiceAccount = flag.String("default_service_account", "", "Unused")
+	oauthScope            = flag.String("oauth_scope", "", "Unused")
+	serviceAccountKeyFile = flag.String("service_account_key_file", "", "Unused")
 )
 
+func InteropCtx(t *testing.T) *HTTP2InteropCtx {
+	ctx := &HTTP2InteropCtx{
+		ServerHost:             *serverHost,
+		ServerPort:             *serverPort,
+		ServerHostnameOverride: *serverHostOverride,
+		UseTLS:                 *useTls,
+		UseTestCa:              *useTestCa,
+		T:                      t,
+	}
+
+	ctx.serverSpec = ctx.ServerHost
+	if ctx.ServerPort != -1 {
+		ctx.serverSpec += ":" + strconv.Itoa(ctx.ServerPort)
+	}
+	if ctx.ServerHostnameOverride == "" {
+		ctx.authority = ctx.ServerHost
+	} else {
+		ctx.authority = ctx.ServerHostnameOverride
+	}
+
+	if ctx.UseTestCa {
+		// It would be odd if useTestCa was true, but not useTls.  meh
+		certData, err := ioutil.ReadFile("src/core/tsi/test_creds/ca.pem")
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		ctx.rootCAs = x509.NewCertPool()
+		if !ctx.rootCAs.AppendCertsFromPEM(certData) {
+			t.Fatal(fmt.Errorf("Unable to parse pem data"))
+		}
+	}
+
+	return ctx
+}
+
+func (ctx *HTTP2InteropCtx) Close() error {
+	// currently a noop
+	return nil
+}
+
 func TestShortPreface(t *testing.T) {
+	ctx := InteropCtx(t)
 	for i := 0; i < len(Preface)-1; i++ {
-		if err := testShortPreface(*serverSpec, Preface[:i]+"X"); err != io.EOF {
+		if err := testShortPreface(ctx, Preface[:i]+"X"); err != io.EOF {
 			t.Error("Expected an EOF but was", err)
 		}
 	}
 }
 
 func TestUnknownFrameType(t *testing.T) {
-	if err := testUnknownFrameType(*serverSpec); err != nil {
+	ctx := InteropCtx(t)
+	if err := testUnknownFrameType(ctx); err != nil {
 		t.Fatal(err)
 	}
 }
 
 func TestTLSApplicationProtocol(t *testing.T) {
-	if err := testTLSApplicationProtocol(*serverSpec); err != io.EOF {
-		t.Fatal("Expected an EOF but was", err)
-	}
+	ctx := InteropCtx(t)
+	err := testTLSApplicationProtocol(ctx); 
+	matchError(t, err, "EOF")
 }
 
 func TestTLSMaxVersion(t *testing.T) {
-	if err := testTLSMaxVersion(*serverSpec, tls.VersionTLS11); err != io.EOF {
-		t.Fatal("Expected an EOF but was", err)
-	}
+	ctx := InteropCtx(t)
+	err := testTLSMaxVersion(ctx, tls.VersionTLS11);
+	matchError(t, err, "EOF", "server selected unsupported protocol")
 }
 
 func TestClientPrefaceWithStreamId(t *testing.T) {
-	if err := testClientPrefaceWithStreamId(*serverSpec); err != io.EOF {
-		t.Fatal("Expected an EOF but was", err)
-	}
+	ctx := InteropCtx(t)
+	err := testClientPrefaceWithStreamId(ctx)
+	matchError(t, err, "EOF")
+}
+
+func matchError(t *testing.T, err error, matches  ... string) {
+  if err == nil {
+    t.Fatal("Expected an error")
+  }
+  for _, s := range matches {
+    if strings.Contains(err.Error(), s) {
+      return
+    }
+  }
+  t.Fatalf("Error %v not in %+v", err, matches)
 }
 
 func TestMain(m *testing.M) {
diff --git a/tools/jenkins/build_docker_and_run_tests.sh b/tools/jenkins/build_docker_and_run_tests.sh
index 5bb2b6b18860037262d67efa1513f7564e9685ba..b44c3805330e2bcb80ffa949ad67bb38513c3205 100755
--- a/tools/jenkins/build_docker_and_run_tests.sh
+++ b/tools/jenkins/build_docker_and_run_tests.sh
@@ -63,6 +63,7 @@ docker run \
   -e "arch=$arch" \
   -e CCACHE_DIR=/tmp/ccache \
   -e XDG_CACHE_HOME=/tmp/xdg-cache-home \
+  -e THIS_IS_REALLY_NEEDED='see https://github.com/docker/docker/issues/14203 for why docker is awful' \
   -i $TTY_FLAG \
   -v "$git_root:/var/local/jenkins/grpc" \
   -v /tmp/ccache:/tmp/ccache \
diff --git a/tools/jenkins/build_interop_image.sh b/tools/jenkins/build_interop_image.sh
index 5dfa24251344878d3dd857da4207ef6c267bfd71..d0c5470ed6bf5489cbff15a27c175d9b8ae7af4e 100755
--- a/tools/jenkins/build_interop_image.sh
+++ b/tools/jenkins/build_interop_image.sh
@@ -84,6 +84,7 @@ CONTAINER_NAME="build_${BASE_NAME}_$(uuidgen)"
 # Prepare image for interop tests, commit it on success.
 (docker run \
   -e CCACHE_DIR=/tmp/ccache \
+  -e THIS_IS_REALLY_NEEDED='see https://github.com/docker/docker/issues/14203 for why docker is awful' \
   -i $TTY_FLAG \
   $MOUNT_ARGS \
   $BUILD_INTEROP_DOCKER_EXTRA_ARGS \
diff --git a/tools/jenkins/grpc_interop_http2/Dockerfile b/tools/jenkins/grpc_interop_http2/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..bb60f09f24748254da3bfc3cfb2ffa2b08f8ba88
--- /dev/null
+++ b/tools/jenkins/grpc_interop_http2/Dockerfile
@@ -0,0 +1,36 @@
+# 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.
+
+FROM golang:1.4
+
+# Using login shell removes Go from path, so we add it.
+RUN ln -s /usr/src/go/bin/go /usr/local/bin
+
+# Define the default command.
+CMD ["bash"]
diff --git a/tools/jenkins/grpc_interop_http2/build_interop.sh b/tools/jenkins/grpc_interop_http2/build_interop.sh
new file mode 100755
index 0000000000000000000000000000000000000000..46ddaf929a8f39731dce016cc814240556e4d206
--- /dev/null
+++ b/tools/jenkins/grpc_interop_http2/build_interop.sh
@@ -0,0 +1,42 @@
+#!/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.
+#
+# Builds http2 interop client in a base image.
+set -e
+
+mkdir -p /var/local/git
+git clone --recursive /var/local/jenkins/grpc /var/local/git/grpc
+
+# copy service account keys if available
+cp -r /var/local/jenkins/service_account $HOME || true
+
+# compile the tests
+(cd /var/local/git/grpc/tools/http2_interop && go test -c)
+
diff --git a/tools/jenkins/run_interop.sh b/tools/jenkins/run_interop.sh
index 5dd477ed77ac2ee927fff38eaaeefc3f8bb7e869..a424aea7fc47641fefd5a39c96d987ea6e569a0a 100755
--- a/tools/jenkins/run_interop.sh
+++ b/tools/jenkins/run_interop.sh
@@ -34,4 +34,4 @@ set -ex
 # Enter the gRPC repo root
 cd $(dirname $0)/../..
 
-tools/run_tests/run_interop_tests.py -l all -s all --cloud_to_prod --cloud_to_prod_auth --use_docker -t -j 12 $@ || true
+tools/run_tests/run_interop_tests.py -l all -s all --cloud_to_prod --cloud_to_prod_auth --use_docker --http2_interop -t -j 12 $@ || true
diff --git a/tools/run_tests/build_csharp.sh b/tools/run_tests/build_csharp.sh
index 6737d88b273fa577083d535442bc70a53ebca3c5..2c3335179229a87bc4b0a2ad305bd4264cfe13db 100755
--- a/tools/run_tests/build_csharp.sh
+++ b/tools/run_tests/build_csharp.sh
@@ -30,7 +30,7 @@
 
 set -ex
 
-if [ "$CONFIG" = "dbg" ]
+if [ "$CONFIG" = "dbg" ] || [ "$CONFIG" = "gcov" ]
 then
   MSBUILD_CONFIG="Debug"
 else
diff --git a/tools/run_tests/dockerjob.py b/tools/run_tests/dockerjob.py
index 1d67fe3033e2d0880fedb512cc96ff0dbeaaf80f..7d64222ba0b53931f57adf0869bc80ee6dd98000 100755
--- a/tools/run_tests/dockerjob.py
+++ b/tools/run_tests/dockerjob.py
@@ -101,7 +101,7 @@ class DockerJob:
 
   def __init__(self, spec):
     self._spec = spec
-    self._job = jobset.Job(spec, bin_hash=None, newline_on_success=True, travis=True, add_env={}, xml_report=None)
+    self._job = jobset.Job(spec, bin_hash=None, newline_on_success=True, travis=True, add_env={})
     self._container_name = spec.container_name
 
   def mapped_port(self, port):
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py
index 8343441a189e09ccc4933b9ddcac2a2ff4b997af..88d95027e28e9934a7f9e55c90173a2474d3bd8a 100755
--- a/tools/run_tests/jobset.py
+++ b/tools/run_tests/jobset.py
@@ -34,15 +34,14 @@ import multiprocessing
 import os
 import platform
 import signal
-import string
 import subprocess
 import sys
 import tempfile
 import time
-import xml.etree.cElementTree as ET
 
 
 _DEFAULT_MAX_JOBS = 16 * multiprocessing.cpu_count()
+_MAX_RESULT_SIZE = 8192
 
 
 # setup a signal handler so that signal.pause registers 'something'
@@ -130,14 +129,6 @@ def which(filename):
   raise Exception('%s not found' % filename)
 
 
-def _filter_stdout(stdout):
-  """Filters out nonprintable and XML-illegal characters from stdout."""
-  # keep whitespaces but remove formfeed and vertical tab characters
-  # that make XML report unparseable.
-  return filter(lambda x: x in string.printable and x != '\f' and x != '\v',
-                stdout.decode(errors='ignore'))
-
-
 class JobSpec(object):
   """Specifies what to run for a job."""
 
@@ -182,6 +173,7 @@ class JobResult(object):
     self.state = 'UNKNOWN'
     self.returncode = -1
     self.elapsed_time = 0
+    self.num_failures = 0
     self.retries = 0
     self.message = ''
     
@@ -189,14 +181,12 @@ class JobResult(object):
 class Job(object):
   """Manages one job."""
 
-  def __init__(self, spec, bin_hash, newline_on_success, travis, add_env, xml_report):
+  def __init__(self, spec, bin_hash, newline_on_success, travis, add_env):
     self._spec = spec
     self._bin_hash = bin_hash
     self._newline_on_success = newline_on_success
     self._travis = travis
     self._add_env = add_env.copy()
-    self._xml_test = ET.SubElement(xml_report, 'testcase',
-                                   name=self._spec.shortname) if xml_report is not None else None
     self._retries = 0
     self._timeout_retries = 0
     self._suppress_failure_message = False
@@ -213,36 +203,40 @@ class Job(object):
     env.update(self._spec.environ)
     env.update(self._add_env)
     self._start = time.time()
-    self._process = subprocess.Popen(args=self._spec.cmdline,
-                                     stderr=subprocess.STDOUT,
-                                     stdout=self._tempfile,
-                                     cwd=self._spec.cwd,
-                                     shell=self._spec.shell,
-                                     env=env)
+    try_start = lambda: subprocess.Popen(args=self._spec.cmdline,
+                                         stderr=subprocess.STDOUT,
+                                         stdout=self._tempfile,
+                                         cwd=self._spec.cwd,
+                                         shell=self._spec.shell,
+                                         env=env)
+    delay = 0.3
+    for i in range(0, 4):
+      try:
+        self._process = try_start()
+        break
+      except OSError:
+        message('WARNING', 'Failed to start %s, retrying in %f seconds' % (self._spec.shortname, delay))
+        time.sleep(delay)
+        delay *= 2
+    else:
+      self._process = try_start()
     self._state = _RUNNING
 
   def state(self, update_cache):
     """Poll current state of the job. Prints messages at completion."""
+    self._tempfile.seek(0)
+    stdout = self._tempfile.read()
+    self.result.message = stdout[-_MAX_RESULT_SIZE:]
     if self._state == _RUNNING and self._process.poll() is not None:
       elapsed = time.time() - self._start
-      self._tempfile.seek(0)
-      stdout = self._tempfile.read()
-      filtered_stdout = _filter_stdout(stdout)
-      # TODO: looks like jenkins master is slow because parsing the junit results XMLs is not
-      # implemented efficiently. This is an experiment to workaround the issue by making sure
-      # results.xml file is small enough.
-      filtered_stdout = filtered_stdout[-128:]
-      self.result.message = filtered_stdout
       self.result.elapsed_time = elapsed
-      if self._xml_test is not None:
-        self._xml_test.set('time', str(elapsed))
-        ET.SubElement(self._xml_test, 'system-out').text = filtered_stdout
       if self._process.returncode != 0:
         if self._retries < self._spec.flake_retries:
           message('FLAKE', '%s [ret=%d, pid=%d]' % (
             self._spec.shortname, self._process.returncode, self._process.pid),
             stdout, do_newline=True)
           self._retries += 1
+          self.result.num_failures += 1
           self.result.retries = self._timeout_retries + self._retries
           self.start()
         else:
@@ -252,9 +246,8 @@ class Job(object):
                 self._spec.shortname, self._process.returncode, self._process.pid),
                 stdout, do_newline=True)
           self.result.state = 'FAILED'
+          self.result.num_failures += 1
           self.result.returncode = self._process.returncode
-          if self._xml_test is not None:
-            ET.SubElement(self._xml_test, 'failure', message='Failure')
       else:
         self._state = _SUCCESS
         message('PASSED', '%s [time=%.1fsec; retries=%d;%d]' % (
@@ -264,13 +257,10 @@ class Job(object):
         if self._bin_hash:
           update_cache.finished(self._spec.identity(), self._bin_hash)
     elif self._state == _RUNNING and time.time() - self._start > self._spec.timeout_seconds:
-      self._tempfile.seek(0)
-      stdout = self._tempfile.read()
-      filtered_stdout = _filter_stdout(stdout)
-      self.result.message = filtered_stdout
       if self._timeout_retries < self._spec.timeout_retries:
         message('TIMEOUT_FLAKE', self._spec.shortname, stdout, do_newline=True)
         self._timeout_retries += 1
+        self.result.num_failures += 1
         self.result.retries = self._timeout_retries + self._retries
         if self._spec.kill_handler:
           self._spec.kill_handler(self)
@@ -280,9 +270,7 @@ class Job(object):
         message('TIMEOUT', self._spec.shortname, stdout, do_newline=True)
         self.kill()
         self.result.state = 'TIMEOUT'
-        if self._xml_test is not None:
-          ET.SubElement(self._xml_test, 'system-out').text = filtered_stdout
-          ET.SubElement(self._xml_test, 'error', message='Timeout')
+        self.result.num_failures += 1
     return self._state
 
   def kill(self):
@@ -294,13 +282,13 @@ class Job(object):
 
   def suppress_failure_message(self):
     self._suppress_failure_message = True
-
+    
 
 class Jobset(object):
   """Manages one run of jobs."""
 
   def __init__(self, check_cancelled, maxjobs, newline_on_success, travis,
-               stop_on_failure, add_env, cache, xml_report):
+               stop_on_failure, add_env, cache):
     self._running = set()
     self._check_cancelled = check_cancelled
     self._cancelled = False
@@ -312,7 +300,6 @@ class Jobset(object):
     self._cache = cache
     self._stop_on_failure = stop_on_failure
     self._hashes = {}
-    self._xml_report = xml_report
     self._add_env = add_env
     self.resultset = {}
     
@@ -344,10 +331,9 @@ class Jobset(object):
                 bin_hash,
                 self._newline_on_success,
                 self._travis,
-                self._add_env,
-                self._xml_report)
+                self._add_env)
       self._running.add(job)
-      self.resultset[job.GetSpec().shortname] = None
+      self.resultset[job.GetSpec().shortname] = []
     return True
 
   def reap(self):
@@ -367,7 +353,7 @@ class Jobset(object):
         break
       for job in dead:
         self._completed += 1
-        self.resultset[job.GetSpec().shortname] = job.result
+        self.resultset[job.GetSpec().shortname].append(job.result)
         self._running.remove(job)
       if dead: return
       if (not self._travis):
@@ -419,13 +405,11 @@ def run(cmdlines,
         infinite_runs=False,
         stop_on_failure=False,
         cache=None,
-        xml_report=None,
         add_env={}):
   js = Jobset(check_cancelled,
               maxjobs if maxjobs is not None else _DEFAULT_MAX_JOBS,
               newline_on_success, travis, stop_on_failure, add_env,
-              cache if cache is not None else NoCache(),
-              xml_report)
+              cache if cache is not None else NoCache())
   for cmdline in cmdlines:
     if not js.start(cmdline):
       break
diff --git a/tools/run_tests/port_server.py b/tools/run_tests/port_server.py
index 3b85486ebfe599732f38f1db2756db9b03edc2e4..14e82b601eadbef7062397a2c33c791bff43318c 100755
--- a/tools/run_tests/port_server.py
+++ b/tools/run_tests/port_server.py
@@ -42,7 +42,7 @@ import time
 # increment this number whenever making a change to ensure that
 # the changes are picked up by running CI servers
 # note that all changes must be backwards compatible
-_MY_VERSION = 5
+_MY_VERSION = 7
 
 
 if len(sys.argv) == 2 and sys.argv[1] == 'dump_version':
diff --git a/tools/run_tests/post_tests_c.sh b/tools/run_tests/post_tests_c.sh
index f2f3ce9432df6493e1a94dce45331615de4ea618..4409526dab256ef620af474e842d007077dad979 100755
--- a/tools/run_tests/post_tests_c.sh
+++ b/tools/run_tests/post_tests_c.sh
@@ -34,8 +34,12 @@ if [ "$CONFIG" != "gcov" ] ; then exit ; fi
 
 root=$(readlink -f $(dirname $0)/../..)
 out=$root/reports/c_cxx_coverage
-tmp=$(mktemp)
+tmp1=$(mktemp)
+tmp2=$(mktemp)
 cd $root
-lcov --capture --directory . --output-file $tmp
-genhtml $tmp --output-directory $out
-rm $tmp
+lcov --capture --directory . --output-file $tmp1
+lcov --extract $tmp1 "$root/src/*" "$root/include/*" --output-file $tmp2
+genhtml $tmp2 --output-directory $out
+rm $tmp2
+rm $tmp1
+
diff --git a/tools/run_tests/report_utils.py b/tools/run_tests/report_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..bb9eca42548aee3e877f6e535ff90fec1843fe57
--- /dev/null
+++ b/tools/run_tests/report_utils.py
@@ -0,0 +1,215 @@
+# 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.
+
+"""Generate XML and HTML test reports."""
+
+import os
+import string
+import xml.etree.cElementTree as ET
+
+
+def _filter_msg(msg, output_format):
+  """Filters out nonprintable and illegal characters from the message."""
+  if output_format in ['XML', 'HTML']:
+    # keep whitespaces but remove formfeed and vertical tab characters
+    # that make XML report unparseable.
+    filtered_msg = filter(
+        lambda x: x in string.printable and x != '\f' and x != '\v',
+        msg.decode(errors='ignore'))
+    if output_format == 'HTML':
+      filtered_msg = filtered_msg.replace('"', '&quot;')
+    return filtered_msg
+  else:
+    return msg
+
+
+def render_xml_report(resultset, xml_report):
+  """Generate JUnit-like XML report."""
+  root = ET.Element('testsuites')
+  testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', 
+                            name='tests')
+  for shortname, results in resultset.iteritems(): 
+    for result in results:
+      xml_test = ET.SubElement(testsuite, 'testcase', name=shortname) 
+      if result.elapsed_time:
+        xml_test.set('time', str(result.elapsed_time))
+      ET.SubElement(xml_test, 'system-out').text = _filter_msg(result.message,
+                                                               'XML')
+      if result.state == 'FAILED':
+        ET.SubElement(xml_test, 'failure', message='Failure')
+      elif result.state == 'TIMEOUT':
+        ET.SubElement(xml_test, 'error', message='Timeout')
+  tree = ET.ElementTree(root)
+  tree.write(xml_report, encoding='UTF-8')
+
+
+# TODO(adelez): Use mako template.
+def fill_one_test_result(shortname, resultset, html_str):
+  if shortname in resultset:
+    # Because interop tests does not have runs_per_test flag, each test is run
+    # once. So there should only be one element for each result.
+    result = resultset[shortname][0] 
+    if result.state == 'PASSED':
+      html_str = '%s<td bgcolor=\"green\">PASS</td>\n' % html_str
+    else:
+      tooltip = ''
+      if result.returncode > 0 or result.message:
+        if result.returncode > 0:
+          tooltip = 'returncode: %d ' % result.returncode
+        if result.message:
+          escaped_msg = _filter_msg(result.message, 'HTML')
+          tooltip = '%smessage: %s' % (tooltip, escaped_msg)       
+      if result.state == 'FAILED':
+        html_str = '%s<td bgcolor=\"red\">' % html_str
+        if tooltip:  
+          html_str = ('%s<a href=\"#\" data-toggle=\"tooltip\" '
+                      'data-placement=\"auto\" title=\"%s\">FAIL</a></td>\n' % 
+                      (html_str, tooltip))
+        else:
+          html_str = '%sFAIL</td>\n' % html_str
+      elif result.state == 'TIMEOUT':
+        html_str = '%s<td bgcolor=\"yellow\">' % html_str
+        if tooltip:
+          html_str = ('%s<a href=\"#\" data-toggle=\"tooltip\" '
+                      'data-placement=\"auto\" title=\"%s\">TIMEOUT</a></td>\n' 
+                      % (html_str, tooltip))
+        else:
+          html_str = '%sTIMEOUT</td>\n' % html_str
+  else:
+    html_str = '%s<td bgcolor=\"magenta\">Not implemented</td>\n' % html_str
+  
+  return html_str
+
+
+def render_html_report(client_langs, server_langs, test_cases, auth_test_cases,
+                       http2_cases, resultset, num_failures, cloud_to_prod, 
+                       http2_interop):
+  """Generate html report."""
+  sorted_test_cases = sorted(test_cases)
+  sorted_auth_test_cases = sorted(auth_test_cases)
+  sorted_http2_cases = sorted(http2_cases)
+  sorted_client_langs = sorted(client_langs)
+  sorted_server_langs = sorted(server_langs)
+  html_str = ('<!DOCTYPE html>\n'
+              '<html lang=\"en\">\n'
+              '<head><title>Interop Test Result</title></head>\n'
+              '<body>\n')
+  if num_failures > 1:
+    html_str = (
+        '%s<p><h2><font color=\"red\">%d tests failed!</font></h2></p>\n' % 
+        (html_str, num_failures))
+  elif num_failures:
+    html_str = (
+        '%s<p><h2><font color=\"red\">%d test failed!</font></h2></p>\n' % 
+        (html_str, num_failures))
+  else:
+    html_str = (
+        '%s<p><h2><font color=\"green\">All tests passed!</font></h2></p>\n' % 
+        html_str)
+  if cloud_to_prod:
+    # Each column header is the client language.
+    html_str = ('%s<h2>Cloud to Prod</h2>\n' 
+                '<table style=\"width:100%%\" border=\"1\">\n'
+                '<tr bgcolor=\"#00BFFF\">\n'
+                '<th>Client languages &#9658;</th>\n') % html_str
+    for client_lang in sorted_client_langs:
+      html_str = '%s<th>%s\n' % (html_str, client_lang)
+    html_str = '%s</tr>\n' % html_str
+    for test_case in sorted_test_cases + sorted_auth_test_cases:
+      html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, test_case)
+      for client_lang in sorted_client_langs:
+        if not test_case in sorted_auth_test_cases:
+          shortname = 'cloud_to_prod:%s:%s' % (client_lang, test_case)
+        else:
+          shortname = 'cloud_to_prod_auth:%s:%s' % (client_lang, test_case)
+        html_str = fill_one_test_result(shortname, resultset, html_str)
+      html_str = '%s</tr>\n' % html_str 
+    html_str = '%s</table>\n' % html_str
+  if http2_interop:
+    # Each column header is the server language.
+    html_str = ('%s<h2>HTTP/2 Interop</h2>\n' 
+                '<table style=\"width:100%%\" border=\"1\">\n'
+                '<tr bgcolor=\"#00BFFF\">\n'
+                '<th>Servers &#9658;<br/>'
+                'Test Cases &#9660;</th>\n') % html_str
+    for server_lang in sorted_server_langs:
+      html_str = '%s<th>%s\n' % (html_str, server_lang)
+    if cloud_to_prod:
+      html_str = '%s<th>%s\n' % (html_str, "prod")
+    html_str = '%s</tr>\n' % html_str
+    for test_case in sorted_http2_cases:
+      html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, test_case)
+      # Fill up the cells with test result.
+      for server_lang in sorted_server_langs:
+        shortname = 'cloud_to_cloud:%s:%s_server:%s' % (
+            "http2", server_lang, test_case)
+        html_str = fill_one_test_result(shortname, resultset, html_str)
+      if cloud_to_prod:
+        shortname = 'cloud_to_prod:%s:%s' % ("http2", test_case)
+        html_str = fill_one_test_result(shortname, resultset, html_str)
+      html_str = '%s</tr>\n' % html_str
+    html_str = '%s</table>\n' % html_str
+  if server_langs:
+    for test_case in sorted_test_cases:
+      # Each column header is the client language.
+      html_str = ('%s<h2>%s</h2>\n' 
+                  '<table style=\"width:100%%\" border=\"1\">\n'
+                  '<tr bgcolor=\"#00BFFF\">\n'
+                  '<th>Client languages &#9658;<br/>'
+                  'Server languages &#9660;</th>\n') % (html_str, test_case)
+      for client_lang in sorted_client_langs:
+        html_str = '%s<th>%s\n' % (html_str, client_lang)
+      html_str = '%s</tr>\n' % html_str
+      # Each row head is the server language.
+      for server_lang in sorted_server_langs:
+        html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, server_lang)
+        # Fill up the cells with test result.
+        for client_lang in sorted_client_langs:
+          shortname = 'cloud_to_cloud:%s:%s_server:%s' % (
+              client_lang, server_lang, test_case)
+          html_str = fill_one_test_result(shortname, resultset, html_str)
+        html_str = '%s</tr>\n' % html_str
+      html_str = '%s</table>\n' % html_str
+
+  html_str = ('%s\n'
+              '<script>\n'
+              '$(document).ready(function(){'
+              '$(\'[data-toggle=\"tooltip\"]\').tooltip();\n'   
+              '});\n'
+              '</script>\n'
+              '</body>\n'
+              '</html>') % html_str  
+  
+  # Write to reports/index.html as set up in Jenkins plugin.
+  html_report_dir = 'reports'
+  if not os.path.exists(html_report_dir):
+    os.mkdir(html_report_dir)
+  html_file_path = os.path.join(html_report_dir, 'index.html')
+  with open(html_file_path, 'w') as f:
+    f.write(html_str)
diff --git a/tools/run_tests/run_csharp.bat b/tools/run_tests/run_csharp.bat
index 310cfe0d2febe85b99b9694b0e01751d83b4c7d8..0e33e5295a4f8f1c946156a935b9320887bc121c 100644
--- a/tools/run_tests/run_csharp.bat
+++ b/tools/run_tests/run_csharp.bat
@@ -2,13 +2,23 @@
 
 setlocal
 
-@rem enter this directory
+@rem enter src/csharp directory
 cd /d %~dp0\..\..\src\csharp
 
-@rem set UUID variable to a random GUID, we will use it to put TestResults.xml to a dedicated directory, so that parallel test runs don't collide
-for /F %%i in ('powershell -Command "[guid]::NewGuid().ToString()"') do (set UUID=%%i)
+if not "%CONFIG%" == "gcov" (
+  @rem Run tests for assembly passed as 1st arg.
 
-packages\NUnit.Runners.2.6.4\tools\nunit-console-x86.exe /domain:None -labels "%1/bin/Debug/%1.dll" -work test-results/%UUID% || goto :error
+  @rem set UUID variable to a random GUID, we will use it to put TestResults.xml to a dedicated directory, so that parallel test runs don't collide
+  for /F %%i in ('powershell -Command "[guid]::NewGuid().ToString()"') do (set UUID=%%i)
+
+  packages\NUnit.Runners.2.6.4\tools\nunit-console-x86.exe /domain:None -labels "%1/bin/Debug/%1.dll" -work test-results/%UUID% || goto :error
+) else (
+  @rem Run all tests with code coverage
+
+  packages\OpenCover.4.6.166\tools\OpenCover.Console.exe -target:"packages\NUnit.Runners.2.6.4\tools\nunit-console-x86.exe" -targetdir:"." -targetargs:"/domain:None -labels Grpc.Core.Tests/bin/Debug/Grpc.Core.Tests.dll Grpc.IntegrationTesting/bin/Debug/Grpc.IntegrationTesting.dll Grpc.Examples.Tests/bin/Debug/Grpc.Examples.Tests.dll Grpc.HealthCheck.Tests/bin/Debug/Grpc.HealthCheck.Tests.dll" -filter:"+[Grpc.Core]*"  -register:user -output:coverage_results.xml || goto :error
+
+  packages\ReportGenerator.2.3.2.0\tools\ReportGenerator.exe -reports:"coverage_results.xml" -targetdir:"..\..\reports\csharp_coverage" -reporttypes:"Html;TextSummary" || goto :error
+)
 
 endlocal
 
diff --git a/tools/run_tests/run_csharp.sh b/tools/run_tests/run_csharp.sh
index c0fedca193532abf2570a7fb2258d8b104166dbf..37e86feaad3c3fe4676c068cf501e83aeb3342ae 100755
--- a/tools/run_tests/run_csharp.sh
+++ b/tools/run_tests/run_csharp.sh
@@ -34,7 +34,7 @@ CONFIG=${CONFIG:-opt}
 
 NUNIT_CONSOLE="mono packages/NUnit.Runners.2.6.4/tools/nunit-console.exe"
 
-if [ "$CONFIG" = "dbg" ]
+if [ "$CONFIG" = "dbg" ] || [ "$CONFIG" = "gcov" ]
 then
   MSBUILD_CONFIG="Debug"
 else
@@ -45,10 +45,24 @@ fi
 cd $(dirname $0)/../..
 
 root=`pwd`
-cd src/csharp
-
 export LD_LIBRARY_PATH=$root/libs/$CONFIG
 
-$NUNIT_CONSOLE -labels "$1/bin/$MSBUILD_CONFIG/$1.dll"
+if [ "$CONFIG" = "gcov" ]
+then
+  (cd src/csharp; $NUNIT_CONSOLE -labels \
+      "Grpc.Core.Tests/bin/$MSBUILD_CONFIG/Grpc.Core.Tests.dll" \
+      "Grpc.Examples.Tests/bin/$MSBUILD_CONFIG/Grpc.Examples.Tests.dll" \
+      "Grpc.HealthCheck.Tests/bin/$MSBUILD_CONFIG/Grpc.HealthCheck.Tests.dll" \
+      "Grpc.IntegrationTesting/bin/$MSBUILD_CONFIG/Grpc.IntegrationTesting.dll")
+
+  gcov objs/gcov/src/csharp/ext/*.o
+  lcov --base-directory . --directory . -c -o coverage.info
+  lcov -e coverage.info '**/src/csharp/ext/*' -o coverage.info
+  genhtml -o reports/csharp_ext_coverage --num-spaces 2 \
+    -t 'gRPC C# native extension test coverage' coverage.info \
+    --rc genhtml_hi_limit=95 --rc genhtml_med_limit=80 --no-prefix
+else
+  (cd src/csharp; $NUNIT_CONSOLE -labels "$1/bin/$MSBUILD_CONFIG/$1.dll")
+fi
 
 
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index e1d60b2de39230af2ba8bb989c36102bdb8a42a0..2634164a2150ea0a7e1cc06d40e9b17f6eab53ee 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -33,10 +33,10 @@
 import argparse
 import dockerjob
 import itertools
-import xml.etree.cElementTree as ET
 import jobset
 import multiprocessing
 import os
+import report_utils
 import subprocess
 import sys
 import tempfile
@@ -159,6 +159,31 @@ class GoLanguage:
     return 'go'
 
 
+class Http2Client:
+  """Represents the HTTP/2 Interop Test
+
+  This pretends to be a language in order to be built and run, but really it
+  isn't.
+  """
+  def __init__(self):
+    self.client_cwd = None
+    self.safename = str(self)
+
+  def client_args(self):
+    return ['tools/http2_interop/http2_interop.test']
+
+  def cloud_to_prod_env(self):
+    return {}
+
+  def global_env(self):
+    return {}
+
+  def unimplemented_test_cases(self):
+    return _TEST_CASES
+
+  def __str__(self):
+    return 'http2'
+
 class NodeLanguage:
 
   def __init__(self):
@@ -281,6 +306,7 @@ _TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
 _AUTH_TEST_CASES = ['compute_engine_creds', 'jwt_token_creds',
                     'oauth2_auth_token', 'per_rpc_creds']
 
+_HTTP2_TEST_CASES = ["tls"]
 
 def docker_run_cmdline(cmdline, image, docker_args=[], cwd=None, environ=None):
   """Wraps given cmdline array to create 'docker run' cmdline from it."""
@@ -439,6 +465,7 @@ def server_jobspec(language, docker_image):
                                       environ=environ,
                                       docker_args=['-p', str(_DEFAULT_SERVER_PORT),
                                                    '--name', container_name])
+
   server_job = jobset.JobSpec(
           cmdline=docker_cmdline,
           environ=environ,
@@ -471,123 +498,6 @@ def build_interop_image_jobspec(language, tag=None):
   return build_job
 
 
-# TODO(adelez): Use mako template.
-def fill_one_test_result(shortname, resultset, html_str):
-  if shortname in resultset:
-    result = resultset[shortname]
-    if result.state == 'PASSED':
-      html_str = '%s<td bgcolor=\"green\">PASS</td>\n' % html_str
-    else:
-      tooltip = ''
-      if result.returncode > 0 or result.message:
-        if result.returncode > 0:
-          tooltip = 'returncode: %d ' % result.returncode
-        if result.message:
-          tooltip = '%smessage: %s' % (tooltip, result.message)     
-      if result.state == 'FAILED':
-        html_str = '%s<td bgcolor=\"red\">' % html_str
-        if tooltip:  
-          html_str = ('%s<a href=\"#\" data-toggle=\"tooltip\" '
-                      'data-placement=\"auto\" title=\"%s\">FAIL</a></td>\n' % 
-                      (html_str, tooltip))
-        else:
-          html_str = '%sFAIL</td>\n' % html_str
-      elif result.state == 'TIMEOUT':
-        html_str = '%s<td bgcolor=\"yellow\">' % html_str
-        if tooltip:
-          html_str = ('%s<a href=\"#\" data-toggle=\"tooltip\" '
-                      'data-placement=\"auto\" title=\"%s\">TIMEOUT</a></td>\n' 
-                      % (html_str, tooltip))
-        else:
-          html_str = '%sTIMEOUT</td>\n' % html_str
-  else:
-    html_str = '%s<td bgcolor=\"magenta\">Not implemented</td>\n' % html_str
-  
-  return html_str
-
-
-def render_html_report(client_langs, server_langs, resultset,
-                       num_failures):
-  """Generate html report."""
-  sorted_test_cases = sorted(_TEST_CASES)
-  sorted_auth_test_cases = sorted(_AUTH_TEST_CASES)
-  sorted_client_langs = sorted(client_langs)
-  sorted_server_langs = sorted(server_langs)
-  html_str = ('<!DOCTYPE html>\n'
-              '<html lang=\"en\">\n'
-              '<head><title>Interop Test Result</title></head>\n'
-              '<body>\n')
-  if num_failures > 1:
-    html_str = (
-        '%s<p><h2><font color=\"red\">%d tests failed!</font></h2></p>\n' % 
-        (html_str, num_failures))
-  elif num_failures:
-    html_str = (
-        '%s<p><h2><font color=\"red\">%d test failed!</font></h2></p>\n' % 
-        (html_str, num_failures))
-  else:
-    html_str = (
-        '%s<p><h2><font color=\"green\">All tests passed!</font></h2></p>\n' % 
-        html_str)
-  if args.cloud_to_prod_auth or args.cloud_to_prod:
-    # Each column header is the client language.
-    html_str = ('%s<h2>Cloud to Prod</h2>\n' 
-                '<table style=\"width:100%%\" border=\"1\">\n'
-                '<tr bgcolor=\"#00BFFF\">\n'
-                '<th>Client languages &#9658;</th>\n') % html_str
-    for client_lang in sorted_client_langs:
-      html_str = '%s<th>%s\n' % (html_str, client_lang)
-    html_str = '%s</tr>\n' % html_str
-    for test_case in sorted_test_cases + sorted_auth_test_cases:
-      html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, test_case)
-      for client_lang in sorted_client_langs:
-        if not test_case in sorted_auth_test_cases:
-          shortname = 'cloud_to_prod:%s:%s' % (client_lang, test_case)
-        else:
-          shortname = 'cloud_to_prod_auth:%s:%s' % (client_lang, test_case)
-        html_str = fill_one_test_result(shortname, resultset, html_str)
-      html_str = '%s</tr>\n' % html_str 
-    html_str = '%s</table>\n' % html_str
-  if servers:
-    for test_case in sorted_test_cases:
-      # Each column header is the client language.
-      html_str = ('%s<h2>%s</h2>\n' 
-                  '<table style=\"width:100%%\" border=\"1\">\n'
-                  '<tr bgcolor=\"#00BFFF\">\n'
-                  '<th>Client languages &#9658;<br/>'
-                  'Server languages &#9660;</th>\n') % (html_str, test_case)
-      for client_lang in sorted_client_langs:
-        html_str = '%s<th>%s\n' % (html_str, client_lang)
-      html_str = '%s</tr>\n' % html_str
-      # Each row head is the server language.
-      for server_lang in sorted_server_langs:
-        html_str = '%s<tr><td><b>%s</b></td>\n' % (html_str, server_lang)
-        # Fill up the cells with test result.
-        for client_lang in sorted_client_langs:
-          shortname = 'cloud_to_cloud:%s:%s_server:%s' % (
-              client_lang, server_lang, test_case)
-          html_str = fill_one_test_result(shortname, resultset, html_str)
-        html_str = '%s</tr>\n' % html_str
-      html_str = '%s</table>\n' % html_str
-
-  html_str = ('%s\n'
-              '<script>\n'
-              '$(document).ready(function(){'
-              '$(\'[data-toggle=\"tooltip\"]\').tooltip();\n'   
-              '});\n'
-              '</script>\n'
-              '</body>\n'
-              '</html>') % html_str  
-  
-  # Write to reports/index.html as set up in Jenkins plugin.
-  html_report_dir = 'reports'
-  if not os.path.exists(html_report_dir):
-    os.mkdir(html_report_dir)
-  html_file_path = os.path.join(html_report_dir, 'index.html')
-  with open(html_file_path, 'w') as f:
-    f.write(html_str)
-
-
 argp = argparse.ArgumentParser(description='Run interop tests.')
 argp.add_argument('-l', '--language',
                   choices=['all'] + sorted(_LANGUAGES),
@@ -633,6 +543,12 @@ argp.add_argument('--allow_flakes',
                   action='store_const',
                   const=True,
                   help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
+argp.add_argument('--http2_interop',
+                  default=False,
+                  action='store_const',
+                  const=True,
+                  help='Enable HTTP/2 interop tests')
+                  
 args = argp.parse_args()
 
 servers = set(s for s in itertools.chain.from_iterable(_SERVERS
@@ -656,12 +572,16 @@ languages = set(_LANGUAGES[l]
                 for l in itertools.chain.from_iterable(
                       _LANGUAGES.iterkeys() if x == 'all' else [x]
                       for x in args.language))
+                      
+http2Interop = Http2Client() if args.http2_interop else None
 
 docker_images={}
 if args.use_docker:
   # languages for which to build docker images
   languages_to_build = set(_LANGUAGES[k] for k in set([str(l) for l in languages] +
                                                     [s for s in servers]))
+  if args.http2_interop:
+    languages_to_build.add(http2Interop)
 
   build_jobs = []
   for l in languages_to_build:
@@ -703,6 +623,15 @@ try:
           test_job = cloud_to_prod_jobspec(language, test_case,
                                            docker_image=docker_images.get(str(language)))
           jobs.append(test_job)
+          
+    # TODO(carl-mastrangelo): Currently prod TLS terminators aren't spec compliant. Reenable
+    # this once a better solution is in place.
+    if args.http2_interop and False:
+      for test_case in _HTTP2_TEST_CASES:
+        test_job = cloud_to_prod_jobspec(http2Interop, test_case,
+                                         docker_image=docker_images.get(str(http2Interop)))
+        jobs.append(test_job)
+     
 
   if args.cloud_to_prod_auth:
     for language in languages:
@@ -730,6 +659,19 @@ try:
                                             server_port,
                                             docker_image=docker_images.get(str(language)))
           jobs.append(test_job)
+          
+    if args.http2_interop:
+      for test_case in _HTTP2_TEST_CASES:
+        if server_name == "go":
+          # TODO(carl-mastrangelo): Reenable after https://github.com/grpc/grpc-go/issues/434
+          continue 
+        test_job = cloud_to_cloud_jobspec(http2Interop,
+                                          test_case,
+                                          server_name,
+                                          server_host,
+                                          server_port,
+                                          docker_image=docker_images.get(str(http2Interop)))
+        jobs.append(test_job)
 
   if not jobs:
     print 'No jobs to run.'
@@ -737,22 +679,19 @@ try:
       dockerjob.remove_image(image, skip_nonexistent=True)
     sys.exit(1)
 
-  root = ET.Element('testsuites')
-  testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', name='tests')
-
   num_failures, resultset = jobset.run(jobs, newline_on_success=True, 
-                                       maxjobs=args.jobs, xml_report=testsuite)
+                                       maxjobs=args.jobs)
   if num_failures:
     jobset.message('FAILED', 'Some tests failed', do_newline=True)
   else:
     jobset.message('SUCCESS', 'All tests passed', do_newline=True)
 
-  tree = ET.ElementTree(root)
-  tree.write('report.xml', encoding='UTF-8')
+  report_utils.render_xml_report(resultset, 'report.xml')
   
-  # Generate HTML report.
-  render_html_report(set([str(l) for l in languages]), servers,
-                     resultset, num_failures)
+  report_utils.render_html_report(
+      set([str(l) for l in languages]), servers, _TEST_CASES, _AUTH_TEST_CASES, 
+      _HTTP2_TEST_CASES, resultset, num_failures,
+      args.cloud_to_prod_auth or args.cloud_to_prod, args.http2_interop)
 
 finally:
   # Check if servers are still running.
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 3859958a9521af0a2364d57abcd5681802be5518..b8017e6fe94ea021ef855d25e8c6868144b11be6 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -46,10 +46,10 @@ import sys
 import tempfile
 import traceback
 import time
-import xml.etree.cElementTree as ET
 import urllib2
 
 import jobset
+import report_utils
 import watch_dirs
 
 ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
@@ -342,10 +342,18 @@ class CSharpLanguage(object):
       cmd = 'tools\\run_tests\\run_csharp.bat'
     else:
       cmd = 'tools/run_tests/run_csharp.sh'
-    return [config.job_spec([cmd, assembly],
-            None, shortname=assembly,
-            environ=_FORCE_ENVIRON_FOR_WRAPPERS)
-            for assembly in assemblies]
+
+    if config.build_config == 'gcov':
+      # On Windows, we only collect C# code coverage.
+      # On Linux, we only collect coverage for native extension.
+      # For code coverage all tests need to run as one suite.
+      return [config.job_spec([cmd], None,
+              environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
+    else:
+      return [config.job_spec([cmd, assembly],
+              None, shortname=assembly,
+              environ=_FORCE_ENVIRON_FOR_WRAPPERS)
+              for assembly in assemblies]
 
   def pre_build_steps(self):
     if self.platform == 'windows':
@@ -411,8 +419,8 @@ class ObjCLanguage(object):
 class Sanity(object):
 
   def test_specs(self, config, travis):
-    return [config.job_spec('tools/run_tests/run_sanity.sh', None),
-            config.job_spec('tools/run_tests/check_sources_and_headers.py', None)]
+    return [config.job_spec(['tools/run_tests/run_sanity.sh'], None),
+            config.job_spec(['tools/run_tests/check_sources_and_headers.py'], None)]
 
   def pre_build_steps(self):
     return []
@@ -450,6 +458,9 @@ class Build(object):
   def build_steps(self):
     return []
 
+  def post_tests_steps(self):
+    return []
+
   def makefile_name(self):
     return 'Makefile'
 
@@ -637,13 +648,16 @@ if platform.system() == 'Windows':
       for target in targets]
 else:
   def make_jobspec(cfg, targets, makefile='Makefile'):
-    return [jobset.JobSpec([os.getenv('MAKE', 'make'),
-                            '-f', makefile,
-                            '-j', '%d' % (multiprocessing.cpu_count() + 1),
-                            'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' %
-                                args.slowdown,
-                            'CONFIG=%s' % cfg] + targets,
-                           timeout_seconds=30*60)]
+    if targets:
+      return [jobset.JobSpec([os.getenv('MAKE', 'make'),
+                              '-f', makefile,
+                              '-j', '%d' % (multiprocessing.cpu_count() + 1),
+                              'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' %
+                              args.slowdown,
+                              'CONFIG=%s' % cfg] + targets,
+                             timeout_seconds=30*60)]
+    else:
+      return []
 make_targets = {}
 for l in languages:
   makefile = l.makefile_name()
@@ -730,7 +744,8 @@ def _start_port_server(port_server_port):
     running = False
   if running:
     current_version = int(subprocess.check_output(
-        [sys.executable, 'tools/run_tests/port_server.py', 'dump_version']))
+        [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
+         'dump_version']))
     print 'my port server is version %d' % current_version
     running = (version >= current_version)
     if not running:
@@ -741,13 +756,18 @@ def _start_port_server(port_server_port):
     fd, logfile = tempfile.mkstemp()
     os.close(fd)
     print 'starting port_server, with log file %s' % logfile
-    args = [sys.executable, 'tools/run_tests/port_server.py', '-p', '%d' % port_server_port, '-l', logfile]
+    args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
+            '-p', '%d' % port_server_port, '-l', logfile]
     env = dict(os.environ)
     env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
     if platform.system() == 'Windows':
+      # Working directory of port server needs to be outside of Jenkins
+      # workspace to prevent file lock issues.
+      tempdir = tempfile.mkdtemp()
       port_server = subprocess.Popen(
           args,
           env=env,
+          cwd=tempdir,
           creationflags = 0x00000008, # detached process
           close_fds=True)
     else:
@@ -798,6 +818,23 @@ def _start_port_server(port_server_port):
         raise
 
 
+def _calculate_num_runs_failures(list_of_results):
+  """Caculate number of runs and failures for a particular test.
+
+  Args:
+    list_of_results: (List) of JobResult object.
+  Returns:
+    A tuple of total number of runs and failures.
+  """
+  num_runs = len(list_of_results)  # By default, there is 1 run per JobResult.
+  num_failures = 0
+  for jobresult in list_of_results:
+    if jobresult.retries > 0:
+      num_runs += jobresult.retries
+    if jobresult.num_failures > 0:
+      num_failures += jobresult.num_failures
+  return num_runs, num_failures
+
 def _build_and_run(
     check_cancelled, newline_on_success, travis, cache, xml_report=None):
   """Do one pass of building & running tests."""
@@ -813,6 +850,7 @@ def _build_and_run(
                  for _ in range(0, args.antagonists)]
   port_server_port = 32767
   _start_port_server(port_server_port)
+  resultset = None
   try:
     infinite_runs = runs_per_test == 0
     one_run = set(
@@ -836,24 +874,30 @@ def _build_and_run(
                      else itertools.repeat(massaged_one_run, runs_per_test))
     all_runs = itertools.chain.from_iterable(runs_sequence)
 
-    root = ET.Element('testsuites') if xml_report else None
-    testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', name='tests') if xml_report else None
-
-    number_failures, _ = jobset.run(
-        all_runs, check_cancelled, newline_on_success=newline_on_success, 
+    number_failures, resultset = jobset.run(
+        all_runs, check_cancelled, newline_on_success=newline_on_success,
         travis=travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
-        stop_on_failure=args.stop_on_failure, 
+        stop_on_failure=args.stop_on_failure,
         cache=cache if not xml_report else None,
-        xml_report=testsuite,
         add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
+    if resultset:
+      for k, v in resultset.iteritems():
+        num_runs, num_failures = _calculate_num_runs_failures(v)
+        if num_failures == num_runs:  # what about infinite_runs???
+          jobset.message('FAILED', k, do_newline=True)
+        elif num_failures > 0:
+          jobset.message(
+              'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
+              do_newline=True)
+        else:
+          jobset.message('PASSED', k, do_newline=True)
     if number_failures:
       return 2
   finally:
     for antagonist in antagonists:
       antagonist.kill()
-    if xml_report:
-      tree = ET.ElementTree(root)
-      tree.write(xml_report, encoding='UTF-8')
+    if xml_report and resultset:
+      report_utils.render_xml_report(resultset, xml_report)
 
   number_failures, _ = jobset.run(
       post_tests_steps, maxjobs=1, stop_on_failure=True,
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 0f4e4874b7501994bb7a07ea3b5dd9e0af9412e2..7f4b339a53d0fe733bf032a7970dcf222d913489 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -1587,6 +1587,23 @@
       "test/cpp/common/secure_auth_context_test.cc"
     ]
   }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util", 
+      "qps"
+    ], 
+    "headers": [], 
+    "language": "c++", 
+    "name": "secure_sync_unary_ping_pong_test", 
+    "src": [
+      "test/cpp/qps/secure_sync_unary_ping_pong_test.cc"
+    ]
+  }, 
   {
     "deps": [
       "gpr", 
@@ -14649,8 +14666,16 @@
       "test/cpp/qps/stats.h", 
       "test/cpp/qps/timer.h", 
       "test/cpp/util/benchmark_config.h", 
-      "test/proto/qpstest.grpc.pb.h", 
-      "test/proto/qpstest.pb.h"
+      "test/proto/benchmarks/control.grpc.pb.h", 
+      "test/proto/benchmarks/control.pb.h", 
+      "test/proto/benchmarks/payloads.grpc.pb.h", 
+      "test/proto/benchmarks/payloads.pb.h", 
+      "test/proto/benchmarks/services.grpc.pb.h", 
+      "test/proto/benchmarks/services.pb.h", 
+      "test/proto/benchmarks/stats.grpc.pb.h", 
+      "test/proto/benchmarks/stats.pb.h", 
+      "test/proto/messages.grpc.pb.h", 
+      "test/proto/messages.pb.h"
     ], 
     "language": "c++", 
     "name": "qps", 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index b03e952c89329e6a07cdd727380e13a242d4cc9e..3abd41dfdc22c37876a468bb4040ed89928467df 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -1497,22 +1497,6 @@
       "windows"
     ]
   }, 
-  {
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "qps_openloop_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
   {
     "ci_platforms": [
       "linux", 
@@ -1558,7 +1542,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c++", 
-    "name": "server_crash_test", 
+    "name": "secure_sync_unary_ping_pong_test", 
     "platforms": [
       "linux", 
       "mac", 
@@ -1569,18 +1553,16 @@
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c++", 
-    "name": "shutdown_test", 
+    "name": "server_crash_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ]
   }, 
   {
@@ -1593,7 +1575,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c++", 
-    "name": "status_test", 
+    "name": "shutdown_test", 
     "platforms": [
       "linux", 
       "mac", 
@@ -1605,34 +1587,34 @@
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ], 
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c++", 
-    "name": "streaming_throughput_test", 
+    "name": "status_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix"
+      "posix", 
+      "windows"
     ]
   }, 
   {
     "ci_platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ], 
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c++", 
-    "name": "stress_test", 
+    "name": "streaming_throughput_test", 
     "platforms": [
       "linux", 
       "mac", 
-      "posix", 
-      "windows"
+      "posix"
     ]
   }, 
   {
diff --git a/vsprojects/vcxproj/qps/qps.vcxproj b/vsprojects/vcxproj/qps/qps.vcxproj
index b361b1b601baca3ab17670fb1fb33c84b4134ed5..9c5a4da0081bc73d5c5eba889a9e75c59b7a19a1 100644
--- a/vsprojects/vcxproj/qps/qps.vcxproj
+++ b/vsprojects/vcxproj/qps/qps.vcxproj
@@ -147,13 +147,45 @@
     <ClInclude Include="..\..\..\test\cpp\util\benchmark_config.h" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="..\..\..\test\proto\qpstest.pb.cc">
+    <ClCompile Include="..\..\..\test\proto\messages.pb.cc">
     </ClCompile>
-    <ClInclude Include="..\..\..\test\proto\qpstest.pb.h">
+    <ClInclude Include="..\..\..\test\proto\messages.pb.h">
     </ClInclude>
-    <ClCompile Include="..\..\..\test\proto\qpstest.grpc.pb.cc">
+    <ClCompile Include="..\..\..\test\proto\messages.grpc.pb.cc">
     </ClCompile>
-    <ClInclude Include="..\..\..\test\proto\qpstest.grpc.pb.h">
+    <ClInclude Include="..\..\..\test\proto\messages.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\control.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\control.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\control.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\control.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\payloads.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\payloads.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\payloads.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\payloads.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\services.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\services.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\services.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\services.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\stats.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\stats.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\stats.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\benchmarks\stats.grpc.pb.h">
     </ClInclude>
     <ClCompile Include="..\..\..\test\cpp\qps\perf_db.pb.cc">
     </ClCompile>
diff --git a/vsprojects/vcxproj/qps/qps.vcxproj.filters b/vsprojects/vcxproj/qps/qps.vcxproj.filters
index cffb5ff118b3702c6fb6659975bd6f26d8db11a7..afa719531647992bfd7eff7debf164fafa5b6b05 100644
--- a/vsprojects/vcxproj/qps/qps.vcxproj.filters
+++ b/vsprojects/vcxproj/qps/qps.vcxproj.filters
@@ -1,9 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="..\..\..\test\proto\qpstest.proto">
+    <ClCompile Include="..\..\..\test\proto\messages.proto">
       <Filter>test\proto</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\control.proto">
+      <Filter>test\proto\benchmarks</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\payloads.proto">
+      <Filter>test\proto\benchmarks</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\services.proto">
+      <Filter>test\proto\benchmarks</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\test\proto\benchmarks\stats.proto">
+      <Filter>test\proto\benchmarks</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\..\test\cpp\qps\perf_db.proto">
       <Filter>test\cpp\qps</Filter>
     </ClCompile>
@@ -90,6 +102,9 @@
     <Filter Include="test\proto">
       <UniqueIdentifier>{44e63a33-67f4-0575-e87a-711a7c9111e2}</UniqueIdentifier>
     </Filter>
+    <Filter Include="test\proto\benchmarks">
+      <UniqueIdentifier>{4180a094-39b4-e46c-1576-940bfe87d284}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
 </Project>