diff --git a/Makefile b/Makefile
index f31b988a35e0c4037b3f3fddddd72d66e83701eb..40f98b90cad16baa124fa4130e4f7d70ba8cb3e0 100644
--- a/Makefile
+++ b/Makefile
@@ -338,6 +338,8 @@ gpr_cmdline_test: bins/$(CONFIG)/gpr_cmdline_test
 gpr_histogram_test: bins/$(CONFIG)/gpr_histogram_test
 gpr_host_port_test: bins/$(CONFIG)/gpr_host_port_test
 gpr_log_test: bins/$(CONFIG)/gpr_log_test
+gpr_file_test: bins/$(CONFIG)/gpr_file_test
+gpr_env_test: bins/$(CONFIG)/gpr_env_test
 gpr_slice_buffer_test: bins/$(CONFIG)/gpr_slice_buffer_test
 gpr_slice_test: bins/$(CONFIG)/gpr_slice_test
 gpr_string_test: bins/$(CONFIG)/gpr_string_test
@@ -571,7 +573,7 @@ privatelibs_cxx:  libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libtips_cl
 
 buildtests: buildtests_c buildtests_cxx
 
-buildtests_c: privatelibs_c bins/$(CONFIG)/alarm_heap_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/fling_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_log_test bins/$(CONFIG)/gpr_slice_buffer_test bins/$(CONFIG)/gpr_slice_test bins/$(CONFIG)/gpr_string_test bins/$(CONFIG)/gpr_sync_test bins/$(CONFIG)/gpr_thd_test bins/$(CONFIG)/gpr_time_test bins/$(CONFIG)/gpr_useful_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/json_rewrite bins/$(CONFIG)/json_rewrite_test bins/$(CONFIG)/json_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/poll_kick_posix_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/time_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/transport_metadata_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fake_security_census_simple_request_test bins/$(CONFIG)/chttp2_fake_security_disappearing_server_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fake_security_invoke_large_request_test bins/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fake_security_no_op_test bins/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test bins/$(CONFIG)/chttp2_fake_security_simple_request_test bins/$(CONFIG)/chttp2_fake_security_thread_stress_test bins/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fullstack_no_op_test bins/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_no_op_test bins/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
+buildtests_c: privatelibs_c bins/$(CONFIG)/alarm_heap_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/fling_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_log_test bins/$(CONFIG)/gpr_file_test bins/$(CONFIG)/gpr_env_test bins/$(CONFIG)/gpr_slice_buffer_test bins/$(CONFIG)/gpr_slice_test bins/$(CONFIG)/gpr_string_test bins/$(CONFIG)/gpr_sync_test bins/$(CONFIG)/gpr_thd_test bins/$(CONFIG)/gpr_time_test bins/$(CONFIG)/gpr_useful_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/json_rewrite bins/$(CONFIG)/json_rewrite_test bins/$(CONFIG)/json_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/poll_kick_posix_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/time_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/transport_metadata_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fake_security_census_simple_request_test bins/$(CONFIG)/chttp2_fake_security_disappearing_server_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fake_security_invoke_large_request_test bins/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fake_security_no_op_test bins/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test bins/$(CONFIG)/chttp2_fake_security_simple_request_test bins/$(CONFIG)/chttp2_fake_security_thread_stress_test bins/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fullstack_no_op_test bins/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_no_op_test bins/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
 
 buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_publisher_test bins/$(CONFIG)/tips_subscriber_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test
 
@@ -632,6 +634,10 @@ test_c: buildtests_c
 	$(Q) ./bins/$(CONFIG)/gpr_host_port_test || ( echo test gpr_host_port_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_log_test"
 	$(Q) ./bins/$(CONFIG)/gpr_log_test || ( echo test gpr_log_test failed ; exit 1 )
+	$(E) "[RUN]     Testing gpr_file_test"
+	$(Q) ./bins/$(CONFIG)/gpr_file_test || ( echo test gpr_file_test failed ; exit 1 )
+	$(E) "[RUN]     Testing gpr_env_test"
+	$(Q) ./bins/$(CONFIG)/gpr_env_test || ( echo test gpr_env_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_slice_buffer_test"
 	$(Q) ./bins/$(CONFIG)/gpr_slice_buffer_test || ( echo test gpr_slice_buffer_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_slice_test"
@@ -1232,6 +1238,12 @@ LIBGPR_SRC = \
     src/core/support/cmdline.c \
     src/core/support/cpu_linux.c \
     src/core/support/cpu_posix.c \
+    src/core/support/env_linux.c \
+    src/core/support/env_posix.c \
+    src/core/support/env_win32.c \
+    src/core/support/file.c \
+    src/core/support/file_posix.c \
+    src/core/support/file_win32.c \
     src/core/support/histogram.c \
     src/core/support/host_port.c \
     src/core/support/log.c \
@@ -1317,6 +1329,12 @@ objs/$(CONFIG)/src/core/support/cancellable.o:
 objs/$(CONFIG)/src/core/support/cmdline.o: 
 objs/$(CONFIG)/src/core/support/cpu_linux.o: 
 objs/$(CONFIG)/src/core/support/cpu_posix.o: 
+objs/$(CONFIG)/src/core/support/env_linux.o: 
+objs/$(CONFIG)/src/core/support/env_posix.o: 
+objs/$(CONFIG)/src/core/support/env_win32.o: 
+objs/$(CONFIG)/src/core/support/file.o: 
+objs/$(CONFIG)/src/core/support/file_posix.o: 
+objs/$(CONFIG)/src/core/support/file_win32.o: 
 objs/$(CONFIG)/src/core/support/histogram.o: 
 objs/$(CONFIG)/src/core/support/host_port.o: 
 objs/$(CONFIG)/src/core/support/log.o: 
@@ -4321,6 +4339,68 @@ endif
 endif
 
 
+GPR_FILE_TEST_SRC = \
+    test/core/support/file_test.c \
+
+GPR_FILE_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_FILE_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/gpr_file_test: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/gpr_file_test: $(GPR_FILE_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(GPR_FILE_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/gpr_file_test
+
+endif
+
+objs/$(CONFIG)/test/core/support/file_test.o:  libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_gpr_file_test: $(GPR_FILE_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GPR_FILE_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+GPR_ENV_TEST_SRC = \
+    test/core/support/env_test.c \
+
+GPR_ENV_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_ENV_TEST_SRC))))
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL with ALPN.
+
+bins/$(CONFIG)/gpr_env_test: openssl_dep_error
+
+else
+
+bins/$(CONFIG)/gpr_env_test: $(GPR_ENV_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(GPR_ENV_TEST_OBJS) libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/gpr_env_test
+
+endif
+
+objs/$(CONFIG)/test/core/support/env_test.o:  libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a
+
+deps_gpr_env_test: $(GPR_ENV_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GPR_ENV_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GPR_SLICE_BUFFER_TEST_SRC = \
     test/core/support/slice_buffer_test.c \
 
diff --git a/build.json b/build.json
index 48d1a218266676ff1d97756b394d1669d62d917f..579ede329f6795b9911e578f26f97eb92957497b 100644
--- a/build.json
+++ b/build.json
@@ -227,8 +227,11 @@
       ],
       "headers": [
         "src/core/support/cpu.h",
+        "src/core/support/env.h",
+        "src/core/support/file.h",
         "src/core/support/murmur_hash.h",
         "src/core/support/string.h",
+        "src/core/support/string_win32.h",
         "src/core/support/thd_internal.h"
       ],
       "src": [
@@ -237,6 +240,12 @@
         "src/core/support/cmdline.c",
         "src/core/support/cpu_linux.c",
         "src/core/support/cpu_posix.c",
+        "src/core/support/env_linux.c",
+        "src/core/support/env_posix.c",
+        "src/core/support/env_win32.c",
+        "src/core/support/file.c",
+        "src/core/support/file_posix.c",
+        "src/core/support/file_win32.c",
         "src/core/support/histogram.c",
         "src/core/support/host_port.c",
         "src/core/support/log.c",
@@ -923,6 +932,30 @@
         "gpr"
       ]
     },
+    {
+      "name": "gpr_file_test",
+      "build": "test",
+      "language": "c",
+      "src": [
+        "test/core/support/file_test.c"
+      ],
+      "deps": [
+        "gpr_test_util",
+        "gpr"
+      ]
+    },
+    {
+      "name": "gpr_env_test",
+      "build": "test",
+      "language": "c",
+      "src": [
+        "test/core/support/env_test.c"
+      ],
+      "deps": [
+        "gpr_test_util",
+        "gpr"
+      ]
+    },
     {
       "name": "gpr_slice_buffer_test",
       "build": "test",
diff --git a/include/grpc++/credentials.h b/include/grpc++/credentials.h
index 987d890b4f8911b7a800a28ce2bea30b5ef7b6bf..52304d7f369cd7a62ad8b2c6b7ddda6668ad4a45 100644
--- a/include/grpc++/credentials.h
+++ b/include/grpc++/credentials.h
@@ -66,14 +66,13 @@ class Credentials final {
 
 // Options used to build SslCredentials
 // pem_roots_cert is the buffer containing the PEM encoding of the server root
-// certificates. This parameter cannot be empty.
+// certificates. If this parameter is empty, the default roots will be used.
 // pem_private_key is the buffer containing the PEM encoding of the client's
 // private key. This parameter can be empty if the client does not have a
 // private key.
 // pem_cert_chain is the buffer containing the PEM encoding of the client's
 // certificate chain. This parameter can be empty if the client does not have
 // a certificate chain.
-// TODO(jboeuf) Change it to point to a file.
 struct SslCredentialsOptions {
   grpc::string pem_root_certs;
   grpc::string pem_private_key;
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 0732a8f83a580c76f999053b4673c56bf804efb8..731959069fc7b213102806e4a438b5e30bfc62a9 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -54,6 +54,12 @@ void grpc_credentials_release(grpc_credentials *creds);
 /* Creates default credentials. */
 grpc_credentials *grpc_default_credentials_create(void);
 
+/* Environment variable that points to the default SSL roots file. This file
+   must be a PEM encoded file with all the roots such as the one that can be
+   downloaded from https://pki.google.com/roots.pem.  */
+#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \
+  "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"
+
 /* Object that holds a private key / certificate chain pair in PEM format. */
 typedef struct {
   /* private_key is the NULL-terminated string containing the PEM encoding of
diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h
index 2bf53483157868ba10c861992ad3d624e846a3bc..e99099c651c0ea027b66942279ba8dc769a475e6 100644
--- a/include/grpc/support/port_platform.h
+++ b/include/grpc/support/port_platform.h
@@ -61,6 +61,8 @@
 #define GPR_POSIX_SOCKET 1
 #define GPR_POSIX_SOCKETADDR 1
 #define GPR_POSIX_SOCKETUTILS 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_FILE 1
 #define GPR_POSIX_STRING 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
@@ -74,6 +76,8 @@
 #define GPR_LINUX_EVENTFD 1
 #define GPR_POSIX_SOCKET 1
 #define GPR_POSIX_SOCKETADDR 1
+#define GPR_LINUX_ENV 1
+#define GPR_POSIX_FILE 1
 #define GPR_POSIX_STRING 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
@@ -93,6 +97,8 @@
 #define GPR_POSIX_SOCKET 1
 #define GPR_POSIX_SOCKETADDR 1
 #define GPR_POSIX_SOCKETUTILS 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_FILE 1
 #define GPR_POSIX_STRING 1
 #define GPR_POSIX_SYNC 1
 #define GPR_POSIX_TIME 1
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index bcb024f2ac562f7add20e32293c1b41fe51697d3..507b91b8a63da4c5c47437ff82a080e26f800156 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -298,6 +298,7 @@ static void channel_op(grpc_channel_element *elem,
                        grpc_channel_element *from_elem, grpc_channel_op *op) {
   channel_data *chand = elem->channel_data;
   grpc_child_channel *child_channel;
+  grpc_channel_op rop;
   GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
 
   switch (op->type) {
@@ -323,6 +324,10 @@ static void channel_op(grpc_channel_element *elem,
       if (child_channel) {
         grpc_child_channel_destroy(child_channel, 1);
       }
+      /* fake a transport closed to satisfy the refcounting in client */
+      rop.type = GRPC_TRANSPORT_CLOSED;
+      rop.dir = GRPC_CALL_UP;
+      grpc_channel_next_op(elem, &rop);
       break;
     case GRPC_TRANSPORT_GOAWAY:
       /* receiving goaway: if it's from our active child, drop the active child;
diff --git a/src/core/iomgr/pollset_kick.c b/src/core/iomgr/pollset_kick.c
index 238ec75c61c3f8e4e5cc3db6454aa42d30625ad0..f0211b8274d70b002bdfbc5255b0691ccde866d8 100644
--- a/src/core/iomgr/pollset_kick.c
+++ b/src/core/iomgr/pollset_kick.c
@@ -48,49 +48,49 @@
 /* This implementation is based on a freelist of wakeup fds, with extra logic to
  * handle kicks while there is no attached fd. */
 
+/* TODO(klempner): Autosize this, and consider providing a way to disable the
+ * cap entirely on systems with large fd limits */
 #define GRPC_MAX_CACHED_WFDS 50
-#define GRPC_WFD_LOW_WATERMARK 25
 
 static grpc_kick_fd_info *fd_freelist = NULL;
 static int fd_freelist_count = 0;
 static gpr_mu fd_freelist_mu;
 
 static grpc_kick_fd_info *allocate_wfd(void) {
-  grpc_kick_fd_info *info;
+  grpc_kick_fd_info *info = NULL;
   gpr_mu_lock(&fd_freelist_mu);
   if (fd_freelist != NULL) {
     info = fd_freelist;
     fd_freelist = fd_freelist->next;
     --fd_freelist_count;
-  } else {
+  }
+  gpr_mu_unlock(&fd_freelist_mu);
+  if (info == NULL) {
     info = gpr_malloc(sizeof(*info));
     grpc_wakeup_fd_create(&info->wakeup_fd);
     info->next = NULL;
   }
-  gpr_mu_unlock(&fd_freelist_mu);
   return info;
 }
 
-static void destroy_wfd(void) {
-  /* assumes fd_freelist_mu is held */
-  grpc_kick_fd_info *current = fd_freelist;
-  fd_freelist = fd_freelist->next;
-  fd_freelist_count--;
-  grpc_wakeup_fd_destroy(&current->wakeup_fd);
-  gpr_free(current);
+static void destroy_wfd(grpc_kick_fd_info* wfd) {
+  grpc_wakeup_fd_destroy(&wfd->wakeup_fd);
+  gpr_free(wfd);
 }
 
 static void free_wfd(grpc_kick_fd_info *fd_info) {
   gpr_mu_lock(&fd_freelist_mu);
-  fd_info->next = fd_freelist;
-  fd_freelist = fd_info;
-  fd_freelist_count++;
-  if (fd_freelist_count > GRPC_MAX_CACHED_WFDS) {
-    while (fd_freelist_count > GRPC_WFD_LOW_WATERMARK) {
-      destroy_wfd();
-    }
+  if (fd_freelist_count < GRPC_MAX_CACHED_WFDS) {
+    fd_info->next = fd_freelist;
+    fd_freelist = fd_info;
+    fd_freelist_count++;
+    fd_info = NULL;
   }
   gpr_mu_unlock(&fd_freelist_mu);
+
+  if (fd_info) {
+    destroy_wfd(fd_info);
+  }
 }
 
 void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state) {
@@ -148,6 +148,11 @@ void grpc_pollset_kick_global_init(void) {
 }
 
 void grpc_pollset_kick_global_destroy(void) {
+  while (fd_freelist != NULL) {
+    grpc_kick_fd_info *current = fd_freelist;
+    fd_freelist = fd_freelist->next;
+    destroy_wfd(current);
+  }
   grpc_wakeup_fd_global_destroy();
   gpr_mu_destroy(&fd_freelist_mu);
 }
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
index 7b7d8f321168d2d863f529f8dbb0ab7c1dccfeb9..6f0d72c0c3d9e2f173f77892afe8e108a10395e5 100644
--- a/src/core/security/credentials.c
+++ b/src/core/security/credentials.c
@@ -216,14 +216,10 @@ static void ssl_copy_key_material(const char *input, unsigned char **output,
 static void ssl_build_config(const char *pem_root_certs,
                              grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
                              grpc_ssl_config *config) {
-  if (pem_root_certs == NULL) {
-    /* TODO(jboeuf): Get them from the environment. */
-    gpr_log(GPR_ERROR, "Default SSL roots not yet implemented.");
-  } else {
+  if (pem_root_certs != NULL) {
     ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
                           &config->pem_root_certs_size);
   }
-
   if (pem_key_cert_pair != NULL) {
     GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
     GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c
index 58cd458415d50644aa6a503b444181523005e1f7..1edec2977597929006740b5b668273800a4b85d9 100644
--- a/src/core/security/security_context.c
+++ b/src/core/security/security_context.c
@@ -39,6 +39,8 @@
 #include "src/core/channel/http_client_filter.h"
 #include "src/core/security/credentials.h"
 #include "src/core/security/secure_endpoint.h"
+#include "src/core/support/env.h"
+#include "src/core/support/file.h"
 #include "src/core/support/string.h"
 #include "src/core/surface/lame_client.h"
 #include "src/core/transport/chttp2/alpn.h"
@@ -319,6 +321,28 @@ static grpc_security_context_vtable ssl_channel_vtable = {
 static grpc_security_context_vtable ssl_server_vtable = {
     ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer};
 
+static gpr_slice default_pem_root_certs;
+
+static void init_default_pem_root_certs(void) {
+  char *default_root_certs_path =
+      gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
+  if (default_root_certs_path == NULL) {
+    default_pem_root_certs = gpr_empty_slice();
+  } else {
+    default_pem_root_certs = gpr_load_file(default_root_certs_path, NULL);
+    gpr_free(default_root_certs_path);
+  }
+}
+
+static size_t get_default_pem_roots(const unsigned char **pem_root_certs) {
+  /* TODO(jboeuf@google.com): Maybe revisit the approach which consists in
+     loading all the roots once for the lifetime of the process. */
+  static gpr_once once = GPR_ONCE_INIT;
+  gpr_once_init(&once, init_default_pem_root_certs);
+  *pem_root_certs = GPR_SLICE_START_PTR(default_pem_root_certs);
+  return GPR_SLICE_LENGTH(default_pem_root_certs);
+}
+
 grpc_security_status grpc_ssl_channel_security_context_create(
     grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
     const char *secure_peer_name, grpc_channel_security_context **ctx) {
@@ -330,6 +354,8 @@ grpc_security_status grpc_ssl_channel_security_context_create(
   tsi_result result = TSI_OK;
   grpc_ssl_channel_security_context *c;
   size_t i;
+  const unsigned char *pem_root_certs;
+  size_t pem_root_certs_size;
 
   for (i = 0; i < num_alpn_protocols; i++) {
     alpn_protocol_strings[i] =
@@ -338,9 +364,8 @@ grpc_security_status grpc_ssl_channel_security_context_create(
         strlen(grpc_chttp2_get_alpn_version_index(i));
   }
 
-  if (config == NULL || secure_peer_name == NULL ||
-      config->pem_root_certs == NULL) {
-    gpr_log(GPR_ERROR, "An ssl channel needs a secure name and root certs.");
+  if (config == NULL || secure_peer_name == NULL) {
+    gpr_log(GPR_ERROR, "An ssl channel needs a config and a secure name.");
     goto error;
   }
   if (!check_request_metadata_creds(request_metadata_creds)) {
@@ -357,11 +382,20 @@ grpc_security_status grpc_ssl_channel_security_context_create(
   if (secure_peer_name != NULL) {
     c->secure_peer_name = gpr_strdup(secure_peer_name);
   }
+  if (config->pem_root_certs == NULL) {
+    pem_root_certs_size = get_default_pem_roots(&pem_root_certs);
+    if (pem_root_certs == NULL || pem_root_certs_size == 0) {
+      gpr_log(GPR_ERROR, "Could not get default pem root certs.");
+      goto error;
+    }
+  } else {
+    pem_root_certs = config->pem_root_certs;
+    pem_root_certs_size = config->pem_root_certs_size;
+  }
   result = tsi_create_ssl_client_handshaker_factory(
       config->pem_private_key, config->pem_private_key_size,
-      config->pem_cert_chain, config->pem_cert_chain_size,
-      config->pem_root_certs, config->pem_root_certs_size,
-      GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings,
+      config->pem_cert_chain, config->pem_cert_chain_size, pem_root_certs,
+      pem_root_certs_size, GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings,
       alpn_protocol_string_lengths, num_alpn_protocols, &c->handshaker_factory);
   if (result != TSI_OK) {
     gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
diff --git a/src/core/statistics/census_rpc_stats.c b/src/core/statistics/census_rpc_stats.c
index 785c091debc2188cd9daecbf251a2bbd79b7d7b5..fc66cb951fef77bca35adde2f538ecff503753dc 100644
--- a/src/core/statistics/census_rpc_stats.c
+++ b/src/core/statistics/census_rpc_stats.c
@@ -141,7 +141,7 @@ static void record_stats(census_ht* store, census_op_id op_id,
                          const census_rpc_stats* stats) {
   gpr_mu_lock(&g_mu);
   if (store != NULL) {
-    trace_obj* trace = NULL;
+    census_trace_obj* trace = NULL;
     census_internal_lock_trace_store();
     trace = census_get_trace_obj_locked(op_id);
     if (trace != NULL) {
diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c
index 3c4ba66f5f4a546866d99772bf0874962a98d3b4..8b98323e64cd8566de2066e010f4abee051fe699 100644
--- a/src/core/statistics/census_tracing.c
+++ b/src/core/statistics/census_tracing.c
@@ -32,38 +32,22 @@
  */
 
 #include "src/core/statistics/census_interface.h"
+#include "src/core/statistics/census_tracing.h"
 
 #include <stdio.h>
 #include <string.h>
 
-#include "src/core/statistics/census_rpc_stats.h"
 #include "src/core/statistics/hash_table.h"
 #include "src/core/support/string.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
 #include <grpc/support/sync.h>
-#include <grpc/support/time.h>
-
-/* Struct for a trace annotation. */
-typedef struct annotation {
-  gpr_timespec ts;                            /* timestamp of the annotation */
-  char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */
-  struct annotation* next;
-} annotation;
-
-typedef struct trace_obj {
-  census_op_id id;
-  gpr_timespec ts;
-  census_rpc_stats rpc_stats;
-  char* method;
-  annotation* annotations;
-} trace_obj;
-
-static void trace_obj_destroy(trace_obj* obj) {
-  annotation* p = obj->annotations;
+
+void census_trace_obj_destroy(census_trace_obj* obj) {
+  census_trace_annotation* p = obj->annotations;
   while (p != NULL) {
-    annotation* next = p->next;
+    census_trace_annotation* next = p->next;
     gpr_free(p);
     p = next;
   }
@@ -71,7 +55,9 @@ static void trace_obj_destroy(trace_obj* obj) {
   gpr_free(obj);
 }
 
-static void delete_trace_obj(void* obj) { trace_obj_destroy((trace_obj*)obj); }
+static void delete_trace_obj(void* obj) {
+  census_trace_obj_destroy((census_trace_obj*)obj);
+}
 
 static const census_ht_option ht_opt = {
     CENSUS_HT_UINT64 /* key type*/, 571 /* n_of_buckets */, NULL /* hash */,
@@ -103,8 +89,8 @@ static void init_mutex_once(void) {
 census_op_id census_tracing_start_op(void) {
   gpr_mu_lock(&g_mu);
   {
-    trace_obj* ret = (trace_obj*)gpr_malloc(sizeof(trace_obj));
-    memset(ret, 0, sizeof(trace_obj));
+    census_trace_obj* ret = gpr_malloc(sizeof(census_trace_obj));
+    memset(ret, 0, sizeof(census_trace_obj));
     g_id++;
     memcpy(&ret->id, &g_id, sizeof(census_op_id));
     ret->rpc_stats.cnt = 1;
@@ -118,7 +104,7 @@ census_op_id census_tracing_start_op(void) {
 
 int census_add_method_tag(census_op_id op_id, const char* method) {
   int ret = 0;
-  trace_obj* trace = NULL;
+  census_trace_obj* trace = NULL;
   gpr_mu_lock(&g_mu);
   trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
   if (trace == NULL) {
@@ -131,11 +117,11 @@ int census_add_method_tag(census_op_id op_id, const char* method) {
 }
 
 void census_tracing_print(census_op_id op_id, const char* anno_txt) {
-  trace_obj* trace = NULL;
+  census_trace_obj* trace = NULL;
   gpr_mu_lock(&g_mu);
   trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
   if (trace != NULL) {
-    annotation* anno = gpr_malloc(sizeof(annotation));
+    census_trace_annotation* anno = gpr_malloc(sizeof(census_trace_annotation));
     anno->ts = gpr_now();
     {
       char* d = anno->txt;
@@ -153,7 +139,7 @@ void census_tracing_print(census_op_id op_id, const char* anno_txt) {
 }
 
 void census_tracing_end_op(census_op_id op_id) {
-  trace_obj* trace = NULL;
+  census_trace_obj* trace = NULL;
   gpr_mu_lock(&g_mu);
   trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
   if (trace != NULL) {
@@ -196,14 +182,58 @@ void census_internal_lock_trace_store(void) { gpr_mu_lock(&g_mu); }
 
 void census_internal_unlock_trace_store(void) { gpr_mu_unlock(&g_mu); }
 
-trace_obj* census_get_trace_obj_locked(census_op_id op_id) {
+census_trace_obj* census_get_trace_obj_locked(census_op_id op_id) {
   if (g_trace_store == NULL) {
     gpr_log(GPR_ERROR, "Census trace store is not initialized.");
     return NULL;
   }
-  return (trace_obj*)census_ht_find(g_trace_store, op_id_as_key(&op_id));
+  return (census_trace_obj*)census_ht_find(g_trace_store, op_id_as_key(&op_id));
 }
 
-const char* census_get_trace_method_name(const trace_obj* trace) {
-  return (const char*)trace->method;
+const char* census_get_trace_method_name(const census_trace_obj* trace) {
+  return trace->method;
+}
+
+static census_trace_annotation* dup_annotation_chain(
+    census_trace_annotation* from) {
+  census_trace_annotation *ret = NULL;
+  census_trace_annotation **to = &ret;
+  for (; from != NULL; from = from->next) {
+    *to = gpr_malloc(sizeof(census_trace_annotation));
+    memcpy(*to, from, sizeof(census_trace_annotation));
+    to = &(*to)->next;
+  }
+  return ret;
+}
+
+static census_trace_obj* trace_obj_dup(census_trace_obj* from) {
+  census_trace_obj* to = NULL;
+  GPR_ASSERT(from != NULL);
+  to = gpr_malloc(sizeof(census_trace_obj));
+  to->id = from->id;
+  to->ts = from->ts;
+  to->rpc_stats = from->rpc_stats;
+  to->method = gpr_strdup(from->method);
+  to->annotations = dup_annotation_chain(from->annotations);
+  return to;
+}
+
+census_trace_obj** census_get_active_ops(int* num_active_ops) {
+  census_trace_obj** ret = NULL;
+  gpr_mu_lock(&g_mu);
+  if (g_trace_store != NULL) {
+    size_t n = 0;
+    census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n);
+    *num_active_ops = (int)n;
+    if (n != 0 ) {
+      size_t i = 0;
+      ret = gpr_malloc(sizeof(census_trace_obj *) * n);
+      for (i = 0; i < n; i++) {
+        ret[i] = trace_obj_dup((census_trace_obj*)all_kvs[i].v);
+      }
+    }
+    gpr_free(all_kvs);
+  }
+  gpr_mu_unlock(&g_mu);
+  return ret;
 }
diff --git a/src/core/statistics/census_tracing.h b/src/core/statistics/census_tracing.h
index f356c9424d51d31cc27465da6d10f7ae81043f19..88a06a4a5248c951ed8dd785f24438ea3c8697cc 100644
--- a/src/core/statistics/census_tracing.h
+++ b/src/core/statistics/census_tracing.h
@@ -34,12 +34,35 @@
 #ifndef __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
 #define __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
 
+#include <grpc/support/time.h>
+#include "src/core/statistics/census_rpc_stats.h"
+
+/* WARNING: The data structures and APIs provided by this file are for GRPC
+   library's internal use ONLY. They might be changed in backward-incompatible
+   ways and are not subject to any deprecation policy.
+   They are not recommended for external use.
+ */
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* Opaque structure for trace object */
-typedef struct trace_obj trace_obj;
+/* Struct for a trace annotation. */
+typedef struct census_trace_annotation {
+  gpr_timespec ts;                            /* timestamp of the annotation */
+  char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */
+  struct census_trace_annotation* next;
+} census_trace_annotation;
+
+typedef struct census_trace_obj {
+  census_op_id id;
+  gpr_timespec ts;
+  census_rpc_stats rpc_stats;
+  char* method;
+  census_trace_annotation* annotations;
+} census_trace_obj;
+
+/* Deletes trace object. */
+void census_trace_obj_destroy(census_trace_obj* obj);
 
 /* Initializes trace store. This function is thread safe. */
 void census_tracing_init(void);
@@ -50,15 +73,21 @@ void census_tracing_shutdown(void);
 /* Gets trace obj corresponding to the input op_id. Returns NULL if trace store
    is not initialized or trace obj is not found. Requires trace store being
    locked before calling this function. */
-trace_obj* census_get_trace_obj_locked(census_op_id op_id);
+census_trace_obj* census_get_trace_obj_locked(census_op_id op_id);
 
 /* The following two functions acquire and release the trace store global lock.
    They are for census internal use only. */
 void census_internal_lock_trace_store(void);
 void census_internal_unlock_trace_store(void);
 
-/* Gets method tag name associated with the input trace object. */
-const char* census_get_trace_method_name(const trace_obj* trace);
+/* Gets method name associated with the input trace object. */
+const char* census_get_trace_method_name(const census_trace_obj* trace);
+
+/* Returns an array of pointers to trace objects of currently active operations
+   and fills in number of active operations. Returns NULL if there are no active
+   operations.
+   Caller owns the returned objects. */
+census_trace_obj** census_get_active_ops(int* num_active_ops);
 
 #ifdef __cplusplus
 }
diff --git a/src/core/support/env.h b/src/core/support/env.h
new file mode 100644
index 0000000000000000000000000000000000000000..81dda7d838aa74b1ee9f8790dadd53df65d22f2f
--- /dev/null
+++ b/src/core/support/env.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __GRPC_SUPPORT_ENV_H__
+#define __GRPC_SUPPORT_ENV_H__
+
+#include <stdio.h>
+
+#include <grpc/support/slice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Env utility functions */
+
+/* Gets the environment variable value with the specified name.
+   Returns a newly allocated string. It is the responsability of the caller to
+   gpr_free the return value if not NULL (which means that the environment
+   variable exists). */
+char *gpr_getenv(const char *name);
+
+/* Sets the the environment with the specified name to the specified value. */
+void gpr_setenv(const char *name, const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GRPC_SUPPORT_ENV_H__ */
diff --git a/src/core/support/env_linux.c b/src/core/support/env_linux.c
new file mode 100644
index 0000000000000000000000000000000000000000..28e3d1450f9d6a4eddfc482e7b2258f753c54eb9
--- /dev/null
+++ b/src/core/support/env_linux.c
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* for secure_getenv. */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX_ENV
+
+#include "src/core/support/env.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/support/string.h"
+
+char *gpr_getenv(const char *name) {
+  char *result = secure_getenv(name);
+  return result == NULL ? result : gpr_strdup(result);
+}
+
+void gpr_setenv(const char *name, const char *value) {
+  int res = setenv(name, value, 1);
+  GPR_ASSERT(res == 0);
+}
+
+#endif /* GPR_LINUX_ENV */
diff --git a/src/core/support/env_posix.c b/src/core/support/env_posix.c
new file mode 100644
index 0000000000000000000000000000000000000000..bcbff9a1770b1e73919ed311800c38be9bca7781
--- /dev/null
+++ b/src/core/support/env_posix.c
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_ENV
+
+#include "src/core/support/env.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/support/string.h"
+
+char *gpr_getenv(const char *name) {
+  char *result = getenv(name);
+  return result == NULL ? result : gpr_strdup(result);
+}
+
+void gpr_setenv(const char *name, const char *value) {
+  int res = setenv(name, value, 1);
+  GPR_ASSERT(res == 0);
+}
+
+#endif /* GPR_POSIX_ENV */
diff --git a/src/core/support/env_win32.c b/src/core/support/env_win32.c
new file mode 100644
index 0000000000000000000000000000000000000000..3159c20f7d7a164e504c3908db640b5a385647fc
--- /dev/null
+++ b/src/core/support/env_win32.c
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include "src/core/support/env.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+char *gpr_getenv(const char *name) {
+  size_t required_size;
+  char *result = NULL;
+
+  getenv_s(&required_size, NULL, 0, name);
+  if (required_size == 0) return NULL;
+  result = gpr_malloc(required_size);
+  getenv_s(&required_size, result, required_size, name);
+  return result;
+}
+
+void gpr_setenv(const char *name, const char *value) {
+  errno_t res = _putenv_s(name, value);
+  GPR_ASSERT(res == 0);
+}
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/support/file.c b/src/core/support/file.c
new file mode 100644
index 0000000000000000000000000000000000000000..c0bb1b66a066d705f5c2c2c5008cb6fbcfc5bd02
--- /dev/null
+++ b/src/core/support/file.c
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright 2014, 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 "src/core/support/file.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/support/string.h"
+
+gpr_slice gpr_load_file(const char *filename, int *success) {
+  unsigned char *contents = NULL;
+  size_t contents_size = 0;
+  unsigned char buf[4096];
+  char *error_msg = NULL;
+  gpr_slice result = gpr_empty_slice();
+  FILE *file = fopen(filename, "rb");
+
+  if (file == NULL) {
+    gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename,
+                 strerror(errno));
+    GPR_ASSERT(error_msg != NULL);
+    goto end;
+  }
+
+  while (1) {
+    size_t bytes_read = fread(buf, 1, sizeof(buf), file);
+    if (bytes_read > 0) {
+      contents = gpr_realloc(contents, contents_size + bytes_read);
+      memcpy(contents + contents_size, buf, bytes_read);
+      contents_size += bytes_read;
+    }
+    if (bytes_read < sizeof(buf)) {
+      if (ferror(file)) {
+        gpr_asprintf(&error_msg, "Error %s occured while reading file %s.",
+                     strerror(errno), filename);
+        GPR_ASSERT(error_msg != NULL);
+        goto end;
+      } else {
+        GPR_ASSERT(feof(file));
+        break;
+      }
+    }
+  }
+  if (success != NULL) *success = 1;
+  result = gpr_slice_new(contents, contents_size, gpr_free);
+
+end:
+  if (error_msg != NULL) {
+    gpr_log(GPR_ERROR, "%s", error_msg);
+    gpr_free(error_msg);
+    if (success != NULL) *success = 0;
+  }
+  if (file != NULL) fclose(file);
+  return result;
+}
diff --git a/src/core/support/file.h b/src/core/support/file.h
new file mode 100644
index 0000000000000000000000000000000000000000..92f420e7ceb3a64cb27a0d51ddc534cb9c0579c6
--- /dev/null
+++ b/src/core/support/file.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __GRPC_SUPPORT_FILE_H__
+#define __GRPC_SUPPORT_FILE_H__
+
+#include <stdio.h>
+
+#include <grpc/support/slice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* File utility functions */
+
+/* Loads the content of a file into a slice. The success parameter, if not NULL,
+   will be set to 1 in case of success and 0 in case of failure. */
+gpr_slice gpr_load_file(const char *filename, int *success);
+
+/* Creates a temporary file from a prefix.
+   If tmp_filename is not NULL, *tmp_filename is assigned the name of the
+   created file and it is the responsibility of the caller to gpr_free it
+   unless an error occurs in which case it will be set to NULL. */
+FILE *gpr_tmpfile(const char *prefix, char **tmp_filename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GRPC_SUPPORT_FILE_H__ */
diff --git a/src/core/support/file_posix.c b/src/core/support/file_posix.c
new file mode 100644
index 0000000000000000000000000000000000000000..cb48b3d52f4e7937876cc190b0c048adafb36025
--- /dev/null
+++ b/src/core/support/file_posix.c
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* Posix code for gpr fdopen and mkstemp support. */
+
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L
+#undef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+/* Don't know why I have to do this for mkstemp, looks like _POSIX_C_SOURCE
+   should be enough... */
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_FILE
+
+#include "src/core/support/file.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/support/string.h"
+
+FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) {
+  FILE *result = NULL;
+  char *template;
+  int fd;
+
+  if (tmp_filename != NULL) *tmp_filename = NULL;
+
+  gpr_asprintf(&template, "/tmp/%s_XXXXXX", prefix);
+  GPR_ASSERT(template != NULL);
+
+  fd = mkstemp(template);
+  if (fd == -1) {
+    gpr_log(GPR_ERROR, "mkstemp failed for template %s with error %s.",
+            template, strerror(errno));
+    goto end;
+  }
+  result = fdopen(fd, "w+");
+  if (result == NULL) {
+    gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).",
+            template, fd, strerror(errno));
+    unlink(template);
+    close(fd);
+    goto end;
+  }
+
+end:
+  if (result != NULL && tmp_filename != NULL) {
+    *tmp_filename = template;
+  } else {
+    gpr_free(template);
+  }
+  return result;
+}
+
+#endif /* GPR_POSIX_FILE */
diff --git a/src/core/support/file_win32.c b/src/core/support/file_win32.c
new file mode 100644
index 0000000000000000000000000000000000000000..af7eebe3de813588f5af654473741c7dc575ff56
--- /dev/null
+++ b/src/core/support/file_win32.c
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <io.h>
+#include <stdio.h>
+#include <string.h>
+#include <tchar.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/support/file.h"
+#include "src/core/support/string_win32.h"
+
+FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) {
+  FILE *result = NULL;
+  LPTSTR template_string = NULL;
+  TCHAR tmp_path[MAX_PATH];
+  TCHAR tmp_filename[MAX_PATH];
+  DWORD status;
+  UINT success;
+
+  if (tmp_filename_out != NULL) *tmp_filename_out = NULL;
+
+  /* Convert our prefix to TCHAR. */
+  template_string = gpr_char_to_tchar(prefix);
+  GPR_ASSERT(template_string);
+
+  /* Get the path to the best temporary folder available. */
+  status = GetTempPath(MAX_PATH, tmp_path);
+  if (status == 0 || status > MAX_PATH) goto end;
+
+  /* Generate a unique filename with our template + temporary path. */
+  success = GetTempFileName(tmp_path, template_string, 0, tmp_filename);
+  if (!success) goto end;
+
+  /* Open a file there. */
+  if (_tfopen_s(&result, tmp_filename, TEXT("wb+")) != 0) goto end;
+
+end:
+  if (result && tmp_filename) {
+    *tmp_filename_out = gpr_tchar_to_char(tmp_filename);
+  }
+
+  gpr_free(tmp_filename);
+  return result;
+}
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/support/string_win32.c b/src/core/support/string_win32.c
index 1c4c8547dc931b4bebf4d4650c9605b2cc23850b..02e1c74d9cefcd81919c44a7cf1c8e620cf6b06a 100644
--- a/src/core/support/string_win32.c
+++ b/src/core/support/string_win32.c
@@ -37,6 +37,7 @@
 
 #ifdef GPR_WIN32
 
+#include <windows.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
@@ -78,4 +79,32 @@ int gpr_asprintf(char **strp, const char *format, ...) {
   return -1;
 }
 
+#if defined UNICODE || defined _UNICODE
+LPTSTR gpr_char_to_tchar(LPCSTR input) {
+  LPTSTR ret;
+  int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);
+  if (needed == 0) return NULL;
+  ret = gpr_malloc(needed * sizeof(TCHAR));
+  MultiByteToWideChar(CP_UTF8, 0, input, -1, ret, needed);
+  return ret;
+}
+
+LPSTR gpr_tchar_to_char(LPCTSTR input) {
+  LPSTR ret;
+  int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);
+  if (needed == 0) return NULL;
+  ret = gpr_malloc(needed);
+  WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL);
+  return ret;
+}
+#else
+char *gpr_tchar_to_char(LPTSTR input) {
+  return gpr_strdup(input);
+}
+
+char *gpr_char_to_tchar(LPTSTR input) {
+  return gpr_strdup(input);
+}
+#endif
+
 #endif /* GPR_WIN32 */
diff --git a/src/core/support/string_win32.h b/src/core/support/string_win32.h
new file mode 100644
index 0000000000000000000000000000000000000000..9102d98256964dff3a5027510aec357015b4c88a
--- /dev/null
+++ b/src/core/support/string_win32.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __GRPC_SUPPORT_STRING_WIN32_H__
+#define __GRPC_SUPPORT_STRING_WIN32_H__
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <windows.h>
+
+/* These allocate new strings using gpr_malloc to convert from and to utf-8. */
+LPTSTR gpr_char_to_tchar(LPCSTR input);
+LPSTR gpr_tchar_to_char(LPCTSTR input);
+
+#endif  /* GPR_WIN32 */
+
+#endif /* __GRPC_SUPPORT_STRING_WIN32_H__ */
diff --git a/src/core/surface/byte_buffer_queue.c b/src/core/surface/byte_buffer_queue.c
index dc280a60c50e543ce764d5c10c83f1c3f1cc7ca5..9709a665bac428c29730383496ce391768508950 100644
--- a/src/core/surface/byte_buffer_queue.c
+++ b/src/core/surface/byte_buffer_queue.c
@@ -35,7 +35,13 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/useful.h>
 
-static void bba_destroy(grpc_bbq_array *array) { gpr_free(array->data); }
+static void bba_destroy(grpc_bbq_array *array, size_t start_pos) {
+  size_t i;
+  for (i = start_pos; i < array->count; i++) {
+    grpc_byte_buffer_destroy(array->data[i]);
+  }
+  gpr_free(array->data);
+}
 
 /* Append an operation to an array, expanding as needed */
 static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) {
@@ -47,8 +53,8 @@ static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) {
 }
 
 void grpc_bbq_destroy(grpc_byte_buffer_queue *q) {
-  bba_destroy(&q->filling);
-  bba_destroy(&q->draining);
+  bba_destroy(&q->filling, 0);
+  bba_destroy(&q->draining, q->drain_pos);
 }
 
 int grpc_bbq_empty(grpc_byte_buffer_queue *q) {
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index feb39262818fad0ad0aeff9e52050ddf61eb1ccb..0af524cead7b044f2124b89ddb4ddc4b1f36ca03 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -275,6 +275,7 @@ static void destroy_call(void *call, int ignored_success) {
   if (c->legacy_state) {
     destroy_legacy_state(c->legacy_state);
   }
+  grpc_bbq_destroy(&c->incoming_queue);
   gpr_free(c);
 }
 
@@ -335,8 +336,10 @@ static void unlock(grpc_call *call) {
   send_action sa = SEND_NOTHING;
   completed_request completed_requests[GRPC_IOREQ_OP_COUNT];
   int num_completed_requests = call->num_completed_requests;
-  int need_more_data = call->need_more_data &&
-                       !is_op_live(call, GRPC_IOREQ_SEND_INITIAL_METADATA);
+  int need_more_data =
+      call->need_more_data &&
+      !call->sending &&
+      call->write_state >= WRITE_STATE_STARTED;
   int i;
 
   if (need_more_data) {
@@ -960,6 +963,8 @@ struct legacy_state {
   char *details;
   grpc_status_code status;
 
+  char *send_details;
+
   size_t msg_in_read_idx;
   grpc_byte_buffer *msg_in;
 
@@ -985,6 +990,8 @@ static void destroy_legacy_state(legacy_state *ls) {
   }
   gpr_free(ls->initial_md_in.metadata);
   gpr_free(ls->trailing_md_in.metadata);
+  gpr_free(ls->details);
+  gpr_free(ls->send_details);
   gpr_free(ls);
 }
 
@@ -1233,8 +1240,7 @@ grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
   reqs[0].data.send_metadata.metadata = ls->md_out[ls->md_out_buffer];
   reqs[1].op = GRPC_IOREQ_SEND_STATUS;
   reqs[1].data.send_status.code = status;
-  /* MEMLEAK */
-  reqs[1].data.send_status.details = gpr_strdup(details);
+  reqs[1].data.send_status.details = ls->send_details = gpr_strdup(details);
   reqs[2].op = GRPC_IOREQ_SEND_CLOSE;
   err = start_ioreq(call, reqs, 3, finish_finish, tag);
   unlock(call);
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index c33ea923e82804fe26be81eb05023df9989cd3d8..b33bd7b3575a0adcc1bc7097c7e2be83ee669df1 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -52,6 +52,9 @@ struct grpc_channel {
 };
 
 #define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c)+1))
+#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) (((grpc_channel *)(channel_stack)) - 1)
+#define CHANNEL_FROM_TOP_ELEM(top_elem) \
+  CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem))
 
 grpc_channel *grpc_channel_create_from_filters(
     const grpc_channel_filter **filters, size_t num_filters,
@@ -60,8 +63,8 @@ grpc_channel *grpc_channel_create_from_filters(
       sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
   grpc_channel *channel = gpr_malloc(size);
   channel->is_client = is_client;
-  /* decremented by grpc_channel_destroy */
-  gpr_ref_init(&channel->refs, 1);
+  /* decremented by grpc_channel_destroy, and grpc_client_channel_closed if is_client */
+  gpr_ref_init(&channel->refs, 1 + is_client);
   channel->metadata_context = mdctx;
   channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status");
   channel->grpc_message_string = grpc_mdstr_from_string(mdctx, "grpc-message");
@@ -158,6 +161,10 @@ void grpc_channel_destroy(grpc_channel *channel) {
   grpc_channel_internal_unref(channel);
 }
 
+void grpc_client_channel_closed(grpc_channel_element *elem) {
+  grpc_channel_internal_unref(CHANNEL_FROM_TOP_ELEM(elem));
+}
+
 grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) {
   return CHANNEL_STACK_FROM_CHANNEL(channel);
 }
diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h
index b3ea2ede4038d669a9abd8c28fd834c54525a992..ff9bbc237ef5f57ab0a1da51614249d278bbbb2e 100644
--- a/src/core/surface/channel.h
+++ b/src/core/surface/channel.h
@@ -45,6 +45,8 @@ grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel);
 grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel);
 grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel);
 
+void grpc_client_channel_closed(grpc_channel_element *elem);
+
 void grpc_channel_internal_ref(grpc_channel *channel);
 void grpc_channel_internal_unref(grpc_channel *channel);
 
diff --git a/src/core/surface/client.c b/src/core/surface/client.c
index fa63e855cc864bc7759a869c4e7da1fbe0b947bb..64ee9d51e807b6a6b416937c208322365d30472b 100644
--- a/src/core/surface/client.c
+++ b/src/core/surface/client.c
@@ -34,6 +34,7 @@
 #include "src/core/surface/client.h"
 
 #include "src/core/surface/call.h"
+#include "src/core/surface/channel.h"
 #include "src/core/support/string.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
@@ -87,7 +88,7 @@ static void channel_op(grpc_channel_element *elem,
       gpr_log(GPR_ERROR, "Client cannot accept new calls");
       break;
     case GRPC_TRANSPORT_CLOSED:
-      gpr_log(GPR_ERROR, "Transport closed");
+      grpc_client_channel_closed(elem);
       break;
     case GRPC_TRANSPORT_GOAWAY:
       gpr_slice_unref(op->data.goaway.message);
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index 2f5eff55844a7e6d0e3e06b3682053401254a355..411dbabfd3261e1b994fae72b8fe09535dc1e300 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -76,6 +76,9 @@ static void channel_op(grpc_channel_element *elem,
     case GRPC_CHANNEL_GOAWAY:
       gpr_slice_unref(op->data.goaway.message);
       break;
+    case GRPC_CHANNEL_DISCONNECT:
+      grpc_client_channel_closed(elem);
+      break;
     default:
       break;
   }
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index a057694f13a07a8a531b025acca183942cf3605f..455bd4337f129628818165291a82e8dff13989e2 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -258,7 +258,6 @@ static void stream_closed(grpc_call_element *elem) {
   gpr_mu_lock(&chand->server->mu);
   switch (calld->state) {
     case ACTIVATED:
-      grpc_call_stream_closed(elem);
       break;
     case PENDING:
       call_list_remove(chand->server, calld, PENDING_START);
@@ -271,6 +270,7 @@ static void stream_closed(grpc_call_element *elem) {
       break;
   }
   gpr_mu_unlock(&chand->server->mu);
+  grpc_call_stream_closed(elem);
 }
 
 static void read_closed(grpc_call_element *elem) {
diff --git a/src/csharp/GrpcCore/Call.cs b/src/csharp/GrpcCore/Call.cs
index bf257e5d5981a1ccfc2775256c6c33e9d0501f6f..d3847a800917d6fae1ceca33338cb77b9e37eaa0 100644
--- a/src/csharp/GrpcCore/Call.cs
+++ b/src/csharp/GrpcCore/Call.cs
@@ -8,10 +8,8 @@ namespace Google.GRPC.Core
         readonly string methodName;
         readonly Func<TRequest, byte[]> requestSerializer;
         readonly Func<byte[], TResponse> responseDeserializer;
-        readonly TimeSpan timeout;
         readonly Channel channel;
 
-        // TODO: channel param should be removed in the future.
         public Call(string methodName, 
                     Func<TRequest, byte[]> requestSerializer,
                     Func<byte[], TResponse> responseDeserializer,
@@ -20,24 +18,22 @@ namespace Google.GRPC.Core
             this.methodName = methodName;
             this.requestSerializer = requestSerializer;
             this.responseDeserializer = responseDeserializer;
-            this.timeout = timeout;
             this.channel = channel;
         }
 
-
-        public Channel Channel
+        public Call(Method<TRequest, TResponse> method, Channel channel)
         {
-            get
-            {
-                return this.channel;
-            }
+            this.methodName = method.Name;
+            this.requestSerializer = method.RequestMarshaller.Serialize;
+            this.responseDeserializer = method.ResponseMarshaller.Deserialize;
+            this.channel = channel;
         }
 
-        public TimeSpan Timeout
+        public Channel Channel
         {
             get
             {
-                return this.timeout;
+                return this.channel;
             }
         }
 
diff --git a/src/csharp/GrpcCore/GrpcCore.csproj b/src/csharp/GrpcCore/GrpcCore.csproj
index f0c84e78ea6c954251e7135fa260964e1eb569a4..2ad0f9154cc00e98563b06c6c7e25ec865f5d39d 100644
--- a/src/csharp/GrpcCore/GrpcCore.csproj
+++ b/src/csharp/GrpcCore/GrpcCore.csproj
@@ -54,6 +54,11 @@
     <Compile Include="Internal\AsyncCall.cs" />
     <Compile Include="Internal\ServerSafeHandle.cs" />
     <Compile Include="Internal\StreamingInputObserver.cs" />
+    <Compile Include="Method.cs" />
+    <Compile Include="IMarshaller.cs" />
+    <Compile Include="ServerCalls.cs" />
+    <Compile Include="ServerCallHandler.cs" />
+    <Compile Include="Internal\ServerWritingObserver.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
diff --git a/src/csharp/GrpcCore/IMarshaller.cs b/src/csharp/GrpcCore/IMarshaller.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eb08d8d3860dfb95238ca2f68075524cf23574f1
--- /dev/null
+++ b/src/csharp/GrpcCore/IMarshaller.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Google.GRPC.Core
+{
+    /// <summary>
+    /// For serializing and deserializing messages.
+    /// </summary>
+    public interface IMarshaller<T>
+    {
+        byte[] Serialize(T value);
+
+        T Deserialize(byte[] payload);
+    }
+
+    /// <summary>
+    /// UTF-8 Marshalling for string. Useful for testing.
+    /// </summary>
+    internal class StringMarshaller : IMarshaller<string> {
+
+        public byte[] Serialize(string value)
+        {
+            return System.Text.Encoding.UTF8.GetBytes(value);
+        }
+
+        public string Deserialize(byte[] payload)
+        {
+            return System.Text.Encoding.UTF8.GetString(payload);
+        }
+    }
+}
+
diff --git a/src/csharp/GrpcCore/Internal/AsyncCall.cs b/src/csharp/GrpcCore/Internal/AsyncCall.cs
index e83ca0eaa9619bea73c4d0c7a011c376e3ff349e..c38363bb2bcce89cbbfe302527dc19d22f98069d 100644
--- a/src/csharp/GrpcCore/Internal/AsyncCall.cs
+++ b/src/csharp/GrpcCore/Internal/AsyncCall.cs
@@ -86,6 +86,14 @@ namespace Google.GRPC.Core.Internal
             return StartRead().Task;
         }
 
+        public Task Halfclosed
+        {
+            get
+            {
+                return halfcloseTcs.Task;
+            }
+        }
+
         public Task<Status> Finished
         {
             get
diff --git a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs
index 0d38bce63e3a5c35db82c384a4884cc91f7be3eb..08d4cf01927c85920bcea6bb640b950b77c0fe51 100644
--- a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs
+++ b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs
@@ -30,8 +30,8 @@ namespace Google.GRPC.Core.Internal
         [DllImport("libgrpc.so")]
         static extern void grpc_server_shutdown(ServerSafeHandle server);
 
-        [DllImport("libgrpc.so")]
-        static extern void grpc_server_shutdown_and_notify(ServerSafeHandle server, IntPtr tag);
+        [DllImport("libgrpc.so", EntryPoint = "grpc_server_shutdown_and_notify")]
+        static extern void grpc_server_shutdown_and_notify_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
 
         [DllImport("libgrpc.so")]
         static extern void grpc_server_destroy(IntPtr server);
@@ -62,6 +62,11 @@ namespace Google.GRPC.Core.Internal
             grpc_server_shutdown(this);
         }
 
+        public void ShutdownAndNotify(EventCallbackDelegate callback)
+        {
+            grpc_server_shutdown_and_notify_CALLBACK(this, callback);
+        }
+
         public GRPCCallError RequestCall(EventCallbackDelegate callback)
         {
             return grpc_server_request_call_old_CALLBACK(this, callback);
diff --git a/src/csharp/GrpcCore/Internal/ServerWritingObserver.cs b/src/csharp/GrpcCore/Internal/ServerWritingObserver.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2b46e9c53d6c9074c02be99e9c88280cacc93a58
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/ServerWritingObserver.cs
@@ -0,0 +1,38 @@
+using System;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core.Internal
+{
+    /// <summary>
+    /// Observer that writes all arriving messages to a call abstraction (in blocking fashion)
+    /// and then halfcloses the call. Used for server-side call handling.
+    /// </summary>
+    internal class ServerWritingObserver<TWrite, TRead> : IObserver<TWrite>
+	{
+        readonly AsyncCall<TWrite, TRead> call;
+
+        public ServerWritingObserver(AsyncCall<TWrite, TRead> call)
+		{
+            this.call = call;
+		}
+
+		public void OnCompleted()
+		{
+            // TODO: how bad is the Wait here?
+            call.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait();
+		}
+
+		public void OnError(Exception error)
+		{
+            // TODO: handle this...
+			throw new InvalidOperationException("This should never be called.");
+		}
+
+		public void OnNext(TWrite value)
+		{
+            // TODO: how bad is the Wait here?
+            call.WriteAsync(value).Wait();
+		}
+	}
+}
+
diff --git a/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs
index d483e53a2db1e43440903cfa10e2366d10896ad7..c5de979351a14058405d685914a81d41d2a733d5 100644
--- a/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs
+++ b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs
@@ -1,7 +1,7 @@
 using System;
 using Google.GRPC.Core.Internal;
 
-namespace Google.GRPC.Core
+namespace Google.GRPC.Core.Internal
 {
     internal class StreamingInputObserver<TWrite, TRead> : IObserver<TWrite>
 	{
diff --git a/src/csharp/GrpcCore/Method.cs b/src/csharp/GrpcCore/Method.cs
new file mode 100644
index 0000000000000000000000000000000000000000..27901156950edc954016178627f0a545132e37e1
--- /dev/null
+++ b/src/csharp/GrpcCore/Method.cs
@@ -0,0 +1,64 @@
+using System;
+
+namespace Google.GRPC.Core
+{
+    public enum MethodType
+    {
+        Unary,
+        ClientStreaming,
+        ServerStreaming,
+        DuplexStreaming
+    }
+
+    /// <summary>
+    /// A description of a service method.
+    /// </summary>
+    public class Method<TRequest, TResponse>
+    {
+        readonly MethodType type;
+        readonly string name;
+        readonly IMarshaller<TRequest> requestMarshaller;
+        readonly IMarshaller<TResponse> responseMarshaller;
+
+        public Method(MethodType type, string name, IMarshaller<TRequest> requestMarshaller, IMarshaller<TResponse> responseMarshaller)
+        {
+            this.type = type;
+            this.name = name;
+            this.requestMarshaller = requestMarshaller;
+            this.responseMarshaller = responseMarshaller;
+        }
+
+        public MethodType Type
+        {
+            get
+            {
+                return this.type;
+            }
+        }
+
+        public string Name
+        {
+            get
+            {
+                return this.name;
+            }
+        }
+
+        public IMarshaller<TRequest> RequestMarshaller
+        {
+            get
+            {
+                return this.requestMarshaller;
+            }
+        }
+
+        public IMarshaller<TResponse> ResponseMarshaller
+        {
+            get
+            {
+                return this.responseMarshaller;
+            }
+        }
+    }
+}
+
diff --git a/src/csharp/GrpcCore/Server.cs b/src/csharp/GrpcCore/Server.cs
index 68da1a8300813a98ad76aef285e60850295f3080..4e9d114f850af8ab1acc35608a9cd79aa6d82d19 100644
--- a/src/csharp/GrpcCore/Server.cs
+++ b/src/csharp/GrpcCore/Server.cs
@@ -1,7 +1,9 @@
 using System;
 using System.Runtime.InteropServices;
 using System.Diagnostics;
+using System.Threading.Tasks;
 using System.Collections.Concurrent;
+using System.Collections.Generic;
 using Google.GRPC.Core.Internal;
 
 namespace Google.GRPC.Core
@@ -15,10 +17,15 @@ namespace Google.GRPC.Core
         // TODO: make sure the delegate doesn't get garbage collected while 
         // native callbacks are in the completion queue.
         readonly EventCallbackDelegate newRpcHandler;
+        readonly EventCallbackDelegate serverShutdownHandler;
 
         readonly BlockingCollection<NewRpcInfo> newRpcQueue = new BlockingCollection<NewRpcInfo>();
         readonly ServerSafeHandle handle;
 
+        readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>();
+
+        readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>();
+
         static Server() {
             GrpcEnvironment.EnsureInitialized();
         }
@@ -28,8 +35,14 @@ namespace Google.GRPC.Core
             // TODO: what is the tag for server shutdown?
             this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero);
             this.newRpcHandler = HandleNewRpc;
+            this.serverShutdownHandler = HandleServerShutdown;
         }
 
+        // only call before Start(), this will be in server builder in the future.
+        internal void AddCallHandler(string methodName, IServerCallHandler handler) {
+            callHandlers.Add(methodName, handler);
+        }
+        // only call before Start()
         public int AddPort(string addr) {
             return handle.AddPort(addr);
         }
@@ -37,49 +50,57 @@ namespace Google.GRPC.Core
         public void Start()
         {
             handle.Start();
+
+            // TODO: this basically means the server is single threaded....
+            StartHandlingRpcs();
         }
 
-        public void RunRpc()
+        /// <summary>
+        /// Requests and handles single RPC call.
+        /// </summary>
+        internal void RunRpc()
         {
             AllowOneRpc();
          
-            try {
-            var rpcInfo = newRpcQueue.Take();
-
-            Console.WriteLine("Server received RPC " + rpcInfo.Method);
-
-            AsyncCall<byte[], byte[]> asyncCall = new AsyncCall<byte[], byte[]>(
-                (payload) => payload, (payload) => payload);
-
-            asyncCall.InitializeServer(rpcInfo.Call);
+            try
+            {
+                var rpcInfo = newRpcQueue.Take();
 
-            asyncCall.Accept(GetCompletionQueue());
+                Console.WriteLine("Server received RPC " + rpcInfo.Method);
 
-            while(true) {
-                byte[] payload = asyncCall.ReadAsync().Result;
-                if (payload == null)
+                IServerCallHandler callHandler;
+                if (!callHandlers.TryGetValue(rpcInfo.Method, out callHandler))
                 {
-                    break;
-                }
+                    callHandler = new NoSuchMethodCallHandler();
+                } 
+                callHandler.StartCall(rpcInfo.Method, rpcInfo.Call, GetCompletionQueue());
             }
-
-            asyncCall.WriteAsync(new byte[] { }).Wait();
-
-            // TODO: what should be the details?
-            asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait();
-
-            asyncCall.Finished.Wait();
-            } catch(Exception e) {
+            catch(Exception e)
+            {
                 Console.WriteLine("Exception while handling RPC: " + e);
             }
         }
 
-        // TODO: implement disposal properly...
-        public void Shutdown() {
-            handle.Shutdown();
+        /// <summary>
+        /// Requests server shutdown and when there are no more calls being serviced,
+        /// cleans up used resources.
+        /// </summary>
+        /// <returns>The async.</returns>
+        public async Task ShutdownAsync() {
+            handle.ShutdownAndNotify(serverShutdownHandler);
+            await shutdownTcs.Task;
+            handle.Dispose();
+        }
 
+        public void Kill() {
+            handle.Dispose();
+        }
 
-            //handle.Dispose();
+        private async Task StartHandlingRpcs() {
+            while (true)
+            {
+                await Task.Factory.StartNew(RunRpc);
+            }
         }
 
         private void AllowOneRpc()
@@ -100,6 +121,18 @@ namespace Google.GRPC.Core
             }
         }
 
+        private void HandleServerShutdown(IntPtr eventPtr)
+        {
+            try
+            {
+                shutdownTcs.SetResult(null);
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine("Caught exception in a native handler: " + e);
+            }
+        }
+
         private static void AssertCallOk(GRPCCallError callError)
         {
             Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
diff --git a/src/csharp/GrpcCore/ServerCallHandler.cs b/src/csharp/GrpcCore/ServerCallHandler.cs
new file mode 100644
index 0000000000000000000000000000000000000000..08d527a019f991842d201d1d13981f19f95404be
--- /dev/null
+++ b/src/csharp/GrpcCore/ServerCallHandler.cs
@@ -0,0 +1,93 @@
+using System;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core
+{
+    internal interface IServerCallHandler
+    {
+        void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq);
+    }
+
+    internal class UnaryRequestServerCallHandler<TRequest, TResponse> : IServerCallHandler
+    {
+        readonly Method<TRequest, TResponse> method;
+        readonly UnaryRequestServerMethod<TRequest, TResponse> handler;
+
+        public UnaryRequestServerCallHandler(Method<TRequest, TResponse> method, UnaryRequestServerMethod<TRequest, TResponse> handler)
+        {
+            this.method = method;
+            this.handler = handler;
+        }
+
+        public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+        {
+            var asyncCall = new AsyncCall<TResponse, TRequest>(
+                (msg) => method.ResponseMarshaller.Serialize(msg),
+                (payload) => method.RequestMarshaller.Deserialize(payload));
+
+            asyncCall.InitializeServer(call);
+            asyncCall.Accept(cq);
+           
+            var request = asyncCall.ReadAsync().Result;
+
+            var responseObserver = new ServerWritingObserver<TResponse, TRequest>(asyncCall);
+            handler(request, responseObserver);
+
+            asyncCall.Halfclosed.Wait();
+            // TODO: wait until writing is finished
+
+            asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait();
+            asyncCall.Finished.Wait();
+        }
+    }
+
+    internal class StreamingRequestServerCallHandler<TRequest, TResponse> : IServerCallHandler
+    {
+        readonly Method<TRequest, TResponse> method;
+        readonly StreamingRequestServerMethod<TRequest, TResponse> handler;
+
+        public StreamingRequestServerCallHandler(Method<TRequest, TResponse> method, StreamingRequestServerMethod<TRequest, TResponse> handler)
+        {
+            this.method = method;
+            this.handler = handler;
+        }
+
+        public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+        {
+            var asyncCall = new AsyncCall<TResponse, TRequest>(
+                (msg) => method.ResponseMarshaller.Serialize(msg),
+                (payload) => method.RequestMarshaller.Deserialize(payload));
+
+            asyncCall.InitializeServer(call);
+            asyncCall.Accept(cq);
+
+            var responseObserver = new ServerWritingObserver<TResponse, TRequest>(asyncCall);
+            var requestObserver = handler(responseObserver);
+
+            // feed the requests
+            asyncCall.StartReadingToStream(requestObserver);
+
+            asyncCall.Halfclosed.Wait();
+
+            asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait();
+            asyncCall.Finished.Wait();
+        }
+    }
+
+    internal class NoSuchMethodCallHandler : IServerCallHandler
+    {
+        public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq)
+        {
+            // We don't care about the payload type here.
+            AsyncCall<byte[], byte[]> asyncCall = new AsyncCall<byte[], byte[]>(
+                (payload) => payload, (payload) => payload);
+
+            asyncCall.InitializeServer(call);
+            asyncCall.Accept(cq);
+            asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_UNIMPLEMENTED, "No such method.")).Wait();
+
+            asyncCall.Finished.Wait();
+        }
+    }
+}
+
diff --git a/src/csharp/GrpcCore/ServerCalls.cs b/src/csharp/GrpcCore/ServerCalls.cs
new file mode 100644
index 0000000000000000000000000000000000000000..86c47189320259e9dd07c16a8cf2a5e139227931
--- /dev/null
+++ b/src/csharp/GrpcCore/ServerCalls.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Google.GRPC.Core
+{
+    // TODO: perhaps add also serverSideStreaming and clientSideStreaming
+
+    public delegate void UnaryRequestServerMethod<TRequest, TResponse> (TRequest request, IObserver<TResponse> responseObserver);
+
+    public delegate IObserver<TRequest> StreamingRequestServerMethod<TRequest, TResponse> (IObserver<TResponse> responseObserver);
+
+    internal static class ServerCalls {
+
+        public static IServerCallHandler UnaryRequestCall<TRequest, TResponse>(Method<TRequest, TResponse> method, UnaryRequestServerMethod<TRequest, TResponse> handler)
+        {
+            return new UnaryRequestServerCallHandler<TRequest, TResponse>(method, handler);
+        }
+
+        public static IServerCallHandler StreamingRequestCall<TRequest, TResponse>(Method<TRequest, TResponse> method, StreamingRequestServerMethod<TRequest, TResponse> handler)
+        {
+            return new StreamingRequestServerCallHandler<TRequest, TResponse>(method, handler);
+        }
+
+    }
+}
+
diff --git a/src/csharp/GrpcCoreTests/ClientServerTest.cs b/src/csharp/GrpcCoreTests/ClientServerTest.cs
index 823ee942882993a521df18f95608bf48f2bc2fd5..511683b00386b88ee610fa70cba244aa4c663027 100644
--- a/src/csharp/GrpcCoreTests/ClientServerTest.cs
+++ b/src/csharp/GrpcCoreTests/ClientServerTest.cs
@@ -8,41 +8,48 @@ namespace Google.GRPC.Core.Tests
 {
     public class ClientServerTest
     {
-        string request = "REQUEST";
         string serverAddr = "localhost:" + Utils.PickUnusedPort();
 
+        private Method<string, string> unaryEchoStringMethod = new Method<string, string>(
+            MethodType.Unary,
+            "/tests.Test/UnaryEchoString",
+            new StringMarshaller(),
+            new StringMarshaller());
+
         [Test]
         public void EmptyCall()
         {
             Server server = new Server();
+
+            server.AddCallHandler(unaryEchoStringMethod.Name, 
+                                  ServerCalls.UnaryRequestCall(unaryEchoStringMethod, HandleUnaryEchoString));
+
             server.AddPort(serverAddr);
             server.Start();
 
-            Task.Factory.StartNew(
-                () => {
-                    server.RunRpc();
-                }
-            );
-
             using (Channel channel = new Channel(serverAddr))
             {
-                CreateCall(channel);
-                string response = Calls.BlockingUnaryCall(CreateCall(channel), request, default(CancellationToken));
-                Console.WriteLine("Received response: " + response);
+                var call = CreateUnaryEchoStringCall(channel);
+
+                Assert.AreEqual("ABC", Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken)));
+                Assert.AreEqual("abcdef", Calls.BlockingUnaryCall(call, "abcdef", default(CancellationToken)));
             }
          
-            server.Shutdown();
+            server.ShutdownAsync().Wait();
 
             GrpcEnvironment.Shutdown();
         }
 
-        private Call<string, string> CreateCall(Channel channel)
+        private Call<string, string> CreateUnaryEchoStringCall(Channel channel)
         {
-            return new Call<string, string>("/tests.Test/EmptyCall",
-                                        (s) => System.Text.Encoding.ASCII.GetBytes(s), 
-                                        (b) => System.Text.Encoding.ASCII.GetString(b),
-                                        Timeout.InfiniteTimeSpan, channel);
+            return new Call<string, string>(unaryEchoStringMethod, channel);
+        }
+
+        private void HandleUnaryEchoString(string request, IObserver<string> responseObserver) {
+            responseObserver.OnNext(request);
+            responseObserver.OnCompleted();
         }
+
     }
 }
 
diff --git a/src/csharp/GrpcCoreTests/ServerTest.cs b/src/csharp/GrpcCoreTests/ServerTest.cs
index b34101bbf597e7baa50b1bf94bc5cfac8a5dbb04..e6de95c3363292aada89742d61293d27f0f79f93 100644
--- a/src/csharp/GrpcCoreTests/ServerTest.cs
+++ b/src/csharp/GrpcCoreTests/ServerTest.cs
@@ -12,7 +12,7 @@ namespace Google.GRPC.Core.Tests
             Server server = new Server();
             server.AddPort("localhost:" + Utils.PickUnusedPort());
             server.Start();
-            server.Shutdown();
+            server.ShutdownAsync().Wait();
 
             GrpcEnvironment.Shutdown();
         }
diff --git a/src/csharp/README.md b/src/csharp/README.md
index 75bfb26252caa6e3072e8bf62024ff0c9f78817b..c2f988cc5e3919b85e3c3eb2b7fdade2c189132a 100755
--- a/src/csharp/README.md
+++ b/src/csharp/README.md
@@ -14,6 +14,34 @@ EXPERIMENTAL ONLY
 - It is very possible that some parts of the code will be heavily refactored or
   completely rewritten.
 
+
+INSTALLATION AND USAGE
+----------------------
+
+- Compile and install the gRPC C Core library
+```
+make shared_c
+sudo make install
+```
+
+- Prerequisites for development: Mono framework, MonoDevelop (IDE)
+```
+sudo apt-get install mono-devel
+sudo apt-get install monodevelop monodevelop-nunit
+sudo apt-get install nunit nunit-console
+```
+
+- Use MonoDevelop to open the solution Grpc.sln (you can also run unit tests
+  from there).
+
+- After building the solution with MonoDevelop, you can use
+  nunit-console to run the unit tests (currently only running one by
+  one will make them pass.
+
+```
+nunit-console GrpcCoreTests.dll
+```
+
 CONTENTS
 --------
 
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
index a70819e47ea630eda7568d40db0120d53345f850..149ac8c07bbb0e5205bc764845801e401f1774ef 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
@@ -39,6 +39,9 @@
 #include "src/core/channel/channel_args.h"
 #include "src/core/security/credentials.h"
 #include "src/core/security/security_context.h"
+#include "src/core/support/env.h"
+#include "src/core/support/file.h"
+#include "src/core/support/string.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
@@ -99,7 +102,7 @@ void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) {
 static void chttp2_init_client_simple_ssl_secure_fullstack(
     grpc_end2end_test_fixture *f, grpc_channel_args *client_args) {
   grpc_credentials *ssl_creds =
-      grpc_ssl_credentials_create(test_root_cert, NULL);
+      grpc_ssl_credentials_create(NULL, NULL);
   grpc_arg ssl_name_override = {GRPC_ARG_STRING,
                                 GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,
                                 {"foo.test.google.com"}};
@@ -129,8 +132,20 @@ static grpc_end2end_test_config configs[] = {
 
 int main(int argc, char **argv) {
   size_t i;
+  FILE *roots_file;
+  size_t roots_size = strlen(test_root_cert);
+  char *roots_filename;
+
   grpc_test_init(argc, argv);
 
+  /* Set the SSL roots env var. */
+  roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename);
+  GPR_ASSERT(roots_filename != NULL);
+  GPR_ASSERT(roots_file != NULL);
+  GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
+  fclose(roots_file);
+  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
+
   grpc_init();
 
   for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
@@ -139,5 +154,9 @@ int main(int argc, char **argv) {
 
   grpc_shutdown();
 
+  /* Cleanup. */
+  remove(roots_filename);
+  gpr_free(roots_filename);
+
   return 0;
 }
diff --git a/test/core/statistics/trace_test.c b/test/core/statistics/trace_test.c
index 6eafcf145686a9bd1936d55db4338054f4c808d1..97e1463ae134edbc7a2c8bdb5f64af6564a1e404 100644
--- a/test/core/statistics/trace_test.c
+++ b/test/core/statistics/trace_test.c
@@ -32,10 +32,12 @@
  */
 
 #include <string.h>
+#include <stdio.h>
 
 #include "src/core/statistics/census_interface.h"
 #include "src/core/statistics/census_tracing.h"
 #include "src/core/statistics/census_tracing.h"
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
 #include <grpc/support/sync.h>
@@ -172,6 +174,74 @@ static void test_trace_print(void) {
   census_tracing_shutdown();
 }
 
+/* Returns 1 if two ids are equal, otherwise returns 0. */
+static int ids_equal(census_op_id id1, census_op_id id2) {
+  return (id1.upper == id2.upper) && (id1.lower == id2.lower);
+}
+
+static void test_get_active_ops(void) {
+  census_op_id id_1, id_2, id_3;
+  census_trace_obj** active_ops;
+  const char* annotation_txt[] = {"annotation 1", "a2"};
+  int i = 0;
+  int n = 0;
+
+  gpr_log(GPR_INFO, "test_get_active_ops");
+  census_tracing_init();
+  /* No active ops before calling start_op(). */
+  active_ops = census_get_active_ops(&n);
+  GPR_ASSERT(active_ops == NULL);
+  GPR_ASSERT(n == 0);
+
+  /* Starts one op */
+  id_1 = census_tracing_start_op();
+  census_add_method_tag(id_1, "foo_1");
+  active_ops = census_get_active_ops(&n);
+  GPR_ASSERT(active_ops != NULL);
+  GPR_ASSERT(n == 1);
+  GPR_ASSERT(ids_equal(active_ops[0]->id, id_1));
+  census_trace_obj_destroy(active_ops[0]);
+  gpr_free(active_ops);
+  active_ops = NULL;
+
+  /* Start the second and the third ops */
+  id_2 = census_tracing_start_op();
+  census_add_method_tag(id_2, "foo_2");
+  id_3 = census_tracing_start_op();
+  census_add_method_tag(id_3, "foo_3");
+
+  active_ops = census_get_active_ops(&n);
+  GPR_ASSERT(n == 3);
+  for (i = 0; i < 3; i++) {
+    census_trace_obj_destroy(active_ops[i]);
+  }
+  gpr_free(active_ops);
+  active_ops = NULL;
+
+  /* End the second op  and add annotations to the third ops*/
+  census_tracing_end_op(id_2);
+  census_tracing_print(id_3, annotation_txt[0]);
+  census_tracing_print(id_3, annotation_txt[1]);
+
+  active_ops = census_get_active_ops(&n);
+  GPR_ASSERT(active_ops != NULL);
+  GPR_ASSERT(n == 2);
+  for (i = 0; i < 2; i++) {
+    census_trace_obj_destroy(active_ops[i]);
+  }
+  gpr_free(active_ops);
+  active_ops = NULL;
+
+  /* End all ops. */
+  census_tracing_end_op(id_1);
+  census_tracing_end_op(id_3);
+  active_ops = census_get_active_ops(&n);
+  GPR_ASSERT(active_ops == NULL);
+  GPR_ASSERT(n == 0);
+
+  census_tracing_shutdown();
+}
+
 int main(int argc, char** argv) {
   grpc_test_init(argc, argv);
   test_init_shutdown();
@@ -180,5 +250,6 @@ int main(int argc, char** argv) {
   test_concurrency();
   test_add_method_tag_to_unknown_op_id();
   test_trace_print();
+  test_get_active_ops();
   return 0;
 }
diff --git a/test/core/support/env_test.c b/test/core/support/env_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..36d7adf80b592deba83ddd673610d1354434b7b0
--- /dev/null
+++ b/test/core/support/env_test.c
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2014, 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 <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/support/env.h"
+#include "src/core/support/string.h"
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST_NAME() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void test_setenv_getenv(void) {
+  const char *name = "FOO";
+  const char *value = "BAR";
+  char *retrieved_value;
+
+  LOG_TEST_NAME();
+
+  gpr_setenv(name, value);
+  retrieved_value = gpr_getenv(name);
+  GPR_ASSERT(retrieved_value != NULL);
+  GPR_ASSERT(!strcmp(value, retrieved_value));
+  gpr_free(retrieved_value);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_setenv_getenv();
+  return 0;
+}
diff --git a/test/core/support/file_test.c b/test/core/support/file_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..b089954186c17829b1613354c38b01ea1047d4b9
--- /dev/null
+++ b/test/core/support/file_test.c
@@ -0,0 +1,159 @@
+/*
+ *
+ * Copyright 2014, 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 <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+
+#include "src/core/support/file.h"
+#include "src/core/support/string.h"
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST_NAME() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static const char prefix[] = "file_test";
+
+static void test_load_empty_file(void) {
+  FILE *tmp = NULL;
+  gpr_slice slice;
+  int success;
+  char *tmp_name;
+
+  LOG_TEST_NAME();
+
+  tmp = gpr_tmpfile(prefix, &tmp_name);
+  GPR_ASSERT(tmp_name != NULL);
+  GPR_ASSERT(tmp != NULL);
+  fclose(tmp);
+
+  slice = gpr_load_file(tmp_name, &success);
+  GPR_ASSERT(success == 1);
+  GPR_ASSERT(GPR_SLICE_LENGTH(slice) == 0);
+
+  remove(tmp_name);
+  gpr_free(tmp_name);
+  gpr_slice_unref(slice);
+}
+
+static void test_load_failure(void) {
+  FILE *tmp = NULL;
+  gpr_slice slice;
+  int success;
+  char *tmp_name;
+
+  LOG_TEST_NAME();
+
+  tmp = gpr_tmpfile(prefix, &tmp_name);
+  GPR_ASSERT(tmp_name != NULL);
+  GPR_ASSERT(tmp != NULL);
+  fclose(tmp);
+  remove(tmp_name);
+
+  slice = gpr_load_file(tmp_name, &success);
+  GPR_ASSERT(success == 0);
+  GPR_ASSERT(GPR_SLICE_LENGTH(slice) == 0);
+  gpr_free(tmp_name);
+  gpr_slice_unref(slice);
+}
+
+static void test_load_small_file(void) {
+  FILE *tmp = NULL;
+  gpr_slice slice;
+  int success;
+  char *tmp_name;
+  const char *blah = "blah";
+
+  LOG_TEST_NAME();
+
+  tmp = gpr_tmpfile(prefix, &tmp_name);
+  GPR_ASSERT(tmp_name != NULL);
+  GPR_ASSERT(tmp != NULL);
+  GPR_ASSERT(fwrite(blah, 1, strlen(blah), tmp) == strlen(blah));
+  fclose(tmp);
+
+  slice = gpr_load_file(tmp_name, &success);
+  GPR_ASSERT(success == 1);
+  GPR_ASSERT(GPR_SLICE_LENGTH(slice) == strlen(blah));
+  GPR_ASSERT(!memcmp(GPR_SLICE_START_PTR(slice), blah, strlen(blah)));
+
+  remove(tmp_name);
+  gpr_free(tmp_name);
+  gpr_slice_unref(slice);
+}
+
+static void test_load_big_file(void) {
+  FILE *tmp = NULL;
+  gpr_slice slice;
+  int success;
+  char *tmp_name;
+  unsigned char buffer[124631];
+  unsigned char *current;
+  size_t i;
+
+  LOG_TEST_NAME();
+
+  for (i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = 42;
+  }
+
+  tmp = gpr_tmpfile(prefix, &tmp_name);
+  GPR_ASSERT(tmp != NULL);
+  GPR_ASSERT(tmp_name != NULL);
+  GPR_ASSERT(fwrite(buffer, 1, sizeof(buffer), tmp) == sizeof(buffer));
+  fclose(tmp);
+
+  slice = gpr_load_file(tmp_name, &success);
+  GPR_ASSERT(success == 1);
+  GPR_ASSERT(GPR_SLICE_LENGTH(slice) == sizeof(buffer));
+  current = GPR_SLICE_START_PTR(slice);
+  for (i = 0; i < sizeof(buffer); i++) {
+    GPR_ASSERT(current[i] == 42);
+  }
+
+  remove(tmp_name);
+  gpr_free(tmp_name);
+  gpr_slice_unref(slice);
+}
+
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_load_empty_file();
+  test_load_failure();
+  test_load_small_file();
+  test_load_big_file();
+  return 0;
+}
diff --git a/test/cpp/util/create_test_channel.cc b/test/cpp/util/create_test_channel.cc
index a521162bea210c02c33781b7b5a5894bf6fe9714..301e9a3c3a36884598eb58d72f5527d9c973f292 100644
--- a/test/cpp/util/create_test_channel.cc
+++ b/test/cpp/util/create_test_channel.cc
@@ -45,6 +45,8 @@ namespace grpc {
 // override_hostname is provided.
 // When ssl is not enabled, override_hostname is ignored.
 // Set use_prod_root to true to use the SSL root for connecting to google.
+// In this case, path to the roots pem file must be set via environment variable
+// GRPC_DEFAULT_SSL_ROOTS_FILE_PATH.
 // Otherwise, root for test SSL cert will be used.
 // creds will be used to create a channel when enable_ssl is true.
 // Use examples:
@@ -60,7 +62,7 @@ std::shared_ptr<ChannelInterface> CreateTestChannel(
   ChannelArguments channel_args;
   if (enable_ssl) {
     const char* roots_certs =
-        use_prod_roots ? prod_roots_certs : test_root_cert;
+        use_prod_roots ? "" : test_root_cert;
     SslCredentialsOptions ssl_opts = {roots_certs, "", ""};
 
     std::unique_ptr<Credentials> channel_creds =
diff --git a/tools/dockerfile/grpc_cxx/Dockerfile b/tools/dockerfile/grpc_cxx/Dockerfile
index 43da9fefc378959184fcf203d519a3541591fb79..9b20e7a58e48e339dfac9932485e625bea616b7c 100644
--- a/tools/dockerfile/grpc_cxx/Dockerfile
+++ b/tools/dockerfile/grpc_cxx/Dockerfile
@@ -22,5 +22,7 @@ RUN cd /var/local/git/grpc && ls \
   && make interop_server
 
 ADD service_account service_account
+ADD cacerts cacerts
+ENV GRPC_DEFAULT_SSL_ROOTS_FILE_PATH /cacerts/roots.pem
 
 CMD ["/var/local/git/grpc/bins/opt/interop_server", "--enable_ssl", "--port=8010"]
diff --git a/tools/gce_setup/builder.sh b/tools/gce_setup/builder.sh
new file mode 100755
index 0000000000000000000000000000000000000000..49b3c436feeccb125056a40a53ac74541fef38ff
--- /dev/null
+++ b/tools/gce_setup/builder.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+main() {
+  # restart builder vm and wait for images to sync to it
+  source grpc_docker.sh
+  ./new_grpc_docker_builder.sh -igrpc-docker-builder-alt-2 -anone
+  cd ../../
+  sleep 3600
+
+  # build images for all languages
+  languages=(cxx java go ruby node)
+  for lan in "${languages[@]}"
+  do
+    grpc_update_image $lan
+  done
+
+  # restart client and server vm and wait for images to sync to them
+  cd tools/gce_setup
+  ./new_grpc_docker_builder.sh -igrpc-docker-testclients -anone
+  ./new_grpc_docker_builder.sh -igrpc-docker-server -anone
+  sleep 3600
+
+  # launch images for all languages on  server
+  grpc_launch_servers grpc-docker-server
+  
+}
+
+set -x
+main "$@"
diff --git a/tools/gce_setup/cloud_prod_runner.sh b/tools/gce_setup/cloud_prod_runner.sh
index 0c1163ad7dbafcfdf0e80a247a60b5dcd67c524d..200f859ede5687d19348729cced70216e72939ac 100755
--- a/tools/gce_setup/cloud_prod_runner.sh
+++ b/tools/gce_setup/cloud_prod_runner.sh
@@ -2,8 +2,8 @@
 
 main() {
   source grpc_docker.sh
-  test_cases=(large_unary empty_unary client_streaming server_streaming)
-  clients=(cxx java go ruby)
+  test_cases=(large_unary empty_unary ping_pong client_streaming server_streaming)
+  clients=(cxx java go ruby node)
   for test_case in "${test_cases[@]}"
   do
     for client in "${clients[@]}"
diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh
index 2ac75f3cc59056bf3e542deb60aaffe6f0955bc7..2e0265386434f2fb3c590cc9a38dedcdb39b9a1e 100755
--- a/tools/gce_setup/grpc_docker.sh
+++ b/tools/gce_setup/grpc_docker.sh
@@ -762,7 +762,16 @@ grpc_interop_test() {
   echo "  $ssh_cmd"
   echo "on $host"
   [[ $dry_run == 1 ]] && return 0  # don't run the command on a dry run
-  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
+  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" & 
+  PID=$!
+  sleep 10
+  echo "pid is $PID"
+  if ps -p $PID
+  then
+    kill $PID
+    return 1
+  fi
+
 }
 
 # Runs a test command on a docker instance.
@@ -808,7 +817,16 @@ grpc_cloud_prod_test() {
   echo "  $ssh_cmd"
   echo "on $host"
   [[ $dry_run == 1 ]] && return 0  # don't run the command on a dry run
-  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
+  gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" & 
+  PID=$!
+  sleep 10
+  echo "pid is $PID"
+  if ps -p $PID
+  then
+    kill $PID
+    return 1
+  fi
+
 }
 
 # Runs a test command on a docker instance.
diff --git a/tools/gce_setup/interop_test_runner.sh b/tools/gce_setup/interop_test_runner.sh
index 12443079128f4e359bf056c949f063136ddab7ff..456ad4b4722e994dc582a71087a784df709c3bf1 100755
--- a/tools/gce_setup/interop_test_runner.sh
+++ b/tools/gce_setup/interop_test_runner.sh
@@ -1,33 +1,8 @@
 #!/bin/bash
 thisfile=$(readlink -ne "${BASH_SOURCE[0]}")
-
-run_test() {
-  local test_case=$1
-  shift
-  local client=$1
-  shift 
-  local server=$1
-  if grpc_interop_test $test_case grpc-docker-testclients $client grpc-docker-server $server
-  then
-    echo "$test_case $client $server passed" >> /tmp/interop_result.txt
-  else
-    echo "$test_case $client $server failed" >> /tmp/interop_result.txt
-  fi
-}
-
-time_out() {
-  local test_case=$1
-  shift
-  local client=$1
-  shift
-  local server=$1
-  if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
-    if ! timeout 20s bash -l -c "source $thisfile && run_test $test_case $client $server"
-    then
-      echo "$test_case $client $server timed out" >> /tmp/interop_result.txt
-    fi
-  fi
-}
+current_time=$(date "+%Y-%m-%d-%H-%M-%S")
+result_file_name=interop_result.$current_time.html
+echo $result_file_name
 
 main() {
   source grpc_docker.sh
@@ -40,13 +15,22 @@ main() {
     do
       for server in "${servers[@]}"
       do
-        time_out $test_case $client $server
+        if grpc_interop_test $test_case grpc-docker-testclients $client grpc-docker-server $server
+        then
+          echo "          ['$test_case', '$client', '$server', true]," >> /tmp/interop_result.txt
+        else
+          echo "          ['$test_case', '$client', '$server', false]," >> /tmp/interop_result.txt
+        fi
       done
     done
   done
   if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+    cat pre.html /tmp/interop_result.txt post.html > /tmp/interop_result.html
     gsutil cp /tmp/interop_result.txt gs://stoked-keyword-656-output/interop_result.txt
+    gsutil cp /tmp/interop_result.html gs://stoked-keyword-656-output/interop_result.html
+    gsutil cp /tmp/interop_result.html gs://stoked-keyword-656-output/result_history/$result_file_name
     rm /tmp/interop_result.txt
+    rm /tmp/interop_result.html
   fi
 }
 
diff --git a/tools/gce_setup/post.html b/tools/gce_setup/post.html
new file mode 100644
index 0000000000000000000000000000000000000000..57cbc8c36941260c210c742225e2ac0a30c12290
--- /dev/null
+++ b/tools/gce_setup/post.html
@@ -0,0 +1,12 @@
+        ]);
+
+        var table = new google.visualization.Table(document.getElementById('table_div'));
+
+        table.draw(data, {showRowNumber: true});
+      }
+    </script>
+  </head>
+  <body>
+    <div id="table_div"></div>
+  </body>
+</html>
diff --git a/tools/gce_setup/pre.html b/tools/gce_setup/pre.html
new file mode 100644
index 0000000000000000000000000000000000000000..74ce5ce20285e2b3a3ed36bb05648632ba34a38a
--- /dev/null
+++ b/tools/gce_setup/pre.html
@@ -0,0 +1,14 @@
+<html>
+  <head>
+    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
+    <script type="text/javascript">
+      google.load("visualization", "1", {packages:["table"]});
+      google.setOnLoadCallback(drawTable);
+
+      function drawTable() {
+        var data = new google.visualization.DataTable();
+        data.addColumn('string', 'TestCase');
+        data.addColumn('string', 'Client');
+        data.addColumn('string', 'Server');
+        data.addColumn('boolean', 'Pass');
+        data.addRows([
diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh
index eea940864da23295532426a4c363ce975cd68f83..a6f73d16367ef011fc1af30f7768ad40249ecad9 100755
--- a/tools/gce_setup/shared_startup_funcs.sh
+++ b/tools/gce_setup/shared_startup_funcs.sh
@@ -389,6 +389,7 @@ grpc_dockerfile_install() {
     grpc_docker_sync_service_account $dockerfile_dir/service_account || return 1;
   }
   [[ $image_label == "grpc/cxx" ]] && {
+    grpc_docker_sync_roots_pem $dockerfile_dir/cacerts || return 1;
     grpc_docker_sync_service_account $dockerfile_dir/service_account || return 1;
   }
 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index 939a1a60951cf47b4948b5426d69983edd9faf48..facef4e0d7de5b28b6df5434dcce6729e8cf69eb 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -109,6 +109,14 @@
     "language": "c", 
     "name": "gpr_log_test"
   }, 
+  {
+    "language": "c", 
+    "name": "gpr_file_test"
+  }, 
+  {
+    "language": "c", 
+    "name": "gpr_env_test"
+  }, 
   {
     "language": "c", 
     "name": "gpr_slice_buffer_test"
diff --git a/vsprojects/vs2013/build_and_run_tests.bat b/vsprojects/vs2013/build_and_run_tests.bat
index 77000739bbab4c7ec8ae0fca899418a8b449ab4c..88d9b2f471867dc6b9f0d9d23b58a83dd9071947 100644
--- a/vsprojects/vs2013/build_and_run_tests.bat
+++ b/vsprojects/vs2013/build_and_run_tests.bat
@@ -49,6 +49,22 @@ echo Running test gpr_log_test
 test_bin\gpr_log_test.exe || echo TEST FAILED: gpr_log_test && exit /b
 echo(
 
+echo Building test gpr_file_test
+cl.exe /c /I..\.. /I..\..\include /nologo /ZI /W3 /WX- /sdl /D WIN32 /D _LIB /D _USE_32BIT_TIME_T /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TC /analyze- /Fo:test_bin\ ..\..\test\core\support\file_test.c 
+link.exe /OUT:"test_bin\gpr_file_test.exe" /INCREMENTAL /NOLOGO /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 Debug\gpr_test_util.lib Debug\gpr.lib test_bin\file_test.obj 
+echo(
+echo Running test gpr_file_test
+test_bin\gpr_file_test.exe || echo TEST FAILED: gpr_file_test && exit /b
+echo(
+
+echo Building test gpr_env_test
+cl.exe /c /I..\.. /I..\..\include /nologo /ZI /W3 /WX- /sdl /D WIN32 /D _LIB /D _USE_32BIT_TIME_T /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TC /analyze- /Fo:test_bin\ ..\..\test\core\support\env_test.c 
+link.exe /OUT:"test_bin\gpr_env_test.exe" /INCREMENTAL /NOLOGO /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 Debug\gpr_test_util.lib Debug\gpr.lib test_bin\env_test.obj 
+echo(
+echo Running test gpr_env_test
+test_bin\gpr_env_test.exe || echo TEST FAILED: gpr_env_test && exit /b
+echo(
+
 echo Building test gpr_slice_buffer_test
 cl.exe /c /I..\.. /I..\..\include /nologo /ZI /W3 /WX- /sdl /D WIN32 /D _LIB /D _USE_32BIT_TIME_T /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TC /analyze- /Fo:test_bin\ ..\..\test\core\support\slice_buffer_test.c 
 link.exe /OUT:"test_bin\gpr_slice_buffer_test.exe" /INCREMENTAL /NOLOGO /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 Debug\gpr_test_util.lib Debug\gpr.lib test_bin\slice_buffer_test.obj 
diff --git a/vsprojects/vs2013/gpr.vcxproj b/vsprojects/vs2013/gpr.vcxproj
index 0d429ab43dee1201c5bd1be00cdfcc6f33ca01af..8276082cfdaee5ae5bc64d91a3eebbd946074ba0 100644
--- a/vsprojects/vs2013/gpr.vcxproj
+++ b/vsprojects/vs2013/gpr.vcxproj
@@ -96,8 +96,11 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\src\core\support\cpu.h" />
+    <ClInclude Include="..\..\src\core\support\env.h" />
+    <ClInclude Include="..\..\src\core\support\file.h" />
     <ClInclude Include="..\..\src\core\support\murmur_hash.h" />
     <ClInclude Include="..\..\src\core\support\string.h" />
+    <ClInclude Include="..\..\src\core\support\string_win32.h" />
     <ClInclude Include="..\..\src\core\support\thd_internal.h" />
   </ItemGroup>
   <ItemGroup>
@@ -111,6 +114,18 @@
     </ClCompile>
     <ClCompile Include="..\..\src\core\support\cpu_posix.c">
     </ClCompile>
+    <ClCompile Include="..\..\src\core\support\env_linux.c">
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\env_posix.c">
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\env_win32.c">
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\file.c">
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\file_posix.c">
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\file_win32.c">
+    </ClCompile>
     <ClCompile Include="..\..\src\core\support\histogram.c">
     </ClCompile>
     <ClCompile Include="..\..\src\core\support\host_port.c">
diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters
index a992558654b93ecd1351e433004b47d1bd65607f..2329acc9daee8ef6371e7d469edbe2306ddb188c 100644
--- a/vsprojects/vs2013/gpr.vcxproj.filters
+++ b/vsprojects/vs2013/gpr.vcxproj.filters
@@ -16,6 +16,24 @@
     <ClCompile Include="..\..\src\core\support\cpu_posix.c">
       <Filter>src\core\support</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\core\support\env_linux.c">
+      <Filter>src\core\support</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\env_posix.c">
+      <Filter>src\core\support</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\env_win32.c">
+      <Filter>src\core\support</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\file.c">
+      <Filter>src\core\support</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\file_posix.c">
+      <Filter>src\core\support</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\support\file_win32.c">
+      <Filter>src\core\support</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\core\support\histogram.c">
       <Filter>src\core\support</Filter>
     </ClCompile>
@@ -146,12 +164,21 @@
     <ClInclude Include="..\..\src\core\support\cpu.h">
       <Filter>src\core\support</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\core\support\env.h">
+      <Filter>src\core\support</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\support\file.h">
+      <Filter>src\core\support</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\core\support\murmur_hash.h">
       <Filter>src\core\support</Filter>
     </ClInclude>
     <ClInclude Include="..\..\src\core\support\string.h">
       <Filter>src\core\support</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\core\support\string_win32.h">
+      <Filter>src\core\support</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\core\support\thd_internal.h">
       <Filter>src\core\support</Filter>
     </ClInclude>