Merge pull request #3247 from yang-g/fix_build_beta

Guard against empty vector
diff --git a/INSTALL b/INSTALL
index de13d65..a0df57d 100644
--- a/INSTALL
+++ b/INSTALL
@@ -11,12 +11,12 @@
 
 On Linux (Debian):
 
- Note: you will need to add the Debian 'testing' distribution to your sources
+ Note: you will need to add the Debian 'jessie-backports' distribution to your sources
  file first.
 
  Add the following line to your `/etc/apt/sources.list` file:
 
-   deb http://ftp.us.debian.org/debian testing main contrib non-free
+   deb http://http.debian.net/debian jessie-backports main
 
  Install the gRPC library:
 
diff --git a/Makefile b/Makefile
index 91d8a0f..4359f78 100644
--- a/Makefile
+++ b/Makefile
@@ -888,6 +888,7 @@
 server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client
 shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test
 status_test: $(BINDIR)/$(CONFIG)/status_test
+streaming_throughput_test: $(BINDIR)/$(CONFIG)/streaming_throughput_test
 sync_streaming_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test
 sync_unary_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test
 thread_stress_test: $(BINDIR)/$(CONFIG)/thread_stress_test
@@ -1730,7 +1731,7 @@
 
 buildtests_c: privatelibs_c $(BINDIR)/$(CONFIG)/alarm_heap_test $(BINDIR)/$(CONFIG)/alarm_list_test $(BINDIR)/$(CONFIG)/alarm_test $(BINDIR)/$(CONFIG)/alpn_test $(BINDIR)/$(CONFIG)/bin_encoder_test $(BINDIR)/$(CONFIG)/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_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)/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)/timers_test $(BINDIR)/$(CONFIG)/transport_metadata_test $(BINDIR)/$(CONFIG)/transport_security_test $(BINDIR)/$(CONFIG)/udp_server_test $(BINDIR)/$(CONFIG)/uri_parser_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_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_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)/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)/server_crash_test $(BINDIR)/$(CONFIG)/server_crash_test_client $(BINDIR)/$(CONFIG)/shutdown_test $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/streaming_throughput_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
@@ -3352,6 +3353,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/shutdown_test || ( echo test shutdown_test failed ; exit 1 )
 	$(E) "[RUN]     Testing status_test"
 	$(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 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"
@@ -10179,6 +10182,46 @@
 endif
 
 
+STREAMING_THROUGHPUT_TEST_SRC = \
+    test/cpp/end2end/streaming_throughput_test.cc \
+
+STREAMING_THROUGHPUT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STREAMING_THROUGHPUT_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/streaming_throughput_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)/streaming_throughput_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/streaming_throughput_test: $(PROTOBUF_DEP) $(STREAMING_THROUGHPUT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(STREAMING_THROUGHPUT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/streaming_throughput_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/streaming_throughput_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+deps_streaming_throughput_test: $(STREAMING_THROUGHPUT_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(STREAMING_THROUGHPUT_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 SYNC_STREAMING_PING_PONG_TEST_SRC = \
     test/cpp/qps/sync_streaming_ping_pong_test.cc \
 
diff --git a/build.yaml b/build.yaml
index d58ef16..757ba13 100644
--- a/build.yaml
+++ b/build.yaml
@@ -970,6 +970,12 @@
   language: c++
   src: [test/cpp/util/status_test.cc]
   deps: [grpc_test_util, grpc++, grpc, gpr_test_util, gpr]
+- name: streaming_throughput_test
+  build: test
+  language: c++
+  src: [test/cpp/end2end/streaming_throughput_test.cc]
+  deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr]
+  platforms: [mac, linux, posix]
 - name: sync_streaming_ping_pong_test
   build: test
   language: c++
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
index fe2b9fa..83133f2 100644
--- a/src/compiler/python_generator.cc
+++ b/src/compiler/python_generator.cc
@@ -339,7 +339,7 @@
     }
     out->Print("}\n");
     out->Print(
-        "return implementations.server("
+        "return early_adopter_implementations.server("
         "\"$PackageQualifiedServiceName$\","
         " method_service_descriptions, port, private_key=private_key,"
         " certificate_chain=certificate_chain)\n",
@@ -422,7 +422,7 @@
     }
     out->Print("}\n");
     out->Print(
-        "return implementations.stub("
+        "return early_adopter_implementations.stub("
         "\"$PackageQualifiedServiceName$\","
         " method_invocation_descriptions, host, port,"
         " metadata_transformer=metadata_transformer, secure=secure,"
@@ -586,13 +586,13 @@
                  "Constructor", name_and_implementation_constructor->second);
     }
     out->Print("}\n");
-    out->Print("server_options = beta.server_options("
+    out->Print("server_options = beta_implementations.server_options("
                "request_deserializers=request_deserializers, "
                "response_serializers=response_serializers, "
                "thread_pool=pool, thread_pool_size=pool_size, "
                "default_timeout=default_timeout, "
                "maximum_timeout=maximum_timeout)\n");
-    out->Print("return beta.server(method_implementations, "
+    out->Print("return beta_implementations.server(method_implementations, "
                "options=server_options)\n");
   }
   return true;
@@ -685,13 +685,13 @@
                  "Cardinality", name_and_cardinality->second);
     }
     out->Print("}\n");
-    out->Print("stub_options = beta.stub_options("
+    out->Print("stub_options = beta_implementations.stub_options("
                "host=host, metadata_transformer=metadata_transformer, "
                "request_serializers=request_serializers, "
                "response_deserializers=response_deserializers, "
                "thread_pool=pool, thread_pool_size=pool_size)\n");
     out->Print(
-        "return beta.dynamic_stub(channel, \'$PackageQualifiedServiceName$\', "
+        "return beta_implementations.dynamic_stub(channel, \'$PackageQualifiedServiceName$\', "
         "cardinalities, options=stub_options)\n",
         "PackageQualifiedServiceName", package_qualified_service_name);
   }
@@ -701,9 +701,9 @@
 bool PrintPreamble(const FileDescriptor* file,
                    const GeneratorConfiguration& config, Printer* out) {
   out->Print("import abc\n");
-  out->Print("from $Package$ import beta\n",
+  out->Print("from $Package$ import implementations as beta_implementations\n",
              "Package", config.beta_package_root);
-  out->Print("from $Package$ import implementations\n",
+  out->Print("from $Package$ import implementations as early_adopter_implementations\n",
              "Package", config.early_adopter_package_root);
   out->Print("from grpc.framework.alpha import utilities as alpha_utilities\n");
   out->Print("from grpc.framework.common import cardinality\n");
diff --git a/src/core/census/grpc_filter.c b/src/core/census/grpc_filter.c
index e01c9a2..8b6ba1d 100644
--- a/src/core/census/grpc_filter.c
+++ b/src/core/census/grpc_filter.c
@@ -36,7 +36,6 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "include/grpc/census.h"
 #include "src/core/channel/channel_stack.h"
 #include "src/core/channel/noop_filter.h"
 #include "src/core/statistics/census_interface.h"
diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c
index 2b61d33..ec832a0 100644
--- a/src/core/channel/http_client_filter.c
+++ b/src/core/channel/http_client_filter.c
@@ -85,16 +85,14 @@
 static void hc_on_recv(void *user_data, int success) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
-  if (success) {
-    size_t i;
-    size_t nops = calld->recv_ops->nops;
-    grpc_stream_op *ops = calld->recv_ops->ops;
-    for (i = 0; i < nops; i++) {
-      grpc_stream_op *op = &ops[i];
-      if (op->type != GRPC_OP_METADATA) continue;
-      calld->got_initial_metadata = 1;
-      grpc_metadata_batch_filter(&op->data.metadata, client_filter, elem);
-    }
+  size_t i;
+  size_t nops = calld->recv_ops->nops;
+  grpc_stream_op *ops = calld->recv_ops->ops;
+  for (i = 0; i < nops; i++) {
+    grpc_stream_op *op = &ops[i];
+    if (op->type != GRPC_OP_METADATA) continue;
+    calld->got_initial_metadata = 1;
+    grpc_metadata_batch_filter(&op->data.metadata, client_filter, elem);
   }
   calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success);
 }
diff --git a/src/core/iomgr/iomgr.c b/src/core/iomgr/iomgr.c
index fdc9adf..1dd0399 100644
--- a/src/core/iomgr/iomgr.c
+++ b/src/core/iomgr/iomgr.c
@@ -108,8 +108,14 @@
   return n;
 }
 
-void grpc_iomgr_shutdown(void) {
+static void dump_objects(const char *kind) {
   grpc_iomgr_object *obj;
+  for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) {
+    gpr_log(GPR_DEBUG, "%s OBJECT: %s %p", kind, obj->name, obj);
+  }
+}
+
+void grpc_iomgr_shutdown(void) {
   grpc_iomgr_closure *closure;
   gpr_timespec shutdown_deadline = gpr_time_add(
       gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN));
@@ -151,12 +157,14 @@
     }
     if (g_root_object.next != &g_root_object) {
       int timeout = 0;
-      gpr_timespec short_deadline = gpr_time_add(
+      while (g_cbs_head == NULL) {
+        gpr_timespec short_deadline = gpr_time_add(
           gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN));
-      while (gpr_cv_wait(&g_rcv, &g_mu, short_deadline) && g_cbs_head == NULL) {
-        if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) {
-          timeout = 1;
-          break;
+        if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline) && g_cbs_head == NULL) {
+          if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) {
+            timeout = 1;
+            break;
+          }
         }
       }
       if (timeout) {
@@ -164,9 +172,7 @@
                 "Failed to free %d iomgr objects before shutdown deadline: "
                 "memory leaks are likely",
                 count_objects());
-        for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) {
-          gpr_log(GPR_DEBUG, "LEAKED OBJECT: %s %p", obj->name, obj);
-        }
+        dump_objects("LEAKED");
         break;
       }
     }
@@ -188,7 +194,7 @@
   obj->name = gpr_strdup(name);
   gpr_mu_lock(&g_mu);
   obj->next = &g_root_object;
-  obj->prev = obj->next->prev;
+  obj->prev = g_root_object.prev;
   obj->next->prev = obj->prev->next = obj;
   gpr_mu_unlock(&g_mu);
 }
diff --git a/src/core/iomgr/socket_windows.c b/src/core/iomgr/socket_windows.c
index 2cbe945..557ca82 100644
--- a/src/core/iomgr/socket_windows.c
+++ b/src/core/iomgr/socket_windows.c
@@ -35,8 +35,12 @@
 
 #ifdef GPR_WINSOCK_SOCKET
 
+#include <winsock2.h>
+#include <mswsock.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
 #include <grpc/support/string_util.h>
 
 #include "src/core/iomgr/iocp_windows.h"
@@ -63,6 +67,24 @@
    various callsites of that function, which happens to be in various
    mutex hold states, and that'd be unsafe to call them directly. */
 void grpc_winsocket_shutdown(grpc_winsocket *winsocket) {
+  /* Grab the function pointer for DisconnectEx for that specific socket.
+     It may change depending on the interface. */
+  int status;
+  GUID guid = WSAID_DISCONNECTEX;
+  LPFN_DISCONNECTEX DisconnectEx;
+  DWORD ioctl_num_bytes;
+
+  status = WSAIoctl(winsocket->socket, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                    &guid, sizeof(guid), &DisconnectEx, sizeof(DisconnectEx),
+                    &ioctl_num_bytes, NULL, NULL);
+
+  if (status == 0) {
+    DisconnectEx(winsocket->socket, NULL, 0, 0);
+  } else {
+    char *utf8_message = gpr_format_message(WSAGetLastError());
+    gpr_log(GPR_ERROR, "Unable to retrieve DisconnectEx pointer : %s", utf8_message);
+    gpr_free(utf8_message);
+  }
   closesocket(winsocket->socket);
 }
 
diff --git a/src/core/iomgr/tcp_client_windows.c b/src/core/iomgr/tcp_client_windows.c
index 665ef28..05198db 100644
--- a/src/core/iomgr/tcp_client_windows.c
+++ b/src/core/iomgr/tcp_client_windows.c
@@ -60,13 +60,13 @@
   grpc_alarm alarm;
   char *addr_name;
   int refs;
-  int aborted;
 } async_connect;
 
-static void async_connect_cleanup(async_connect *ac) {
+static void async_connect_unlock_and_cleanup(async_connect *ac) {
   int done = (--ac->refs == 0);
   gpr_mu_unlock(&ac->mu);
   if (done) {
+    if (ac->socket != NULL) grpc_winsocket_destroy(ac->socket);
     gpr_mu_destroy(&ac->mu);
     gpr_free(ac->addr_name);
     gpr_free(ac);
@@ -77,10 +77,11 @@
   async_connect *ac = acp;
   gpr_mu_lock(&ac->mu);
   /* If the alarm didn't occur, it got cancelled. */
+  gpr_log(GPR_DEBUG, "on_alarm: %p", ac->socket);
   if (ac->socket != NULL && occured) {
     grpc_winsocket_shutdown(ac->socket);
   }
-  async_connect_cleanup(ac);
+  async_connect_unlock_and_cleanup(ac);
 }
 
 static void on_connect(void *acp, int from_iocp) {
@@ -90,12 +91,12 @@
   grpc_winsocket_callback_info *info = &ac->socket->write_info;
   void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb;
   void *cb_arg = ac->cb_arg;
-  int aborted;
-
+  
   grpc_alarm_cancel(&ac->alarm);
 
   gpr_mu_lock(&ac->mu);
-  aborted = ac->aborted;
+
+  gpr_log(GPR_DEBUG, "on_connect: %p", ac->socket);
 
   if (from_iocp) {
     DWORD transfered_bytes = 0;
@@ -107,31 +108,16 @@
       char *utf8_message = gpr_format_message(WSAGetLastError());
       gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
       gpr_free(utf8_message);
-    } else if (!aborted) {
+    } else {
       ep = grpc_tcp_create(ac->socket, ac->addr_name);
+      ac->socket = NULL;
     }
-  } else {
-    gpr_log(GPR_ERROR, "on_connect is shutting down");
-    /* If the connection timeouts, we will still get a notification from
-       the IOCP whatever happens. So we're just going to flag that connection
-       as being in the process of being aborted, and wait for the IOCP. We
-       can't just orphan the socket now, because the IOCP might already have
-       gotten a successful connection, which is our worst-case scenario.
-       We need to call our callback now to respect the deadline. */
-    ac->aborted = 1;
-    gpr_mu_unlock(&ac->mu);
-    cb(cb_arg, NULL);
-    return;
   }
 
-  /* If we don't have an endpoint, it means the connection failed,
-     so it doesn't matter if it aborted or failed. We need to orphan
-     that socket. */
-  if (!ep || aborted) grpc_winsocket_destroy(ac->socket);
-  async_connect_cleanup(ac);
+  async_connect_unlock_and_cleanup(ac);
   /* If the connection was aborted, the callback was already called when
      the deadline was met. */
-  if (!aborted) cb(cb_arg, ep);
+  cb(cb_arg, ep);
 }
 
 /* Tries to issue one async connection, then schedules both an IOCP
@@ -212,7 +198,6 @@
   gpr_mu_init(&ac->mu);
   ac->refs = 2;
   ac->addr_name = grpc_sockaddr_to_uri(addr);
-  ac->aborted = 0;
 
   grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac,
                   gpr_now(GPR_CLOCK_MONOTONIC));
@@ -223,7 +208,7 @@
   utf8_message = gpr_format_message(WSAGetLastError());
   gpr_log(GPR_ERROR, message, utf8_message);
   gpr_free(utf8_message);
-  if (socket) {
+  if (socket != NULL) {
     grpc_winsocket_destroy(socket);
   } else if (sock != INVALID_SOCKET) {
     closesocket(sock);
diff --git a/src/core/support/sync_win32.c b/src/core/support/sync_win32.c
index df23492..f546477 100644
--- a/src/core/support/sync_win32.c
+++ b/src/core/support/sync_win32.c
@@ -88,9 +88,9 @@
     SleepConditionVariableCS(cv, &mu->cs, INFINITE);
   } else {
     gpr_timespec now = gpr_now(abs_deadline.clock_type);
-    gpr_int64 now_ms = now.tv_sec * 1000 + now.tv_nsec / 1000000;
+    gpr_int64 now_ms = (gpr_int64)now.tv_sec * 1000 + now.tv_nsec / 1000000;
     gpr_int64 deadline_ms =
-        abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000;
+      (gpr_int64)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000;
     if (now_ms >= deadline_ms) {
       timeout = 1;
     } else {
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 4426bbb..a8b4d65 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -630,9 +630,6 @@
   call->cancel_alarm = 0;
 
   if (!call->receiving && need_more_data(call)) {
-    op.recv_ops = &call->recv_ops;
-    op.recv_state = &call->recv_state;
-    op.on_done_recv = &call->on_done_recv;
     if (grpc_bbq_empty(&call->incoming_queue) && call->reading_message) {
       op.max_recv_bytes = call->incoming_message_length -
                           call->incoming_message.length + MAX_RECV_PEEK_AHEAD;
@@ -644,9 +641,16 @@
         op.max_recv_bytes = MAX_RECV_PEEK_AHEAD - buffered_bytes;
       }
     }
-    call->receiving = 1;
-    GRPC_CALL_INTERNAL_REF(call, "receiving");
-    start_op = 1;
+    /* TODO(ctiller): 1024 is basically to cover a bug
+       I don't understand yet */
+    if (op.max_recv_bytes > 1024) {
+      op.recv_ops = &call->recv_ops;
+      op.recv_state = &call->recv_state;
+      op.on_done_recv = &call->on_done_recv;
+      call->receiving = 1;
+      GRPC_CALL_INTERNAL_REF(call, "receiving");
+      start_op = 1;
+    }
   }
 
   if (!call->sending) {
diff --git a/src/core/transport/chttp2/writing.c b/src/core/transport/chttp2/writing.c
index 2c8c48f..ac79044 100644
--- a/src/core/transport/chttp2/writing.c
+++ b/src/core/transport/chttp2/writing.c
@@ -113,6 +113,10 @@
 
     if (!stream_global->read_closed &&
         stream_global->unannounced_incoming_window > 0) {
+      GPR_ASSERT(stream_writing->announce_window == 0);
+      GRPC_CHTTP2_FLOWCTL_TRACE_STREAM(
+          "write", transport_writing, stream_writing, announce_window,
+          stream_global->unannounced_incoming_window);
       stream_writing->announce_window =
           stream_global->unannounced_incoming_window;
       GRPC_CHTTP2_FLOWCTL_TRACE_STREAM(
@@ -196,6 +200,9 @@
           &transport_writing->outbuf,
           grpc_chttp2_window_update_create(stream_writing->id,
                                            stream_writing->announce_window));
+      GRPC_CHTTP2_FLOWCTL_TRACE_STREAM(
+          "write", transport_writing, stream_writing, announce_window,
+          -(gpr_int64)stream_writing->announce_window);
       stream_writing->announce_window = 0;
     }
     if (stream_writing->send_closed == GRPC_SEND_CLOSED_WITH_RST_STREAM) {
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index aa6a860..9e3d7dd 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -695,9 +695,9 @@
     }
     grpc_chttp2_incoming_metadata_live_op_buffer_end(
         &stream_global->outstanding_metadata);
+    grpc_chttp2_list_add_read_write_state_changed(transport_global,
+                                                  stream_global);
     if (stream_global->id != 0) {
-      grpc_chttp2_list_add_read_write_state_changed(transport_global,
-                                                    stream_global);
       grpc_chttp2_list_add_writable_stream(transport_global, stream_global);
     }
   }
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index a5945be..a0bcf43 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -42,6 +42,9 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
     </Reference>
+    <Reference Include="CommandLine">
+      <HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
+    </Reference>
     <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index 8343e54..0884c6e 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -37,6 +37,7 @@
 using System.Threading;
 using System.Threading.Tasks;
 
+using CommandLine;
 using Google.Apis.Auth.OAuth2;
 using Google.Protobuf;
 using Grpc.Auth;
@@ -44,25 +45,54 @@
 using Grpc.Core.Utils;
 using Grpc.Testing;
 using NUnit.Framework;
+using CommandLine.Text;
+using System.IO;
 
 namespace Grpc.IntegrationTesting
 {
     public class InteropClient
     {
-        private const string ServiceAccountUser = "155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk@developer.gserviceaccount.com";
-        private const string ComputeEngineUser = "155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel@developer.gserviceaccount.com";
-        private const string AuthScope = "https://www.googleapis.com/auth/xapi.zoo";
-        private const string AuthScopeResponse = "xapi.zoo";
-
         private class ClientOptions
         {
-            public bool help;
-            public string serverHost = "127.0.0.1";
-            public string serverHostOverride = TestCredentials.DefaultHostOverride;
-            public int? serverPort;
-            public string testCase = "large_unary";
-            public bool useTls;
-            public bool useTestCa;
+            [Option("server_host", DefaultValue = "127.0.0.1")]
+            public string ServerHost { get; set; }
+
+            [Option("server_host_override", DefaultValue = TestCredentials.DefaultHostOverride)]
+            public string ServerHostOverride { get; set; }
+
+            [Option("server_port", Required = true)]
+            public int ServerPort { get; set; }
+
+            [Option("test_case", DefaultValue = "large_unary")]
+            public string TestCase { get; set; }
+
+            [Option("use_tls")]
+            public bool UseTls { get; set; }
+
+            [Option("use_test_ca")]
+            public bool UseTestCa { get; set; }
+
+            [Option("default_service_account", Required = false)]
+            public string DefaultServiceAccount { get; set; }
+
+            [Option("oauth_scope", Required = false)]
+            public string OAuthScope { get; set; }
+
+            [Option("service_account_key_file", Required = false)]
+            public string ServiceAccountKeyFile { get; set; }
+
+            [HelpOption]
+            public string GetUsage()
+            {
+                var help = new HelpText
+                {
+                    Heading = "gRPC C# interop testing client",
+                    AddDashesToOption = true
+                };
+                help.AddPreOptionsLine("Usage:");
+                help.AddOptions(this);
+                return help;
+            }
         }
 
         ClientOptions options;
@@ -74,26 +104,9 @@
 
         public static void Run(string[] args)
         {
-            Console.WriteLine("gRPC C# interop testing client");
-            ClientOptions options = ParseArguments(args);
-
-            if (options.serverHost == null || !options.serverPort.HasValue || options.testCase == null)
+            var options = new ClientOptions();
+            if (!Parser.Default.ParseArguments(args, options))
             {
-                Console.WriteLine("Missing required argument.");
-                Console.WriteLine();
-                options.help = true;
-            }
-
-            if (options.help)
-            {
-                Console.WriteLine("Usage:");
-                Console.WriteLine("  --server_host=HOSTNAME");
-                Console.WriteLine("  --server_host_override=HOSTNAME");
-                Console.WriteLine("  --server_port=PORT");
-                Console.WriteLine("  --test_case=TESTCASE");
-                Console.WriteLine("  --use_tls=BOOLEAN");
-                Console.WriteLine("  --use_test_ca=BOOLEAN");
-                Console.WriteLine();
                 Environment.Exit(1);
             }
 
@@ -103,30 +116,27 @@
 
         private async Task Run()
         {
-            Credentials credentials = null;
-            if (options.useTls)
-            {
-                credentials = TestCredentials.CreateTestClientCredentials(options.useTestCa);
-            }
-
+            var credentials = options.UseTls ? TestCredentials.CreateTestClientCredentials(options.UseTestCa) : Credentials.Insecure;
+            
             List<ChannelOption> channelOptions = null;
-            if (!string.IsNullOrEmpty(options.serverHostOverride))
+            if (!string.IsNullOrEmpty(options.ServerHostOverride))
             {
                 channelOptions = new List<ChannelOption>
                 {
-                    new ChannelOption(ChannelOptions.SslTargetNameOverride, options.serverHostOverride)
+                    new ChannelOption(ChannelOptions.SslTargetNameOverride, options.ServerHostOverride)
                 };
             }
-
-            var channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions);
+            Console.WriteLine(options.ServerHost);
+            Console.WriteLine(options.ServerPort);
+            var channel = new Channel(options.ServerHost, options.ServerPort, credentials, channelOptions);
             TestService.TestServiceClient client = new TestService.TestServiceClient(channel);
-            await RunTestCaseAsync(options.testCase, client);
+            await RunTestCaseAsync(client, options);
             channel.ShutdownAsync().Wait();
         }
 
-        private async Task RunTestCaseAsync(string testCase, TestService.TestServiceClient client)
+        private async Task RunTestCaseAsync(TestService.TestServiceClient client, ClientOptions options)
         {
-            switch (testCase)
+            switch (options.TestCase)
             {
                 case "empty_unary":
                     RunEmptyUnary(client);
@@ -146,20 +156,17 @@
                 case "empty_stream":
                     await RunEmptyStreamAsync(client);
                     break;
-                case "service_account_creds":
-                    await RunServiceAccountCredsAsync(client);
-                    break;
                 case "compute_engine_creds":
-                    await RunComputeEngineCredsAsync(client);
+                    await RunComputeEngineCredsAsync(client, options.DefaultServiceAccount, options.OAuthScope);
                     break;
                 case "jwt_token_creds":
-                    await RunJwtTokenCredsAsync(client);
+                    await RunJwtTokenCredsAsync(client, options.DefaultServiceAccount);
                     break;
                 case "oauth2_auth_token":
-                    await RunOAuth2AuthTokenAsync(client);
+                    await RunOAuth2AuthTokenAsync(client, options.DefaultServiceAccount, options.OAuthScope);
                     break;
                 case "per_rpc_creds":
-                    await RunPerRpcCredsAsync(client);
+                    await RunPerRpcCredsAsync(client, options.DefaultServiceAccount, options.OAuthScope);
                     break;
                 case "cancel_after_begin":
                     await RunCancelAfterBeginAsync(client);
@@ -174,7 +181,7 @@
                     RunBenchmarkEmptyUnary(client);
                     break;
                 default:
-                    throw new ArgumentException("Unknown test case " + testCase);
+                    throw new ArgumentException("Unknown test case " + options.TestCase);
             }
         }
 
@@ -313,32 +320,7 @@
             Console.WriteLine("Passed!");
         }
 
-        public static async Task RunServiceAccountCredsAsync(TestService.TestServiceClient client)
-        {
-            Console.WriteLine("running service_account_creds");
-            var credential = await GoogleCredential.GetApplicationDefaultAsync();
-            credential = credential.CreateScoped(new[] { AuthScope });
-            client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
-
-            var request = new SimpleRequest
-            {
-                ResponseType = PayloadType.COMPRESSABLE,
-                ResponseSize = 314159,
-                Payload = CreateZerosPayload(271828),
-                FillUsername = true,
-                FillOauthScope = true
-            };
-
-            var response = client.UnaryCall(request);
-
-            Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
-            Assert.AreEqual(314159, response.Payload.Body.Length);
-            Assert.AreEqual(AuthScopeResponse, response.OauthScope);
-            Assert.AreEqual(ServiceAccountUser, response.Username);
-            Console.WriteLine("Passed!");
-        }
-
-        public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client)
+        public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
         {
             Console.WriteLine("running compute_engine_creds");
             var credential = await GoogleCredential.GetApplicationDefaultAsync();
@@ -358,16 +340,16 @@
 
             Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
             Assert.AreEqual(314159, response.Payload.Body.Length);
-            Assert.AreEqual(AuthScopeResponse, response.OauthScope);
-            Assert.AreEqual(ComputeEngineUser, response.Username);
+            Assert.False(string.IsNullOrEmpty(response.OauthScope));
+            Assert.True(oauthScope.Contains(response.OauthScope));
+            Assert.AreEqual(defaultServiceAccount, response.Username);
             Console.WriteLine("Passed!");
         }
 
-        public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client)
+        public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount)
         {
             Console.WriteLine("running jwt_token_creds");
             var credential = await GoogleCredential.GetApplicationDefaultAsync();
-            // check this a credential with scope support, but don't add the scope.
             Assert.IsTrue(credential.IsCreateScopedRequired);
             client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
 
@@ -377,21 +359,20 @@
                 ResponseSize = 314159,
                 Payload = CreateZerosPayload(271828),
                 FillUsername = true,
-                FillOauthScope = true
             };
 
             var response = client.UnaryCall(request);
 
             Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
             Assert.AreEqual(314159, response.Payload.Body.Length);
-            Assert.AreEqual(ServiceAccountUser, response.Username);
+            Assert.AreEqual(defaultServiceAccount, response.Username);
             Console.WriteLine("Passed!");
         }
 
-        public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client)
+        public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
         {
             Console.WriteLine("running oauth2_auth_token");
-            ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
+            ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
             string oauth2Token = await credential.GetAccessTokenForRequestAsync();
 
             client.HeaderInterceptor = AuthInterceptors.FromAccessToken(oauth2Token);
@@ -404,31 +385,29 @@
 
             var response = client.UnaryCall(request);
 
-            Assert.AreEqual(AuthScopeResponse, response.OauthScope);
-            Assert.AreEqual(ServiceAccountUser, response.Username);
+            Assert.False(string.IsNullOrEmpty(response.OauthScope));
+            Assert.True(oauthScope.Contains(response.OauthScope));
+            Assert.AreEqual(defaultServiceAccount, response.Username);
             Console.WriteLine("Passed!");
         }
 
-        public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client)
+        public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
         {
             Console.WriteLine("running per_rpc_creds");
-
-            ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
-            string oauth2Token = await credential.GetAccessTokenForRequestAsync();
-            var headerInterceptor = AuthInterceptors.FromAccessToken(oauth2Token);
+            ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
+            string accessToken = await credential.GetAccessTokenForRequestAsync();
+            var headerInterceptor = AuthInterceptors.FromAccessToken(accessToken);
 
             var request = new SimpleRequest
             {
                 FillUsername = true,
-                FillOauthScope = true
             };
 
             var headers = new Metadata();
             headerInterceptor(null, "", headers);
             var response = client.UnaryCall(request, headers: headers);
 
-            Assert.AreEqual(AuthScopeResponse, response.OauthScope);
-            Assert.AreEqual(ServiceAccountUser, response.Username);
+            Assert.AreEqual(defaultServiceAccount, response.Username);
             Console.WriteLine("Passed!");
         }
 
@@ -508,68 +487,5 @@
         {
             return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
         }
-
-        private static ClientOptions ParseArguments(string[] args)
-        {
-            var options = new ClientOptions();
-            foreach (string arg in args)
-            {
-                ParseArgument(arg, options);
-                if (options.help)
-                {
-                    break;
-                }
-            }
-            return options;
-        }
-
-        private static void ParseArgument(string arg, ClientOptions options)
-        {
-            Match match;
-            match = Regex.Match(arg, "--server_host=(.*)");
-            if (match.Success)
-            {
-                options.serverHost = match.Groups[1].Value.Trim();
-                return;
-            }
-
-            match = Regex.Match(arg, "--server_host_override=(.*)");
-            if (match.Success)
-            {
-                options.serverHostOverride = match.Groups[1].Value.Trim();
-                return;
-            }
-
-            match = Regex.Match(arg, "--server_port=(.*)");
-            if (match.Success)
-            {
-                options.serverPort = int.Parse(match.Groups[1].Value.Trim());
-                return;
-            }
-
-            match = Regex.Match(arg, "--test_case=(.*)");
-            if (match.Success)
-            {
-                options.testCase = match.Groups[1].Value.Trim();
-                return;
-            }
-
-            match = Regex.Match(arg, "--use_tls=(.*)");
-            if (match.Success)
-            {
-                options.useTls = bool.Parse(match.Groups[1].Value.Trim());
-                return;
-            }
-
-            match = Regex.Match(arg, "--use_test_ca=(.*)");
-            if (match.Success)
-            {
-                options.useTestCa = bool.Parse(match.Groups[1].Value.Trim());
-                return;
-            }
-
-            Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg));
-            options.help = true;
-        }
     }
 }
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
index 718278f..513f872 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
@@ -37,6 +37,9 @@
 using System.IO;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
+
+using CommandLine;
+using CommandLine.Text;
 using Grpc.Core;
 using Grpc.Core.Utils;
 using Grpc.Testing;
@@ -48,9 +51,24 @@
     {
         private class ServerOptions
         {
-            public bool help;
-            public int? port = 8070;
-            public bool useTls;
+            [Option("port", DefaultValue = 8070)]
+            public int Port { get; set; }
+
+            [Option("use_tls")]
+            public bool UseTls { get; set; }
+
+            [HelpOption]
+            public string GetUsage()
+            {
+                var help = new HelpText
+                {
+                    Heading = "gRPC C# interop testing server",
+                    AddDashesToOption = true
+                };
+                help.AddPreOptionsLine("Usage:");
+                help.AddOptions(this);
+                return help;
+            }
         }
 
         ServerOptions options;
@@ -62,22 +80,9 @@
 
         public static void Run(string[] args)
         {
-            Console.WriteLine("gRPC C# interop testing server");
-            ServerOptions options = ParseArguments(args);
-
-            if (!options.port.HasValue)
+            var options = new ServerOptions();
+            if (!Parser.Default.ParseArguments(args, options))
             {
-                Console.WriteLine("Missing required argument.");
-                Console.WriteLine();
-                options.help = true;
-            }
-
-            if (options.help)
-            {
-                Console.WriteLine("Usage:");
-                Console.WriteLine("  --port=PORT");
-                Console.WriteLine("  --use_tls=BOOLEAN");
-                Console.WriteLine();
                 Environment.Exit(1);
             }
 
@@ -93,54 +98,19 @@
             };
 
             string host = "0.0.0.0";
-            int port = options.port.Value;
-            if (options.useTls)
+            int port = options.Port;
+            if (options.UseTls)
             {
                 server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials());
             }
             else
             {
-                server.Ports.Add(host, options.port.Value, ServerCredentials.Insecure);
+                server.Ports.Add(host, options.Port, ServerCredentials.Insecure);
             }
             Console.WriteLine("Running server on " + string.Format("{0}:{1}", host, port));
             server.Start();
 
             server.ShutdownTask.Wait();
         }
-
-        private static ServerOptions ParseArguments(string[] args)
-        {
-            var options = new ServerOptions();
-            foreach (string arg in args)
-            {
-                ParseArgument(arg, options);
-                if (options.help)
-                {
-                    break;
-                }
-            }
-            return options;
-        }
-
-        private static void ParseArgument(string arg, ServerOptions options)
-        {
-            Match match;
-            match = Regex.Match(arg, "--port=(.*)");
-            if (match.Success)
-            {
-                options.port = int.Parse(match.Groups[1].Value.Trim());
-                return;
-            }
-
-            match = Regex.Match(arg, "--use_tls=(.*)");
-            if (match.Success)
-            {
-                options.useTls = bool.Parse(match.Groups[1].Value.Trim());
-                return;
-            }
-
-            Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg));
-            options.help = true;
-        }
     }
 }
diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config
index 8dfded1..bdb3dad 100644
--- a/src/csharp/Grpc.IntegrationTesting/packages.config
+++ b/src/csharp/Grpc.IntegrationTesting/packages.config
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
+  <package id="CommandLineParser" version="1.9.71" targetFramework="net45" />
   <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
   <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
   <package id="Google.Protobuf" version="3.0.0-alpha4" targetFramework="net45" />
diff --git a/src/node/README.md b/src/node/README.md
index 0b97680..7719d08 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -11,10 +11,10 @@
 
 **Linux (Debian):**
 
-Add [Debian testing][] to your `sources.list` file. Example:
+Add [Debian jessie-backports][] to your `sources.list` file. Example:
 
 ```sh
-echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \
+echo "deb http://http.debian.net/debian jessie-backports main" | \
 sudo tee -a /etc/apt/sources.list
 ```
 
@@ -113,4 +113,4 @@
 
 [homebrew]:http://brew.sh
 [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
-[Debian testing]:https://www.debian.org/releases/stretch/
+[Debian jessie-backports]:http://backports.debian.org/Instructions/
diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h
index 4eda499..35f7e16 100644
--- a/src/objective-c/GRPCClient/GRPCCall.h
+++ b/src/objective-c/GRPCClient/GRPCCall.h
@@ -48,11 +48,112 @@
 #import <Foundation/Foundation.h>
 #import <RxLibrary/GRXWriter.h>
 
+#pragma mark gRPC errors
+
+// 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.
+typedef NS_ENUM(NSUInteger, GRPCErrorCode) {
+  // 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.
+  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).
+  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.
+  GRPCErrorCodeDeadlineExceeded = 4,
+
+  // 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.
+  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).
+  GRPCErrorCodePermissionDenied = 7,
+
+  // 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.
+  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.
+  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).
+  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.
+  GRPCErrorCodeOutOfRange = 11,
+
+  // 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.
+  GRPCErrorCodeInternal = 13,
+
+  // 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.
+  GRPCErrorCodeDataLoss = 15,
+};
+
 // 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.
+@protocol GRPCRequestHeaders <NSObject>
+
+@property(nonatomic, readonly) NSUInteger count;
+
+- (id)objectForKeyedSubscript:(NSString *)key;
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
+
+- (void)removeAllObjects;
+- (void)removeObjectForKey:(NSString *)key;
+
+@end
+
 // Represents a single gRPC remote call.
 @interface GRPCCall : GRXWriter
 
@@ -68,10 +169,8 @@
 //
 // After the call is started, trying to modify this property is an error.
 //
-// For convenience, the property is initialized to an empty NSMutableDictionary, and the setter
-// accepts (and copies) both mutable and immutable dictionaries.
-- (NSMutableDictionary *)requestHeaders; // nonatomic
-- (void)setRequestHeaders:(NSDictionary *)requestHeaders; // nonatomic, copy
+// 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
diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m
index ff5d1c5..b6986bf 100644
--- a/src/objective-c/GRPCClient/GRPCCall.m
+++ b/src/objective-c/GRPCClient/GRPCCall.m
@@ -37,6 +37,7 @@
 #include <grpc/support/time.h>
 #import <RxLibrary/GRXConcurrentWriteable.h>
 
+#import "private/GRPCRequestHeaders.h"
 #import "private/GRPCWrappedCall.h"
 #import "private/NSData+GRPC.h"
 #import "private/NSDictionary+GRPC.h"
@@ -93,7 +94,7 @@
   // the response arrives.
   GRPCCall *_retainSelf;
 
-  NSMutableDictionary *_requestHeaders;
+  GRPCRequestHeaders *_requestHeaders;
 }
 
 @synthesize state = _state;
@@ -124,21 +125,11 @@
 
     _requestWriter = requestWriter;
 
-    _requestHeaders = [NSMutableDictionary dictionary];
+    _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self];
   }
   return self;
 }
 
-#pragma mark Metadata
-
-- (NSMutableDictionary *)requestHeaders {
-  return _requestHeaders;
-}
-
-- (void)setRequestHeaders:(NSDictionary *)requestHeaders {
-  _requestHeaders = [NSMutableDictionary dictionaryWithDictionary:requestHeaders];
-}
-
 #pragma mark Finish
 
 - (void)finishWithError:(NSError *)errorOrNil {
@@ -230,10 +221,10 @@
 
 #pragma mark Send headers
 
-- (void)sendHeaders:(NSDictionary *)headers {
+- (void)sendHeaders:(id<GRPCRequestHeaders>)headers {
   // TODO(jcanizales): Add error handlers for async failures
-  [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc]
-                                            initWithMetadata:headers ?: @{} handler:nil]]];
+  [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers
+                                                                                handler:nil]]];
 }
 
 #pragma mark GRXWriteable implementation
diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h
new file mode 100644
index 0000000..1391b57
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#include <grpc/grpc.h>
+
+#import "GRPCCall.h"
+
+@interface GRPCRequestHeaders : NSObject<GRPCRequestHeaders>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray;
+
+- (instancetype)initWithCall:(GRPCCall *)call;
+
+- (id)objectForKeyedSubscript:(NSString *)key;
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
+
+- (void)removeAllObjects;
+- (void)removeObjectForKey:(NSString *)key;
+
+@end
diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m
new file mode 100644
index 0000000..761677c
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m
@@ -0,0 +1,119 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import "GRPCRequestHeaders.h"
+
+#import <Foundation/Foundation.h>
+
+#import "../GRPCCall.h"
+#import "NSDictionary+GRPC.h"
+
+// Used by the setter.
+static void CheckIsNonNilASCII(NSString *name, NSString* value) {
+  if (!value) {
+    [NSException raise:NSInvalidArgumentException format:@"%@ cannot be nil", name];
+  }
+  if (![value canBeConvertedToEncoding:NSASCIIStringEncoding]) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@ %@ contains non-ASCII characters", name, value];
+  }
+}
+
+// Precondition: key isn't nil.
+static void CheckKeyValuePairIsValid(NSString *key, id value) {
+  if ([key hasSuffix:@"-bin"]) {
+    if (![value isKindOfClass:NSData.class]) {
+      [NSException raise:NSInvalidArgumentException
+                  format:@"Expected NSData value for header %@ ending in \"-bin\", "
+       @"instead got %@", key, value];
+    }
+  } else {
+    if (![value isKindOfClass:NSString.class]) {
+      [NSException raise:NSInvalidArgumentException
+                  format:@"Expected NSString value for header %@ not ending in \"-bin\", "
+       @"instead got %@", key, value];
+    }
+    CheckIsNonNilASCII(@"Text header value", (NSString *)value);
+  }
+}
+
+@implementation GRPCRequestHeaders {
+  __weak GRPCCall *_call;
+  NSMutableDictionary *_delegate;
+}
+
+- (instancetype)initWithCall:(GRPCCall *)call {
+  if ((self = [super init])) {
+    _call = call;
+    _delegate = [NSMutableDictionary dictionary];
+  }
+  return self;
+}
+
+- (void)checkCallIsNotStarted {
+  if (_call.state != GRXWriterStateNotStarted) {
+    [NSException raise:@"Invalid modification"
+                format:@"Cannot modify request headers after call is started"];
+  }
+}
+
+- (id)objectForKeyedSubscript:(NSString *)key {
+  return _delegate[key.lowercaseString];
+}
+
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
+  [self checkCallIsNotStarted];
+  CheckIsNonNilASCII(@"Header name", key);
+  key = key.lowercaseString;
+  CheckKeyValuePairIsValid(key, obj);
+  _delegate[key] = obj;
+}
+
+- (void)removeObjectForKey:(NSString *)key {
+  [self checkCallIsNotStarted];
+  [_delegate removeObjectForKey:key.lowercaseString];
+}
+
+- (void)removeAllObjects {
+  [self checkCallIsNotStarted];
+  [_delegate removeAllObjects];
+}
+
+- (NSUInteger)count {
+  return _delegate.count;
+}
+
+- (grpc_metadata *)grpc_metadataArray {
+  return _delegate.grpc_metadataArray;
+}
+@end
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
index da11cbb..4ca2766 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
@@ -35,6 +35,7 @@
 #include <grpc/grpc.h>
 
 #import "GRPCChannel.h"
+#import "GRPCRequestHeaders.h"
 
 @interface GRPCOperation : NSObject
 @property(nonatomic, readonly) grpc_op op;
@@ -44,7 +45,7 @@
 
 @interface GRPCOpSendMetadata : GRPCOperation
 
-- (instancetype)initWithMetadata:(NSDictionary *)metadata
+- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata
                          handler:(void(^)())handler NS_DESIGNATED_INITIALIZER;
 
 @end
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index fe3d51d..cea7c47 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -65,7 +65,7 @@
   return [self initWithMetadata:nil handler:nil];
 }
 
-- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)())handler {
+- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler {
   if (self = [super init]) {
     _op.op = GRPC_OP_SEND_INITIAL_METADATA;
     _op.data.send_initial_metadata.count = metadata.count;
diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
index 99c890e..7477da7 100644
--- a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
+++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
@@ -40,8 +40,8 @@
 @interface NSData (GRPCMetadata)
 + (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata;
 
-// Fill a metadata object with the binary value in this NSData and the given key.
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key;
+// Fill a metadata object with the binary value in this NSData.
+- (void)grpc_initMetadata:(grpc_metadata *)metadata;
 @end
 
 @implementation NSData (GRPCMetadata)
@@ -50,9 +50,7 @@
   return [self dataWithBytes:metadata->value length:metadata->value_length];
 }
 
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key {
-  // TODO(jcanizales): Encode Unicode chars as ASCII.
-  metadata->key = [key stringByAppendingString:@"-bin"].UTF8String;
+- (void)grpc_initMetadata:(grpc_metadata *)metadata {
   metadata->value = self.bytes;
   metadata->value_length = self.length;
 }
@@ -63,8 +61,8 @@
 @interface NSString (GRPCMetadata)
 + (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata;
 
-// Fill a metadata object with the textual value in this NSString and the given key.
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key;
+// Fill a metadata object with the textual value in this NSString.
+- (void)grpc_initMetadata:(grpc_metadata *)metadata;
 @end
 
 @implementation NSString (GRPCMetadata)
@@ -74,22 +72,8 @@
                             encoding:NSASCIIStringEncoding];
 }
 
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key {
-  if ([key hasSuffix:@"-bin"]) {
-    // Disallow this, as at best it will confuse the server. If the app really needs to send a
-    // textual header with a name ending in "-bin", it can be done by removing the suffix and
-    // encoding the NSString as a NSData object.
-    //
-    // Why raise an exception: In the most common case, the developer knows this won't happen in
-    // their code, so the exception isn't triggered. In the rare cases when the developer can't
-    // tell, it's easy enough to add a sanitizing filter before the header is set. There, the
-    // developer can choose whether to drop such a header, or trim its name. Doing either ourselves,
-    // silently, would be very unintuitive for the user.
-    [NSException raise:NSInvalidArgumentException
-                format:@"Metadata keys ending in '-bin' are reserved for NSData values."];
-  }
-  // TODO(jcanizales): Encode Unicode chars as ASCII.
-  metadata->key = key.UTF8String;
+// Precondition: This object contains only ASCII characters.
+- (void)grpc_initMetadata:(grpc_metadata *)metadata {
   metadata->value = self.UTF8String;
   metadata->value_length = self.length;
 }
@@ -105,8 +89,6 @@
 + (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count {
   NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:count];
   for (grpc_metadata *entry = entries; entry < entries + count; entry++) {
-    // TODO(jcanizales): Verify in a C library test that it's converting header names to lower case
-    // automatically.
     NSString *name = [NSString stringWithCString:entry->key encoding:NSASCIIStringEncoding];
     if (!name || metadata[name]) {
       // Log if name is nil?
@@ -114,7 +96,6 @@
     }
     id value;
     if ([name hasSuffix:@"-bin"]) {
-      name = [name substringToIndex:name.length - 4];
       value = [NSData grpc_dataFromMetadataValue:entry];
     } else {
       value = [NSString grpc_stringFromMetadataValue:entry];
@@ -124,19 +105,21 @@
   return metadata;
 }
 
+// Preconditions: All keys are ASCII strings. Keys ending in -bin have NSData values; the others
+// have NSString values.
 - (grpc_metadata *)grpc_metadataArray {
   grpc_metadata *metadata = gpr_malloc([self count] * sizeof(grpc_metadata));
-  int i = 0;
-  for (id key in self) {
+  grpc_metadata *current = metadata;
+  for (NSString* key in self) {
     id value = self[key];
-    grpc_metadata *current = &metadata[i];
-    if ([value respondsToSelector:@selector(grpc_initMetadata:withKey:)]) {
-      [value grpc_initMetadata:current withKey:key];
+    current->key = key.UTF8String;
+    if ([value respondsToSelector:@selector(grpc_initMetadata:)]) {
+      [value grpc_initMetadata:current];
     } else {
       [NSException raise:NSInvalidArgumentException
                   format:@"Metadata values must be NSString or NSData."];
     }
-    i += 1;
+    ++current;
   }
   return metadata;
 }
diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/NSError+GRPC.h
index e712791..f4729dc 100644
--- a/src/objective-c/GRPCClient/private/NSError+GRPC.h
+++ b/src/objective-c/GRPCClient/private/NSError+GRPC.h
@@ -34,29 +34,6 @@
 #import <Foundation/Foundation.h>
 #include <grpc/grpc.h>
 
-// TODO(jcanizales): Make the domain string public.
-extern NSString *const kGRPCErrorDomain;
-
-// TODO(jcanizales): Make this public and document each code.
-typedef NS_ENUM(NSInteger, GRPCErrorCode) {
-  GRPCErrorCodeCancelled = 1,
-  GRPCErrorCodeUnknown = 2,
-  GRPCErrorCodeInvalidArgument = 3,
-  GRPCErrorCodeDeadlineExceeded = 4,
-  GRPCErrorCodeNotFound = 5,
-  GRPCErrorCodeAlreadyExists = 6,
-  GRPCErrorCodePermissionDenied = 7,
-  GRPCErrorCodeUnauthenticated = 16,
-  GRPCErrorCodeResourceExhausted = 8,
-  GRPCErrorCodeFailedPrecondition = 9,
-  GRPCErrorCodeAborted = 10,
-  GRPCErrorCodeOutOfRange = 11,
-  GRPCErrorCodeUnimplemented = 12,
-  GRPCErrorCodeInternal = 13,
-  GRPCErrorCodeUnavailable = 14,
-  GRPCErrorCodeDataLoss = 15
-};
-
 @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|.
diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m
index 889d71a..9bf66f3 100644
--- a/src/objective-c/ProtoRPC/ProtoRPC.m
+++ b/src/objective-c/ProtoRPC/ProtoRPC.m
@@ -37,6 +37,22 @@
 #import <RxLibrary/GRXWriteable.h>
 #import <RxLibrary/GRXWriter+Transformations.h>
 
+static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) {
+  NSDictionary *info = @{
+                         NSLocalizedDescriptionKey: @"Unable to parse response from the server",
+                         NSLocalizedRecoverySuggestionErrorKey: @"If this RPC is idempotent, retry "
+                         @"with exponential backoff. Otherwise, query the server status before "
+                         @"retrying.",
+                         NSUnderlyingErrorKey: parsingError,
+                         @"Expected class": expectedClass,
+                         @"Received value": proto,
+                         };
+  // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public.
+  return [NSError errorWithDomain:@"io.grpc"
+                             code:13
+                         userInfo:info];
+}
+
 @implementation ProtoRPC {
   id<GRXWriteable> _responseWriteable;
 }
@@ -65,14 +81,25 @@
   }
   // A writer that serializes the proto messages to send.
   GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) {
-    // TODO(jcanizales): Fail with an understandable error message if the requestsWriter isn't
-    // sending GPBMessages.
+    if (![proto isKindOfClass:GPBMessage.class]) {
+      [NSException raise:NSInvalidArgumentException
+                  format:@"Request must be a proto message: %@", proto];
+    }
     return [proto data];
   }];
   if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) {
+    __weak ProtoRPC *weakSelf = self;
+
     // A writeable that parses the proto messages received.
     _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
-      [responsesWriteable writeValue:[responseClass parseFromData:value error:NULL]];
+      // TODO(jcanizales): This is done in the main thread, and needs to happen in another thread.
+      NSError *error = nil;
+      id parsed = [responseClass parseFromData:value error:&error];
+      if (parsed) {
+        [responsesWriteable writeValue:parsed];
+      } else {
+        [weakSelf finishWithError:ErrorForBadProto(value, responseClass, error)];
+      }
     } completionHandler:^(NSError *errorOrNil) {
       [responsesWriteable writesFinishedWithError:errorOrNil];
     }];
diff --git a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
index dcb0c4e..d4f8084 100644
--- a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
+++ b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
@@ -15,14 +15,14 @@
     ms.source_files = "*.pbobjc.{h,m}"
     ms.header_mappings_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 = "*.pbrpc.{h,m}"
     ss.header_mappings_dir = "."
     ss.requires_arc = true
-    ss.dependency "gRPC", "~> 0.5"
+    ss.dependency "gRPC", "~> 0.7"
     ss.dependency "#{s.name}/Messages"
   end
 end
diff --git a/src/objective-c/examples/SwiftSample/Bridging-Header.h b/src/objective-c/examples/SwiftSample/Bridging-Header.h
index 33db2dd..65f768a 100644
--- a/src/objective-c/examples/SwiftSample/Bridging-Header.h
+++ b/src/objective-c/examples/SwiftSample/Bridging-Header.h
@@ -39,6 +39,7 @@
 #import <RxLibrary/GRXWriter+Immediate.h>
 #import <GRPCClient/GRPCCall.h>
 #import <ProtoRPC/ProtoMethod.h>
+#import <ProtoRPC/ProtoRPC.h>
 #import <RemoteTest/Test.pbrpc.h>
 
 #endif
diff --git a/src/objective-c/examples/SwiftSample/Podfile b/src/objective-c/examples/SwiftSample/Podfile
index 7b5941e..3611b00 100644
--- a/src/objective-c/examples/SwiftSample/Podfile
+++ b/src/objective-c/examples/SwiftSample/Podfile
@@ -1,6 +1,7 @@
 source 'https://github.com/CocoaPods/Specs.git'
 platform :ios, '8.0'
 
+pod 'Protobuf', :path => "../../../../third_party/protobuf"
 pod 'gRPC', :path => "../../../.."
 pod 'RemoteTest', :path => "../RemoteTestClient"
 
diff --git a/src/objective-c/examples/SwiftSample/ViewController.swift b/src/objective-c/examples/SwiftSample/ViewController.swift
index e4e7aea..76dad9e 100644
--- a/src/objective-c/examples/SwiftSample/ViewController.swift
+++ b/src/objective-c/examples/SwiftSample/ViewController.swift
@@ -45,17 +45,37 @@
     request.fillUsername = true
     request.fillOauthScope = true
 
+
     // Example gRPC call using a generated proto client library:
 
     let service = RMTTestService(host: RemoteHost)
-    service.unaryCallWithRequest(request) { (response: RMTSimpleResponse?, error: NSError?) in
+    service.unaryCallWithRequest(request) { response, error in
       if let response = response {
-        NSLog("Finished successfully with response:\n\(response)")
+        NSLog("1. Finished successfully with response:\n\(response)")
       } else {
-        NSLog("Finished with error: \(error!)")
+        NSLog("1. Finished with error: \(error!)")
       }
     }
 
+
+    // Same but manipulating headers:
+
+    var RPC : ProtoRPC! // Needed to convince Swift to capture by reference (__block)
+    RPC = service.RPCToUnaryCallWithRequest(request) { response, error in
+      if let response = response {
+        NSLog("2. Finished successfully with response:\n\(response)")
+      } else {
+        NSLog("2. Finished with error: \(error!)")
+      }
+      NSLog("2. Response headers: \(RPC.responseHeaders)")
+      NSLog("2. Response trailers: \(RPC.responseTrailers)")
+    }
+
+    RPC.requestHeaders["My-Header"] = "My value"
+
+    RPC.start()
+
+
     // Same example call using the generic gRPC client library:
 
     let method = ProtoMethod(package: "grpc.testing", service: "TestService", method: "UnaryCall")
@@ -64,14 +84,16 @@
 
     let call = GRPCCall(host: RemoteHost, path: method.HTTPPath, requestsWriter: requestsWriter)
 
-    let responsesWriteable = GRXWriteable { (value: AnyObject?, error: NSError?) in
-      if let value = value as? NSData {
-        NSLog("Received response:\n\(RMTSimpleResponse(data: value, error: nil))")
-      } else {
-        NSLog("Finished with error: \(error!)")
-      }
-    }
+    call.requestHeaders["My-Header"] = "My value"
 
-    call.startWithWriteable(responsesWriteable)
+    call.startWithWriteable(GRXWriteable { response, error in
+      if let response = response as? NSData {
+        NSLog("3. Received response:\n\(RMTSimpleResponse(data: response, error: nil))")
+      } else {
+        NSLog("3. Finished with error: \(error!)")
+      }
+      NSLog("3. Response headers: \(call.responseHeaders)")
+      NSLog("3. Response trailers: \(call.responseTrailers)")
+    })
   }
 }
diff --git a/src/php/README.md b/src/php/README.md
index afa09d7..51322c7 100644
--- a/src/php/README.md
+++ b/src/php/README.md
@@ -32,10 +32,10 @@
 
 **Linux (Debian):**
 
-Add [Debian testing][] to your `sources.list` file. Example:
+Add [Debian jessie-backports][] to your `sources.list` file. Example:
 
 ```sh
-echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \
+echo "deb http://http.debian.net/debian jessie-backports main" | \
 sudo tee -a /etc/apt/sources.list
 ```
 
@@ -167,4 +167,4 @@
 [homebrew]:http://brew.sh
 [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
 [Node]:https://github.com/grpc/grpc/tree/master/src/node/examples
-[Debian testing]:https://www.debian.org/releases/stretch/
+[Debian jessie-backports]:http://backports.debian.org/Instructions/
diff --git a/src/python/README.md b/src/python/README.md
index 67d1a17..afe7c73 100644
--- a/src/python/README.md
+++ b/src/python/README.md
@@ -16,10 +16,10 @@
 
 **Linux (Debian):**
 
-Add [Debian testing][] to your `sources.list` file. Example:
+Add [Debian jessie-backports][] to your `sources.list` file. Example:
 
 ```sh
-echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \
+echo "deb http://http.debian.net/debian jessie-backports main" | \
 sudo tee -a /etc/apt/sources.list
 ```
 
@@ -92,4 +92,4 @@
 [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
 [Quick Start]:http://www.grpc.io/docs/tutorials/basic/python.html
 [detailed example]:http://www.grpc.io/docs/installation/python.html
-[Debian testing]:https://www.debian.org/releases/stretch/
+[Debian jessie-backports]:http://backports.debian.org/Instructions/
diff --git a/src/python/grpcio/grpc/_adapter/_intermediary_low.py b/src/python/grpcio/grpc/_adapter/_intermediary_low.py
index 735ad20..e2feec6 100644
--- a/src/python/grpcio/grpc/_adapter/_intermediary_low.py
+++ b/src/python/grpcio/grpc/_adapter/_intermediary_low.py
@@ -59,6 +59,7 @@
 
 _IGNORE_ME_TAG = object()
 Code = _types.StatusCode
+WriteFlags = _types.OpWriteFlags
 
 
 class Status(collections.namedtuple('Status', ['code', 'details'])):
@@ -125,9 +126,9 @@
       ], _TagAdapter(finish_tag, Event.Kind.FINISH))
     return err0 if err0 != _types.CallError.OK else err1 if err1 != _types.CallError.OK else err2 if err2 != _types.CallError.OK else _types.CallError.OK
 
-  def write(self, message, tag):
+  def write(self, message, tag, flags):
     return self._internal.start_batch([
-          _types.OpArgs.send_message(message, 0)
+          _types.OpArgs.send_message(message, flags)
       ], _TagAdapter(tag, Event.Kind.WRITE_ACCEPTED))
 
   def complete(self, tag):
@@ -163,8 +164,11 @@
   def cancel(self):
     return self._internal.cancel()
 
+  def peer(self):
+    return self._internal.peer()
+
   def set_credentials(self, creds):
-    return self._internal.set_credentials(creds)
+    return self._internal.set_credentials(creds._internal)
 
 
 class Channel(object):
diff --git a/src/python/grpcio/grpc/_adapter/fore.py b/src/python/grpcio/grpc/_adapter/fore.py
index daa41e8..acdd69c 100644
--- a/src/python/grpcio/grpc/_adapter/fore.py
+++ b/src/python/grpcio/grpc/_adapter/fore.py
@@ -56,7 +56,7 @@
 def _write(call, rpc_state, payload):
   serialized_payload = rpc_state.serializer(payload)
   if rpc_state.write.low is _LowWrite.OPEN:
-    call.write(serialized_payload, call)
+    call.write(serialized_payload, call, 0)
     rpc_state.write.low = _LowWrite.ACTIVE
   else:
     rpc_state.write.pending.append(serialized_payload)
@@ -164,7 +164,7 @@
 
     if rpc_state.write.pending:
       serialized_payload = rpc_state.write.pending.pop(0)
-      call.write(serialized_payload, call)
+      call.write(serialized_payload, call, 0)
     elif rpc_state.write.high is _common.HighWrite.CLOSED:
       _status(call, rpc_state)
     else:
diff --git a/src/python/grpcio/grpc/_adapter/rear.py b/src/python/grpcio/grpc/_adapter/rear.py
index fd6f45f..17fa47f 100644
--- a/src/python/grpcio/grpc/_adapter/rear.py
+++ b/src/python/grpcio/grpc/_adapter/rear.py
@@ -78,7 +78,7 @@
 
 def _write(operation_id, call, outstanding, write_state, serialized_payload):
   if write_state.low is _LowWrite.OPEN:
-    call.write(serialized_payload, operation_id)
+    call.write(serialized_payload, operation_id, 0)
     outstanding.add(_low.Event.Kind.WRITE_ACCEPTED)
     write_state.low = _LowWrite.ACTIVE
   elif write_state.low is _LowWrite.ACTIVE:
@@ -144,7 +144,7 @@
     if event.write_accepted:
       if rpc_state.common.write.pending:
         rpc_state.call.write(
-            rpc_state.common.write.pending.pop(0), operation_id)
+            rpc_state.common.write.pending.pop(0), operation_id, 0)
         rpc_state.outstanding.add(_low.Event.Kind.WRITE_ACCEPTED)
       elif rpc_state.common.write.high is _common.HighWrite.CLOSED:
         rpc_state.call.complete(operation_id)
@@ -263,7 +263,7 @@
         low_state = _LowWrite.OPEN
     else:
       serialized_payload = request_serializer(payload)
-      call.write(serialized_payload, operation_id)
+      call.write(serialized_payload, operation_id, 0)
       outstanding.add(_low.Event.Kind.WRITE_ACCEPTED)
       low_state = _LowWrite.ACTIVE
 
diff --git a/src/python/grpcio/grpc/_links/invocation.py b/src/python/grpcio/grpc/_links/invocation.py
index fecb550..67ef86a 100644
--- a/src/python/grpcio/grpc/_links/invocation.py
+++ b/src/python/grpcio/grpc/_links/invocation.py
@@ -37,6 +37,7 @@
 
 from grpc._adapter import _intermediary_low
 from grpc._links import _constants
+from grpc.beta import interfaces as beta_interfaces
 from grpc.framework.foundation import activated
 from grpc.framework.foundation import logging_pool
 from grpc.framework.foundation import relay
@@ -73,11 +74,28 @@
   CLOSED = 'CLOSED'
 
 
+class _Context(beta_interfaces.GRPCInvocationContext):
+
+  def __init__(self):
+    self._lock = threading.Lock()
+    self._disable_next_compression = False
+
+  def disable_next_request_compression(self):
+    with self._lock:
+      self._disable_next_compression = True
+
+  def next_compression_disabled(self):
+    with self._lock:
+      disabled = self._disable_next_compression
+      self._disable_next_compression = False
+      return disabled
+
+
 class _RPCState(object):
 
   def __init__(
       self, call, request_serializer, response_deserializer, sequence_number,
-      read, allowance, high_write, low_write, due):
+      read, allowance, high_write, low_write, due, context):
     self.call = call
     self.request_serializer = request_serializer
     self.response_deserializer = response_deserializer
@@ -87,6 +105,7 @@
     self.high_write = high_write
     self.low_write = low_write
     self.due = due
+    self.context = context
 
 
 def _no_longer_due(kind, rpc_state, key, rpc_states):
@@ -209,7 +228,7 @@
 
   def _invoke(
       self, operation_id, group, method, initial_metadata, payload, termination,
-      timeout, allowance):
+      timeout, allowance, options):
     """Invoke an RPC.
 
     Args:
@@ -224,6 +243,7 @@
       timeout: A duration of time in seconds to allow for the RPC.
       allowance: The number of payloads (beyond the free first one) that the
         local ticket exchange mate has granted permission to be read.
+      options: A beta_interfaces.GRPCCallOptions value or None.
     """
     if termination is links.Ticket.Termination.COMPLETION:
       high_write = _HighWrite.CLOSED
@@ -241,6 +261,8 @@
     call = _intermediary_low.Call(
         self._channel, self._completion_queue, '/%s/%s' % (group, method),
         self._host, time.time() + timeout)
+    if options is not None and options.credentials is not None:
+      call.set_credentials(options.credentials._intermediary_low_credentials)
     if transformed_initial_metadata is not None:
       for metadata_key, metadata_value in transformed_initial_metadata:
         call.add_metadata(metadata_key, metadata_value)
@@ -254,17 +276,33 @@
         low_write = _LowWrite.OPEN
         due = set((_METADATA, _FINISH,))
     else:
-      call.write(request_serializer(payload), operation_id)
+      if options is not None and options.disable_compression:
+        flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS
+      else:
+        flags = 0
+      call.write(request_serializer(payload), operation_id, flags)
       low_write = _LowWrite.ACTIVE
       due = set((_WRITE, _METADATA, _FINISH,))
+    context = _Context()
     self._rpc_states[operation_id] = _RPCState(
-        call, request_serializer, response_deserializer, 0,
+        call, request_serializer, response_deserializer, 1,
         _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance),
-        high_write, low_write, due)
+        high_write, low_write, due, context)
+    protocol = links.Protocol(links.Protocol.Kind.INVOCATION_CONTEXT, context)
+    ticket = links.Ticket(
+        operation_id, 0, None, None, None, None, None, None, None, None, None,
+        None, None, protocol)
+    self._relay.add_value(ticket)
 
   def _advance(self, operation_id, rpc_state, payload, termination, allowance):
     if payload is not None:
-      rpc_state.call.write(rpc_state.request_serializer(payload), operation_id)
+      disable_compression = rpc_state.context.next_compression_disabled()
+      if disable_compression:
+        flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS
+      else:
+        flags = 0
+      rpc_state.call.write(
+          rpc_state.request_serializer(payload), operation_id, flags)
       rpc_state.low_write = _LowWrite.ACTIVE
       rpc_state.due.add(_WRITE)
 
@@ -292,10 +330,15 @@
         if self._completion_queue is None:
           logging.error('Received invocation ticket %s after stop!', ticket)
         else:
+          if (ticket.protocol is not None and
+              ticket.protocol.kind is links.Protocol.Kind.CALL_OPTION):
+            grpc_call_options = ticket.protocol.value
+          else:
+            grpc_call_options = None
           self._invoke(
               ticket.operation_id, ticket.group, ticket.method,
               ticket.initial_metadata, ticket.payload, ticket.termination,
-              ticket.timeout, ticket.allowance)
+              ticket.timeout, ticket.allowance, grpc_call_options)
       else:
         rpc_state = self._rpc_states.get(ticket.operation_id)
         if rpc_state is not None:
diff --git a/src/python/grpcio/grpc/_links/service.py b/src/python/grpcio/grpc/_links/service.py
index 34d3b26..f56df84 100644
--- a/src/python/grpcio/grpc/_links/service.py
+++ b/src/python/grpcio/grpc/_links/service.py
@@ -37,6 +37,7 @@
 
 from grpc._adapter import _intermediary_low
 from grpc._links import _constants
+from grpc.beta import interfaces as beta_interfaces
 from grpc.framework.foundation import logging_pool
 from grpc.framework.foundation import relay
 from grpc.framework.interfaces.links import links
@@ -89,12 +90,34 @@
   CLOSED = 'CLOSED'
 
 
+class _Context(beta_interfaces.GRPCServicerContext):
+
+  def __init__(self, call):
+    self._lock = threading.Lock()
+    self._call = call
+    self._disable_next_compression = False
+
+  def peer(self):
+    with self._lock:
+      return self._call.peer()
+
+  def disable_next_response_compression(self):
+    with self._lock:
+      self._disable_next_compression = True
+
+  def next_compression_disabled(self):
+    with self._lock:
+      disabled = self._disable_next_compression
+      self._disable_next_compression = False
+      return disabled
+
+
 class _RPCState(object):
 
   def __init__(
       self, request_deserializer, response_serializer, sequence_number, read,
       early_read, allowance, high_write, low_write, premetadataed,
-      terminal_metadata, code, message, due):
+      terminal_metadata, code, message, due, context):
     self.request_deserializer = request_deserializer
     self.response_serializer = response_serializer
     self.sequence_number = sequence_number
@@ -110,6 +133,7 @@
     self.code = code
     self.message = message
     self.due = due
+    self.context = context
 
 
 def _no_longer_due(kind, rpc_state, key, rpc_states):
@@ -163,14 +187,16 @@
         (group, method), _IDENTITY)
 
     call.read(call)
+    context = _Context(call)
     self._rpc_states[call] = _RPCState(
         request_deserializer, response_serializer, 1, _Read.READING, None, 1,
         _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None,
-        set((_READ, _FINISH,)))
+        set((_READ, _FINISH,)), context)
+    protocol = links.Protocol(links.Protocol.Kind.SERVICER_CONTEXT, context)
     ticket = links.Ticket(
         call, 0, group, method, links.Ticket.Subscription.FULL,
         service_acceptance.deadline - time.time(), None, event.metadata, None,
-        None, None, None, None, 'TODO: Service Context Object!')
+        None, None, None, None, protocol)
     self._relay.add_value(ticket)
 
   def _on_read_event(self, event):
@@ -311,7 +337,12 @@
           self._relay.add_value(early_read_ticket)
 
       if ticket.payload is not None:
-        call.write(rpc_state.response_serializer(ticket.payload), call)
+        disable_compression = rpc_state.context.next_compression_disabled()
+        if disable_compression:
+          flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS
+        else:
+          flags = 0
+        call.write(rpc_state.response_serializer(ticket.payload), call, flags)
         rpc_state.due.add(_WRITE)
         rpc_state.low_write = _LowWrite.ACTIVE
 
diff --git a/src/python/grpcio/grpc/beta/_connectivity_channel.py b/src/python/grpcio/grpc/beta/_connectivity_channel.py
index 457ede7..61674a7 100644
--- a/src/python/grpcio/grpc/beta/_connectivity_channel.py
+++ b/src/python/grpcio/grpc/beta/_connectivity_channel.py
@@ -33,18 +33,24 @@
 import time
 
 from grpc._adapter import _low
+from grpc._adapter import _types
+from grpc.beta import interfaces
 from grpc.framework.foundation import callable_util
 
 _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = (
     'Exception calling channel subscription callback!')
 
+_LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = {
+    state: connectivity for state, connectivity in zip(
+        _types.ConnectivityState, interfaces.ChannelConnectivity)
+}
+
 
 class ConnectivityChannel(object):
 
-  def __init__(self, low_channel, mapping):
+  def __init__(self, low_channel):
     self._lock = threading.Lock()
     self._low_channel = low_channel
-    self._mapping = mapping
 
     self._polling = False
     self._connectivity = None
@@ -88,7 +94,8 @@
     try_to_connect = initial_try_to_connect
     low_connectivity = low_channel.check_connectivity_state(try_to_connect)
     with self._lock:
-      self._connectivity = self._mapping[low_connectivity]
+      self._connectivity = _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[
+          low_connectivity]
       callbacks = tuple(
           callback for callback, unused_but_known_to_be_none_connectivity
           in self._callbacks_and_connectivities)
@@ -112,7 +119,8 @@
       if event.success or try_to_connect:
         low_connectivity = low_channel.check_connectivity_state(try_to_connect)
         with self._lock:
-          self._connectivity = self._mapping[low_connectivity]
+          self._connectivity = _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[
+              low_connectivity]
           if not self._delivering:
             callbacks = self._deliveries(self._connectivity)
             if callbacks:
diff --git a/src/python/grpcio/grpc/beta/_server.py b/src/python/grpcio/grpc/beta/_server.py
index ebf91d8..daa42c4 100644
--- a/src/python/grpcio/grpc/beta/_server.py
+++ b/src/python/grpcio/grpc/beta/_server.py
@@ -72,7 +72,7 @@
   event.set()
 
 
-class Server(object):
+class Server(interfaces.Server):
 
   def __init__(self, grpc_link, end_link, pool):
     self._grpc_link = grpc_link
@@ -82,9 +82,9 @@
   def add_insecure_port(self, address):
     return self._grpc_link.add_port(address, None)
 
-  def add_secure_port(self, address, intermediary_low_server_credentials):
+  def add_secure_port(self, address, server_credentials):
     return self._grpc_link.add_port(
-        address, intermediary_low_server_credentials)
+        address, server_credentials._intermediary_low_credentials)  # pylint: disable=protected-access
 
   def start(self):
     self._grpc_link.join_link(self._end_link)
diff --git a/src/python/grpcio/grpc/beta/beta.py b/src/python/grpcio/grpc/beta/implementations.py
similarity index 75%
rename from src/python/grpcio/grpc/beta/beta.py
rename to src/python/grpcio/grpc/beta/implementations.py
index b3a1610..9b461fb 100644
--- a/src/python/grpcio/grpc/beta/beta.py
+++ b/src/python/grpcio/grpc/beta/implementations.py
@@ -40,6 +40,7 @@
 from grpc.beta import _connectivity_channel
 from grpc.beta import _server
 from grpc.beta import _stub
+from grpc.beta import interfaces
 from grpc.framework.common import cardinality  # pylint: disable=unused-import
 from grpc.framework.interfaces.face import face  # pylint: disable=unused-import
 
@@ -47,32 +48,6 @@
     'Exception calling channel subscription callback!')
 
 
-@enum.unique
-class ChannelConnectivity(enum.Enum):
-  """Mirrors grpc_connectivity_state in the gRPC Core.
-
-  Attributes:
-    IDLE: The channel is idle.
-    CONNECTING: The channel is connecting.
-    READY: The channel is ready to conduct RPCs.
-    TRANSIENT_FAILURE: The channel has seen a failure from which it expects to
-      recover.
-    FATAL_FAILURE: The channel has seen a failure from which it cannot recover.
-  """
-
-  IDLE = (_types.ConnectivityState.IDLE, 'idle',)
-  CONNECTING = (_types.ConnectivityState.CONNECTING, 'connecting',)
-  READY = (_types.ConnectivityState.READY, 'ready',)
-  TRANSIENT_FAILURE = (
-      _types.ConnectivityState.TRANSIENT_FAILURE, 'transient failure',)
-  FATAL_FAILURE = (_types.ConnectivityState.FATAL_FAILURE, 'fatal failure',)
-
-_LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = {
-    state: connectivity for state, connectivity in zip(
-        _types.ConnectivityState, ChannelConnectivity)
-}
-
-
 class ClientCredentials(object):
   """A value encapsulating the data required to create a secure Channel.
 
@@ -118,13 +93,14 @@
     self._low_channel = low_channel
     self._intermediary_low_channel = intermediary_low_channel
     self._connectivity_channel = _connectivity_channel.ConnectivityChannel(
-        low_channel, _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY)
+        low_channel)
 
   def subscribe(self, callback, try_to_connect=None):
     """Subscribes to this Channel's connectivity.
 
     Args:
-      callback: A callable to be invoked and passed this Channel's connectivity.
+      callback: A callable to be invoked and passed an
+        interfaces.ChannelConnectivity identifying this Channel's connectivity.
         The callable will be invoked immediately upon subscription and again for
         every change to this Channel's connectivity thereafter until it is
         unsubscribed.
@@ -144,7 +120,7 @@
     self._connectivity_channel.unsubscribe(callback)
 
 
-def create_insecure_channel(host, port):
+def insecure_channel(host, port):
   """Creates an insecure Channel to a remote host.
 
   Args:
@@ -159,7 +135,7 @@
   return Channel(intermediary_low_channel._internal, intermediary_low_channel)  # pylint: disable=protected-access
 
 
-def create_secure_channel(host, port, client_credentials):
+def secure_channel(host, port, client_credentials):
   """Creates a secure Channel to a remote host.
 
   Args:
@@ -313,86 +289,6 @@
         intermediary_low_credentials._internal, intermediary_low_credentials)  # pylint: disable=protected-access
 
 
-class Server(object):
-  """Services RPCs."""
-  __metaclass__ = abc.ABCMeta
-
-  @abc.abstractmethod
-  def add_insecure_port(self, address):
-    """Reserves a port for insecure RPC service once this Server becomes active.
-
-    This method may only be called before calling this Server's start method is
-    called.
-
-    Args:
-      address: The address for which to open a port.
-
-    Returns:
-      An integer port on which RPCs will be serviced after this link has been
-        started. This is typically the same number as the port number contained
-        in the passed address, but will likely be different if the port number
-        contained in the passed address was zero.
-    """
-    raise NotImplementedError()
-
-  @abc.abstractmethod
-  def add_secure_port(self, address, server_credentials):
-    """Reserves a port for secure RPC service after this Server becomes active.
-
-    This method may only be called before calling this Server's start method is
-    called.
-
-    Args:
-      address: The address for which to open a port.
-      server_credentials: A ServerCredentials.
-
-    Returns:
-      An integer port on which RPCs will be serviced after this link has been
-        started. This is typically the same number as the port number contained
-        in the passed address, but will likely be different if the port number
-        contained in the passed address was zero.
-    """
-    raise NotImplementedError()
-
-  @abc.abstractmethod
-  def start(self):
-    """Starts this Server's service of RPCs.
-
-    This method may only be called while the server is not serving RPCs (i.e. it
-    is not idempotent).
-    """
-    raise NotImplementedError()
-
-  @abc.abstractmethod
-  def stop(self, grace):
-    """Stops this Server's service of RPCs.
-
-    All calls to this method immediately stop service of new RPCs. When existing
-    RPCs are aborted is controlled by the grace period parameter passed to this
-    method.
-
-    This method may be called at any time and is idempotent. Passing a smaller
-    grace value than has been passed in a previous call will have the effect of
-    stopping the Server sooner. Passing a larger grace value than has been
-    passed in a previous call will not have the effect of stopping the sooner
-    later.
-
-    Args:
-      grace: A duration of time in seconds to allow existing RPCs to complete
-        before being aborted by this Server's stopping. May be zero for
-        immediate abortion of all in-progress RPCs.
-
-    Returns:
-      A threading.Event that will be set when this Server has completely
-      stopped. The returned event may not be set until after the full grace
-      period (if some ongoing RPC continues for the full length of the period)
-      of it may be set much sooner (such as if this Server had no RPCs underway
-      at the time it was stopped or if all RPCs that it had underway completed
-      very early in the grace period).
-    """
-    raise NotImplementedError()
-
-
 class ServerOptions(object):
   """A value encapsulating the various options for creation of a Server.
 
@@ -450,27 +346,8 @@
       thread_pool, thread_pool_size, default_timeout, maximum_timeout)
 
 
-class _Server(Server):
-
-  def __init__(self, underserver):
-    self._underserver = underserver
-
-  def add_insecure_port(self, address):
-    return self._underserver.add_insecure_port(address)
-
-  def add_secure_port(self, address, server_credentials):
-    return self._underserver.add_secure_port(
-        address, server_credentials._intermediary_low_credentials)  # pylint: disable=protected-access
-
-  def start(self):
-    self._underserver.start()
-
-  def stop(self, grace):
-    return self._underserver.stop(grace)
-
-
 def server(service_implementations, options=None):
-  """Creates a Server with which RPCs can be serviced.
+  """Creates an interfaces.Server with which RPCs can be serviced.
 
   Args:
     service_implementations: A dictionary from service name-method name pair to
@@ -479,13 +356,12 @@
       functionality of the returned Server.
 
   Returns:
-    A Server with which RPCs can be serviced.
+    An interfaces.Server with which RPCs can be serviced.
   """
   effective_options = _EMPTY_SERVER_OPTIONS if options is None else options
-  underserver = _server.server(
+  return _server.server(
       service_implementations, effective_options.multi_method_implementation,
       effective_options.request_deserializers,
       effective_options.response_serializers, effective_options.thread_pool,
       effective_options.thread_pool_size, effective_options.default_timeout,
       effective_options.maximum_timeout)
-  return _Server(underserver)
diff --git a/src/python/grpcio/grpc/beta/interfaces.py b/src/python/grpcio/grpc/beta/interfaces.py
index 25e6a9c..07c8618 100644
--- a/src/python/grpcio/grpc/beta/interfaces.py
+++ b/src/python/grpcio/grpc/beta/interfaces.py
@@ -29,8 +29,31 @@
 
 """Constants and interfaces of the Beta API of gRPC Python."""
 
+import abc
 import enum
 
+from grpc._adapter import _types
+
+
+@enum.unique
+class ChannelConnectivity(enum.Enum):
+  """Mirrors grpc_connectivity_state in the gRPC Core.
+
+  Attributes:
+    IDLE: The channel is idle.
+    CONNECTING: The channel is connecting.
+    READY: The channel is ready to conduct RPCs.
+    TRANSIENT_FAILURE: The channel has seen a failure from which it expects to
+      recover.
+    FATAL_FAILURE: The channel has seen a failure from which it cannot recover.
+  """
+  IDLE = (_types.ConnectivityState.IDLE, 'idle',)
+  CONNECTING = (_types.ConnectivityState.CONNECTING, 'connecting',)
+  READY = (_types.ConnectivityState.READY, 'ready',)
+  TRANSIENT_FAILURE = (
+      _types.ConnectivityState.TRANSIENT_FAILURE, 'transient failure',)
+  FATAL_FAILURE = (_types.ConnectivityState.FATAL_FAILURE, 'fatal failure',)
+
 
 @enum.unique
 class StatusCode(enum.Enum):
@@ -52,3 +75,140 @@
   UNAVAILABLE         = 14
   DATA_LOSS           = 15
   UNAUTHENTICATED     = 16
+
+
+class GRPCCallOptions(object):
+  """A value encapsulating gRPC-specific options passed on RPC invocation.
+
+  This class and its instances have no supported interface - it exists to
+  define the type of its instances and its instances exist to be passed to
+  other functions.
+  """
+
+  def __init__(self, disable_compression, subcall_of, credentials):
+    self.disable_compression = disable_compression
+    self.subcall_of = subcall_of
+    self.credentials = credentials
+
+
+def grpc_call_options(disable_compression=False, credentials=None):
+  """Creates a GRPCCallOptions value to be passed at RPC invocation.
+
+  All parameters are optional and should always be passed by keyword.
+
+  Args:
+    disable_compression: A boolean indicating whether or not compression should
+      be disabled for the request object of the RPC. Only valid for
+      request-unary RPCs.
+    credentials: A ClientCredentials object to use for the invoked RPC.
+  """
+  return GRPCCallOptions(disable_compression, None, credentials)
+
+
+class GRPCServicerContext(object):
+  """Exposes gRPC-specific options and behaviors to code servicing RPCs."""
+  __metaclass__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def peer(self):
+    """Identifies the peer that invoked the RPC being serviced.
+
+    Returns:
+      A string identifying the peer that invoked the RPC being serviced.
+    """
+    raise NotImplementedError()
+
+  @abc.abstractmethod
+  def disable_next_response_compression(self):
+    """Disables compression of the next response passed by the application."""
+    raise NotImplementedError()
+
+
+class GRPCInvocationContext(object):
+  """Exposes gRPC-specific options and behaviors to code invoking RPCs."""
+  __metaclass__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def disable_next_request_compression(self):
+    """Disables compression of the next request passed by the application."""
+    raise NotImplementedError()
+
+
+class Server(object):
+  """Services RPCs."""
+  __metaclass__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def add_insecure_port(self, address):
+    """Reserves a port for insecure RPC service once this Server becomes active.
+
+    This method may only be called before calling this Server's start method is
+    called.
+
+    Args:
+      address: The address for which to open a port.
+
+    Returns:
+      An integer port on which RPCs will be serviced after this link has been
+        started. This is typically the same number as the port number contained
+        in the passed address, but will likely be different if the port number
+        contained in the passed address was zero.
+    """
+    raise NotImplementedError()
+
+  @abc.abstractmethod
+  def add_secure_port(self, address, server_credentials):
+    """Reserves a port for secure RPC service after this Server becomes active.
+
+    This method may only be called before calling this Server's start method is
+    called.
+
+    Args:
+      address: The address for which to open a port.
+      server_credentials: A ServerCredentials.
+
+    Returns:
+      An integer port on which RPCs will be serviced after this link has been
+        started. This is typically the same number as the port number contained
+        in the passed address, but will likely be different if the port number
+        contained in the passed address was zero.
+    """
+    raise NotImplementedError()
+
+  @abc.abstractmethod
+  def start(self):
+    """Starts this Server's service of RPCs.
+
+    This method may only be called while the server is not serving RPCs (i.e. it
+    is not idempotent).
+    """
+    raise NotImplementedError()
+
+  @abc.abstractmethod
+  def stop(self, grace):
+    """Stops this Server's service of RPCs.
+
+    All calls to this method immediately stop service of new RPCs. When existing
+    RPCs are aborted is controlled by the grace period parameter passed to this
+    method.
+
+    This method may be called at any time and is idempotent. Passing a smaller
+    grace value than has been passed in a previous call will have the effect of
+    stopping the Server sooner. Passing a larger grace value than has been
+    passed in a previous call will not have the effect of stopping the sooner
+    later.
+
+    Args:
+      grace: A duration of time in seconds to allow existing RPCs to complete
+        before being aborted by this Server's stopping. May be zero for
+        immediate abortion of all in-progress RPCs.
+
+    Returns:
+      A threading.Event that will be set when this Server has completely
+      stopped. The returned event may not be set until after the full grace
+      period (if some ongoing RPC continues for the full length of the period)
+      of it may be set much sooner (such as if this Server had no RPCs underway
+      at the time it was stopped or if all RPCs that it had underway completed
+      very early in the grace period).
+    """
+    raise NotImplementedError()
diff --git a/src/python/grpcio/grpc/beta/utilities.py b/src/python/grpcio/grpc/beta/utilities.py
index 1b5356e..fb07a76 100644
--- a/src/python/grpcio/grpc/beta/utilities.py
+++ b/src/python/grpcio/grpc/beta/utilities.py
@@ -32,7 +32,9 @@
 import threading
 import time
 
-from grpc.beta import beta
+# implementations is referenced from specification in this module.
+from grpc.beta import implementations  # pylint: disable=unused-import
+from grpc.beta import interfaces
 from grpc.framework.foundation import callable_util
 from grpc.framework.foundation import future
 
@@ -70,7 +72,8 @@
 
   def _update(self, connectivity):
     with self._condition:
-      if not self._cancelled and connectivity is beta.ChannelConnectivity.READY:
+      if (not self._cancelled and
+          connectivity is interfaces.ChannelConnectivity.READY):
         self._matured = True
         self._channel.unsubscribe(self._update)
         self._condition.notify_all()
@@ -141,19 +144,19 @@
 
 
 def channel_ready_future(channel):
-  """Creates a future.Future that matures when a beta.Channel is ready.
+  """Creates a future.Future tracking when an implementations.Channel is ready.
 
-  Cancelling the returned future.Future does not tell the given beta.Channel to
-  abandon attempts it may have been making to connect; cancelling merely
-  deactivates the return future.Future's subscription to the given
-  beta.Channel's connectivity.
+  Cancelling the returned future.Future does not tell the given
+  implementations.Channel to abandon attempts it may have been making to
+  connect; cancelling merely deactivates the return future.Future's
+  subscription to the given implementations.Channel's connectivity.
 
   Args:
-    channel: A beta.Channel.
+    channel: An implementations.Channel.
 
   Returns:
     A future.Future that matures when the given Channel has connectivity
-      beta.ChannelConnectivity.READY.
+      interfaces.ChannelConnectivity.READY.
   """
   ready_future = _ChannelReadyFuture(channel)
   ready_future.start()
diff --git a/src/python/grpcio/grpc/framework/core/_end.py b/src/python/grpcio/grpc/framework/core/_end.py
index 336e9c2..8e07d90 100644
--- a/src/python/grpcio/grpc/framework/core/_end.py
+++ b/src/python/grpcio/grpc/framework/core/_end.py
@@ -168,7 +168,7 @@
 
   def operate(
       self, group, method, subscription, timeout, initial_metadata=None,
-      payload=None, completion=None):
+      payload=None, completion=None, protocol_options=None):
     """See base.End.operate for specification."""
     operation_id = uuid.uuid4()
     with self._lock:
@@ -177,9 +177,9 @@
       termination_action = _termination_action(
           self._lock, self._stats, operation_id, self._cycle)
       operation = _operation.invocation_operate(
-          operation_id, group, method, subscription, timeout, initial_metadata,
-          payload, completion, self._mate.accept_ticket, termination_action,
-          self._cycle.pool)
+          operation_id, group, method, subscription, timeout, protocol_options,
+          initial_metadata, payload, completion, self._mate.accept_ticket,
+          termination_action, self._cycle.pool)
       self._cycle.operations[operation_id] = operation
       return operation.context, operation.operator
 
diff --git a/src/python/grpcio/grpc/framework/core/_ingestion.py b/src/python/grpcio/grpc/framework/core/_ingestion.py
index 9a7959a..4129a8c 100644
--- a/src/python/grpcio/grpc/framework/core/_ingestion.py
+++ b/src/python/grpcio/grpc/framework/core/_ingestion.py
@@ -140,7 +140,7 @@
 
   def __init__(
       self, lock, pool, subscription, subscription_creator, termination_manager,
-      transmission_manager, expiration_manager):
+      transmission_manager, expiration_manager, protocol_manager):
     """Constructor.
 
     Args:
@@ -157,12 +157,14 @@
       transmission_manager: The _interfaces.TransmissionManager for the
         operation.
       expiration_manager: The _interfaces.ExpirationManager for the operation.
+      protocol_manager: The _interfaces.ProtocolManager for the operation.
     """
     self._lock = lock
     self._pool = pool
     self._termination_manager = termination_manager
     self._transmission_manager = transmission_manager
     self._expiration_manager = expiration_manager
+    self._protocol_manager = protocol_manager
 
     if subscription is None:
       self._subscription_creator = subscription_creator
@@ -296,6 +298,8 @@
           self._abort_and_notify(
               base.Outcome.Kind.REMOTE_FAILURE, code, details)
     elif outcome.return_value.subscription.kind is base.Subscription.Kind.FULL:
+      self._protocol_manager.set_protocol_receiver(
+          outcome.return_value.subscription.protocol_receiver)
       self._operator_post_create(outcome.return_value.subscription)
     else:
       # TODO(nathaniel): Support other subscriptions.
@@ -378,7 +382,7 @@
 
 def invocation_ingestion_manager(
     subscription, lock, pool, termination_manager, transmission_manager,
-    expiration_manager):
+    expiration_manager, protocol_manager):
   """Creates an IngestionManager appropriate for invocation-side use.
 
   Args:
@@ -390,18 +394,20 @@
     transmission_manager: The _interfaces.TransmissionManager for the
       operation.
     expiration_manager: The _interfaces.ExpirationManager for the operation.
+    protocol_manager: The _interfaces.ProtocolManager for the operation.
 
   Returns:
     An IngestionManager appropriate for invocation-side use.
   """
   return _IngestionManager(
       lock, pool, subscription, None, termination_manager, transmission_manager,
-      expiration_manager)
+      expiration_manager, protocol_manager)
 
 
 def service_ingestion_manager(
     servicer, operation_context, output_operator, lock, pool,
-    termination_manager, transmission_manager, expiration_manager):
+    termination_manager, transmission_manager, expiration_manager,
+    protocol_manager):
   """Creates an IngestionManager appropriate for service-side use.
 
   The returned IngestionManager will require its set_group_and_name method to be
@@ -420,6 +426,7 @@
     transmission_manager: The _interfaces.TransmissionManager for the
       operation.
     expiration_manager: The _interfaces.ExpirationManager for the operation.
+    protocol_manager: The _interfaces.ProtocolManager for the operation.
 
   Returns:
     An IngestionManager appropriate for service-side use.
@@ -428,4 +435,4 @@
       servicer, operation_context, output_operator)
   return _IngestionManager(
       lock, pool, None, subscription_creator, termination_manager,
-      transmission_manager, expiration_manager)
+      transmission_manager, expiration_manager, protocol_manager)
diff --git a/src/python/grpcio/grpc/framework/core/_interfaces.py b/src/python/grpcio/grpc/framework/core/_interfaces.py
index 2a534cb..ffa686b 100644
--- a/src/python/grpcio/grpc/framework/core/_interfaces.py
+++ b/src/python/grpcio/grpc/framework/core/_interfaces.py
@@ -111,8 +111,8 @@
 
   @abc.abstractmethod
   def kick_off(
-      self, group, method, timeout, initial_metadata, payload, completion,
-      allowance):
+      self, group, method, timeout, protocol_options, initial_metadata,
+      payload, completion, allowance):
     """Transmits the values associated with operation invocation."""
     raise NotImplementedError()
 
@@ -203,6 +203,31 @@
     raise NotImplementedError()
 
 
+class ProtocolManager(object):
+  """A manager of protocol-specific values passing through an operation."""
+  __metaclass__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def set_protocol_receiver(self, protocol_receiver):
+    """Registers the customer object that will receive protocol objects.
+
+    Args:
+      protocol_receiver: A base.ProtocolReceiver to which protocol objects for
+        the operation should be passed.
+    """
+    raise NotImplementedError()
+
+  @abc.abstractmethod
+  def accept_protocol_context(self, protocol_context):
+    """Accepts the protocol context object for the operation.
+
+    Args:
+      protocol_context: An object designated for use as the protocol context
+        of the operation, with further semantics implementation-determined.
+    """
+    raise NotImplementedError()
+
+
 class EmissionManager(base.Operator):
   """A manager of values emitted by customer code."""
   __metaclass__ = abc.ABCMeta
diff --git a/src/python/grpcio/grpc/framework/core/_operation.py b/src/python/grpcio/grpc/framework/core/_operation.py
index f5679d0..020c0c9 100644
--- a/src/python/grpcio/grpc/framework/core/_operation.py
+++ b/src/python/grpcio/grpc/framework/core/_operation.py
@@ -36,6 +36,7 @@
 from grpc.framework.core import _expiration
 from grpc.framework.core import _ingestion
 from grpc.framework.core import _interfaces
+from grpc.framework.core import _protocol
 from grpc.framework.core import _reception
 from grpc.framework.core import _termination
 from grpc.framework.core import _transmission
@@ -84,8 +85,9 @@
 
 
 def invocation_operate(
-    operation_id, group, method, subscription, timeout, initial_metadata,
-    payload, completion, ticket_sink, termination_action, pool):
+    operation_id, group, method, subscription, timeout, protocol_options,
+    initial_metadata, payload, completion, ticket_sink, termination_action,
+    pool):
   """Constructs objects necessary for front-side operation management.
 
   Args:
@@ -95,6 +97,8 @@
     subscription: A base.Subscription describing the customer's interest in the
       results of the operation.
     timeout: A length of time in seconds to allow for the operation.
+    protocol_options: A transport-specific, application-specific, and/or
+      protocol-specific value relating to the invocation. May be None.
     initial_metadata: An initial metadata value to be sent to the other side of
       the operation. May be None if the initial metadata will be passed later or
       if there will be no initial metadata passed at all.
@@ -120,23 +124,27 @@
         operation_id, ticket_sink, lock, pool, termination_manager)
     expiration_manager = _expiration.invocation_expiration_manager(
         timeout, lock, termination_manager, transmission_manager)
+    protocol_manager = _protocol.invocation_protocol_manager(
+        subscription, lock, pool, termination_manager, transmission_manager,
+        expiration_manager)
     operation_context = _context.OperationContext(
         lock, termination_manager, transmission_manager, expiration_manager)
     emission_manager = _emission.EmissionManager(
         lock, termination_manager, transmission_manager, expiration_manager)
     ingestion_manager = _ingestion.invocation_ingestion_manager(
         subscription, lock, pool, termination_manager, transmission_manager,
-        expiration_manager)
+        expiration_manager, protocol_manager)
     reception_manager = _reception.ReceptionManager(
         termination_manager, transmission_manager, expiration_manager,
-        ingestion_manager)
+        protocol_manager, ingestion_manager)
 
     termination_manager.set_expiration_manager(expiration_manager)
     transmission_manager.set_expiration_manager(expiration_manager)
     emission_manager.set_ingestion_manager(ingestion_manager)
 
     transmission_manager.kick_off(
-        group, method, timeout, initial_metadata, payload, completion, None)
+        group, method, timeout, protocol_options, initial_metadata, payload,
+        completion, None)
 
   return _EasyOperation(
       lock, termination_manager, transmission_manager, expiration_manager,
@@ -170,16 +178,20 @@
         ticket.timeout, servicer_package.default_timeout,
         servicer_package.maximum_timeout, lock, termination_manager,
         transmission_manager)
+    protocol_manager = _protocol.service_protocol_manager(
+        lock, pool, termination_manager, transmission_manager,
+        expiration_manager)
     operation_context = _context.OperationContext(
         lock, termination_manager, transmission_manager, expiration_manager)
     emission_manager = _emission.EmissionManager(
         lock, termination_manager, transmission_manager, expiration_manager)
     ingestion_manager = _ingestion.service_ingestion_manager(
         servicer_package.servicer, operation_context, emission_manager, lock,
-        pool, termination_manager, transmission_manager, expiration_manager)
+        pool, termination_manager, transmission_manager, expiration_manager,
+        protocol_manager)
     reception_manager = _reception.ReceptionManager(
         termination_manager, transmission_manager, expiration_manager,
-        ingestion_manager)
+        protocol_manager, ingestion_manager)
 
     termination_manager.set_expiration_manager(expiration_manager)
     transmission_manager.set_expiration_manager(expiration_manager)
diff --git a/src/python/grpcio/grpc/framework/core/_protocol.py b/src/python/grpcio/grpc/framework/core/_protocol.py
new file mode 100644
index 0000000..3177b5e
--- /dev/null
+++ b/src/python/grpcio/grpc/framework/core/_protocol.py
@@ -0,0 +1,176 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""State and behavior for passing protocol objects in an operation."""
+
+import collections
+import enum
+
+from grpc.framework.core import _constants
+from grpc.framework.core import _interfaces
+from grpc.framework.core import _utilities
+from grpc.framework.foundation import callable_util
+from grpc.framework.interfaces.base import base
+
+_EXCEPTION_LOG_MESSAGE = 'Exception delivering protocol object!'
+
+_LOCAL_FAILURE_OUTCOME = _utilities.Outcome(
+    base.Outcome.Kind.LOCAL_FAILURE, None, None)
+
+
+class _Awaited(
+    collections.namedtuple('_Awaited', ('kind', 'value',))):
+
+  @enum.unique
+  class Kind(enum.Enum):
+    NOT_YET_ARRIVED = 'not yet arrived'
+    ARRIVED = 'arrived'
+
+_NOT_YET_ARRIVED = _Awaited(_Awaited.Kind.NOT_YET_ARRIVED, None)
+_ARRIVED_AND_NONE = _Awaited(_Awaited.Kind.ARRIVED, None)
+
+
+class _Transitory(
+    collections.namedtuple('_Transitory', ('kind', 'value',))):
+
+  @enum.unique
+  class Kind(enum.Enum):
+    NOT_YET_SEEN = 'not yet seen'
+    PRESENT = 'present'
+    GONE = 'gone'
+
+_NOT_YET_SEEN = _Transitory(_Transitory.Kind.NOT_YET_SEEN, None)
+_GONE = _Transitory(_Transitory.Kind.GONE, None)
+
+
+class _ProtocolManager(_interfaces.ProtocolManager):
+  """An implementation of _interfaces.ExpirationManager."""
+
+  def __init__(
+      self, protocol_receiver, lock, pool, termination_manager,
+      transmission_manager, expiration_manager):
+    """Constructor.
+
+    Args:
+      protocol_receiver: An _Awaited wrapping of the base.ProtocolReceiver to
+        which protocol objects should be passed during the operation. May be
+        of kind _Awaited.Kind.NOT_YET_ARRIVED if the customer's subscription is
+        not yet known and may be of kind _Awaited.Kind.ARRIVED but with a value
+        of None if the customer's subscription did not include a
+        ProtocolReceiver.
+      lock: The operation-wide lock.
+      pool: A thread pool.
+      termination_manager: The _interfaces.TerminationManager for the operation.
+      transmission_manager: The _interfaces.TransmissionManager for the
+        operation.
+      expiration_manager: The _interfaces.ExpirationManager for the operation.
+    """
+    self._lock = lock
+    self._pool = pool
+    self._termination_manager = termination_manager
+    self._transmission_manager = transmission_manager
+    self._expiration_manager = expiration_manager
+
+    self._protocol_receiver = protocol_receiver
+    self._context = _NOT_YET_SEEN
+
+  def _abort_and_notify(self, outcome):
+    if self._termination_manager.outcome is None:
+      self._termination_manager.abort(outcome)
+      self._transmission_manager.abort(outcome)
+      self._expiration_manager.terminate()
+
+  def _deliver(self, behavior, value):
+    def deliver():
+      delivery_outcome = callable_util.call_logging_exceptions(
+          behavior, _EXCEPTION_LOG_MESSAGE, value)
+      if delivery_outcome.kind is callable_util.Outcome.Kind.RAISED:
+        with self._lock:
+          self._abort_and_notify(_LOCAL_FAILURE_OUTCOME)
+    self._pool.submit(
+        callable_util.with_exceptions_logged(
+            deliver, _constants.INTERNAL_ERROR_LOG_MESSAGE))
+
+  def set_protocol_receiver(self, protocol_receiver):
+    """See _interfaces.ProtocolManager.set_protocol_receiver for spec."""
+    self._protocol_receiver = _Awaited(_Awaited.Kind.ARRIVED, protocol_receiver)
+    if (self._context.kind is _Transitory.Kind.PRESENT and
+        protocol_receiver is not None):
+      self._deliver(protocol_receiver.context, self._context.value)
+      self._context = _GONE
+
+  def accept_protocol_context(self, protocol_context):
+    """See _interfaces.ProtocolManager.accept_protocol_context for spec."""
+    if self._protocol_receiver.kind is _Awaited.Kind.ARRIVED:
+      if self._protocol_receiver.value is not None:
+        self._deliver(self._protocol_receiver.value.context, protocol_context)
+      self._context = _GONE
+    else:
+      self._context = _Transitory(_Transitory.Kind.PRESENT, protocol_context)
+
+
+def invocation_protocol_manager(
+    subscription, lock, pool, termination_manager, transmission_manager,
+    expiration_manager):
+  """Creates an _interfaces.ProtocolManager for invocation-side use.
+
+  Args:
+    subscription: The local customer's subscription to the operation.
+    lock: The operation-wide lock.
+    pool: A thread pool.
+    termination_manager: The _interfaces.TerminationManager for the operation.
+    transmission_manager: The _interfaces.TransmissionManager for the
+      operation.
+    expiration_manager: The _interfaces.ExpirationManager for the operation.
+  """
+  if subscription.kind is base.Subscription.Kind.FULL:
+    awaited_protocol_receiver = _Awaited(
+        _Awaited.Kind.ARRIVED, subscription.protocol_receiver)
+  else:
+    awaited_protocol_receiver = _ARRIVED_AND_NONE
+  return _ProtocolManager(
+      awaited_protocol_receiver, lock, pool, termination_manager,
+      transmission_manager, expiration_manager)
+
+
+def service_protocol_manager(
+    lock, pool, termination_manager, transmission_manager, expiration_manager):
+  """Creates an _interfaces.ProtocolManager for service-side use.
+
+  Args:
+    lock: The operation-wide lock.
+    pool: A thread pool.
+    termination_manager: The _interfaces.TerminationManager for the operation.
+    transmission_manager: The _interfaces.TransmissionManager for the
+      operation.
+    expiration_manager: The _interfaces.ExpirationManager for the operation.
+  """
+  return _ProtocolManager(
+      _NOT_YET_ARRIVED, lock, pool, termination_manager, transmission_manager,
+      expiration_manager)
diff --git a/src/python/grpcio/grpc/framework/core/_reception.py b/src/python/grpcio/grpc/framework/core/_reception.py
index d374cf0..ff81450 100644
--- a/src/python/grpcio/grpc/framework/core/_reception.py
+++ b/src/python/grpcio/grpc/framework/core/_reception.py
@@ -51,23 +51,31 @@
     base.Outcome.Kind.RECEPTION_FAILURE, None, None)
 
 
+def _carrying_protocol_context(ticket):
+  return ticket.protocol is not None and ticket.protocol.kind in (
+      links.Protocol.Kind.INVOCATION_CONTEXT,
+      links.Protocol.Kind.SERVICER_CONTEXT,)
+
+
 class ReceptionManager(_interfaces.ReceptionManager):
   """A ReceptionManager based around a _Receiver passed to it."""
 
   def __init__(
       self, termination_manager, transmission_manager, expiration_manager,
-      ingestion_manager):
+      protocol_manager, ingestion_manager):
     """Constructor.
 
     Args:
       termination_manager: The operation's _interfaces.TerminationManager.
       transmission_manager: The operation's _interfaces.TransmissionManager.
       expiration_manager: The operation's _interfaces.ExpirationManager.
+      protocol_manager: The operation's _interfaces.ProtocolManager.
       ingestion_manager: The operation's _interfaces.IngestionManager.
     """
     self._termination_manager = termination_manager
     self._transmission_manager = transmission_manager
     self._expiration_manager = expiration_manager
+    self._protocol_manager = protocol_manager
     self._ingestion_manager = ingestion_manager
 
     self._lowest_unseen_sequence_number = 0
@@ -100,6 +108,10 @@
   def _process_one(self, ticket):
     if ticket.sequence_number == 0:
       self._ingestion_manager.set_group_and_method(ticket.group, ticket.method)
+      if _carrying_protocol_context(ticket):
+        self._protocol_manager.accept_protocol_context(ticket.protocol.value)
+      else:
+        self._protocol_manager.accept_protocol_context(None)
     if ticket.timeout is not None:
       self._expiration_manager.change_timeout(ticket.timeout)
     if ticket.termination is None:
diff --git a/src/python/grpcio/grpc/framework/core/_transmission.py b/src/python/grpcio/grpc/framework/core/_transmission.py
index 8f852cf..65b12c4 100644
--- a/src/python/grpcio/grpc/framework/core/_transmission.py
+++ b/src/python/grpcio/grpc/framework/core/_transmission.py
@@ -207,18 +207,19 @@
     self._transmitting = True
 
   def kick_off(
-      self, group, method, timeout, initial_metadata, payload, completion,
-      allowance):
+      self, group, method, timeout, protocol_options, initial_metadata,
+      payload, completion, allowance):
     """See _interfaces.TransmissionManager.kickoff for specification."""
     # TODO(nathaniel): Support other subscriptions.
     subscription = links.Ticket.Subscription.FULL
     terminal_metadata, code, message, termination = _explode_completion(
         completion)
     self._remote_allowance = 1 if payload is None else 0
+    protocol = links.Protocol(links.Protocol.Kind.CALL_OPTION, protocol_options)
     ticket = links.Ticket(
         self._operation_id, 0, group, method, subscription, timeout, allowance,
         initial_metadata, payload, terminal_metadata, code, message,
-        termination, None)
+        termination, protocol)
     self._lowest_unused_sequence_number = 1
     self._transmit(ticket)
 
diff --git a/src/python/grpcio/grpc/framework/crust/_calls.py b/src/python/grpcio/grpc/framework/crust/_calls.py
index 4c6bf16..bff940d 100644
--- a/src/python/grpcio/grpc/framework/crust/_calls.py
+++ b/src/python/grpcio/grpc/framework/crust/_calls.py
@@ -38,10 +38,14 @@
 _EMPTY_COMPLETION = utilities.completion(None, None, None)
 
 
-def _invoke(end, group, method, timeout, initial_metadata, payload, complete):
+def _invoke(
+    end, group, method, timeout, protocol_options, initial_metadata, payload,
+    complete):
   rendezvous = _control.Rendezvous(None, None)
+  subscription = utilities.full_subscription(
+      rendezvous, _control.protocol_receiver(rendezvous))
   operation_context, operator = end.operate(
-      group, method, utilities.full_subscription(rendezvous), timeout,
+      group, method, subscription, timeout, protocol_options=protocol_options,
       initial_metadata=initial_metadata, payload=payload,
       completion=_EMPTY_COMPLETION if complete else None)
   rendezvous.set_operator_and_context(operator, operation_context)
@@ -93,36 +97,43 @@
 
 
 def blocking_unary_unary(
-    end, group, method, timeout, with_call, initial_metadata, payload):
+    end, group, method, timeout, with_call, protocol_options, initial_metadata,
+    payload):
   """Services in a blocking fashion a unary-unary servicer method."""
   rendezvous, unused_operation_context, unused_outcome = _invoke(
-      end, group, method, timeout, initial_metadata, payload, True)
+      end, group, method, timeout, protocol_options, initial_metadata, payload,
+      True)
   if with_call:
     return next(rendezvous), rendezvous
   else:
     return next(rendezvous)
 
 
-def future_unary_unary(end, group, method, timeout, initial_metadata, payload):
+def future_unary_unary(
+    end, group, method, timeout, protocol_options, initial_metadata, payload):
   """Services a value-in value-out servicer method by returning a Future."""
   rendezvous, unused_operation_context, unused_outcome = _invoke(
-      end, group, method, timeout, initial_metadata, payload, True)
+      end, group, method, timeout, protocol_options, initial_metadata, payload,
+      True)
   return rendezvous
 
 
-def inline_unary_stream(end, group, method, timeout, initial_metadata, payload):
+def inline_unary_stream(
+    end, group, method, timeout, protocol_options, initial_metadata, payload):
   """Services a value-in stream-out servicer method."""
   rendezvous, unused_operation_context, unused_outcome = _invoke(
-      end, group, method, timeout, initial_metadata, payload, True)
+      end, group, method, timeout, protocol_options, initial_metadata, payload,
+      True)
   return rendezvous
 
 
 def blocking_stream_unary(
-    end, group, method, timeout, with_call, initial_metadata, payload_iterator,
-    pool):
+    end, group, method, timeout, with_call, protocol_options, initial_metadata,
+    payload_iterator, pool):
   """Services in a blocking fashion a stream-in value-out servicer method."""
   rendezvous, operation_context, outcome = _invoke(
-      end, group, method, timeout, initial_metadata, None, False)
+      end, group, method, timeout, protocol_options, initial_metadata, None,
+      False)
   if outcome is None:
     def in_pool():
       for payload in payload_iterator:
@@ -141,10 +152,12 @@
 
 
 def future_stream_unary(
-    end, group, method, timeout, initial_metadata, payload_iterator, pool):
+    end, group, method, timeout, protocol_options, initial_metadata,
+    payload_iterator, pool):
   """Services a stream-in value-out servicer method by returning a Future."""
   rendezvous, operation_context, outcome = _invoke(
-      end, group, method, timeout, initial_metadata, None, False)
+      end, group, method, timeout, protocol_options, initial_metadata, None,
+      False)
   if outcome is None:
     def in_pool():
       for payload in payload_iterator:
@@ -155,10 +168,12 @@
 
 
 def inline_stream_stream(
-    end, group, method, timeout, initial_metadata, payload_iterator, pool):
+    end, group, method, timeout, protocol_options, initial_metadata,
+    payload_iterator, pool):
   """Services a stream-in stream-out servicer method."""
   rendezvous, operation_context, outcome = _invoke(
-      end, group, method, timeout, initial_metadata, None, False)
+      end, group, method, timeout, protocol_options, initial_metadata, None,
+      False)
   if outcome is None:
     def in_pool():
       for payload in payload_iterator:
@@ -169,36 +184,40 @@
 
 
 def event_unary_unary(
-    end, group, method, timeout, initial_metadata, payload, receiver,
-    abortion_callback, pool):
+    end, group, method, timeout, protocol_options, initial_metadata, payload,
+    receiver, abortion_callback, pool):
   rendezvous, operation_context, outcome = _invoke(
-      end, group, method, timeout, initial_metadata, payload, True)
+      end, group, method, timeout, protocol_options, initial_metadata, payload,
+      True)
   return _event_return_unary(
       receiver, abortion_callback, rendezvous, operation_context, outcome, pool)
 
 
 def event_unary_stream(
-    end, group, method, timeout, initial_metadata, payload,
+    end, group, method, timeout, protocol_options, initial_metadata, payload,
     receiver, abortion_callback, pool):
   rendezvous, operation_context, outcome = _invoke(
-      end, group, method, timeout, initial_metadata, payload, True)
+      end, group, method, timeout, protocol_options, initial_metadata, payload,
+      True)
   return _event_return_stream(
       receiver, abortion_callback, rendezvous, operation_context, outcome, pool)
 
 
 def event_stream_unary(
-    end, group, method, timeout, initial_metadata, receiver, abortion_callback,
-    pool):
+    end, group, method, timeout, protocol_options, initial_metadata, receiver,
+    abortion_callback, pool):
   rendezvous, operation_context, outcome = _invoke(
-      end, group, method, timeout, initial_metadata, None, False)
+      end, group, method, timeout, protocol_options, initial_metadata, None,
+      False)
   return _event_return_unary(
       receiver, abortion_callback, rendezvous, operation_context, outcome, pool)
 
 
 def event_stream_stream(
-    end, group, method, timeout, initial_metadata, receiver, abortion_callback,
-    pool):
+    end, group, method, timeout, protocol_options, initial_metadata, receiver,
+    abortion_callback, pool):
   rendezvous, operation_context, outcome = _invoke(
-      end, group, method, timeout, initial_metadata, None, False)
+      end, group, method, timeout, protocol_options, initial_metadata, None,
+      False)
   return _event_return_stream(
       receiver, abortion_callback, rendezvous, operation_context, outcome, pool)
diff --git a/src/python/grpcio/grpc/framework/crust/_control.py b/src/python/grpcio/grpc/framework/crust/_control.py
index 7bddf46..5e9efdf 100644
--- a/src/python/grpcio/grpc/framework/crust/_control.py
+++ b/src/python/grpcio/grpc/framework/crust/_control.py
@@ -182,6 +182,8 @@
     self._operator = operator
     self._operation_context = operation_context
 
+    self._protocol_context = _NOT_YET_ARRIVED
+
     self._up_initial_metadata = _NOT_YET_ARRIVED
     self._up_payload = None
     self._up_allowance = 1
@@ -442,6 +444,16 @@
         else:
           return self._termination.abortion
 
+  def protocol_context(self):
+    with self._condition:
+      while True:
+        if self._protocol_context.kind is _Awaited.Kind.ARRIVED:
+          return self._protocol_context.value
+        elif self._termination.abortion_error is not None:
+          raise self._termination.abortion_error
+        else:
+          self._condition.wait()
+
   def initial_metadata(self):
     with self._condition:
       while True:
@@ -514,11 +526,30 @@
       else:
         self._down_details = _Transitory(_Transitory.Kind.PRESENT, details)
 
+  def set_protocol_context(self, protocol_context):
+    with self._condition:
+      self._protocol_context = _Awaited(
+          _Awaited.Kind.ARRIVED, protocol_context)
+      self._condition.notify_all()
+
   def set_outcome(self, outcome):
     with self._condition:
       return self._set_outcome(outcome)
 
 
+class _ProtocolReceiver(base.ProtocolReceiver):
+
+  def __init__(self, rendezvous):
+    self._rendezvous = rendezvous
+
+  def context(self, protocol_context):
+    self._rendezvous.set_protocol_context(protocol_context)
+
+
+def protocol_receiver(rendezvous):
+  return _ProtocolReceiver(rendezvous)
+
+
 def pool_wrap(behavior, operation_context):
   """Wraps an operation-related behavior so that it may be called in a pool.
 
diff --git a/src/python/grpcio/grpc/framework/crust/_service.py b/src/python/grpcio/grpc/framework/crust/_service.py
index 6ff7249..9903415 100644
--- a/src/python/grpcio/grpc/framework/crust/_service.py
+++ b/src/python/grpcio/grpc/framework/crust/_service.py
@@ -52,6 +52,9 @@
   def cancel(self):
     self._rendezvous.cancel()
 
+  def protocol_context(self):
+    return self._rendezvous.protocol_context()
+
   def invocation_metadata(self):
     return self._rendezvous.initial_metadata()
 
@@ -71,10 +74,12 @@
 def _adaptation(pool, in_pool):
   def adaptation(operator, operation_context):
     rendezvous = _control.Rendezvous(operator, operation_context)
+    subscription = utilities.full_subscription(
+        rendezvous, _control.protocol_receiver(rendezvous))
     outcome = operation_context.add_termination_callback(rendezvous.set_outcome)
     if outcome is None:
       pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous)
-      return utilities.full_subscription(rendezvous)
+      return subscription
     else:
       raise abandonment.Abandoned()
   return adaptation
@@ -151,6 +156,8 @@
 def adapt_multi_method(multi_method, pool):
   def adaptation(group, method, operator, operation_context):
     rendezvous = _control.Rendezvous(operator, operation_context)
+    subscription = utilities.full_subscription(
+        rendezvous, _control.protocol_receiver(rendezvous))
     outcome = operation_context.add_termination_callback(rendezvous.set_outcome)
     if outcome is None:
       def in_pool():
@@ -160,7 +167,7 @@
           request_consumer.consume(request)
         request_consumer.terminate()
       pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous)
-      return utilities.full_subscription(rendezvous)
+      return subscription
     else:
       raise abandonment.Abandoned()
   return adaptation
diff --git a/src/python/grpcio/grpc/framework/crust/implementations.py b/src/python/grpcio/grpc/framework/crust/implementations.py
index d38fab8..4ebc4e9 100644
--- a/src/python/grpcio/grpc/framework/crust/implementations.py
+++ b/src/python/grpcio/grpc/framework/crust/implementations.py
@@ -66,22 +66,23 @@
     self._pool = pool
 
   def __call__(
-      self, request, timeout, metadata=None, with_call=False):
+      self, request, timeout, metadata=None, with_call=False,
+      protocol_options=None):
     return _calls.blocking_unary_unary(
         self._end, self._group, self._method, timeout, with_call,
-        metadata, request)
+        protocol_options, metadata, request)
 
-  def future(self, request, timeout, metadata=None):
+  def future(self, request, timeout, metadata=None, protocol_options=None):
     return _calls.future_unary_unary(
-        self._end, self._group, self._method, timeout, metadata,
-        request)
+        self._end, self._group, self._method, timeout, protocol_options,
+        metadata, request)
 
   def event(
       self, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     return _calls.event_unary_unary(
-        self._end, self._group, self._method, timeout, metadata,
-        request, receiver, abortion_callback, self._pool)
+        self._end, self._group, self._method, timeout, protocol_options,
+        metadata, request, receiver, abortion_callback, self._pool)
 
 
 class _UnaryStreamMultiCallable(face.UnaryStreamMultiCallable):
@@ -92,17 +93,17 @@
     self._method = method
     self._pool = pool
 
-  def __call__(self, request, timeout, metadata=None):
+  def __call__(self, request, timeout, metadata=None, protocol_options=None):
     return _calls.inline_unary_stream(
-        self._end, self._group, self._method, timeout, metadata,
-        request)
+        self._end, self._group, self._method, timeout, protocol_options,
+        metadata, request)
 
   def event(
       self, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     return _calls.event_unary_stream(
-        self._end, self._group, self._method, timeout, metadata,
-        request, receiver, abortion_callback, self._pool)
+        self._end, self._group, self._method, timeout, protocol_options,
+        metadata, request, receiver, abortion_callback, self._pool)
 
 
 class _StreamUnaryMultiCallable(face.StreamUnaryMultiCallable):
@@ -115,21 +116,23 @@
 
   def __call__(
       self, request_iterator, timeout, metadata=None,
-      with_call=False):
+      with_call=False, protocol_options=None):
     return _calls.blocking_stream_unary(
         self._end, self._group, self._method, timeout, with_call,
+        protocol_options, metadata, request_iterator, self._pool)
+
+  def future(
+      self, request_iterator, timeout, metadata=None, protocol_options=None):
+    return _calls.future_stream_unary(
+        self._end, self._group, self._method, timeout, protocol_options,
         metadata, request_iterator, self._pool)
 
-  def future(self, request_iterator, timeout, metadata=None):
-    return _calls.future_stream_unary(
-        self._end, self._group, self._method, timeout, metadata,
-        request_iterator, self._pool)
-
   def event(
-      self, receiver, abortion_callback, timeout, metadata=None):
+      self, receiver, abortion_callback, timeout, metadata=None,
+      protocol_options=None):
     return _calls.event_stream_unary(
-        self._end, self._group, self._method, timeout, metadata,
-        receiver, abortion_callback, self._pool)
+        self._end, self._group, self._method, timeout, protocol_options,
+        metadata, receiver, abortion_callback, self._pool)
 
 
 class _StreamStreamMultiCallable(face.StreamStreamMultiCallable):
@@ -140,16 +143,18 @@
     self._method = method
     self._pool = pool
 
-  def __call__(self, request_iterator, timeout, metadata=None):
+  def __call__(
+      self, request_iterator, timeout, metadata=None, protocol_options=None):
     return _calls.inline_stream_stream(
-        self._end, self._group, self._method, timeout, metadata,
-        request_iterator, self._pool)
+        self._end, self._group, self._method, timeout, protocol_options,
+        metadata, request_iterator, self._pool)
 
   def event(
-      self, receiver, abortion_callback, timeout, metadata=None):
+      self, receiver, abortion_callback, timeout, metadata=None,
+      protocol_options=None):
     return _calls.event_stream_stream(
-        self._end, self._group, self._method, timeout, metadata,
-        receiver, abortion_callback, self._pool)
+        self._end, self._group, self._method, timeout, protocol_options,
+        metadata, receiver, abortion_callback, self._pool)
 
 
 class _GenericStub(face.GenericStub):
@@ -161,66 +166,70 @@
 
   def blocking_unary_unary(
       self, group, method, request, timeout, metadata=None,
-      with_call=None):
+      with_call=None, protocol_options=None):
     return _calls.blocking_unary_unary(
-        self._end, group, method, timeout, with_call, metadata,
-        request)
+        self._end, group, method, timeout, with_call, protocol_options,
+        metadata, request)
 
   def future_unary_unary(
-      self, group, method, request, timeout, metadata=None):
+      self, group, method, request, timeout, metadata=None,
+      protocol_options=None):
     return _calls.future_unary_unary(
-        self._end, group, method, timeout, metadata, request)
+        self._end, group, method, timeout, protocol_options, metadata, request)
 
   def inline_unary_stream(
-      self, group, method, request, timeout, metadata=None):
+      self, group, method, request, timeout, metadata=None,
+      protocol_options=None):
     return _calls.inline_unary_stream(
-        self._end, group, method, timeout, metadata, request)
+        self._end, group, method, timeout, protocol_options, metadata, request)
 
   def blocking_stream_unary(
       self, group, method, request_iterator, timeout, metadata=None,
-      with_call=None):
+      with_call=None, protocol_options=None):
     return _calls.blocking_stream_unary(
-        self._end, group, method, timeout, with_call, metadata,
-        request_iterator, self._pool)
+        self._end, group, method, timeout, with_call, protocol_options,
+        metadata, request_iterator, self._pool)
 
   def future_stream_unary(
-      self, group, method, request_iterator, timeout, metadata=None):
+      self, group, method, request_iterator, timeout, metadata=None,
+      protocol_options=None):
     return _calls.future_stream_unary(
-        self._end, group, method, timeout, metadata,
+        self._end, group, method, timeout, protocol_options, metadata,
         request_iterator, self._pool)
 
   def inline_stream_stream(
-      self, group, method, request_iterator, timeout, metadata=None):
+      self, group, method, request_iterator, timeout, metadata=None,
+      protocol_options=None):
     return _calls.inline_stream_stream(
-        self._end, group, method, timeout, metadata,
+        self._end, group, method, timeout, protocol_options, metadata,
         request_iterator, self._pool)
 
   def event_unary_unary(
       self, group, method, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     return _calls.event_unary_unary(
-        self._end, group, method, timeout, metadata, request,
+        self._end, group, method, timeout, protocol_options, metadata, request,
         receiver, abortion_callback, self._pool)
 
   def event_unary_stream(
       self, group, method, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     return _calls.event_unary_stream(
-        self._end, group, method, timeout, metadata, request,
+        self._end, group, method, timeout, protocol_options, metadata, request,
         receiver, abortion_callback, self._pool)
 
   def event_stream_unary(
       self, group, method, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     return _calls.event_stream_unary(
-        self._end, group, method, timeout, metadata, receiver,
+        self._end, group, method, timeout, protocol_options, metadata, receiver,
         abortion_callback, self._pool)
 
   def event_stream_stream(
       self, group, method, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     return _calls.event_stream_stream(
-        self._end, group, method, timeout, metadata, receiver,
+        self._end, group, method, timeout, protocol_options, metadata, receiver,
         abortion_callback, self._pool)
 
   def unary_unary(self, group, method):
diff --git a/src/python/grpcio/grpc/framework/interfaces/base/base.py b/src/python/grpcio/grpc/framework/interfaces/base/base.py
index 0d9d6b4..a1e70be 100644
--- a/src/python/grpcio/grpc/framework/interfaces/base/base.py
+++ b/src/python/grpcio/grpc/framework/interfaces/base/base.py
@@ -184,6 +184,19 @@
     """
     raise NotImplementedError()
 
+class ProtocolReceiver(object):
+  """A means of receiving protocol values during an operation."""
+  __metaclass__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def context(self, protocol_context):
+    """Accepts the protocol context object for the operation.
+
+    Args:
+      protocol_context: The protocol context object for the operation.
+    """
+    raise NotImplementedError()
+
 
 class Subscription(object):
   """Describes customer code's interest in values from the other side.
@@ -199,7 +212,11 @@
       otherwise.
     operator: An Operator to be passed values from the other side of the
       operation. Must be non-None if kind is Kind.FULL. Must be None otherwise.
+    protocol_receiver: A ProtocolReceiver to be passed protocol objects as they
+      become available during the operation. Must be non-None if kind is
+      Kind.FULL.
   """
+  __metaclass__ = abc.ABCMeta
 
   @enum.unique
   class Kind(enum.Enum):
@@ -274,7 +291,7 @@
   @abc.abstractmethod
   def operate(
       self, group, method, subscription, timeout, initial_metadata=None,
-      payload=None, completion=None):
+      payload=None, completion=None, protocol_options=None):
     """Commences an operation.
 
     Args:
@@ -290,6 +307,8 @@
       payload: An initial payload for the operation.
       completion: A Completion value indicating the end of transmission to the
         other side of the operation.
+      protocol_options: A value specified by the provider of a Base interface
+        implementation affording custom state and behavior.
 
     Returns:
       A pair of objects affording information about the operation and action
diff --git a/src/python/grpcio/grpc/framework/interfaces/base/utilities.py b/src/python/grpcio/grpc/framework/interfaces/base/utilities.py
index a9ee1a0..87a8501 100644
--- a/src/python/grpcio/grpc/framework/interfaces/base/utilities.py
+++ b/src/python/grpcio/grpc/framework/interfaces/base/utilities.py
@@ -45,11 +45,12 @@
     base.Subscription,
     collections.namedtuple(
         '_Subscription',
-        ('kind', 'termination_callback', 'allowance', 'operator',))):
+        ('kind', 'termination_callback', 'allowance', 'operator',
+         'protocol_receiver',))):
   """A trivial implementation of base.Subscription."""
 
 _NONE_SUBSCRIPTION = _Subscription(
-    base.Subscription.Kind.NONE, None, None, None)
+    base.Subscription.Kind.NONE, None, None, None, None)
 
 
 def completion(terminal_metadata, code, message):
@@ -66,14 +67,16 @@
   return _Completion(terminal_metadata, code, message)
 
 
-def full_subscription(operator):
+def full_subscription(operator, protocol_receiver):
   """Creates a "full" base.Subscription for the given base.Operator.
 
   Args:
     operator: A base.Operator to be used in an operation.
+    protocol_receiver: A base.ProtocolReceiver to be used in an operation.
 
   Returns:
     A base.Subscription of kind base.Subscription.Kind.FULL wrapping the given
-      base.Operator.
+      base.Operator and base.ProtocolReceiver.
   """
-  return _Subscription(base.Subscription.Kind.FULL, None, None, operator)
+  return _Subscription(
+      base.Subscription.Kind.FULL, None, None, operator, protocol_receiver)
diff --git a/src/python/grpcio/grpc/framework/interfaces/face/face.py b/src/python/grpcio/grpc/framework/interfaces/face/face.py
index 948e750..bc9a434 100644
--- a/src/python/grpcio/grpc/framework/interfaces/face/face.py
+++ b/src/python/grpcio/grpc/framework/interfaces/face/face.py
@@ -184,6 +184,16 @@
     """
     raise NotImplementedError()
 
+  @abc.abstractmethod
+  def protocol_context(self):
+    """Accesses a custom object specified by an implementation provider.
+
+    Returns:
+      A value specified by the provider of a Face interface implementation
+        affording custom state and behavior.
+    """
+    raise NotImplementedError()
+
 
 class Call(RpcContext):
   """Invocation-side utility object for an RPC."""
@@ -354,7 +364,8 @@
 
   @abc.abstractmethod
   def __call__(
-      self, request, timeout, metadata=None, with_call=False):
+      self, request, timeout, metadata=None, with_call=False,
+      protocol_options=None):
     """Synchronously invokes the underlying RPC.
 
     Args:
@@ -364,6 +375,8 @@
         the RPC.
       with_call: Whether or not to include return a Call for the RPC in addition
         to the reponse.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       The response value for the RPC, and a Call for the RPC if with_call was
@@ -375,7 +388,7 @@
     raise NotImplementedError()
 
   @abc.abstractmethod
-  def future(self, request, timeout, metadata=None):
+  def future(self, request, timeout, metadata=None, protocol_options=None):
     """Asynchronously invokes the underlying RPC.
 
     Args:
@@ -383,6 +396,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and a future.Future. In the
@@ -395,7 +410,7 @@
   @abc.abstractmethod
   def event(
       self, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     """Asynchronously invokes the underlying RPC.
 
     Args:
@@ -406,6 +421,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A Call for the RPC.
@@ -418,7 +435,7 @@
   __metaclass__ = abc.ABCMeta
 
   @abc.abstractmethod
-  def __call__(self, request, timeout, metadata=None):
+  def __call__(self, request, timeout, metadata=None, protocol_options=None):
     """Invokes the underlying RPC.
 
     Args:
@@ -426,6 +443,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and an iterator of response
@@ -437,7 +456,7 @@
   @abc.abstractmethod
   def event(
       self, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     """Asynchronously invokes the underlying RPC.
 
     Args:
@@ -448,6 +467,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A Call object for the RPC.
@@ -462,7 +483,7 @@
   @abc.abstractmethod
   def __call__(
       self, request_iterator, timeout, metadata=None,
-      with_call=False):
+      with_call=False, protocol_options=None):
     """Synchronously invokes the underlying RPC.
 
     Args:
@@ -472,6 +493,8 @@
         the RPC.
       with_call: Whether or not to include return a Call for the RPC in addition
         to the reponse.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       The response value for the RPC, and a Call for the RPC if with_call was
@@ -483,7 +506,8 @@
     raise NotImplementedError()
 
   @abc.abstractmethod
-  def future(self, request_iterator, timeout, metadata=None):
+  def future(
+      self, request_iterator, timeout, metadata=None, protocol_options=None):
     """Asynchronously invokes the underlying RPC.
 
     Args:
@@ -491,6 +515,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and a future.Future. In the
@@ -502,7 +528,8 @@
 
   @abc.abstractmethod
   def event(
-      self, receiver, abortion_callback, timeout, metadata=None):
+      self, receiver, abortion_callback, timeout, metadata=None,
+      protocol_options=None):
     """Asynchronously invokes the underlying RPC.
 
     Args:
@@ -512,6 +539,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A single object that is both a Call object for the RPC and a
@@ -525,7 +554,8 @@
   __metaclass__ = abc.ABCMeta
 
   @abc.abstractmethod
-  def __call__(self, request_iterator, timeout, metadata=None):
+  def __call__(
+      self, request_iterator, timeout, metadata=None, protocol_options=None):
     """Invokes the underlying RPC.
 
     Args:
@@ -533,6 +563,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and an iterator of response
@@ -543,7 +575,8 @@
 
   @abc.abstractmethod
   def event(
-      self, receiver, abortion_callback, timeout, metadata=None):
+      self, receiver, abortion_callback, timeout, metadata=None,
+      protocol_options=None):
     """Asynchronously invokes the underlying RPC.
 
     Args:
@@ -553,6 +586,8 @@
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of
         the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A single object that is both a Call object for the RPC and a
@@ -646,7 +681,7 @@
   @abc.abstractmethod
   def blocking_unary_unary(
       self, group, method, request, timeout, metadata=None,
-      with_call=False):
+      with_call=False, protocol_options=None):
     """Invokes a unary-request-unary-response method.
 
     This method blocks until either returning the response value of the RPC
@@ -661,6 +696,8 @@
       metadata: A metadata value to be passed to the service-side of the RPC.
       with_call: Whether or not to include return a Call for the RPC in addition
         to the reponse.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       The response value for the RPC, and a Call for the RPC if with_call was
@@ -673,7 +710,8 @@
 
   @abc.abstractmethod
   def future_unary_unary(
-      self, group, method, request, timeout, metadata=None):
+      self, group, method, request, timeout, metadata=None,
+      protocol_options=None):
     """Invokes a unary-request-unary-response method.
 
     Args:
@@ -682,6 +720,8 @@
       request: The request value for the RPC.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and a future.Future. In the
@@ -693,7 +733,8 @@
 
   @abc.abstractmethod
   def inline_unary_stream(
-      self, group, method, request, timeout, metadata=None):
+      self, group, method, request, timeout, metadata=None,
+      protocol_options=None):
     """Invokes a unary-request-stream-response method.
 
     Args:
@@ -702,6 +743,8 @@
       request: The request value for the RPC.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and an iterator of response
@@ -713,7 +756,7 @@
   @abc.abstractmethod
   def blocking_stream_unary(
       self, group, method, request_iterator, timeout, metadata=None,
-      with_call=False):
+      with_call=False, protocol_options=None):
     """Invokes a stream-request-unary-response method.
 
     This method blocks until either returning the response value of the RPC
@@ -728,6 +771,8 @@
       metadata: A metadata value to be passed to the service-side of the RPC.
       with_call: Whether or not to include return a Call for the RPC in addition
         to the reponse.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       The response value for the RPC, and a Call for the RPC if with_call was
@@ -740,7 +785,8 @@
 
   @abc.abstractmethod
   def future_stream_unary(
-      self, group, method, request_iterator, timeout, metadata=None):
+      self, group, method, request_iterator, timeout, metadata=None,
+      protocol_options=None):
     """Invokes a stream-request-unary-response method.
 
     Args:
@@ -749,6 +795,8 @@
       request_iterator: An iterator that yields request values for the RPC.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and a future.Future. In the
@@ -760,7 +808,8 @@
 
   @abc.abstractmethod
   def inline_stream_stream(
-      self, group, method, request_iterator, timeout, metadata=None):
+      self, group, method, request_iterator, timeout, metadata=None,
+      protocol_options=None):
     """Invokes a stream-request-stream-response method.
 
     Args:
@@ -769,6 +818,8 @@
       request_iterator: An iterator that yields request values for the RPC.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       An object that is both a Call for the RPC and an iterator of response
@@ -780,7 +831,7 @@
   @abc.abstractmethod
   def event_unary_unary(
       self, group, method, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     """Event-driven invocation of a unary-request-unary-response method.
 
     Args:
@@ -792,6 +843,8 @@
         in the event of RPC abortion.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A Call for the RPC.
@@ -801,7 +854,7 @@
   @abc.abstractmethod
   def event_unary_stream(
       self, group, method, request, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     """Event-driven invocation of a unary-request-stream-response method.
 
     Args:
@@ -813,6 +866,8 @@
         in the event of RPC abortion.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A Call for the RPC.
@@ -822,7 +877,7 @@
   @abc.abstractmethod
   def event_stream_unary(
       self, group, method, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     """Event-driven invocation of a unary-request-unary-response method.
 
     Args:
@@ -833,6 +888,8 @@
         in the event of RPC abortion.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A pair of a Call object for the RPC and a stream.Consumer to which the
@@ -843,7 +900,7 @@
   @abc.abstractmethod
   def event_stream_stream(
       self, group, method, receiver, abortion_callback, timeout,
-      metadata=None):
+      metadata=None, protocol_options=None):
     """Event-driven invocation of a unary-request-stream-response method.
 
     Args:
@@ -854,6 +911,8 @@
         in the event of RPC abortion.
       timeout: A duration of time in seconds to allow for the RPC.
       metadata: A metadata value to be passed to the service-side of the RPC.
+      protocol_options: A value specified by the provider of a Face interface
+        implementation affording custom state and behavior.
 
     Returns:
       A pair of a Call object for the RPC and a stream.Consumer to which the
diff --git a/src/python/grpcio/grpc/framework/interfaces/links/links.py b/src/python/grpcio/grpc/framework/interfaces/links/links.py
index b98a30a..24f0e3b 100644
--- a/src/python/grpcio/grpc/framework/interfaces/links/links.py
+++ b/src/python/grpcio/grpc/framework/interfaces/links/links.py
@@ -34,14 +34,13 @@
 import enum
 
 
-class Transport(collections.namedtuple('Transport', ('kind', 'value',))):
-  """A sum type for handles to an underlying transport system.
+class Protocol(collections.namedtuple('Protocol', ('kind', 'value',))):
+  """A sum type for handles to a system that transmits tickets.
 
   Attributes:
-    kind: A Kind value identifying the kind of value being passed to or from
-      the underlying transport.
-    value: The value being passed through RPC Framework between the high-level
-      application and the underlying transport.
+    kind: A Kind value identifying the kind of value being passed.
+    value: The value being passed between the high-level application and the
+      system affording ticket transport.
   """
 
   @enum.unique
@@ -56,8 +55,7 @@
         'Ticket',
         ('operation_id', 'sequence_number', 'group', 'method', 'subscription',
          'timeout', 'allowance', 'initial_metadata', 'payload',
-         'terminal_metadata', 'code', 'message', 'termination',
-         'transport',))):
+         'terminal_metadata', 'code', 'message', 'termination', 'protocol',))):
   """A sum type for all values sent from a front to a back.
 
   Attributes:
@@ -99,8 +97,8 @@
     termination: A Termination value describing the end of the operation, or
       None if the operation has not yet terminated. If set, no further tickets
       may be sent in the same direction.
-    transport: A Transport value or None, with further semantics being a matter
-      between high-level application and underlying transport.
+    protocol: A Protocol value or None, with further semantics being a matter
+      between high-level application and underlying ticket transport.
   """
 
   @enum.unique
diff --git a/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py b/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py
index 4c8c64b..259b978 100644
--- a/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py
+++ b/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py
@@ -42,7 +42,7 @@
 import time
 import unittest
 
-from grpc.beta import beta
+from grpc.beta import implementations
 from grpc.framework.foundation import future
 from grpc.framework.interfaces.face import face
 from grpc_test.framework.common import test_constants
@@ -170,7 +170,7 @@
   server = getattr(test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer)
   port = server.add_insecure_port('[::]:0')
   server.start()
-  channel = beta.create_insecure_channel('localhost', port)
+  channel = implementations.insecure_channel('localhost', port)
   stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)(channel)
   yield servicer_methods, stub
   server.stop(0)
diff --git a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py
index 27a5b82..90ad0b9 100644
--- a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py
+++ b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py
@@ -191,7 +191,7 @@
                      metadata[server_leading_binary_metadata_key])
 
     for datum in test_data:
-      client_call.write(datum, write_tag)
+      client_call.write(datum, write_tag, _low.WriteFlags.WRITE_NO_COMPRESS)
       write_accepted = self.client_events.get()
       self.assertIsNotNone(write_accepted)
       self.assertIs(write_accepted.kind, _low.Event.Kind.WRITE_ACCEPTED)
@@ -206,7 +206,7 @@
       self.assertIsNotNone(read_accepted.bytes)
       server_data.append(read_accepted.bytes)
 
-      server_call.write(read_accepted.bytes, write_tag)
+      server_call.write(read_accepted.bytes, write_tag, 0)
       write_accepted = self.server_events.get()
       self.assertIsNotNone(write_accepted)
       self.assertEqual(_low.Event.Kind.WRITE_ACCEPTED, write_accepted.kind)
@@ -370,14 +370,14 @@
     self.assertIsNotNone(metadata_accepted)
 
     for datum in test_data:
-      client_call.write(datum, write_tag)
+      client_call.write(datum, write_tag, 0)
       write_accepted = self.client_events.get()
 
       server_call.read(read_tag)
       read_accepted = self.server_events.get()
       server_data.append(read_accepted.bytes)
 
-      server_call.write(read_accepted.bytes, write_tag)
+      server_call.write(read_accepted.bytes, write_tag, 0)
       write_accepted = self.server_events.get()
       self.assertIsNotNone(write_accepted)
 
diff --git a/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py b/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py
new file mode 100644
index 0000000..fad57da
--- /dev/null
+++ b/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py
@@ -0,0 +1,232 @@
+# 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.
+
+"""Tests Face interface compliance of the gRPC Python Beta API."""
+
+import threading
+import unittest
+
+from grpc.beta import implementations
+from grpc.beta import interfaces
+from grpc.framework.common import cardinality
+from grpc.framework.interfaces.face import utilities
+from grpc_test import resources
+from grpc_test.beta import test_utilities
+from grpc_test.framework.common import test_constants
+
+_SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
+
+_GROUP = 'group'
+_UNARY_UNARY = 'unary-unary'
+_UNARY_STREAM = 'unary-stream'
+_STREAM_UNARY = 'stream-unary'
+_STREAM_STREAM = 'stream-stream'
+
+_REQUEST = b'abc'
+_RESPONSE = b'123'
+
+
+class _Servicer(object):
+
+  def __init__(self):
+    self._condition = threading.Condition()
+    self._peer = None
+    self._serviced = False
+
+  def unary_unary(self, request, context):
+    with self._condition:
+      self._request = request
+      self._peer = context.protocol_context().peer()
+      context.protocol_context().disable_next_response_compression()
+      self._serviced = True
+      self._condition.notify_all()
+      return _RESPONSE
+
+  def unary_stream(self, request, context):
+    with self._condition:
+      self._request = request
+      self._peer = context.protocol_context().peer()
+      context.protocol_context().disable_next_response_compression()
+      self._serviced = True
+      self._condition.notify_all()
+      return
+      yield
+
+  def stream_unary(self, request_iterator, context):
+    for request in request_iterator:
+      self._request = request
+    with self._condition:
+      self._peer = context.protocol_context().peer()
+      context.protocol_context().disable_next_response_compression()
+      self._serviced = True
+      self._condition.notify_all()
+      return _RESPONSE
+
+  def stream_stream(self, request_iterator, context):
+    for request in request_iterator:
+      with self._condition:
+        self._peer = context.protocol_context().peer()
+        context.protocol_context().disable_next_response_compression()
+        yield _RESPONSE
+    with self._condition:
+      self._serviced = True
+      self._condition.notify_all()
+
+  def peer(self):
+    with self._condition:
+      return self._peer
+
+  def block_until_serviced(self):
+    with self._condition:
+      while not self._serviced:
+        self._condition.wait()
+
+
+class _BlockingIterator(object):
+
+  def __init__(self, upstream):
+    self._condition = threading.Condition()
+    self._upstream = upstream
+    self._allowed = []
+
+  def __iter__(self):
+    return self
+
+  def next(self):
+    with self._condition:
+      while True:
+        if self._allowed is None:
+          raise StopIteration()
+        elif self._allowed:
+          return self._allowed.pop(0)
+        else:
+          self._condition.wait()
+
+  def allow(self):
+    with self._condition:
+      try:
+        self._allowed.append(next(self._upstream))
+      except StopIteration:
+        self._allowed = None
+      self._condition.notify_all()
+
+
+class BetaFeaturesTest(unittest.TestCase):
+
+  def setUp(self):
+    self._servicer = _Servicer()
+    method_implementations = {
+        (_GROUP, _UNARY_UNARY):
+            utilities.unary_unary_inline(self._servicer.unary_unary),
+        (_GROUP, _UNARY_STREAM):
+            utilities.unary_stream_inline(self._servicer.unary_stream),
+        (_GROUP, _STREAM_UNARY):
+            utilities.stream_unary_inline(self._servicer.stream_unary),
+        (_GROUP, _STREAM_STREAM):
+            utilities.stream_stream_inline(self._servicer.stream_stream),
+    }
+
+    cardinalities = {
+        _UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY,
+        _UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM,
+        _STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY,
+        _STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM,
+    }
+
+    server_options = implementations.server_options(
+        thread_pool_size=test_constants.POOL_SIZE)
+    self._server = implementations.server(
+        method_implementations, options=server_options)
+    server_credentials = implementations.ssl_server_credentials(
+        [(resources.private_key(), resources.certificate_chain(),),])
+    port = self._server.add_secure_port('[::]:0', server_credentials)
+    self._server.start()
+    self._client_credentials = implementations.ssl_client_credentials(
+        resources.test_root_certificates(), None, None)
+    channel = test_utilities.not_really_secure_channel(
+        'localhost', port, self._client_credentials, _SERVER_HOST_OVERRIDE)
+    stub_options = implementations.stub_options(
+        thread_pool_size=test_constants.POOL_SIZE)
+    self._dynamic_stub = implementations.dynamic_stub(
+        channel, _GROUP, cardinalities, options=stub_options)
+
+  def tearDown(self):
+    self._dynamic_stub = None
+    self._server.stop(test_constants.SHORT_TIMEOUT).wait()
+
+  def test_unary_unary(self):
+    call_options = interfaces.grpc_call_options(
+        disable_compression=True, credentials=self._client_credentials)
+    response = getattr(self._dynamic_stub, _UNARY_UNARY)(
+        _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options)
+    self.assertEqual(_RESPONSE, response)
+    self.assertIsNotNone(self._servicer.peer())
+
+  def test_unary_stream(self):
+    call_options = interfaces.grpc_call_options(
+        disable_compression=True, credentials=self._client_credentials)
+    response_iterator = getattr(self._dynamic_stub, _UNARY_STREAM)(
+        _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options)
+    self._servicer.block_until_serviced()
+    self.assertIsNotNone(self._servicer.peer())
+
+  def test_stream_unary(self):
+    call_options = interfaces.grpc_call_options(
+        credentials=self._client_credentials)
+    request_iterator = _BlockingIterator(iter((_REQUEST,)))
+    response_future = getattr(self._dynamic_stub, _STREAM_UNARY).future(
+        request_iterator, test_constants.LONG_TIMEOUT,
+        protocol_options=call_options)
+    response_future.protocol_context().disable_next_request_compression()
+    request_iterator.allow()
+    response_future.protocol_context().disable_next_request_compression()
+    request_iterator.allow()
+    self._servicer.block_until_serviced()
+    self.assertIsNotNone(self._servicer.peer())
+    self.assertEqual(_RESPONSE, response_future.result())
+
+  def test_stream_stream(self):
+    call_options = interfaces.grpc_call_options(
+        credentials=self._client_credentials)
+    request_iterator = _BlockingIterator(iter((_REQUEST,)))
+    response_iterator = getattr(self._dynamic_stub, _STREAM_STREAM)(
+        request_iterator, test_constants.SHORT_TIMEOUT,
+        protocol_options=call_options)
+    response_iterator.protocol_context().disable_next_request_compression()
+    request_iterator.allow()
+    response = next(response_iterator)
+    response_iterator.protocol_context().disable_next_request_compression()
+    request_iterator.allow()
+    self._servicer.block_until_serviced()
+    self.assertIsNotNone(self._servicer.peer())
+    self.assertEqual(_RESPONSE, response)
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py b/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py
index 0384648..b3c05bd 100644
--- a/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py
+++ b/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py
@@ -36,14 +36,9 @@
 from grpc._adapter import _low
 from grpc._adapter import _types
 from grpc.beta import _connectivity_channel
+from grpc.beta import interfaces
 from grpc_test.framework.common import test_constants
 
-_MAPPING_FUNCTION = lambda integer: integer * 200 + 17
-_MAPPING = {
-    state: _MAPPING_FUNCTION(state) for state in _types.ConnectivityState}
-_IDLE, _CONNECTING, _READY, _TRANSIENT_FAILURE, _FATAL_FAILURE = map(
-    _MAPPING_FUNCTION, _types.ConnectivityState)
-
 
 def _drive_completion_queue(completion_queue):
   while True:
@@ -84,7 +79,7 @@
     callback = _Callback()
 
     connectivity_channel = _connectivity_channel.ConnectivityChannel(
-        low_channel, _MAPPING)
+        low_channel)
     connectivity_channel.subscribe(callback.update, try_to_connect=False)
     first_connectivities = callback.block_until_connectivities_satisfy(bool)
     connectivity_channel.subscribe(callback.update, try_to_connect=True)
@@ -98,11 +93,16 @@
     connectivity_channel.unsubscribe(callback.update)
     fifth_connectivities = callback.connectivities()
 
-    self.assertSequenceEqual((_IDLE,), first_connectivities)
-    self.assertNotIn(_READY, second_connectivities)
-    self.assertNotIn(_READY, third_connectivities)
-    self.assertNotIn(_READY, fourth_connectivities)
-    self.assertNotIn(_READY, fifth_connectivities)
+    self.assertSequenceEqual(
+        (interfaces.ChannelConnectivity.IDLE,), first_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.READY, second_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.READY, third_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.READY, fourth_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.READY, fifth_connectivities)
 
   def test_immediately_connectable_channel_connectivity(self):
     server_completion_queue = _low.CompletionQueue()
@@ -117,7 +117,7 @@
     second_callback = _Callback()
 
     connectivity_channel = _connectivity_channel.ConnectivityChannel(
-        low_channel, _MAPPING)
+        low_channel)
     connectivity_channel.subscribe(first_callback.update, try_to_connect=False)
     first_connectivities = first_callback.block_until_connectivities_satisfy(
         bool)
@@ -132,9 +132,11 @@
         bool)
     # Wait for a connection that will happen (or may already have happened).
     first_callback.block_until_connectivities_satisfy(
-        lambda connectivities: _READY in connectivities)
+        lambda connectivities:
+        interfaces.ChannelConnectivity.READY in connectivities)
     second_callback.block_until_connectivities_satisfy(
-        lambda connectivities: _READY in connectivities)
+        lambda connectivities:
+        interfaces.ChannelConnectivity.READY in connectivities)
     connectivity_channel.unsubscribe(first_callback.update)
     connectivity_channel.unsubscribe(second_callback.update)
 
@@ -142,12 +144,19 @@
     server_completion_queue.shutdown()
     server_completion_queue_thread.join()
 
-    self.assertSequenceEqual((_IDLE,), first_connectivities)
-    self.assertSequenceEqual((_IDLE,), second_connectivities)
-    self.assertNotIn(_TRANSIENT_FAILURE, third_connectivities)
-    self.assertNotIn(_FATAL_FAILURE, third_connectivities)
-    self.assertNotIn(_TRANSIENT_FAILURE, fourth_connectivities)
-    self.assertNotIn(_FATAL_FAILURE, fourth_connectivities)
+    self.assertSequenceEqual(
+        (interfaces.ChannelConnectivity.IDLE,), first_connectivities)
+    self.assertSequenceEqual(
+        (interfaces.ChannelConnectivity.IDLE,), second_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.TRANSIENT_FAILURE, third_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.FATAL_FAILURE, third_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.TRANSIENT_FAILURE,
+        fourth_connectivities)
+    self.assertNotIn(
+        interfaces.ChannelConnectivity.FATAL_FAILURE, fourth_connectivities)
 
   def test_reachable_then_unreachable_channel_connectivity(self):
     server_completion_queue = _low.CompletionQueue()
@@ -161,14 +170,16 @@
     callback = _Callback()
 
     connectivity_channel = _connectivity_channel.ConnectivityChannel(
-        low_channel, _MAPPING)
+        low_channel)
     connectivity_channel.subscribe(callback.update, try_to_connect=True)
     callback.block_until_connectivities_satisfy(
-        lambda connectivities: _READY in connectivities)
+        lambda connectivities:
+        interfaces.ChannelConnectivity.READY in connectivities)
     # Now take down the server and confirm that channel readiness is repudiated.
     server.shutdown()
     callback.block_until_connectivities_satisfy(
-        lambda connectivities: connectivities[-1] is not _READY)
+        lambda connectivities:
+        connectivities[-1] is not interfaces.ChannelConnectivity.READY)
     connectivity_channel.unsubscribe(callback.update)
 
     server.shutdown()
diff --git a/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py b/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py
index e9087a7..aa33e1e 100644
--- a/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py
+++ b/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py
@@ -32,7 +32,7 @@
 import collections
 import unittest
 
-from grpc.beta import beta
+from grpc.beta import implementations
 from grpc.beta import interfaces
 from grpc_test import resources
 from grpc_test import test_common as grpc_test_common
@@ -81,25 +81,26 @@
         method: method_object.cardinality()
         for (group, method), method_object in methods.iteritems()}
 
-    server_options = beta.server_options(
+    server_options = implementations.server_options(
         request_deserializers=serialization_behaviors.request_deserializers,
         response_serializers=serialization_behaviors.response_serializers,
         thread_pool_size=test_constants.POOL_SIZE)
-    server = beta.server(method_implementations, options=server_options)
-    server_credentials = beta.ssl_server_credentials(
+    server = implementations.server(
+        method_implementations, options=server_options)
+    server_credentials = implementations.ssl_server_credentials(
         [(resources.private_key(), resources.certificate_chain(),),])
     port = server.add_secure_port('[::]:0', server_credentials)
     server.start()
-    client_credentials = beta.ssl_client_credentials(
+    client_credentials = implementations.ssl_client_credentials(
         resources.test_root_certificates(), None, None)
-    channel = test_utilities.create_not_really_secure_channel(
+    channel = test_utilities.not_really_secure_channel(
         'localhost', port, client_credentials, _SERVER_HOST_OVERRIDE)
-    stub_options = beta.stub_options(
+    stub_options = implementations.stub_options(
         request_serializers=serialization_behaviors.request_serializers,
         response_deserializers=serialization_behaviors.response_deserializers,
         thread_pool_size=test_constants.POOL_SIZE)
-    generic_stub = beta.generic_stub(channel, options=stub_options)
-    dynamic_stub = beta.dynamic_stub(
+    generic_stub = implementations.generic_stub(channel, options=stub_options)
+    dynamic_stub = implementations.dynamic_stub(
         channel, service, cardinalities, options=stub_options)
     return generic_stub, {service: dynamic_stub}, server
 
diff --git a/src/python/grpcio_test/grpc_test/beta/_not_found_test.py b/src/python/grpcio_test/grpc_test/beta/_not_found_test.py
index ecd10f2..5feb997 100644
--- a/src/python/grpcio_test/grpc_test/beta/_not_found_test.py
+++ b/src/python/grpcio_test/grpc_test/beta/_not_found_test.py
@@ -31,7 +31,7 @@
 
 import unittest
 
-from grpc.beta import beta
+from grpc.beta import implementations
 from grpc.beta import interfaces
 from grpc.framework.interfaces.face import face
 from grpc_test.framework.common import test_constants
@@ -40,10 +40,10 @@
 class NotFoundTest(unittest.TestCase):
 
   def setUp(self):
-    self._server = beta.server({})
+    self._server = implementations.server({})
     port = self._server.add_insecure_port('[::]:0')
-    channel = beta.create_insecure_channel('localhost', port)
-    self._generic_stub = beta.generic_stub(channel)
+    channel = implementations.insecure_channel('localhost', port)
+    self._generic_stub = implementations.generic_stub(channel)
     self._server.start()
 
   def tearDown(self):
diff --git a/src/python/grpcio_test/grpc_test/beta/_utilities_test.py b/src/python/grpcio_test/grpc_test/beta/_utilities_test.py
index 998e74c..996cea9 100644
--- a/src/python/grpcio_test/grpc_test/beta/_utilities_test.py
+++ b/src/python/grpcio_test/grpc_test/beta/_utilities_test.py
@@ -35,7 +35,7 @@
 
 from grpc._adapter import _low
 from grpc._adapter import _types
-from grpc.beta import beta
+from grpc.beta import implementations
 from grpc.beta import utilities
 from grpc.framework.foundation import future
 from grpc_test.framework.common import test_constants
@@ -69,7 +69,7 @@
 class ChannelConnectivityTest(unittest.TestCase):
 
   def test_lonely_channel_connectivity(self):
-    channel = beta.create_insecure_channel('localhost', 12345)
+    channel = implementations.insecure_channel('localhost', 12345)
     callback = _Callback()
 
     ready_future = utilities.channel_ready_future(channel)
@@ -94,7 +94,7 @@
     server_completion_queue_thread = threading.Thread(
         target=_drive_completion_queue, args=(server_completion_queue,))
     server_completion_queue_thread.start()
-    channel = beta.create_insecure_channel('localhost', port)
+    channel = implementations.insecure_channel('localhost', port)
     callback = _Callback()
 
     try:
diff --git a/src/python/grpcio_test/grpc_test/beta/test_utilities.py b/src/python/grpcio_test/grpc_test/beta/test_utilities.py
index 3386704..24a8600 100644
--- a/src/python/grpcio_test/grpc_test/beta/test_utilities.py
+++ b/src/python/grpcio_test/grpc_test/beta/test_utilities.py
@@ -30,25 +30,27 @@
 """Test-appropriate entry points into the gRPC Python Beta API."""
 
 from grpc._adapter import _intermediary_low
-from grpc.beta import beta
+from grpc.beta import implementations
 
 
-def create_not_really_secure_channel(
+def not_really_secure_channel(
     host, port, client_credentials, server_host_override):
   """Creates an insecure Channel to a remote host.
 
   Args:
     host: The name of the remote host to which to connect.
     port: The port of the remote host to which to connect.
-    client_credentials: The beta.ClientCredentials with which to connect.
+    client_credentials: The implementations.ClientCredentials with which to
+      connect.
     server_host_override: The target name used for SSL host name checking.
 
   Returns:
-    A beta.Channel to the remote host through which RPCs may be conducted.
+    An implementations.Channel to the remote host through which RPCs may be
+      conducted.
   """
   hostport = '%s:%d' % (host, port)
   intermediary_low_channel = _intermediary_low.Channel(
       hostport, client_credentials._intermediary_low_credentials,
       server_host_override=server_host_override)
-  return beta.Channel(
+  return implementations.Channel(
       intermediary_low_channel._internal, intermediary_low_channel)
diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py
index 5065a3f..ddda101 100644
--- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py
+++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py
@@ -119,6 +119,17 @@
           'Deliberately raised exception from Operator.advance (in a test)!')
 
 
+class _ProtocolReceiver(base.ProtocolReceiver):
+
+  def __init__(self):
+    self._condition = threading.Condition()
+    self._contexts = []
+
+  def context(self, protocol_context):
+    with self._condition:
+      self._contexts.append(protocol_context)
+
+
 class _Servicer(base.Servicer):
   """A base.Servicer with instrumented for testing."""
 
@@ -144,7 +155,7 @@
             controller.service_on_termination)
         if outcome is not None:
           controller.service_on_termination(outcome)
-        return utilities.full_subscription(operator)
+        return utilities.full_subscription(operator, _ProtocolReceiver())
 
 
 class _OperationTest(unittest.TestCase):
@@ -169,7 +180,8 @@
       test_operator = _Operator(
           self._controller, self._controller.on_invocation_advance,
           self._pool, None)
-      subscription = utilities.full_subscription(test_operator)
+      subscription = utilities.full_subscription(
+          test_operator, _ProtocolReceiver())
     else:
       # TODO(nathaniel): support and test other subscription kinds.
       self.fail('Non-full subscriptions not yet supported!')
diff --git a/src/ruby/README.md b/src/ruby/README.md
index 7f75c0e..8c56ceb 100644
--- a/src/ruby/README.md
+++ b/src/ruby/README.md
@@ -19,10 +19,10 @@
 
 **Linux (Debian):**
 
-Add [Debian testing][] to your `sources.list` file. Example:
+Add [Debian jessie-backports][] to your `sources.list` file. Example:
 
 ```sh
-echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \
+echo "deb http://http.debian.net/debian jessie-backports main" | \
 sudo tee -a /etc/apt/sources.list
 ```
 
@@ -99,4 +99,4 @@
 [ruby extensions]:http://guides.rubygems.org/gems-with-extensions/
 [rubydoc]: http://www.rubydoc.info/gems/grpc
 [grpc.io]: http://www.grpc.io/docs/installation/ruby.html
-[Debian testing]:https://www.debian.org/releases/stretch/
+[Debian jessie-backports]:http://backports.debian.org/Instructions/
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index 1d98879..41ac83b 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -145,6 +145,7 @@
       gpr_event_wait(&a.done_write, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)));
 
   if (flags & GRPC_BAD_CLIENT_DISCONNECT) {
+    grpc_endpoint_shutdown(sfd.client);
     grpc_endpoint_destroy(sfd.client);
     sfd.client = NULL;
   }
@@ -153,6 +154,7 @@
 
   /* Shutdown */
   if (sfd.client) {
+    grpc_endpoint_shutdown(sfd.client);
     grpc_endpoint_destroy(sfd.client);
   }
   grpc_server_shutdown_and_notify(a.server, a.cq, NULL);
diff --git a/test/core/security/secure_endpoint_test.c b/test/core/security/secure_endpoint_test.c
index c76ddcd..047d377 100644
--- a/test/core/security/secure_endpoint_test.c
+++ b/test/core/security/secure_endpoint_test.c
@@ -39,6 +39,7 @@
 #include "src/core/security/secure_endpoint.h"
 #include "src/core/iomgr/endpoint_pair.h"
 #include "src/core/iomgr/iomgr.h"
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include "test/core/util/test_config.h"
@@ -163,12 +164,12 @@
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
 
-  grpc_iomgr_init();
+  grpc_init();
   grpc_pollset_init(&g_pollset);
   grpc_endpoint_tests(configs[0], &g_pollset);
   test_leftover(configs[1], 1);
   grpc_pollset_shutdown(&g_pollset, destroy_pollset, &g_pollset);
-  grpc_iomgr_shutdown();
+  grpc_shutdown();
 
   return 0;
 }
diff --git a/test/core/surface/completion_queue_test.c b/test/core/surface/completion_queue_test.c
index a5298a2..c382b2a 100644
--- a/test/core/surface/completion_queue_test.c
+++ b/test/core/surface/completion_queue_test.c
@@ -325,7 +325,7 @@
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
-  grpc_iomgr_init();
+  grpc_init();
   test_no_op();
   test_wait_empty();
   test_shutdown_then_next_polling();
@@ -336,6 +336,6 @@
   test_threading(1, 10);
   test_threading(10, 1);
   test_threading(10, 10);
-  grpc_iomgr_shutdown();
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/util/port_windows.c b/test/core/util/port_windows.c
index 2f64626..a57fe69 100644
--- a/test/core/util/port_windows.c
+++ b/test/core/util/port_windows.c
@@ -52,6 +52,30 @@
 
 #define NUM_RANDOM_PORTS_TO_PICK 100
 
+static int *chosen_ports = NULL;
+static size_t num_chosen_ports = 0;
+
+static int has_port_been_chosen(int port) {
+  size_t i;
+  for (i = 0; i < num_chosen_ports; i++) {
+    if (chosen_ports[i] == port) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static void free_chosen_ports() { gpr_free(chosen_ports); }
+
+static void chose_port(int port) {
+  if (chosen_ports == NULL) {
+    atexit(free_chosen_ports);
+  }
+  num_chosen_ports++;
+  chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports);
+  chosen_ports[num_chosen_ports - 1] = port;
+}
+
 static int is_port_available(int *port, int is_tcp) {
   const int proto = is_tcp ? IPPROTO_TCP : 0;
   const SOCKET fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto);
@@ -202,6 +226,10 @@
       port = 0;
     }
 
+    if (has_port_been_chosen(port)) {
+      continue;
+    }
+
     if (!is_port_available(&port, is_tcp)) {
       continue;
     }
@@ -218,6 +246,7 @@
     /* TODO(ctiller): consider caching this port in some structure, to avoid
                       handing it out again */
 
+    chose_port(port);
     return port;
   }
 
diff --git a/test/cpp/end2end/streaming_throughput_test.cc b/test/cpp/end2end/streaming_throughput_test.cc
new file mode 100644
index 0000000..63d49c0
--- /dev/null
+++ b/test/cpp/end2end/streaming_throughput_test.cc
@@ -0,0 +1,189 @@
+/*
+ *
+ * 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 <atomic>
+#include <mutex>
+#include <thread>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/security/credentials.h>
+#include <grpc++/security/server_credentials.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <gtest/gtest.h>
+
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/echo_duplicate.grpc.pb.h"
+#include "test/cpp/util/echo.grpc.pb.h"
+
+using grpc::cpp::test::util::EchoRequest;
+using grpc::cpp::test::util::EchoResponse;
+using std::chrono::system_clock;
+
+const char* kLargeString = "("
+  "To be, or not to be- that is the question:"
+  "Whether 'tis nobler in the mind to suffer"
+  "The slings and arrows of outrageous fortune"
+  "Or to take arms against a sea of troubles,"
+  "And by opposing end them. To die- to sleep-"
+  "No more; and by a sleep to say we end"
+  "The heartache, and the thousand natural shock"
+  "That flesh is heir to. 'Tis a consummation"
+  "Devoutly to be wish'd. To die- to sleep."
+  "To sleep- perchance to dream: ay, there's the rub!"
+  "For in that sleep of death what dreams may come"
+  "When we have shuffled off this mortal coil,"
+  "Must give us pause. There's the respect"
+  "That makes calamity of so long life."
+  "For who would bear the whips and scorns of time,"
+  "Th' oppressor's wrong, the proud man's contumely,"
+  "The pangs of despis'd love, the law's delay,"
+  "The insolence of office, and the spurns"
+  "That patient merit of th' unworthy takes,"
+  "When he himself might his quietus make"
+  "With a bare bodkin? Who would these fardels bear,"
+  "To grunt and sweat under a weary life,"
+  "But that the dread of something after death-"
+  "The undiscover'd country, from whose bourn"
+  "No traveller returns- puzzles the will,"
+  "And makes us rather bear those ills we have"
+  "Than fly to others that we know not of?"
+  "Thus conscience does make cowards of us all,"
+  "And thus the native hue of resolution"
+  "Is sicklied o'er with the pale cast of thought,"
+  "And enterprises of great pith and moment"
+  "With this regard their currents turn awry"
+  "And lose the name of action.- Soft you now!"
+  "The fair Ophelia!- Nymph, in thy orisons"
+  "Be all my sins rememb'red.";
+
+namespace grpc {
+namespace testing {
+
+class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service {
+ public:
+  static void BidiStream_Sender(ServerReaderWriter<EchoResponse, EchoRequest>* stream, std::atomic<bool>* should_exit) {
+    EchoResponse response;
+    response.set_message(kLargeString);
+    while (!should_exit->load()) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(1));
+      stream->Write(response);
+    }
+  }
+
+  // Only implement the one method we will be calling for brevity.
+  Status BidiStream(ServerContext* context,
+                    ServerReaderWriter<EchoResponse, EchoRequest>* stream)
+      GRPC_OVERRIDE {
+    EchoRequest request;
+    std::atomic<bool> should_exit(false);
+    std::thread sender(std::bind(&TestServiceImpl::BidiStream_Sender, stream, &should_exit));
+
+    while (stream->Read(&request)) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(3));
+    }
+    should_exit.store(true);
+    sender.join();
+    return Status::OK;
+  }
+};
+
+class End2endTest : public ::testing::Test {
+ protected:
+  void SetUp() GRPC_OVERRIDE {
+    int port = grpc_pick_unused_port_or_die();
+    server_address_ << "localhost:" << port;
+    // Setup server
+    ServerBuilder builder;
+    builder.AddListeningPort(server_address_.str(),
+                             InsecureServerCredentials());
+    builder.RegisterService(&service_);
+    server_ = builder.BuildAndStart();
+  }
+
+  void TearDown() GRPC_OVERRIDE { server_->Shutdown(); }
+
+  void ResetStub() {
+    std::shared_ptr<Channel> channel = CreateChannel(
+        server_address_.str(), InsecureCredentials());
+    stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel));
+  }
+
+  std::unique_ptr<grpc::cpp::test::util::TestService::Stub> stub_;
+  std::unique_ptr<Server> server_;
+  std::ostringstream server_address_;
+  TestServiceImpl service_;
+};
+
+static void Drainer(ClientReaderWriter<EchoRequest, EchoResponse>* reader) {
+  EchoResponse response;
+  while (reader->Read(&response)) {
+      // Just drain out the responses as fast as possible.
+  }
+}
+
+TEST_F(End2endTest, StreamingThroughput) {
+  ResetStub();
+  grpc::ClientContext context;
+  auto stream = stub_->BidiStream(&context);
+
+  auto reader = stream.get();
+  std::thread receiver(std::bind(Drainer, reader));
+
+  for (int i = 0; i < 10000; i++) {
+    EchoRequest request;
+    request.set_message(kLargeString);
+    ASSERT_TRUE(stream->Write(request));
+    if (i % 1000 == 0) {
+      gpr_log(GPR_INFO, "Send count = %d", i);
+    }
+  }
+  stream->WritesDone();
+  receiver.join();
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/tools/distpackages/.gitignore b/tools/distpackages/.gitignore
deleted file mode 100644
index 7a7d3f3..0000000
--- a/tools/distpackages/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-deb_out
diff --git a/tools/distpackages/build_deb_packages.sh b/tools/distpackages/build_deb_packages.sh
deleted file mode 100755
index 0beb41e..0000000
--- a/tools/distpackages/build_deb_packages.sh
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/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.
-
-# Where to put resulting .deb packages.
-set -x
-deb_dest="/tmp/deb_out"
-mkdir -p $deb_dest
-
-# Where the grpc disto is
-grpc_root="/var/local/git/grpc"
-
-# Update version from default values if the file /version.txt exists
-#
-# - when present, /version.txt will added by the docker build.
-pkg_version='0.5.0'
-if [ -f /version.txt ]; then
-  pkg_version=$(cat /version.txt)
-fi
-version="${pkg_version}.0"
-release_tag="release-${pkg_version//./_}"
-echo "Target release => $pkg_version, will checkout tag $release_tag"
-
-# Switch grpc_root to the release tag
-pushd $grpc_root
-git checkout $release_tag || { echo "bad release tag ${release_tag}"; exit 1; }
-popd
-
-if [ -f /.dockerinit ]; then
-  # We're in Docker where uname -p returns "unknown".
-  arch=x86_64
-else
-  arch=`uname -p`
-fi
-
-if [ $arch != "x86_64" ]
-then
-  echo Unsupported architecture.
-  exit 1
-fi
-
-# Build debian packages
-for pkg_name in libgrpc libgrpc-dev
-do
-  echo
-  echo "Building package $pkg_name"
-  tmp_dir=`mktemp -d`
-  echo "Using tmp dir $tmp_dir to build the package"
-
-  cp -a templates/$pkg_name $tmp_dir
-
-  arch_lib_dir=$tmp_dir/$pkg_name/usr/lib/$arch-linux-gnu
-
-  if [ $pkg_name == "libgrpc" ]
-  then
-    # Copy shared libraries
-    pushd $grpc_root
-    make install-shared_c prefix=$tmp_dir/$pkg_name/usr/lib
-    popd
-    mv $tmp_dir/$pkg_name/usr/lib/lib $arch_lib_dir
-
-    # non-dev package should contain so.0 symlinks
-    for symlink in $arch_lib_dir/*.so
-    do
-      mv $symlink $symlink.0
-    done
-  fi
-
-  if [ $pkg_name == "libgrpc-dev" ]
-  then
-    # Copy headers and static libraries
-    pushd $grpc_root
-    make install-headers_c install-static_c prefix=$tmp_dir/$pkg_name/usr/lib
-    popd
-
-    mv $tmp_dir/$pkg_name/usr/lib/include $tmp_dir/$pkg_name/usr/include
-    mv $tmp_dir/$pkg_name/usr/lib/lib $arch_lib_dir
-
-    # create symlinks to shared libraries
-    for libname in $arch_lib_dir/*.a
-    do
-      base=`basename $libname .a`
-      ln -s $base.so.$version $arch_lib_dir/$base.so
-    done
-  fi
-
-  # Adjust mode for some files in the package
-  find $tmp_dir/$pkg_name -type d | xargs chmod 755
-  find $tmp_dir/$pkg_name -type d | xargs chmod a-s
-  find $tmp_dir/$pkg_name -type f | xargs chmod 644
-  chmod 755 $tmp_dir/$pkg_name/DEBIAN/{postinst,postrm}
-
-  # Build the debian package
-  fakeroot dpkg-deb --build $tmp_dir/$pkg_name || { echo "dpkg-deb failed"; exit 1; }
-
-  deb_path=$deb_dest/${pkg_name}_${pkg_version}_amd64.deb
-
-  # Copy the .deb file to destination dir
-  cp $tmp_dir/$pkg_name.deb $deb_path
-
-  echo "Resulting package: $deb_path"
-  echo "Package info:"
-  dpkg-deb -I $deb_path
-  echo "Package contents:"
-  dpkg-deb -c $deb_path
-  echo "Problems reported by lintian:"
-  lintian $deb_path
-  echo
-done
diff --git a/tools/distpackages/templates/libgrpc-dev/DEBIAN/control b/tools/distpackages/templates/libgrpc-dev/DEBIAN/control
deleted file mode 100644
index 289a278..0000000
--- a/tools/distpackages/templates/libgrpc-dev/DEBIAN/control
+++ /dev/null
@@ -1,10 +0,0 @@
-Package: libgrpc-dev
-Version: 0.5.0
-Architecture: amd64
-Maintainer: Jan Tattermusch <jtattermusch@google.com>
-Depends: libgrpc, libc6-dev | libc-dev
-Section: libdevel
-Priority: optional
-Homepage: https://github.com/grpc/grpc
-Description: gRPC C Core - header files
- Core C libraries for gRPC - An RPC library and framework.
diff --git a/tools/distpackages/templates/libgrpc-dev/usr/share/doc/libgrpc-dev/changelog.gz b/tools/distpackages/templates/libgrpc-dev/usr/share/doc/libgrpc-dev/changelog.gz
deleted file mode 100644
index 4f557b8..0000000
--- a/tools/distpackages/templates/libgrpc-dev/usr/share/doc/libgrpc-dev/changelog.gz
+++ /dev/null
Binary files differ
diff --git a/tools/distpackages/templates/libgrpc-dev/usr/share/doc/libgrpc-dev/copyright b/tools/distpackages/templates/libgrpc-dev/usr/share/doc/libgrpc-dev/copyright
deleted file mode 100644
index 83fb6f4..0000000
--- a/tools/distpackages/templates/libgrpc-dev/usr/share/doc/libgrpc-dev/copyright
+++ /dev/null
@@ -1,34 +0,0 @@
-Copyright:
-
-    Copyright (C) 2015 Google Inc.
-
-License (new style BSD, with Google Inc. as copyright holder):
-
-  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.
\ No newline at end of file
diff --git a/tools/distpackages/templates/libgrpc/DEBIAN/control b/tools/distpackages/templates/libgrpc/DEBIAN/control
deleted file mode 100644
index 5854b1f..0000000
--- a/tools/distpackages/templates/libgrpc/DEBIAN/control
+++ /dev/null
@@ -1,11 +0,0 @@
-Package: libgrpc
-Version: 0.5.0
-Architecture: amd64
-Maintainer: Jan Tattermusch <jtattermusch@google.com>
-Depends: libc6, openssl (1.0.2-1)
-Build-Depends-Indep: openssl (1.0.2-1)
-Section: libs
-Priority: optional
-Homepage: https://github.com/grpc/grpc
-Description: gRPC C Core
- Core C libraries for gRPC - An RPC library and framework.
diff --git a/tools/distpackages/templates/libgrpc/DEBIAN/postinst b/tools/distpackages/templates/libgrpc/DEBIAN/postinst
deleted file mode 100644
index 379f1fa..0000000
--- a/tools/distpackages/templates/libgrpc/DEBIAN/postinst
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-set -e
-# Automatically added by dh_makeshlibs
-if [ "$1" = "configure" ]; then
-	ldconfig
-fi
-# End automatically added section
diff --git a/tools/distpackages/templates/libgrpc/DEBIAN/postrm b/tools/distpackages/templates/libgrpc/DEBIAN/postrm
deleted file mode 100644
index 3e73d38..0000000
--- a/tools/distpackages/templates/libgrpc/DEBIAN/postrm
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-set -e
-# Automatically added by dh_makeshlibs
-if [ "$1" = "remove" ]; then
-	ldconfig
-fi
-# End automatically added section
diff --git a/tools/distpackages/templates/libgrpc/DEBIAN/shlibs b/tools/distpackages/templates/libgrpc/DEBIAN/shlibs
deleted file mode 100644
index 31d0e0a..0000000
--- a/tools/distpackages/templates/libgrpc/DEBIAN/shlibs
+++ /dev/null
@@ -1,3 +0,0 @@
-libgpr 0
-libgrpc 0
-libgrpc_unsecure 0
diff --git a/tools/distpackages/templates/libgrpc/usr/share/doc/libgrpc/changelog.gz b/tools/distpackages/templates/libgrpc/usr/share/doc/libgrpc/changelog.gz
deleted file mode 100644
index 12d4cd9..0000000
--- a/tools/distpackages/templates/libgrpc/usr/share/doc/libgrpc/changelog.gz
+++ /dev/null
Binary files differ
diff --git a/tools/distpackages/templates/libgrpc/usr/share/doc/libgrpc/copyright b/tools/distpackages/templates/libgrpc/usr/share/doc/libgrpc/copyright
deleted file mode 100644
index 83fb6f4..0000000
--- a/tools/distpackages/templates/libgrpc/usr/share/doc/libgrpc/copyright
+++ /dev/null
@@ -1,34 +0,0 @@
-Copyright:
-
-    Copyright (C) 2015 Google Inc.
-
-License (new style BSD, with Google Inc. as copyright holder):
-
-  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.
\ No newline at end of file
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 2da02b2..8f6511b 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -478,8 +478,10 @@
 if platform.system() == 'Windows':
   def make_jobspec(cfg, targets):
     extra_args = []
-    if args.travis:
-      extra_args.extend(["/m", "/p:GenerateDebugInformation=false"])
+    # better do parallel compilation
+    extra_args.extend(["/m"])
+    # disable PDB generation: it's broken, and we don't need it during CI
+    extra_args.extend(["/p:GenerateDebugInformation=false", "/p:DebugInformationFormat=None"])
     return [
       jobset.JobSpec(['vsprojects\\build.bat', 
                       'vsprojects\\%s.sln' % target, 
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 629a323..1774e56 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -1631,6 +1631,22 @@
       "grpc", 
       "grpc++", 
       "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "language": "c++", 
+    "name": "streaming_throughput_test", 
+    "src": [
+      "test/cpp/end2end/streaming_throughput_test.cc"
+    ]
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
       "grpc_test_util", 
       "qps"
     ], 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index ad37688..5976d5d 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -1572,6 +1572,22 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c++", 
+    "name": "streaming_throughput_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
     "name": "sync_streaming_ping_pong_test", 
     "platforms": [
       "linux",