Updating moe_db.txt with the latest equivalence since the ruby import changed the exported structure.
	Change on 2014/12/01 by nnoble <nnoble@google.com>
-------------
new [] file for grpc testing.
	Change on 2014/12/02 by donnadionne <donnadionne@google.com>
-------------
Fix unfinished calls in thread_stress_test.

Previously we had an early return if we cancelled a stream part way through a
message. Correct this, so that half close and full close signals are propagated
up the stack correctly so that higher level state machines can see the
termination.
	Change on 2014/12/02 by ctiller <ctiller@google.com>
-------------
Remove dependency on internal C code.
	Change on 2014/12/02 by ctiller <ctiller@google.com>
-------------
Turn off the flaky bit from thread_stress_test.
	Change on 2014/12/02 by ctiller <ctiller@google.com>
-------------
Add test cases of empty request/response, request streaming, response streaming, and half duplex streaming.

Bring up the GFE/ESF for mannual test:
[] build java/com/google/net/[]/testing/integration/hexa:server_components_env
[]-bin/java/com/google/net/[]/testing/integration/hexa/server_components_env --manual --rpc_port=25000 --use_autobahn
	Change on 2014/12/02 by chenw <chenw@google.com>
-------------
Make echo/server.c and fling/server.c shutdown cleanly on SIGINT, and update
the relevant tests to exercise this mechanism.

Now "[] coverage" and the memory leak detector are able to see into the
server processes.
	Change on 2014/12/02 by pmarks <pmarks@google.com>
-------------
Allow the # of channels to be configurable in this performance test. The threads will use the channels in statically-defined round-robin order (not based on when RPCs complete on any channel). The interesting cases are #channels=1 or #channels=#threads (we previously only had the latter case)
	Change on 2014/12/02 by vpai <vpai@google.com>
-------------
Fixed a typo and reworded a comment.
	Change on 2014/12/02 by gnezdo <gnezdo@google.com>
-------------
Require the grpc_call in this ClientContext to be NULL before allowing set_call to be invoked. Otherwise, it's an indication of a leak somewhere.
	Change on 2014/12/02 by vpai <vpai@google.com>
-------------
Correctly return status other than ok and add a test for it.
	Change on 2014/12/02 by yangg <yangg@google.com>
-------------
Better C++ guards for grpc_security.h
	Change on 2014/12/02 by nnoble <nnoble@google.com>
-------------
Use nullptr instead of NULL for consistency.
	Change on 2014/12/02 by vpai <vpai@google.com>
-------------
Updates the ruby gRPC service class to require the serialization method to be
a static method

- this brings it inline with the proto3 ruby API
- it adds a monkey patch to allow existing proto (beefcake) to continue working.
	Change on 2014/12/02 by temiola <temiola@google.com>
-------------
Adding a buildable unit to the blue print file.

Added the buildable unit as its name will be usesd as tap project id.

This test will fail right away in tap until tests are actually added.
	Change on 2014/12/02 by donnadionne <donnadionne@google.com>
-------------
Move interop ESF C++ server from Java to grpc directory.

Tests passed:
[] test javatests/com/google/net/[]/testing/integration/hexa/...
[] test net/grpc/testing/interop/esf_server/...
	Change on 2014/12/02 by chenw <chenw@google.com>
-------------
Return a lame channel as opposed to NULL when secure channel creation fails.

- Looks like we're going to need something similar server-side.
- I changed the prototype of the lame client channel factory to take an
explicit void as I think this is better practice in C. Let me know if you
disagree and I will revert these changes.
	Change on 2014/12/02 by jboeuf <jboeuf@google.com>
-------------
Putting ALPN support where it belongs.
	Change on 2014/12/02 by jboeuf <jboeuf@google.com>
-------------
GOAWAY send path.

Sends a GOAWAY frame when shutting down.
This is not read and understood yet.
	Change on 2014/12/03 by ctiller <ctiller@google.com>
-------------
Adds support for secure channels and servers.

- wraps new C apis (credentials, server_credentials) and Server#add_secure_http_port
- adds tests to ensure credentials and server credentials can be created
- updates client_server_spec to run the client_server wrapper layer end-to-end tests using a secure channel
	Change on 2014/12/03 by temiola <temiola@google.com>
-------------
Fix existing issues regarding out of order events.

At the client side, using pluck as the client_metadata_read can happen anytime after invoke.

At the server side, allow halfclose_ok and rpc_end to come in reverse order.
	Change on 2014/12/03 by yangg <yangg@google.com>
-------------
Don't track coverage of tests.
	Change on 2014/12/03 by ctiller <ctiller@google.com>
-------------
Change UnaryCall to conform standard test requirement of LargeUnaryCall.
	Change on 2014/12/03 by yangg <yangg@google.com>
-------------
updating alpn version to h2-15 ensure all interop are on the same version and working.

Java and go are not ready for h2-16 yet.
	Change on 2014/12/03 by donnadionne <donnadionne@google.com>
-------------
Add config to bring echo server in [].

This is used to test production GFE as its bckend.
	Change on 2014/12/03 by chenw <chenw@google.com>
-------------
In preparation for fixing shutdown race issues, change em to take ownership of
the file descriptor. Add an API to grpc_tcp to take an already created
grpc_em_fd object, and change tcp_client to use that API.

This is needed because otherwise an em user's close() of the file descriptor
may race with libevent internals. That's not an issue yet because destroy()
frees the events inline, but that can't be done safely if there is a concurrent
poller.
	Change on 2014/12/03 by klempner <klempner@google.com>
-------------
Fixing TAP opensource build

We don't want to compile and run C++ tests in the C target.
	Change on 2014/12/03 by nnoble <nnoble@google.com>
-------------
Move and separate interop tests by languages.

Small fixes to the test runner.

Improving logging.
	Change on 2014/12/03 by donnadionne <donnadionne@google.com>
-------------
Fixing the opensource build:

-) The C/C++ split wasn't done up to the 'dep' target level
-) The alpn.c file was missing from build.json
	Change on 2014/12/03 by nnoble <nnoble@google.com>
-------------
Adding blue print files after projects exist
	Change on 2014/12/03 by donnadionne <donnadionne@google.com>
-------------
Refactor StreamContext using the new completion_queue_pluck API.

The dedicated the poller thread has been removed.
This CL keeps the current behavior to make it short. There is one following to
make it usable for both client and server.

The tags for pluck is based on the address of this StreamContext object for potential debug use.

The Read/Write and Wait cannot be called concurrently now and this might need to be fixed.
	Change on 2014/12/03 by yangg <yangg@google.com>
-------------
Binary encoding utilities.

Support base64 encoding, HPACK static huffman encoding, and doing both at once.
	Change on 2014/12/03 by ctiller <ctiller@google.com>
-------------
Enforce Makefile regeneration in presubmits.
	Change on 2014/12/03 by ctiller <ctiller@google.com>
-------------
Make CloseSend() send a real zero-length control message to indicate EOS.
	Change on 2014/12/03 by zhaoq <zhaoq@google.com>
-------------
Prefer to create dualstack sockets for TCP clients and servers, with automatic
fallback for environments where IPV6_V6ONLY can't be turned off.
	Change on 2014/12/03 by pmarks <pmarks@google.com>
-------------
Add opensource path to build targets.

Ensure that MOE is going to run.
	Change on 2014/12/03 by ctiller <ctiller@google.com>
-------------
Add PingPong test case. Delete FullDuplex test case. The latter is not specified for client in

https://docs.google.com/document/d/1dwrPpIu5EqiKVsquZfoOqTj7vP8fa1i49gornJo50Qw/edit#
	Change on 2014/12/03 by chenw <chenw@google.com>
-------------
Make generate_projects.sh check out the generated targets.
	Change on 2014/12/03 by ctiller <ctiller@google.com>
-------------
rspec cleanup

- stops declaring specs within the GRPC module
- splits Bidi streaming specs into a separate test suite

adding tests in the GRPC module was a mistake, it pollutes the module and can
affect other tests that run later by the test runner

the bidi tests are currently flaky, having them run in their own test suite
allows having two separate continuous builds (once ruby gRPC is on GitHub),
one that includes bidi where we tolerate flakiness, and another that does not,
where there should be no flakiness at all
	Change on 2014/12/03 by temiola <temiola@google.com>
-------------
Adding support for composite and IAM credentials.

- For now, we don't do any checks on credentials compatibility in the
composite credentials. Maybe we'll add that later.
- Refactored the end to end security tests so that we always use the public API
(except for the fake security context which is not exposed).
	Change on 2014/12/03 by jboeuf <jboeuf@google.com>
-------------
Make GPR library buildable in Visual Studio 2013.
	Change on 2014/12/04 by jtattermusch <jtattermusch@google.com>
-------------
Adds codegen for ruby

This is being added now that ruby's proto and grpc apis are defined and stable
	Change on 2014/12/04 by temiola <temiola@google.com>
-------------
Prevent NewStream() from sending negative or 0 timeout.
	Change on 2014/12/04 by zhaoq <zhaoq@google.com>
-------------
Add a grpc_sockaddr_to_string() function, and use it when logging bind
failures.  Also improve const-correctness in some earlier code.

I'm not certain whether inet_ntop() will need any platform-specific
implementations, but for now the compiler offers no complaints.

Demo:
$ []-bin/net/grpc/c/echo_server 1.2.3.4:80
... tcp_server.c:139] bind addr=[::ffff:1.2.3.4]:80: Permission denied
	Change on 2014/12/04 by pmarks <pmarks@google.com>
-------------
Refactoring - moves c wrapped classes to a submodule Google::RPC::Core

- this allows for an explicit rule when reading through gRPC ruby code for telling
when an object is pure ruby or wrapped C
	Change on 2014/12/04 by temiola <temiola@google.com>
-------------
Fixes the bidi_call

[]
	Change on 2014/12/04 by temiola <temiola@google.com>
-------------
Fixing dev build when activating surface traces.
	Change on 2014/12/04 by nnoble <nnoble@google.com>
-------------
Updates the tests to reflect that fact that some Credentials compose works.
	Change on 2014/12/04 by temiola <temiola@google.com>
-------------
Making the generate_project_test actually do something.
	Change on 2014/12/04 by nnoble <nnoble@google.com>
-------------
Rename "esf_server" to "[]4_server". Delete "test_sever" from Java directory.
	Change on 2014/12/04 by chenw <chenw@google.com>
-------------
Added PHP client interop tests. Tested large_unary against the C++ server.
	Change on 2014/12/04 by mlumish <mlumish@google.com>
-------------
Refactor grpc_create_dualstack_socket() by pulling the setsockopt into its own
function.  This separates the magic test flag from the real fallback logic.
	Change on 2014/12/04 by pmarks <pmarks@google.com>
-------------
Fixes the type of the constant used for test cert hostname
	Change on 2014/12/04 by temiola <temiola@google.com>
-------------
Disabling these tests as they're causing flakiness.
	Change on 2014/12/04 by ctiller <ctiller@google.com>
-------------
Change intptr --> uintptr.

Handles the case where a void* turns into a negative number, which then gets
hashed into a negative bucket and segfaults.
	Change on 2014/12/04 by ctiller <ctiller@google.com>
-------------
Add a test fixture to force parsers to handle one byte at a time.

This should expand coverage and hopefully prevent errors at some point (it
seems to pass out of the box for now though).
	Change on 2014/12/04 by ctiller <ctiller@google.com>
-------------
The code generator isn't +x.
	Change on 2014/12/04 by ctiller <ctiller@google.com>
-------------
Updates math_client and math_server to allow construction using crednetials

By:
- Extending rpc_server constructor so that it takes a credentials keyword param
- Extending client_stub constructor so that it takes a credentials keyword param
	Change on 2014/12/04 by temiola <temiola@google.com>
-------------
Format output a little more nicely.

Print each line of output separately - previously logging.info was truncating this at some maximum length, and logs were getting lost.
	Change on 2014/12/04 by ctiller <ctiller@google.com>
-------------
Up timeout for this test.

Under TSAN, if we process one byte at a time, this timeout can be reached - and I think this is the cause of the following flake:
[]
	Change on 2014/12/05 by ctiller <ctiller@google.com>
-------------
Adding more error logging for ssl.
	Change on 2014/12/05 by jboeuf <jboeuf@google.com>
-------------
Read path for goaway.

Still need to add hooks to deprecate a channel on the client side when goaway
is received.
	Change on 2014/12/05 by ctiller <ctiller@google.com>
-------------
Separate accept() into server_accept() and server_end_of_initial_metadata().

This allows servers to initiate reads before finishing writing metadata.
	Change on 2014/12/05 by ctiller <ctiller@google.com>
-------------
Fix for breakage 11512317 - adding missing test files.
	Change on 2014/12/05 by nnoble <nnoble@google.com>
-------------
grpc c++ server side streaming support.

This is based on [] There is a lot of room to clean up the internal implementation which may require refactoring of CompletionQueue. The current cl serves as a working implementation with the missing interfaces.

The sample generated files are included and will be removed before submitting.
	Change on 2014/12/05 by yangg <yangg@google.com>
-------------
Changed to the latest timeout format again (search "grpc-timeout" in [] for the spec).
	Change on 2014/12/05 by zhaoq <zhaoq@google.com>
-------------
Fixing opensource build.
	Change on 2014/12/05 by nnoble <nnoble@google.com>
-------------
Making absolutely sure we can do the moe export by adding a sh_test for it.
	Change on 2014/12/05 by nnoble <nnoble@google.com>
-------------
Change :scheme psuedo-header from "grpc" to "http" or "https".
	Change on 2014/12/05 by zhaoq <zhaoq@google.com>
-------------
Add server credential wrapping for c++ server. It only wraps ssl and []2 for now.

The ServerCredentials class and the factory class are in a similar fashion as
client side wrapping. The difference is the factory method returns shared_ptr
instead of unique_ptr as the server builder needs to keep a reference to it for
actually creating the server later.

The integration will happen in a following cl.
	Change on 2014/12/05 by yangg <yangg@google.com>
-------------
Fixed bugs in new_grpc_docker_builder.sh
	Change on 2014/12/05 by mlumish <mlumish@google.com>
-------------
In secure endpoint, hold a refcount for the life of a write callback if the
write does not complete immediately.
	Change on 2014/12/05 by klempner <klempner@google.com>
-------------
Add migration support to MOE and have TAP verify it doesn't break.

Migration support allows mirroring commits from [] into the git repo, instead of just a dump of the current source.
	Change on 2014/12/05 by ejona <ejona@google.com>
-------------
Change initial window size to 65535 according http2 draft 15.
	Change on 2014/12/05 by zhaoq <zhaoq@google.com>
-------------
Re-enable the flaky cases in dualstack_socket_test, with additional logging to
help track down the problem if it surfaces again.

This also seems like a good opportunity to make grpc_socket_utils a separate
library, as it's not really specific to TCP.

Example output:
logspam: [], 26570) resolved 2 addrs in 37ms:
logspam:   [0] [::1]:26570
logspam:   [1] 127.0.0.1:26570
	Change on 2014/12/05 by pmarks <pmarks@google.com>
-------------
Opensource build fixes.

-) A function that has a return type should actually return something.
-) Don't pass unsigned chars to strlen and strncmp.
	Change on 2014/12/05 by nnoble <nnoble@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=81458281
diff --git a/Makefile b/Makefile
index 4e0cc7e..84ddb54 100644
--- a/Makefile
+++ b/Makefile
@@ -66,25 +66,25 @@
 all: static shared
 static: static_c static_cxx
 
-static_c: make_dirs dep libs/libgpr.a libs/libgrpc.a libs/libgrpc_unsecure.a
+static_c: make_dirs dep_c libs/libgpr.a libs/libgrpc.a libs/libgrpc_unsecure.a
 
-static_cxx: make_dirs dep libs/libgrpc++.a
+static_cxx: make_dirs dep_cxx libs/libgrpc++.a
 
 shared: shared_c shared_cxx
 
-shared_c: make_dirs dep libs/libgpr.so.$(VERSION) libs/libgrpc.so.$(VERSION) libs/libgrpc_unsecure.so.$(VERSION)
+shared_c: make_dirs dep_c libs/libgpr.so.$(VERSION) libs/libgrpc.so.$(VERSION) libs/libgrpc_unsecure.so.$(VERSION)
 
-shared_cxx: make_dirs dep libs/libgrpc++.so.$(VERSION)
+shared_cxx: make_dirs dep_cxx libs/libgrpc++.so.$(VERSION)
 
 privatelibs: privatelibs_c privatelibs_cxx
 
-privatelibs_c: make_dirs dep libs/libgrpc_test_util.a libs/libend2end_fixture_chttp2_fake_security.a libs/libend2end_fixture_chttp2_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/libend2end_fixture_chttp2_socket_pair.a libs/libend2end_test_cancel_after_accept.a libs/libend2end_test_cancel_after_accept_and_writes_closed.a libs/libend2end_test_cancel_after_invoke.a libs/libend2end_test_cancel_before_invoke.a libs/libend2end_test_cancel_in_a_vacuum.a libs/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/libend2end_test_early_server_shutdown_finishes_tags.a libs/libend2end_test_invoke_large_request.a libs/libend2end_test_max_concurrent_streams.a libs/libend2end_test_no_op.a libs/libend2end_test_ping_pong_streaming.a libs/libend2end_test_request_response_with_metadata_and_payload.a libs/libend2end_test_request_response_with_payload.a libs/libend2end_test_simple_delayed_request.a libs/libend2end_test_simple_request.a libs/libend2end_test_thread_stress_test.a libs/libend2end_test_writes_done_hangs_with_pending_read.a libs/libend2end_certs.a
+privatelibs_c: make_dirs dep_c libs/libgrpc_test_util.a libs/libend2end_fixture_chttp2_fake_security.a libs/libend2end_fixture_chttp2_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/libend2end_fixture_chttp2_socket_pair.a libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_cancel_after_accept.a libs/libend2end_test_cancel_after_accept_and_writes_closed.a libs/libend2end_test_cancel_after_invoke.a libs/libend2end_test_cancel_before_invoke.a libs/libend2end_test_cancel_in_a_vacuum.a libs/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/libend2end_test_early_server_shutdown_finishes_tags.a libs/libend2end_test_invoke_large_request.a libs/libend2end_test_max_concurrent_streams.a libs/libend2end_test_no_op.a libs/libend2end_test_ping_pong_streaming.a libs/libend2end_test_request_response_with_metadata_and_payload.a libs/libend2end_test_request_response_with_payload.a libs/libend2end_test_simple_delayed_request.a libs/libend2end_test_simple_request.a libs/libend2end_test_thread_stress_test.a libs/libend2end_test_writes_done_hangs_with_pending_read.a libs/libend2end_certs.a
 
-privatelibs_cxx: make_dirs dep libs/libgrpc_test_util.a libs/libend2end_fixture_chttp2_fake_security.a libs/libend2end_fixture_chttp2_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/libend2end_fixture_chttp2_socket_pair.a libs/libend2end_test_cancel_after_accept.a libs/libend2end_test_cancel_after_accept_and_writes_closed.a libs/libend2end_test_cancel_after_invoke.a libs/libend2end_test_cancel_before_invoke.a libs/libend2end_test_cancel_in_a_vacuum.a libs/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/libend2end_test_early_server_shutdown_finishes_tags.a libs/libend2end_test_invoke_large_request.a libs/libend2end_test_max_concurrent_streams.a libs/libend2end_test_no_op.a libs/libend2end_test_ping_pong_streaming.a libs/libend2end_test_request_response_with_metadata_and_payload.a libs/libend2end_test_request_response_with_payload.a libs/libend2end_test_simple_delayed_request.a libs/libend2end_test_simple_request.a libs/libend2end_test_thread_stress_test.a libs/libend2end_test_writes_done_hangs_with_pending_read.a libs/libend2end_certs.a
+privatelibs_cxx: make_dirs dep_cxx libs/libgrpc_test_util.a libs/libend2end_fixture_chttp2_fake_security.a libs/libend2end_fixture_chttp2_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/libend2end_fixture_chttp2_socket_pair.a libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_cancel_after_accept.a libs/libend2end_test_cancel_after_accept_and_writes_closed.a libs/libend2end_test_cancel_after_invoke.a libs/libend2end_test_cancel_before_invoke.a libs/libend2end_test_cancel_in_a_vacuum.a libs/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/libend2end_test_early_server_shutdown_finishes_tags.a libs/libend2end_test_invoke_large_request.a libs/libend2end_test_max_concurrent_streams.a libs/libend2end_test_no_op.a libs/libend2end_test_ping_pong_streaming.a libs/libend2end_test_request_response_with_metadata_and_payload.a libs/libend2end_test_request_response_with_payload.a libs/libend2end_test_simple_delayed_request.a libs/libend2end_test_simple_request.a libs/libend2end_test_thread_stress_test.a libs/libend2end_test_writes_done_hangs_with_pending_read.a libs/libend2end_certs.a
 
 buildtests: buildtests_c buildtests_cxx
 
-buildtests_c: privatelibs_c bins/grpc_byte_buffer_reader_test bins/gpr_cancellable_test bins/gpr_log_test bins/gpr_cmdline_test bins/gpr_histogram_test bins/gpr_host_port_test bins/gpr_slice_buffer_test bins/gpr_slice_test bins/gpr_string_test bins/gpr_sync_test bins/gpr_thd_test bins/gpr_time_test bins/murmur_hash_test bins/grpc_em_test bins/grpc_em_pipe_test bins/grpc_stream_op_test bins/chttp2_stream_encoder_test bins/hpack_table_test bins/chttp2_stream_map_test bins/hpack_parser_test bins/transport_metadata_test bins/chttp2_status_conversion_test bins/chttp2_transport_end2end_test bins/grpc_tcp_test bins/resolve_address_test bins/tcp_server_test bins/tcp_client_test bins/grpc_channel_stack_test bins/metadata_buffer_test bins/grpc_completion_queue_test bins/census_window_stats_test bins/census_statistics_quick_test bins/census_statistics_performance_test bins/census_statistics_multiple_writers_test bins/census_statistics_multiple_writers_circular_buffer_test bins/census_stub_test bins/census_hash_table_test bins/fling_server bins/fling_client bins/fling_test bins/echo_server bins/echo_client bins/echo_test bins/message_compress_test bins/secure_endpoint_test bins/httpcli_format_request_test bins/httpcli_parser_test bins/httpcli_test bins/grpc_credentials_test bins/fling_stream_test bins/lame_client_test bins/chttp2_fake_security_cancel_after_accept_test bins/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/chttp2_fake_security_cancel_after_invoke_test bins/chttp2_fake_security_cancel_before_invoke_test bins/chttp2_fake_security_cancel_in_a_vacuum_test bins/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/chttp2_fake_security_invoke_large_request_test bins/chttp2_fake_security_max_concurrent_streams_test bins/chttp2_fake_security_no_op_test bins/chttp2_fake_security_ping_pong_streaming_test bins/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/chttp2_fake_security_request_response_with_payload_test bins/chttp2_fake_security_simple_delayed_request_test bins/chttp2_fake_security_simple_request_test bins/chttp2_fake_security_thread_stress_test_test bins/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/chttp2_fullstack_cancel_after_accept_test bins/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/chttp2_fullstack_cancel_after_invoke_test bins/chttp2_fullstack_cancel_before_invoke_test bins/chttp2_fullstack_cancel_in_a_vacuum_test bins/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/chttp2_fullstack_invoke_large_request_test bins/chttp2_fullstack_max_concurrent_streams_test bins/chttp2_fullstack_no_op_test bins/chttp2_fullstack_ping_pong_streaming_test bins/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/chttp2_fullstack_request_response_with_payload_test bins/chttp2_fullstack_simple_delayed_request_test bins/chttp2_fullstack_simple_request_test bins/chttp2_fullstack_thread_stress_test_test bins/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/chttp2_simple_ssl_fullstack_no_op_test bins/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/chttp2_simple_ssl_fullstack_simple_request_test bins/chttp2_simple_ssl_fullstack_thread_stress_test_test bins/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test bins/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/chttp2_socket_pair_cancel_after_accept_test bins/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/chttp2_socket_pair_cancel_after_invoke_test bins/chttp2_socket_pair_cancel_before_invoke_test bins/chttp2_socket_pair_cancel_in_a_vacuum_test bins/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/chttp2_socket_pair_invoke_large_request_test bins/chttp2_socket_pair_max_concurrent_streams_test bins/chttp2_socket_pair_no_op_test bins/chttp2_socket_pair_ping_pong_streaming_test bins/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/chttp2_socket_pair_request_response_with_payload_test bins/chttp2_socket_pair_simple_delayed_request_test bins/chttp2_socket_pair_simple_request_test bins/chttp2_socket_pair_thread_stress_test_test bins/chttp2_socket_pair_writes_done_hangs_with_pending_read_test
+buildtests_c: privatelibs_c bins/grpc_byte_buffer_reader_test bins/gpr_cancellable_test bins/gpr_log_test bins/gpr_cmdline_test bins/gpr_histogram_test bins/gpr_host_port_test bins/gpr_slice_buffer_test bins/gpr_slice_test bins/gpr_string_test bins/gpr_sync_test bins/gpr_thd_test bins/gpr_time_test bins/murmur_hash_test bins/grpc_em_test bins/grpc_em_pipe_test bins/grpc_stream_op_test bins/alpn_test bins/chttp2_stream_encoder_test bins/hpack_table_test bins/chttp2_stream_map_test bins/hpack_parser_test bins/transport_metadata_test bins/chttp2_status_conversion_test bins/chttp2_transport_end2end_test bins/grpc_tcp_test bins/dualstack_socket_test bins/no_server_test bins/resolve_address_test bins/socket_utils_test bins/tcp_server_test bins/tcp_client_test bins/grpc_channel_stack_test bins/metadata_buffer_test bins/grpc_completion_queue_test bins/census_window_stats_test bins/census_statistics_quick_test bins/census_statistics_performance_test bins/census_statistics_multiple_writers_test bins/census_statistics_multiple_writers_circular_buffer_test bins/census_stub_test bins/census_hash_table_test bins/fling_server bins/fling_client bins/fling_test bins/echo_server bins/echo_client bins/echo_test bins/message_compress_test bins/bin_encoder_test bins/secure_endpoint_test bins/httpcli_format_request_test bins/httpcli_parser_test bins/httpcli_test bins/grpc_credentials_test bins/fling_stream_test bins/lame_client_test bins/chttp2_fake_security_cancel_after_accept_test bins/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/chttp2_fake_security_cancel_after_invoke_test bins/chttp2_fake_security_cancel_before_invoke_test bins/chttp2_fake_security_cancel_in_a_vacuum_test bins/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/chttp2_fake_security_invoke_large_request_test bins/chttp2_fake_security_max_concurrent_streams_test bins/chttp2_fake_security_no_op_test bins/chttp2_fake_security_ping_pong_streaming_test bins/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/chttp2_fake_security_request_response_with_payload_test bins/chttp2_fake_security_simple_delayed_request_test bins/chttp2_fake_security_simple_request_test bins/chttp2_fake_security_thread_stress_test_test bins/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/chttp2_fullstack_cancel_after_accept_test bins/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/chttp2_fullstack_cancel_after_invoke_test bins/chttp2_fullstack_cancel_before_invoke_test bins/chttp2_fullstack_cancel_in_a_vacuum_test bins/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/chttp2_fullstack_invoke_large_request_test bins/chttp2_fullstack_max_concurrent_streams_test bins/chttp2_fullstack_no_op_test bins/chttp2_fullstack_ping_pong_streaming_test bins/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/chttp2_fullstack_request_response_with_payload_test bins/chttp2_fullstack_simple_delayed_request_test bins/chttp2_fullstack_simple_request_test bins/chttp2_fullstack_thread_stress_test_test bins/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/chttp2_simple_ssl_fullstack_no_op_test bins/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/chttp2_simple_ssl_fullstack_simple_request_test bins/chttp2_simple_ssl_fullstack_thread_stress_test_test bins/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test bins/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/chttp2_socket_pair_cancel_after_accept_test bins/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/chttp2_socket_pair_cancel_after_invoke_test bins/chttp2_socket_pair_cancel_before_invoke_test bins/chttp2_socket_pair_cancel_in_a_vacuum_test bins/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/chttp2_socket_pair_invoke_large_request_test bins/chttp2_socket_pair_max_concurrent_streams_test bins/chttp2_socket_pair_no_op_test bins/chttp2_socket_pair_ping_pong_streaming_test bins/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/chttp2_socket_pair_request_response_with_payload_test bins/chttp2_socket_pair_simple_delayed_request_test bins/chttp2_socket_pair_simple_request_test bins/chttp2_socket_pair_thread_stress_test_test bins/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test bins/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
 
 buildtests_cxx: privatelibs_cxx bins/thread_pool_test bins/status_test
 
@@ -123,6 +123,8 @@
 	$(Q) ./bins/grpc_em_pipe_test || ( echo test grpc_em_pipe_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpc_stream_op_test"
 	$(Q) ./bins/grpc_stream_op_test || ( echo test grpc_stream_op_test failed ; exit 1 )
+	$(E) "[RUN]     Testing alpn_test"
+	$(Q) ./bins/alpn_test || ( echo test alpn_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_stream_encoder_test"
 	$(Q) ./bins/chttp2_stream_encoder_test || ( echo test chttp2_stream_encoder_test failed ; exit 1 )
 	$(E) "[RUN]     Testing hpack_table_test"
@@ -139,8 +141,14 @@
 	$(Q) ./bins/chttp2_transport_end2end_test || ( echo test chttp2_transport_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpc_tcp_test"
 	$(Q) ./bins/grpc_tcp_test || ( echo test grpc_tcp_test failed ; exit 1 )
+	$(E) "[RUN]     Testing dualstack_socket_test"
+	$(Q) ./bins/dualstack_socket_test || ( echo test dualstack_socket_test failed ; exit 1 )
+	$(E) "[RUN]     Testing no_server_test"
+	$(Q) ./bins/no_server_test || ( echo test no_server_test failed ; exit 1 )
 	$(E) "[RUN]     Testing resolve_address_test"
 	$(Q) ./bins/resolve_address_test || ( echo test resolve_address_test failed ; exit 1 )
+	$(E) "[RUN]     Testing socket_utils_test"
+	$(Q) ./bins/socket_utils_test || ( echo test socket_utils_test failed ; exit 1 )
 	$(E) "[RUN]     Testing tcp_server_test"
 	$(Q) ./bins/tcp_server_test || ( echo test tcp_server_test failed ; exit 1 )
 	$(E) "[RUN]     Testing tcp_client_test"
@@ -171,6 +179,8 @@
 	$(Q) ./bins/echo_test || ( echo test echo_test failed ; exit 1 )
 	$(E) "[RUN]     Testing message_compress_test"
 	$(Q) ./bins/message_compress_test || ( echo test message_compress_test failed ; exit 1 )
+	$(E) "[RUN]     Testing bin_encoder_test"
+	$(Q) ./bins/bin_encoder_test || ( echo test bin_encoder_test failed ; exit 1 )
 	$(E) "[RUN]     Testing secure_endpoint_test"
 	$(Q) ./bins/secure_endpoint_test || ( echo test secure_endpoint_test failed ; exit 1 )
 	$(E) "[RUN]     Testing httpcli_format_request_test"
@@ -355,6 +365,40 @@
 	$(Q) ./bins/chttp2_socket_pair_thread_stress_test_test || ( echo test chttp2_socket_pair_thread_stress_test_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_socket_pair_writes_done_hangs_with_pending_read_test"
 	$(Q) ./bins/chttp2_socket_pair_writes_done_hangs_with_pending_read_test || ( echo test chttp2_socket_pair_writes_done_hangs_with_pending_read_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_no_op_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_no_op_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_no_op_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_simple_request_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_simple_request_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_simple_request_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test failed ; exit 1 )
+	$(E) "[RUN]     Testing chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test"
+	$(Q) ./bins/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test || ( echo test chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test failed ; exit 1 )
 
 
 tests_cxx: buildtests_cxx
@@ -432,7 +476,11 @@
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
 
-dep: deps_libgpr deps_libgrpc deps_libgrpc_test_util deps_libgrpc++ deps_libgrpc++_test_util deps_libend2end_fixture_chttp2_fake_security deps_libend2end_fixture_chttp2_fullstack deps_libend2end_fixture_chttp2_simple_ssl_fullstack deps_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack deps_libend2end_fixture_chttp2_socket_pair deps_libend2end_test_cancel_after_accept deps_libend2end_test_cancel_after_accept_and_writes_closed deps_libend2end_test_cancel_after_invoke deps_libend2end_test_cancel_before_invoke deps_libend2end_test_cancel_in_a_vacuum deps_libend2end_test_early_server_shutdown_finishes_inflight_calls deps_libend2end_test_early_server_shutdown_finishes_tags deps_libend2end_test_invoke_large_request deps_libend2end_test_max_concurrent_streams deps_libend2end_test_no_op deps_libend2end_test_ping_pong_streaming deps_libend2end_test_request_response_with_metadata_and_payload deps_libend2end_test_request_response_with_payload deps_libend2end_test_simple_delayed_request deps_libend2end_test_simple_request deps_libend2end_test_thread_stress_test deps_libend2end_test_writes_done_hangs_with_pending_read deps_libend2end_certs deps_libgrpc_unsecure deps_gen_hpack_tables deps_grpc_byte_buffer_reader_test deps_gpr_cancellable_test deps_gpr_log_test deps_gpr_cmdline_test deps_gpr_histogram_test deps_gpr_host_port_test deps_gpr_slice_buffer_test deps_gpr_slice_test deps_gpr_string_test deps_gpr_sync_test deps_gpr_thd_test deps_gpr_time_test deps_murmur_hash_test deps_grpc_em_test deps_grpc_em_pipe_test deps_grpc_stream_op_test deps_chttp2_stream_encoder_test deps_hpack_table_test deps_chttp2_stream_map_test deps_hpack_parser_test deps_transport_metadata_test deps_chttp2_status_conversion_test deps_chttp2_transport_end2end_test deps_grpc_tcp_test deps_resolve_address_test deps_tcp_server_test deps_tcp_client_test deps_grpc_channel_stack_test deps_metadata_buffer_test deps_grpc_completion_queue_test deps_grpc_completion_queue_benchmark deps_census_window_stats_test deps_census_statistics_quick_test deps_census_statistics_performance_test deps_census_statistics_multiple_writers_test deps_census_statistics_multiple_writers_circular_buffer_test deps_census_stub_test deps_census_hash_table_test deps_fling_server deps_fling_client deps_fling_test deps_echo_server deps_echo_client deps_echo_test deps_low_level_ping_pong_benchmark deps_message_compress_test deps_secure_endpoint_test deps_httpcli_format_request_test deps_httpcli_parser_test deps_httpcli_test deps_grpc_credentials_test deps_fling_stream_test deps_lame_client_test deps_thread_pool_test deps_status_test deps_chttp2_fake_security_cancel_after_accept_test deps_chttp2_fake_security_cancel_after_accept_and_writes_closed_test deps_chttp2_fake_security_cancel_after_invoke_test deps_chttp2_fake_security_cancel_before_invoke_test deps_chttp2_fake_security_cancel_in_a_vacuum_test deps_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fake_security_early_server_shutdown_finishes_tags_test deps_chttp2_fake_security_invoke_large_request_test deps_chttp2_fake_security_max_concurrent_streams_test deps_chttp2_fake_security_no_op_test deps_chttp2_fake_security_ping_pong_streaming_test deps_chttp2_fake_security_request_response_with_metadata_and_payload_test deps_chttp2_fake_security_request_response_with_payload_test deps_chttp2_fake_security_simple_delayed_request_test deps_chttp2_fake_security_simple_request_test deps_chttp2_fake_security_thread_stress_test_test deps_chttp2_fake_security_writes_done_hangs_with_pending_read_test deps_chttp2_fullstack_cancel_after_accept_test deps_chttp2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_fullstack_cancel_after_invoke_test deps_chttp2_fullstack_cancel_before_invoke_test deps_chttp2_fullstack_cancel_in_a_vacuum_test deps_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_fullstack_invoke_large_request_test deps_chttp2_fullstack_max_concurrent_streams_test deps_chttp2_fullstack_no_op_test deps_chttp2_fullstack_ping_pong_streaming_test deps_chttp2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_fullstack_request_response_with_payload_test deps_chttp2_fullstack_simple_delayed_request_test deps_chttp2_fullstack_simple_request_test deps_chttp2_fullstack_thread_stress_test_test deps_chttp2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_fullstack_no_op_test deps_chttp2_simple_ssl_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_fullstack_simple_request_test deps_chttp2_simple_ssl_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test deps_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_socket_pair_cancel_after_accept_test deps_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test deps_chttp2_socket_pair_cancel_after_invoke_test deps_chttp2_socket_pair_cancel_before_invoke_test deps_chttp2_socket_pair_cancel_in_a_vacuum_test deps_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_socket_pair_early_server_shutdown_finishes_tags_test deps_chttp2_socket_pair_invoke_large_request_test deps_chttp2_socket_pair_max_concurrent_streams_test deps_chttp2_socket_pair_no_op_test deps_chttp2_socket_pair_ping_pong_streaming_test deps_chttp2_socket_pair_request_response_with_metadata_and_payload_test deps_chttp2_socket_pair_request_response_with_payload_test deps_chttp2_socket_pair_simple_delayed_request_test deps_chttp2_socket_pair_simple_request_test deps_chttp2_socket_pair_thread_stress_test_test deps_chttp2_socket_pair_writes_done_hangs_with_pending_read_test
+dep: dep_c dep_cxx
+
+dep_c: deps_libgpr deps_libgrpc deps_libgrpc_test_util deps_libend2end_fixture_chttp2_fake_security deps_libend2end_fixture_chttp2_fullstack deps_libend2end_fixture_chttp2_simple_ssl_fullstack deps_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack deps_libend2end_fixture_chttp2_socket_pair deps_libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time deps_libend2end_test_cancel_after_accept deps_libend2end_test_cancel_after_accept_and_writes_closed deps_libend2end_test_cancel_after_invoke deps_libend2end_test_cancel_before_invoke deps_libend2end_test_cancel_in_a_vacuum deps_libend2end_test_early_server_shutdown_finishes_inflight_calls deps_libend2end_test_early_server_shutdown_finishes_tags deps_libend2end_test_invoke_large_request deps_libend2end_test_max_concurrent_streams deps_libend2end_test_no_op deps_libend2end_test_ping_pong_streaming deps_libend2end_test_request_response_with_metadata_and_payload deps_libend2end_test_request_response_with_payload deps_libend2end_test_simple_delayed_request deps_libend2end_test_simple_request deps_libend2end_test_thread_stress_test deps_libend2end_test_writes_done_hangs_with_pending_read deps_libend2end_certs deps_libgrpc_unsecure deps_gen_hpack_tables deps_grpc_byte_buffer_reader_test deps_gpr_cancellable_test deps_gpr_log_test deps_gpr_cmdline_test deps_gpr_histogram_test deps_gpr_host_port_test deps_gpr_slice_buffer_test deps_gpr_slice_test deps_gpr_string_test deps_gpr_sync_test deps_gpr_thd_test deps_gpr_time_test deps_murmur_hash_test deps_grpc_em_test deps_grpc_em_pipe_test deps_grpc_stream_op_test deps_alpn_test deps_chttp2_stream_encoder_test deps_hpack_table_test deps_chttp2_stream_map_test deps_hpack_parser_test deps_transport_metadata_test deps_chttp2_status_conversion_test deps_chttp2_transport_end2end_test deps_grpc_tcp_test deps_dualstack_socket_test deps_no_server_test deps_resolve_address_test deps_socket_utils_test deps_tcp_server_test deps_tcp_client_test deps_grpc_channel_stack_test deps_metadata_buffer_test deps_grpc_completion_queue_test deps_grpc_completion_queue_benchmark deps_census_window_stats_test deps_census_statistics_quick_test deps_census_statistics_performance_test deps_census_statistics_multiple_writers_test deps_census_statistics_multiple_writers_circular_buffer_test deps_census_stub_test deps_census_hash_table_test deps_fling_server deps_fling_client deps_fling_test deps_echo_server deps_echo_client deps_echo_test deps_low_level_ping_pong_benchmark deps_message_compress_test deps_bin_encoder_test deps_secure_endpoint_test deps_httpcli_format_request_test deps_httpcli_parser_test deps_httpcli_test deps_grpc_credentials_test deps_fling_stream_test deps_lame_client_test deps_chttp2_fake_security_cancel_after_accept_test deps_chttp2_fake_security_cancel_after_accept_and_writes_closed_test deps_chttp2_fake_security_cancel_after_invoke_test deps_chttp2_fake_security_cancel_before_invoke_test deps_chttp2_fake_security_cancel_in_a_vacuum_test deps_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fake_security_early_server_shutdown_finishes_tags_test deps_chttp2_fake_security_invoke_large_request_test deps_chttp2_fake_security_max_concurrent_streams_test deps_chttp2_fake_security_no_op_test deps_chttp2_fake_security_ping_pong_streaming_test deps_chttp2_fake_security_request_response_with_metadata_and_payload_test deps_chttp2_fake_security_request_response_with_payload_test deps_chttp2_fake_security_simple_delayed_request_test deps_chttp2_fake_security_simple_request_test deps_chttp2_fake_security_thread_stress_test_test deps_chttp2_fake_security_writes_done_hangs_with_pending_read_test deps_chttp2_fullstack_cancel_after_accept_test deps_chttp2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_fullstack_cancel_after_invoke_test deps_chttp2_fullstack_cancel_before_invoke_test deps_chttp2_fullstack_cancel_in_a_vacuum_test deps_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_fullstack_invoke_large_request_test deps_chttp2_fullstack_max_concurrent_streams_test deps_chttp2_fullstack_no_op_test deps_chttp2_fullstack_ping_pong_streaming_test deps_chttp2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_fullstack_request_response_with_payload_test deps_chttp2_fullstack_simple_delayed_request_test deps_chttp2_fullstack_simple_request_test deps_chttp2_fullstack_thread_stress_test_test deps_chttp2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_fullstack_no_op_test deps_chttp2_simple_ssl_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_fullstack_simple_request_test deps_chttp2_simple_ssl_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test deps_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_socket_pair_cancel_after_accept_test deps_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test deps_chttp2_socket_pair_cancel_after_invoke_test deps_chttp2_socket_pair_cancel_before_invoke_test deps_chttp2_socket_pair_cancel_in_a_vacuum_test deps_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_socket_pair_early_server_shutdown_finishes_tags_test deps_chttp2_socket_pair_invoke_large_request_test deps_chttp2_socket_pair_max_concurrent_streams_test deps_chttp2_socket_pair_no_op_test deps_chttp2_socket_pair_ping_pong_streaming_test deps_chttp2_socket_pair_request_response_with_metadata_and_payload_test deps_chttp2_socket_pair_request_response_with_payload_test deps_chttp2_socket_pair_simple_delayed_request_test deps_chttp2_socket_pair_simple_request_test deps_chttp2_socket_pair_thread_stress_test_test deps_chttp2_socket_pair_writes_done_hangs_with_pending_read_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test deps_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test deps_chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test deps_chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test deps_chttp2_socket_pair_one_byte_at_a_time_no_op_test deps_chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test deps_chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test deps_chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test deps_chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test deps_chttp2_socket_pair_one_byte_at_a_time_simple_request_test deps_chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test deps_chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
+
+dep_cxx: deps_libgrpc++ deps_libgrpc++_test_util deps_thread_pool_test deps_status_test
 
 install: install-headers install-static install-shared
 
@@ -460,7 +508,7 @@
 	$(E) "[INSTALL] Installing libgrpc_unsecure.so"
 	$(Q) $(INSTALL) libs/libgrpc_unsecure.so.$(VERSION) $(prefix)/lib/libgrpc_unsecure.so.$(VERSION)
 
-clean: clean_libgpr clean_libgrpc clean_libgrpc_test_util clean_libgrpc++ clean_libgrpc++_test_util clean_libend2end_fixture_chttp2_fake_security clean_libend2end_fixture_chttp2_fullstack clean_libend2end_fixture_chttp2_simple_ssl_fullstack clean_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack clean_libend2end_fixture_chttp2_socket_pair clean_libend2end_test_cancel_after_accept clean_libend2end_test_cancel_after_accept_and_writes_closed clean_libend2end_test_cancel_after_invoke clean_libend2end_test_cancel_before_invoke clean_libend2end_test_cancel_in_a_vacuum clean_libend2end_test_early_server_shutdown_finishes_inflight_calls clean_libend2end_test_early_server_shutdown_finishes_tags clean_libend2end_test_invoke_large_request clean_libend2end_test_max_concurrent_streams clean_libend2end_test_no_op clean_libend2end_test_ping_pong_streaming clean_libend2end_test_request_response_with_metadata_and_payload clean_libend2end_test_request_response_with_payload clean_libend2end_test_simple_delayed_request clean_libend2end_test_simple_request clean_libend2end_test_thread_stress_test clean_libend2end_test_writes_done_hangs_with_pending_read clean_libend2end_certs clean_libgrpc_unsecure clean_gen_hpack_tables clean_grpc_byte_buffer_reader_test clean_gpr_cancellable_test clean_gpr_log_test clean_gpr_cmdline_test clean_gpr_histogram_test clean_gpr_host_port_test clean_gpr_slice_buffer_test clean_gpr_slice_test clean_gpr_string_test clean_gpr_sync_test clean_gpr_thd_test clean_gpr_time_test clean_murmur_hash_test clean_grpc_em_test clean_grpc_em_pipe_test clean_grpc_stream_op_test clean_chttp2_stream_encoder_test clean_hpack_table_test clean_chttp2_stream_map_test clean_hpack_parser_test clean_transport_metadata_test clean_chttp2_status_conversion_test clean_chttp2_transport_end2end_test clean_grpc_tcp_test clean_resolve_address_test clean_tcp_server_test clean_tcp_client_test clean_grpc_channel_stack_test clean_metadata_buffer_test clean_grpc_completion_queue_test clean_grpc_completion_queue_benchmark clean_census_window_stats_test clean_census_statistics_quick_test clean_census_statistics_performance_test clean_census_statistics_multiple_writers_test clean_census_statistics_multiple_writers_circular_buffer_test clean_census_stub_test clean_census_hash_table_test clean_fling_server clean_fling_client clean_fling_test clean_echo_server clean_echo_client clean_echo_test clean_low_level_ping_pong_benchmark clean_message_compress_test clean_secure_endpoint_test clean_httpcli_format_request_test clean_httpcli_parser_test clean_httpcli_test clean_grpc_credentials_test clean_fling_stream_test clean_lame_client_test clean_thread_pool_test clean_status_test clean_chttp2_fake_security_cancel_after_accept_test clean_chttp2_fake_security_cancel_after_accept_and_writes_closed_test clean_chttp2_fake_security_cancel_after_invoke_test clean_chttp2_fake_security_cancel_before_invoke_test clean_chttp2_fake_security_cancel_in_a_vacuum_test clean_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fake_security_early_server_shutdown_finishes_tags_test clean_chttp2_fake_security_invoke_large_request_test clean_chttp2_fake_security_max_concurrent_streams_test clean_chttp2_fake_security_no_op_test clean_chttp2_fake_security_ping_pong_streaming_test clean_chttp2_fake_security_request_response_with_metadata_and_payload_test clean_chttp2_fake_security_request_response_with_payload_test clean_chttp2_fake_security_simple_delayed_request_test clean_chttp2_fake_security_simple_request_test clean_chttp2_fake_security_thread_stress_test_test clean_chttp2_fake_security_writes_done_hangs_with_pending_read_test clean_chttp2_fullstack_cancel_after_accept_test clean_chttp2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_fullstack_cancel_after_invoke_test clean_chttp2_fullstack_cancel_before_invoke_test clean_chttp2_fullstack_cancel_in_a_vacuum_test clean_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_fullstack_invoke_large_request_test clean_chttp2_fullstack_max_concurrent_streams_test clean_chttp2_fullstack_no_op_test clean_chttp2_fullstack_ping_pong_streaming_test clean_chttp2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_fullstack_request_response_with_payload_test clean_chttp2_fullstack_simple_delayed_request_test clean_chttp2_fullstack_simple_request_test clean_chttp2_fullstack_thread_stress_test_test clean_chttp2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_fullstack_no_op_test clean_chttp2_simple_ssl_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_fullstack_simple_request_test clean_chttp2_simple_ssl_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test clean_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_socket_pair_cancel_after_accept_test clean_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test clean_chttp2_socket_pair_cancel_after_invoke_test clean_chttp2_socket_pair_cancel_before_invoke_test clean_chttp2_socket_pair_cancel_in_a_vacuum_test clean_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_socket_pair_early_server_shutdown_finishes_tags_test clean_chttp2_socket_pair_invoke_large_request_test clean_chttp2_socket_pair_max_concurrent_streams_test clean_chttp2_socket_pair_no_op_test clean_chttp2_socket_pair_ping_pong_streaming_test clean_chttp2_socket_pair_request_response_with_metadata_and_payload_test clean_chttp2_socket_pair_request_response_with_payload_test clean_chttp2_socket_pair_simple_delayed_request_test clean_chttp2_socket_pair_simple_request_test clean_chttp2_socket_pair_thread_stress_test_test clean_chttp2_socket_pair_writes_done_hangs_with_pending_read_test
+clean: clean_libgpr clean_libgrpc clean_libgrpc_test_util clean_libgrpc++ clean_libgrpc++_test_util clean_libend2end_fixture_chttp2_fake_security clean_libend2end_fixture_chttp2_fullstack clean_libend2end_fixture_chttp2_simple_ssl_fullstack clean_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack clean_libend2end_fixture_chttp2_socket_pair clean_libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time clean_libend2end_test_cancel_after_accept clean_libend2end_test_cancel_after_accept_and_writes_closed clean_libend2end_test_cancel_after_invoke clean_libend2end_test_cancel_before_invoke clean_libend2end_test_cancel_in_a_vacuum clean_libend2end_test_early_server_shutdown_finishes_inflight_calls clean_libend2end_test_early_server_shutdown_finishes_tags clean_libend2end_test_invoke_large_request clean_libend2end_test_max_concurrent_streams clean_libend2end_test_no_op clean_libend2end_test_ping_pong_streaming clean_libend2end_test_request_response_with_metadata_and_payload clean_libend2end_test_request_response_with_payload clean_libend2end_test_simple_delayed_request clean_libend2end_test_simple_request clean_libend2end_test_thread_stress_test clean_libend2end_test_writes_done_hangs_with_pending_read clean_libend2end_certs clean_libgrpc_unsecure clean_gen_hpack_tables clean_grpc_byte_buffer_reader_test clean_gpr_cancellable_test clean_gpr_log_test clean_gpr_cmdline_test clean_gpr_histogram_test clean_gpr_host_port_test clean_gpr_slice_buffer_test clean_gpr_slice_test clean_gpr_string_test clean_gpr_sync_test clean_gpr_thd_test clean_gpr_time_test clean_murmur_hash_test clean_grpc_em_test clean_grpc_em_pipe_test clean_grpc_stream_op_test clean_alpn_test clean_chttp2_stream_encoder_test clean_hpack_table_test clean_chttp2_stream_map_test clean_hpack_parser_test clean_transport_metadata_test clean_chttp2_status_conversion_test clean_chttp2_transport_end2end_test clean_grpc_tcp_test clean_dualstack_socket_test clean_no_server_test clean_resolve_address_test clean_socket_utils_test clean_tcp_server_test clean_tcp_client_test clean_grpc_channel_stack_test clean_metadata_buffer_test clean_grpc_completion_queue_test clean_grpc_completion_queue_benchmark clean_census_window_stats_test clean_census_statistics_quick_test clean_census_statistics_performance_test clean_census_statistics_multiple_writers_test clean_census_statistics_multiple_writers_circular_buffer_test clean_census_stub_test clean_census_hash_table_test clean_fling_server clean_fling_client clean_fling_test clean_echo_server clean_echo_client clean_echo_test clean_low_level_ping_pong_benchmark clean_message_compress_test clean_bin_encoder_test clean_secure_endpoint_test clean_httpcli_format_request_test clean_httpcli_parser_test clean_httpcli_test clean_grpc_credentials_test clean_fling_stream_test clean_lame_client_test clean_thread_pool_test clean_status_test clean_chttp2_fake_security_cancel_after_accept_test clean_chttp2_fake_security_cancel_after_accept_and_writes_closed_test clean_chttp2_fake_security_cancel_after_invoke_test clean_chttp2_fake_security_cancel_before_invoke_test clean_chttp2_fake_security_cancel_in_a_vacuum_test clean_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fake_security_early_server_shutdown_finishes_tags_test clean_chttp2_fake_security_invoke_large_request_test clean_chttp2_fake_security_max_concurrent_streams_test clean_chttp2_fake_security_no_op_test clean_chttp2_fake_security_ping_pong_streaming_test clean_chttp2_fake_security_request_response_with_metadata_and_payload_test clean_chttp2_fake_security_request_response_with_payload_test clean_chttp2_fake_security_simple_delayed_request_test clean_chttp2_fake_security_simple_request_test clean_chttp2_fake_security_thread_stress_test_test clean_chttp2_fake_security_writes_done_hangs_with_pending_read_test clean_chttp2_fullstack_cancel_after_accept_test clean_chttp2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_fullstack_cancel_after_invoke_test clean_chttp2_fullstack_cancel_before_invoke_test clean_chttp2_fullstack_cancel_in_a_vacuum_test clean_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_fullstack_invoke_large_request_test clean_chttp2_fullstack_max_concurrent_streams_test clean_chttp2_fullstack_no_op_test clean_chttp2_fullstack_ping_pong_streaming_test clean_chttp2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_fullstack_request_response_with_payload_test clean_chttp2_fullstack_simple_delayed_request_test clean_chttp2_fullstack_simple_request_test clean_chttp2_fullstack_thread_stress_test_test clean_chttp2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_fullstack_no_op_test clean_chttp2_simple_ssl_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_fullstack_simple_request_test clean_chttp2_simple_ssl_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test clean_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_socket_pair_cancel_after_accept_test clean_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test clean_chttp2_socket_pair_cancel_after_invoke_test clean_chttp2_socket_pair_cancel_before_invoke_test clean_chttp2_socket_pair_cancel_in_a_vacuum_test clean_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_socket_pair_early_server_shutdown_finishes_tags_test clean_chttp2_socket_pair_invoke_large_request_test clean_chttp2_socket_pair_max_concurrent_streams_test clean_chttp2_socket_pair_no_op_test clean_chttp2_socket_pair_ping_pong_streaming_test clean_chttp2_socket_pair_request_response_with_metadata_and_payload_test clean_chttp2_socket_pair_request_response_with_payload_test clean_chttp2_socket_pair_simple_delayed_request_test clean_chttp2_socket_pair_simple_request_test clean_chttp2_socket_pair_thread_stress_test_test clean_chttp2_socket_pair_writes_done_hangs_with_pending_read_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test clean_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test clean_chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test clean_chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test clean_chttp2_socket_pair_one_byte_at_a_time_no_op_test clean_chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test clean_chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test clean_chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test clean_chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test clean_chttp2_socket_pair_one_byte_at_a_time_simple_request_test clean_chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test clean_chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
 	$(Q) $(RM) -r deps objs libs bins gens
 
 
@@ -484,6 +532,7 @@
     src/core/support/slice_buffer.c \
     src/core/support/string.c \
     src/core/support/string_posix.c \
+    src/core/support/string_win32.c \
     src/core/support/sync.c \
     src/core/support/sync_posix.c \
     src/core/support/thd_posix.c \
@@ -583,13 +632,17 @@
     src/core/surface/server_chttp2.c \
     src/core/surface/server_create.c \
     src/core/surface/surface_em.c \
+    src/core/transport/chttp2/alpn.c \
+    src/core/transport/chttp2/bin_encoder.c \
     src/core/transport/chttp2/frame_data.c \
+    src/core/transport/chttp2/frame_goaway.c \
     src/core/transport/chttp2/frame_ping.c \
     src/core/transport/chttp2/frame_rst_stream.c \
     src/core/transport/chttp2/frame_settings.c \
     src/core/transport/chttp2/frame_window_update.c \
     src/core/transport/chttp2/hpack_parser.c \
     src/core/transport/chttp2/hpack_table.c \
+    src/core/transport/chttp2/huffsyms.c \
     src/core/transport/chttp2/status_conversion.c \
     src/core/transport/chttp2/stream_encoder.c \
     src/core/transport/chttp2/stream_map.c \
@@ -914,6 +967,33 @@
 	$(Q) $(RM) libs/libend2end_fixture_chttp2_socket_pair.so.$(VERSION)
 
 
+LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SRC = \
+    test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c \
+
+
+LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SRC))))
+LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SRC))))
+
+libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a: $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_OBJS)
+	$(E) "[AR]      Creating $@"
+	$(Q) $(AR) rcs libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_OBJS)
+
+
+
+deps_libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time: $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_DEPS)
+endif
+
+clean_libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time:
+	$(E) "[CLEAN]   Cleaning libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time files"
+	$(Q) $(RM) $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_OBJS)
+	$(Q) $(RM) $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_DEPS)
+	$(Q) $(RM) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a
+	$(Q) $(RM) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.so.$(VERSION)
+
+
 LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_SRC = \
     test/core/end2end/tests/cancel_after_accept.c \
 
@@ -1441,13 +1521,17 @@
     src/core/surface/server_chttp2.c \
     src/core/surface/server_create.c \
     src/core/surface/surface_em.c \
+    src/core/transport/chttp2/alpn.c \
+    src/core/transport/chttp2/bin_encoder.c \
     src/core/transport/chttp2/frame_data.c \
+    src/core/transport/chttp2/frame_goaway.c \
     src/core/transport/chttp2/frame_ping.c \
     src/core/transport/chttp2/frame_rst_stream.c \
     src/core/transport/chttp2/frame_settings.c \
     src/core/transport/chttp2/frame_window_update.c \
     src/core/transport/chttp2/hpack_parser.c \
     src/core/transport/chttp2/hpack_table.c \
+    src/core/transport/chttp2/huffsyms.c \
     src/core/transport/chttp2/status_conversion.c \
     src/core/transport/chttp2/stream_encoder.c \
     src/core/transport/chttp2/stream_map.c \
@@ -1515,9 +1599,9 @@
 GEN_HPACK_TABLES_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(GEN_HPACK_TABLES_SRC))))
 GEN_HPACK_TABLES_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(GEN_HPACK_TABLES_SRC))))
 
-bins/gen_hpack_tables: $(GEN_HPACK_TABLES_OBJS) libs/libgrpc_test_util.a libs/libgpr.a
+bins/gen_hpack_tables: $(GEN_HPACK_TABLES_OBJS) libs/libgrpc_test_util.a libs/libgpr.a libs/libgrpc.a
 	$(E) "[LD]      Linking $@"
-	$(Q) $(LD) $(LDFLAGS) $(GEN_HPACK_TABLES_OBJS) -Llibs -lgrpc_test_util -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/gen_hpack_tables
+	$(Q) $(LD) $(LDFLAGS) $(GEN_HPACK_TABLES_OBJS) -Llibs -lgrpc_test_util -lgpr -lgrpc $(LDLIBS) $(LDLIBS_SECURE) -o bins/gen_hpack_tables
 
 deps_gen_hpack_tables: $(GEN_HPACK_TABLES_DEPS)
 
@@ -1900,6 +1984,29 @@
 	$(Q) $(RM) bins/grpc_stream_op_test
 
 
+ALPN_TEST_SRC = \
+    test/core/transport/chttp2/alpn_test.c \
+
+ALPN_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(ALPN_TEST_SRC))))
+ALPN_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(ALPN_TEST_SRC))))
+
+bins/alpn_test: $(ALPN_TEST_OBJS) libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(ALPN_TEST_OBJS) -Llibs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/alpn_test
+
+deps_alpn_test: $(ALPN_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(ALPN_TEST_DEPS)
+endif
+
+clean_alpn_test:
+	$(E) "[CLEAN]   Cleaning alpn_test files"
+	$(Q) $(RM) $(ALPN_TEST_OBJS)
+	$(Q) $(RM) $(ALPN_TEST_DEPS)
+	$(Q) $(RM) bins/alpn_test
+
+
 CHTTP2_STREAM_ENCODER_TEST_SRC = \
     test/core/transport/chttp2/stream_encoder_test.c \
 
@@ -2084,6 +2191,52 @@
 	$(Q) $(RM) bins/grpc_tcp_test
 
 
+DUALSTACK_SOCKET_TEST_SRC = \
+    test/core/end2end/dualstack_socket_test.c \
+
+DUALSTACK_SOCKET_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(DUALSTACK_SOCKET_TEST_SRC))))
+DUALSTACK_SOCKET_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(DUALSTACK_SOCKET_TEST_SRC))))
+
+bins/dualstack_socket_test: $(DUALSTACK_SOCKET_TEST_OBJS) libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(DUALSTACK_SOCKET_TEST_OBJS) -Llibs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/dualstack_socket_test
+
+deps_dualstack_socket_test: $(DUALSTACK_SOCKET_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(DUALSTACK_SOCKET_TEST_DEPS)
+endif
+
+clean_dualstack_socket_test:
+	$(E) "[CLEAN]   Cleaning dualstack_socket_test files"
+	$(Q) $(RM) $(DUALSTACK_SOCKET_TEST_OBJS)
+	$(Q) $(RM) $(DUALSTACK_SOCKET_TEST_DEPS)
+	$(Q) $(RM) bins/dualstack_socket_test
+
+
+NO_SERVER_TEST_SRC = \
+    test/core/end2end/no_server_test.c \
+
+NO_SERVER_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(NO_SERVER_TEST_SRC))))
+NO_SERVER_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(NO_SERVER_TEST_SRC))))
+
+bins/no_server_test: $(NO_SERVER_TEST_OBJS) libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(NO_SERVER_TEST_OBJS) -Llibs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/no_server_test
+
+deps_no_server_test: $(NO_SERVER_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(NO_SERVER_TEST_DEPS)
+endif
+
+clean_no_server_test:
+	$(E) "[CLEAN]   Cleaning no_server_test files"
+	$(Q) $(RM) $(NO_SERVER_TEST_OBJS)
+	$(Q) $(RM) $(NO_SERVER_TEST_DEPS)
+	$(Q) $(RM) bins/no_server_test
+
+
 RESOLVE_ADDRESS_TEST_SRC = \
     test/core/endpoint/resolve_address_test.c \
 
@@ -2107,6 +2260,29 @@
 	$(Q) $(RM) bins/resolve_address_test
 
 
+SOCKET_UTILS_TEST_SRC = \
+    test/core/endpoint/socket_utils_test.c \
+
+SOCKET_UTILS_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(SOCKET_UTILS_TEST_SRC))))
+SOCKET_UTILS_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(SOCKET_UTILS_TEST_SRC))))
+
+bins/socket_utils_test: $(SOCKET_UTILS_TEST_OBJS) libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(SOCKET_UTILS_TEST_OBJS) -Llibs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/socket_utils_test
+
+deps_socket_utils_test: $(SOCKET_UTILS_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(SOCKET_UTILS_TEST_DEPS)
+endif
+
+clean_socket_utils_test:
+	$(E) "[CLEAN]   Cleaning socket_utils_test files"
+	$(Q) $(RM) $(SOCKET_UTILS_TEST_OBJS)
+	$(Q) $(RM) $(SOCKET_UTILS_TEST_DEPS)
+	$(Q) $(RM) bins/socket_utils_test
+
+
 TCP_SERVER_TEST_SRC = \
     test/core/endpoint/tcp_server_test.c \
 
@@ -2590,6 +2766,29 @@
 	$(Q) $(RM) bins/message_compress_test
 
 
+BIN_ENCODER_TEST_SRC = \
+    test/core/transport/chttp2/bin_encoder_test.c \
+
+BIN_ENCODER_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(BIN_ENCODER_TEST_SRC))))
+BIN_ENCODER_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(BIN_ENCODER_TEST_SRC))))
+
+bins/bin_encoder_test: $(BIN_ENCODER_TEST_OBJS) libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(BIN_ENCODER_TEST_OBJS) -Llibs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/bin_encoder_test
+
+deps_bin_encoder_test: $(BIN_ENCODER_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(BIN_ENCODER_TEST_DEPS)
+endif
+
+clean_bin_encoder_test:
+	$(E) "[CLEAN]   Cleaning bin_encoder_test files"
+	$(Q) $(RM) $(BIN_ENCODER_TEST_OBJS)
+	$(Q) $(RM) $(BIN_ENCODER_TEST_DEPS)
+	$(Q) $(RM) bins/bin_encoder_test
+
+
 SECURE_ENDPOINT_TEST_SRC = \
     test/core/endpoint/secure_endpoint_test.c \
 
@@ -4667,8 +4866,382 @@
 	$(Q) $(RM) bins/chttp2_socket_pair_writes_done_hangs_with_pending_read_test
 
 
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_cancel_after_accept.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_cancel_after_accept -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_cancel_after_accept_and_writes_closed.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_cancel_after_accept_and_writes_closed -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_cancel_after_invoke.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_cancel_after_invoke -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_AFTER_INVOKE_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_cancel_before_invoke.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_cancel_before_invoke -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_BEFORE_INVOKE_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_cancel_in_a_vacuum.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_cancel_in_a_vacuum -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_CANCEL_IN_A_VACUUM_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_early_server_shutdown_finishes_inflight_calls -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_early_server_shutdown_finishes_tags.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_early_server_shutdown_finishes_tags -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_invoke_large_request.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_invoke_large_request -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_INVOKE_LARGE_REQUEST_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_max_concurrent_streams.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_max_concurrent_streams -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_MAX_CONCURRENT_STREAMS_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_no_op_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_no_op.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_no_op -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_no_op_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_no_op_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_no_op_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_no_op_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_NO_OP_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_no_op_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_ping_pong_streaming.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_ping_pong_streaming -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_PING_PONG_STREAMING_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_request_response_with_metadata_and_payload.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_request_response_with_metadata_and_payload -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_request_response_with_payload.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_request_response_with_payload -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_REQUEST_RESPONSE_WITH_PAYLOAD_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_simple_delayed_request.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_simple_delayed_request -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_DELAYED_REQUEST_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_simple_request_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_simple_request.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_simple_request -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_simple_request_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_simple_request_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_simple_request_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_simple_request_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_SIMPLE_REQUEST_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_simple_request_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_thread_stress_test.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_thread_stress_test -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_THREAD_STRESS_TEST_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test
+
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_SRC = \
+
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_OBJS = $(addprefix objs/, $(addsuffix .o, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_SRC))))
+CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_DEPS = $(addprefix deps/, $(addsuffix .dep, $(basename $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_SRC))))
+
+bins/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_OBJS) libs/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/libend2end_test_writes_done_hangs_with_pending_read.a libs/libend2end_certs.a libs/libgrpc_test_util.a libs/libgrpc.a libs/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_OBJS) -Llibs -lend2end_fixture_chttp2_socket_pair_one_byte_at_a_time -lend2end_test_writes_done_hangs_with_pending_read -lend2end_certs -lgrpc_test_util -lgrpc -lgpr $(LDLIBS) $(LDLIBS_SECURE) -o bins/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
+
+deps_chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test: $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_DEPS)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_DEPS)
+endif
+
+clean_chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test:
+	$(E) "[CLEAN]   Cleaning chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test files"
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_OBJS)
+	$(Q) $(RM) $(CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_WRITES_DONE_HANGS_WITH_PENDING_READ_TEST_DEPS)
+	$(Q) $(RM) bins/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
 
 
 
 
-.PHONY: all strip tools buildtests tests make_dirs install clean deps_libgpr clean_libgpr deps_libgrpc clean_libgrpc deps_libgrpc_test_util clean_libgrpc_test_util deps_libgrpc++ clean_libgrpc++ deps_libgrpc++_test_util clean_libgrpc++_test_util deps_libend2end_fixture_chttp2_fake_security clean_libend2end_fixture_chttp2_fake_security deps_libend2end_fixture_chttp2_fullstack clean_libend2end_fixture_chttp2_fullstack deps_libend2end_fixture_chttp2_simple_ssl_fullstack clean_libend2end_fixture_chttp2_simple_ssl_fullstack deps_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack clean_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack deps_libend2end_fixture_chttp2_socket_pair clean_libend2end_fixture_chttp2_socket_pair deps_libend2end_test_cancel_after_accept clean_libend2end_test_cancel_after_accept deps_libend2end_test_cancel_after_accept_and_writes_closed clean_libend2end_test_cancel_after_accept_and_writes_closed deps_libend2end_test_cancel_after_invoke clean_libend2end_test_cancel_after_invoke deps_libend2end_test_cancel_before_invoke clean_libend2end_test_cancel_before_invoke deps_libend2end_test_cancel_in_a_vacuum clean_libend2end_test_cancel_in_a_vacuum deps_libend2end_test_early_server_shutdown_finishes_inflight_calls clean_libend2end_test_early_server_shutdown_finishes_inflight_calls deps_libend2end_test_early_server_shutdown_finishes_tags clean_libend2end_test_early_server_shutdown_finishes_tags deps_libend2end_test_invoke_large_request clean_libend2end_test_invoke_large_request deps_libend2end_test_max_concurrent_streams clean_libend2end_test_max_concurrent_streams deps_libend2end_test_no_op clean_libend2end_test_no_op deps_libend2end_test_ping_pong_streaming clean_libend2end_test_ping_pong_streaming deps_libend2end_test_request_response_with_metadata_and_payload clean_libend2end_test_request_response_with_metadata_and_payload deps_libend2end_test_request_response_with_payload clean_libend2end_test_request_response_with_payload deps_libend2end_test_simple_delayed_request clean_libend2end_test_simple_delayed_request deps_libend2end_test_simple_request clean_libend2end_test_simple_request deps_libend2end_test_thread_stress_test clean_libend2end_test_thread_stress_test deps_libend2end_test_writes_done_hangs_with_pending_read clean_libend2end_test_writes_done_hangs_with_pending_read deps_libend2end_certs clean_libend2end_certs deps_libgrpc_unsecure clean_libgrpc_unsecure deps_gen_hpack_tables clean_gen_hpack_tables deps_grpc_byte_buffer_reader_test clean_grpc_byte_buffer_reader_test deps_gpr_cancellable_test clean_gpr_cancellable_test deps_gpr_log_test clean_gpr_log_test deps_gpr_cmdline_test clean_gpr_cmdline_test deps_gpr_histogram_test clean_gpr_histogram_test deps_gpr_host_port_test clean_gpr_host_port_test deps_gpr_slice_buffer_test clean_gpr_slice_buffer_test deps_gpr_slice_test clean_gpr_slice_test deps_gpr_string_test clean_gpr_string_test deps_gpr_sync_test clean_gpr_sync_test deps_gpr_thd_test clean_gpr_thd_test deps_gpr_time_test clean_gpr_time_test deps_murmur_hash_test clean_murmur_hash_test deps_grpc_em_test clean_grpc_em_test deps_grpc_em_pipe_test clean_grpc_em_pipe_test deps_grpc_stream_op_test clean_grpc_stream_op_test deps_chttp2_stream_encoder_test clean_chttp2_stream_encoder_test deps_hpack_table_test clean_hpack_table_test deps_chttp2_stream_map_test clean_chttp2_stream_map_test deps_hpack_parser_test clean_hpack_parser_test deps_transport_metadata_test clean_transport_metadata_test deps_chttp2_status_conversion_test clean_chttp2_status_conversion_test deps_chttp2_transport_end2end_test clean_chttp2_transport_end2end_test deps_grpc_tcp_test clean_grpc_tcp_test deps_resolve_address_test clean_resolve_address_test deps_tcp_server_test clean_tcp_server_test deps_tcp_client_test clean_tcp_client_test deps_grpc_channel_stack_test clean_grpc_channel_stack_test deps_metadata_buffer_test clean_metadata_buffer_test deps_grpc_completion_queue_test clean_grpc_completion_queue_test deps_grpc_completion_queue_benchmark clean_grpc_completion_queue_benchmark deps_census_window_stats_test clean_census_window_stats_test deps_census_statistics_quick_test clean_census_statistics_quick_test deps_census_statistics_performance_test clean_census_statistics_performance_test deps_census_statistics_multiple_writers_test clean_census_statistics_multiple_writers_test deps_census_statistics_multiple_writers_circular_buffer_test clean_census_statistics_multiple_writers_circular_buffer_test deps_census_stub_test clean_census_stub_test deps_census_hash_table_test clean_census_hash_table_test deps_fling_server clean_fling_server deps_fling_client clean_fling_client deps_fling_test clean_fling_test deps_echo_server clean_echo_server deps_echo_client clean_echo_client deps_echo_test clean_echo_test deps_low_level_ping_pong_benchmark clean_low_level_ping_pong_benchmark deps_message_compress_test clean_message_compress_test deps_secure_endpoint_test clean_secure_endpoint_test deps_httpcli_format_request_test clean_httpcli_format_request_test deps_httpcli_parser_test clean_httpcli_parser_test deps_httpcli_test clean_httpcli_test deps_grpc_credentials_test clean_grpc_credentials_test deps_fling_stream_test clean_fling_stream_test deps_lame_client_test clean_lame_client_test deps_thread_pool_test clean_thread_pool_test deps_status_test clean_status_test deps_chttp2_fake_security_cancel_after_accept_test clean_chttp2_fake_security_cancel_after_accept_test deps_chttp2_fake_security_cancel_after_accept_and_writes_closed_test clean_chttp2_fake_security_cancel_after_accept_and_writes_closed_test deps_chttp2_fake_security_cancel_after_invoke_test clean_chttp2_fake_security_cancel_after_invoke_test deps_chttp2_fake_security_cancel_before_invoke_test clean_chttp2_fake_security_cancel_before_invoke_test deps_chttp2_fake_security_cancel_in_a_vacuum_test clean_chttp2_fake_security_cancel_in_a_vacuum_test deps_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fake_security_early_server_shutdown_finishes_tags_test clean_chttp2_fake_security_early_server_shutdown_finishes_tags_test deps_chttp2_fake_security_invoke_large_request_test clean_chttp2_fake_security_invoke_large_request_test deps_chttp2_fake_security_max_concurrent_streams_test clean_chttp2_fake_security_max_concurrent_streams_test deps_chttp2_fake_security_no_op_test clean_chttp2_fake_security_no_op_test deps_chttp2_fake_security_ping_pong_streaming_test clean_chttp2_fake_security_ping_pong_streaming_test deps_chttp2_fake_security_request_response_with_metadata_and_payload_test clean_chttp2_fake_security_request_response_with_metadata_and_payload_test deps_chttp2_fake_security_request_response_with_payload_test clean_chttp2_fake_security_request_response_with_payload_test deps_chttp2_fake_security_simple_delayed_request_test clean_chttp2_fake_security_simple_delayed_request_test deps_chttp2_fake_security_simple_request_test clean_chttp2_fake_security_simple_request_test deps_chttp2_fake_security_thread_stress_test_test clean_chttp2_fake_security_thread_stress_test_test deps_chttp2_fake_security_writes_done_hangs_with_pending_read_test clean_chttp2_fake_security_writes_done_hangs_with_pending_read_test deps_chttp2_fullstack_cancel_after_accept_test clean_chttp2_fullstack_cancel_after_accept_test deps_chttp2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_fullstack_cancel_after_invoke_test clean_chttp2_fullstack_cancel_after_invoke_test deps_chttp2_fullstack_cancel_before_invoke_test clean_chttp2_fullstack_cancel_before_invoke_test deps_chttp2_fullstack_cancel_in_a_vacuum_test clean_chttp2_fullstack_cancel_in_a_vacuum_test deps_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_fullstack_invoke_large_request_test clean_chttp2_fullstack_invoke_large_request_test deps_chttp2_fullstack_max_concurrent_streams_test clean_chttp2_fullstack_max_concurrent_streams_test deps_chttp2_fullstack_no_op_test clean_chttp2_fullstack_no_op_test deps_chttp2_fullstack_ping_pong_streaming_test clean_chttp2_fullstack_ping_pong_streaming_test deps_chttp2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_fullstack_request_response_with_payload_test clean_chttp2_fullstack_request_response_with_payload_test deps_chttp2_fullstack_simple_delayed_request_test clean_chttp2_fullstack_simple_delayed_request_test deps_chttp2_fullstack_simple_request_test clean_chttp2_fullstack_simple_request_test deps_chttp2_fullstack_thread_stress_test_test clean_chttp2_fullstack_thread_stress_test_test deps_chttp2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_fullstack_no_op_test clean_chttp2_simple_ssl_fullstack_no_op_test deps_chttp2_simple_ssl_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_fullstack_simple_request_test clean_chttp2_simple_ssl_fullstack_simple_request_test deps_chttp2_simple_ssl_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test clean_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test deps_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_socket_pair_cancel_after_accept_test clean_chttp2_socket_pair_cancel_after_accept_test deps_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test clean_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test deps_chttp2_socket_pair_cancel_after_invoke_test clean_chttp2_socket_pair_cancel_after_invoke_test deps_chttp2_socket_pair_cancel_before_invoke_test clean_chttp2_socket_pair_cancel_before_invoke_test deps_chttp2_socket_pair_cancel_in_a_vacuum_test clean_chttp2_socket_pair_cancel_in_a_vacuum_test deps_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_socket_pair_early_server_shutdown_finishes_tags_test clean_chttp2_socket_pair_early_server_shutdown_finishes_tags_test deps_chttp2_socket_pair_invoke_large_request_test clean_chttp2_socket_pair_invoke_large_request_test deps_chttp2_socket_pair_max_concurrent_streams_test clean_chttp2_socket_pair_max_concurrent_streams_test deps_chttp2_socket_pair_no_op_test clean_chttp2_socket_pair_no_op_test deps_chttp2_socket_pair_ping_pong_streaming_test clean_chttp2_socket_pair_ping_pong_streaming_test deps_chttp2_socket_pair_request_response_with_metadata_and_payload_test clean_chttp2_socket_pair_request_response_with_metadata_and_payload_test deps_chttp2_socket_pair_request_response_with_payload_test clean_chttp2_socket_pair_request_response_with_payload_test deps_chttp2_socket_pair_simple_delayed_request_test clean_chttp2_socket_pair_simple_delayed_request_test deps_chttp2_socket_pair_simple_request_test clean_chttp2_socket_pair_simple_request_test deps_chttp2_socket_pair_thread_stress_test_test clean_chttp2_socket_pair_thread_stress_test_test deps_chttp2_socket_pair_writes_done_hangs_with_pending_read_test clean_chttp2_socket_pair_writes_done_hangs_with_pending_read_test
+
+
+.PHONY: all strip tools buildtests tests make_dirs install clean deps_libgpr clean_libgpr deps_libgrpc clean_libgrpc deps_libgrpc_test_util clean_libgrpc_test_util deps_libgrpc++ clean_libgrpc++ deps_libgrpc++_test_util clean_libgrpc++_test_util deps_libend2end_fixture_chttp2_fake_security clean_libend2end_fixture_chttp2_fake_security deps_libend2end_fixture_chttp2_fullstack clean_libend2end_fixture_chttp2_fullstack deps_libend2end_fixture_chttp2_simple_ssl_fullstack clean_libend2end_fixture_chttp2_simple_ssl_fullstack deps_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack clean_libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack deps_libend2end_fixture_chttp2_socket_pair clean_libend2end_fixture_chttp2_socket_pair deps_libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time clean_libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time deps_libend2end_test_cancel_after_accept clean_libend2end_test_cancel_after_accept deps_libend2end_test_cancel_after_accept_and_writes_closed clean_libend2end_test_cancel_after_accept_and_writes_closed deps_libend2end_test_cancel_after_invoke clean_libend2end_test_cancel_after_invoke deps_libend2end_test_cancel_before_invoke clean_libend2end_test_cancel_before_invoke deps_libend2end_test_cancel_in_a_vacuum clean_libend2end_test_cancel_in_a_vacuum deps_libend2end_test_early_server_shutdown_finishes_inflight_calls clean_libend2end_test_early_server_shutdown_finishes_inflight_calls deps_libend2end_test_early_server_shutdown_finishes_tags clean_libend2end_test_early_server_shutdown_finishes_tags deps_libend2end_test_invoke_large_request clean_libend2end_test_invoke_large_request deps_libend2end_test_max_concurrent_streams clean_libend2end_test_max_concurrent_streams deps_libend2end_test_no_op clean_libend2end_test_no_op deps_libend2end_test_ping_pong_streaming clean_libend2end_test_ping_pong_streaming deps_libend2end_test_request_response_with_metadata_and_payload clean_libend2end_test_request_response_with_metadata_and_payload deps_libend2end_test_request_response_with_payload clean_libend2end_test_request_response_with_payload deps_libend2end_test_simple_delayed_request clean_libend2end_test_simple_delayed_request deps_libend2end_test_simple_request clean_libend2end_test_simple_request deps_libend2end_test_thread_stress_test clean_libend2end_test_thread_stress_test deps_libend2end_test_writes_done_hangs_with_pending_read clean_libend2end_test_writes_done_hangs_with_pending_read deps_libend2end_certs clean_libend2end_certs deps_libgrpc_unsecure clean_libgrpc_unsecure deps_gen_hpack_tables clean_gen_hpack_tables deps_grpc_byte_buffer_reader_test clean_grpc_byte_buffer_reader_test deps_gpr_cancellable_test clean_gpr_cancellable_test deps_gpr_log_test clean_gpr_log_test deps_gpr_cmdline_test clean_gpr_cmdline_test deps_gpr_histogram_test clean_gpr_histogram_test deps_gpr_host_port_test clean_gpr_host_port_test deps_gpr_slice_buffer_test clean_gpr_slice_buffer_test deps_gpr_slice_test clean_gpr_slice_test deps_gpr_string_test clean_gpr_string_test deps_gpr_sync_test clean_gpr_sync_test deps_gpr_thd_test clean_gpr_thd_test deps_gpr_time_test clean_gpr_time_test deps_murmur_hash_test clean_murmur_hash_test deps_grpc_em_test clean_grpc_em_test deps_grpc_em_pipe_test clean_grpc_em_pipe_test deps_grpc_stream_op_test clean_grpc_stream_op_test deps_alpn_test clean_alpn_test deps_chttp2_stream_encoder_test clean_chttp2_stream_encoder_test deps_hpack_table_test clean_hpack_table_test deps_chttp2_stream_map_test clean_chttp2_stream_map_test deps_hpack_parser_test clean_hpack_parser_test deps_transport_metadata_test clean_transport_metadata_test deps_chttp2_status_conversion_test clean_chttp2_status_conversion_test deps_chttp2_transport_end2end_test clean_chttp2_transport_end2end_test deps_grpc_tcp_test clean_grpc_tcp_test deps_dualstack_socket_test clean_dualstack_socket_test deps_no_server_test clean_no_server_test deps_resolve_address_test clean_resolve_address_test deps_socket_utils_test clean_socket_utils_test deps_tcp_server_test clean_tcp_server_test deps_tcp_client_test clean_tcp_client_test deps_grpc_channel_stack_test clean_grpc_channel_stack_test deps_metadata_buffer_test clean_metadata_buffer_test deps_grpc_completion_queue_test clean_grpc_completion_queue_test deps_grpc_completion_queue_benchmark clean_grpc_completion_queue_benchmark deps_census_window_stats_test clean_census_window_stats_test deps_census_statistics_quick_test clean_census_statistics_quick_test deps_census_statistics_performance_test clean_census_statistics_performance_test deps_census_statistics_multiple_writers_test clean_census_statistics_multiple_writers_test deps_census_statistics_multiple_writers_circular_buffer_test clean_census_statistics_multiple_writers_circular_buffer_test deps_census_stub_test clean_census_stub_test deps_census_hash_table_test clean_census_hash_table_test deps_fling_server clean_fling_server deps_fling_client clean_fling_client deps_fling_test clean_fling_test deps_echo_server clean_echo_server deps_echo_client clean_echo_client deps_echo_test clean_echo_test deps_low_level_ping_pong_benchmark clean_low_level_ping_pong_benchmark deps_message_compress_test clean_message_compress_test deps_bin_encoder_test clean_bin_encoder_test deps_secure_endpoint_test clean_secure_endpoint_test deps_httpcli_format_request_test clean_httpcli_format_request_test deps_httpcli_parser_test clean_httpcli_parser_test deps_httpcli_test clean_httpcli_test deps_grpc_credentials_test clean_grpc_credentials_test deps_fling_stream_test clean_fling_stream_test deps_lame_client_test clean_lame_client_test deps_thread_pool_test clean_thread_pool_test deps_status_test clean_status_test deps_chttp2_fake_security_cancel_after_accept_test clean_chttp2_fake_security_cancel_after_accept_test deps_chttp2_fake_security_cancel_after_accept_and_writes_closed_test clean_chttp2_fake_security_cancel_after_accept_and_writes_closed_test deps_chttp2_fake_security_cancel_after_invoke_test clean_chttp2_fake_security_cancel_after_invoke_test deps_chttp2_fake_security_cancel_before_invoke_test clean_chttp2_fake_security_cancel_before_invoke_test deps_chttp2_fake_security_cancel_in_a_vacuum_test clean_chttp2_fake_security_cancel_in_a_vacuum_test deps_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fake_security_early_server_shutdown_finishes_tags_test clean_chttp2_fake_security_early_server_shutdown_finishes_tags_test deps_chttp2_fake_security_invoke_large_request_test clean_chttp2_fake_security_invoke_large_request_test deps_chttp2_fake_security_max_concurrent_streams_test clean_chttp2_fake_security_max_concurrent_streams_test deps_chttp2_fake_security_no_op_test clean_chttp2_fake_security_no_op_test deps_chttp2_fake_security_ping_pong_streaming_test clean_chttp2_fake_security_ping_pong_streaming_test deps_chttp2_fake_security_request_response_with_metadata_and_payload_test clean_chttp2_fake_security_request_response_with_metadata_and_payload_test deps_chttp2_fake_security_request_response_with_payload_test clean_chttp2_fake_security_request_response_with_payload_test deps_chttp2_fake_security_simple_delayed_request_test clean_chttp2_fake_security_simple_delayed_request_test deps_chttp2_fake_security_simple_request_test clean_chttp2_fake_security_simple_request_test deps_chttp2_fake_security_thread_stress_test_test clean_chttp2_fake_security_thread_stress_test_test deps_chttp2_fake_security_writes_done_hangs_with_pending_read_test clean_chttp2_fake_security_writes_done_hangs_with_pending_read_test deps_chttp2_fullstack_cancel_after_accept_test clean_chttp2_fullstack_cancel_after_accept_test deps_chttp2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_fullstack_cancel_after_invoke_test clean_chttp2_fullstack_cancel_after_invoke_test deps_chttp2_fullstack_cancel_before_invoke_test clean_chttp2_fullstack_cancel_before_invoke_test deps_chttp2_fullstack_cancel_in_a_vacuum_test clean_chttp2_fullstack_cancel_in_a_vacuum_test deps_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_fullstack_invoke_large_request_test clean_chttp2_fullstack_invoke_large_request_test deps_chttp2_fullstack_max_concurrent_streams_test clean_chttp2_fullstack_max_concurrent_streams_test deps_chttp2_fullstack_no_op_test clean_chttp2_fullstack_no_op_test deps_chttp2_fullstack_ping_pong_streaming_test clean_chttp2_fullstack_ping_pong_streaming_test deps_chttp2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_fullstack_request_response_with_payload_test clean_chttp2_fullstack_request_response_with_payload_test deps_chttp2_fullstack_simple_delayed_request_test clean_chttp2_fullstack_simple_delayed_request_test deps_chttp2_fullstack_simple_request_test clean_chttp2_fullstack_simple_request_test deps_chttp2_fullstack_thread_stress_test_test clean_chttp2_fullstack_thread_stress_test_test deps_chttp2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_fullstack_no_op_test clean_chttp2_simple_ssl_fullstack_no_op_test deps_chttp2_simple_ssl_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_fullstack_simple_request_test clean_chttp2_simple_ssl_fullstack_simple_request_test deps_chttp2_simple_ssl_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test deps_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test clean_chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test clean_chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test deps_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test clean_chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test deps_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test clean_chttp2_simple_ssl_with_oauth2_fullstack_no_op_test deps_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test clean_chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test clean_chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test clean_chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test deps_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test clean_chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test_test deps_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test clean_chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test deps_chttp2_socket_pair_cancel_after_accept_test clean_chttp2_socket_pair_cancel_after_accept_test deps_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test clean_chttp2_socket_pair_cancel_after_accept_and_writes_closed_test deps_chttp2_socket_pair_cancel_after_invoke_test clean_chttp2_socket_pair_cancel_after_invoke_test deps_chttp2_socket_pair_cancel_before_invoke_test clean_chttp2_socket_pair_cancel_before_invoke_test deps_chttp2_socket_pair_cancel_in_a_vacuum_test clean_chttp2_socket_pair_cancel_in_a_vacuum_test deps_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_socket_pair_early_server_shutdown_finishes_tags_test clean_chttp2_socket_pair_early_server_shutdown_finishes_tags_test deps_chttp2_socket_pair_invoke_large_request_test clean_chttp2_socket_pair_invoke_large_request_test deps_chttp2_socket_pair_max_concurrent_streams_test clean_chttp2_socket_pair_max_concurrent_streams_test deps_chttp2_socket_pair_no_op_test clean_chttp2_socket_pair_no_op_test deps_chttp2_socket_pair_ping_pong_streaming_test clean_chttp2_socket_pair_ping_pong_streaming_test deps_chttp2_socket_pair_request_response_with_metadata_and_payload_test clean_chttp2_socket_pair_request_response_with_metadata_and_payload_test deps_chttp2_socket_pair_request_response_with_payload_test clean_chttp2_socket_pair_request_response_with_payload_test deps_chttp2_socket_pair_simple_delayed_request_test clean_chttp2_socket_pair_simple_delayed_request_test deps_chttp2_socket_pair_simple_request_test clean_chttp2_socket_pair_simple_request_test deps_chttp2_socket_pair_thread_stress_test_test clean_chttp2_socket_pair_thread_stress_test_test deps_chttp2_socket_pair_writes_done_hangs_with_pending_read_test clean_chttp2_socket_pair_writes_done_hangs_with_pending_read_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test deps_chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test clean_chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test deps_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test clean_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test deps_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test clean_chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test deps_chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test clean_chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test deps_chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test clean_chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test deps_chttp2_socket_pair_one_byte_at_a_time_no_op_test clean_chttp2_socket_pair_one_byte_at_a_time_no_op_test deps_chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test clean_chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test deps_chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test clean_chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test deps_chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test clean_chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test deps_chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test clean_chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test deps_chttp2_socket_pair_one_byte_at_a_time_simple_request_test clean_chttp2_socket_pair_one_byte_at_a_time_simple_request_test deps_chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test clean_chttp2_socket_pair_one_byte_at_a_time_thread_stress_test_test deps_chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test clean_chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test
diff --git a/build.json b/build.json
index b869eb6..7f89652 100644
--- a/build.json
+++ b/build.json
@@ -30,6 +30,7 @@
         "src/core/support/slice_buffer.c",
         "src/core/support/string.c",
         "src/core/support/string_posix.c",
+        "src/core/support/string_win32.c",
         "src/core/support/sync.c",
         "src/core/support/sync_posix.c",
         "src/core/support/thd_posix.c",
@@ -129,13 +130,17 @@
         "src/core/surface/server_chttp2.c",
         "src/core/surface/server_create.c",
         "src/core/surface/surface_em.c",
+        "src/core/transport/chttp2/alpn.c",
+        "src/core/transport/chttp2/bin_encoder.c",
         "src/core/transport/chttp2/frame_data.c",
+        "src/core/transport/chttp2/frame_goaway.c",
         "src/core/transport/chttp2/frame_ping.c",
         "src/core/transport/chttp2/frame_rst_stream.c",
         "src/core/transport/chttp2/frame_settings.c",
         "src/core/transport/chttp2/frame_window_update.c",
         "src/core/transport/chttp2/hpack_parser.c",
         "src/core/transport/chttp2/hpack_table.c",
+        "src/core/transport/chttp2/huffsyms.c",
         "src/core/transport/chttp2/status_conversion.c",
         "src/core/transport/chttp2/stream_encoder.c",
         "src/core/transport/chttp2/stream_map.c",
@@ -221,7 +226,9 @@
         "src/core/surface/server.h",
         "src/core/surface/surface_em.h",
         "src/core/surface/surface_trace.h",
+        "src/core/transport/chttp2/bin_encoder.h",
         "src/core/transport/chttp2/frame_data.h",
+        "src/core/transport/chttp2/frame_goaway.h",
         "src/core/transport/chttp2/frame.h",
         "src/core/transport/chttp2/frame_ping.h",
         "src/core/transport/chttp2/frame_rst_stream.h",
@@ -229,6 +236,7 @@
         "src/core/transport/chttp2/frame_window_update.h",
         "src/core/transport/chttp2/hpack_parser.h",
         "src/core/transport/chttp2/hpack_table.h",
+        "src/core/transport/chttp2/huffsyms.h",
         "src/core/transport/chttp2/http2_errors.h",
         "src/core/transport/chttp2/status_conversion.h",
         "src/core/transport/chttp2/stream_encoder.h",
@@ -332,7 +340,8 @@
       ],
       "deps": [
         "grpc_test_util",
-        "gpr"
+        "gpr",
+        "grpc"
       ]
     },
 
@@ -530,6 +539,18 @@
       ]
     },
     {
+      "name": "alpn_test",
+      "build": "test",
+      "src": [
+        "test/core/transport/chttp2/alpn_test.c"
+      ],
+      "deps": [
+        "grpc_test_util",
+        "grpc",
+        "gpr"
+      ]
+    },
+    {
       "name": "chttp2_stream_encoder_test",
       "build": "test",
       "src": [
@@ -626,6 +647,30 @@
       ]
     },
     {
+      "name": "dualstack_socket_test",
+      "build": "test",
+      "src": [
+        "test/core/end2end/dualstack_socket_test.c"
+      ],
+      "deps": [
+        "grpc_test_util",
+        "grpc",
+        "gpr"
+      ]
+    },
+    {
+      "name": "no_server_test",
+      "build": "test",
+      "src": [
+        "test/core/end2end/no_server_test.c"
+      ],
+      "deps": [
+        "grpc_test_util",
+        "grpc",
+        "gpr"
+      ]
+    },
+    {
       "name": "resolve_address_test",
       "build": "test",
       "src": [
@@ -638,6 +683,18 @@
       ]
     },
     {
+      "name": "socket_utils_test",
+      "build": "test",
+      "src": [
+        "test/core/endpoint/socket_utils_test.c"
+      ],
+      "deps": [
+        "grpc_test_util",
+        "grpc",
+        "gpr"
+      ]
+    },
+    {
       "name": "tcp_server_test",
       "build": "test",
       "src": [
@@ -893,7 +950,18 @@
         "gpr"
       ]
     },
-
+    {
+      "name": "bin_encoder_test",
+      "build": "test",
+      "src": [
+        "test/core/transport/chttp2/bin_encoder_test.c"
+      ],
+      "deps": [
+        "grpc_test_util",
+        "grpc",
+        "gpr"
+      ]
+    },
     {
       "name": "secure_endpoint_test",
       "build": "test",
diff --git a/include/grpc++/async_server_context.h b/include/grpc++/async_server_context.h
index dd4097b..237a685 100644
--- a/include/grpc++/async_server_context.h
+++ b/include/grpc++/async_server_context.h
@@ -75,6 +75,8 @@
   grpc::string host() const { return host_; }
   system_clock::time_point absolute_deadline() { return absolute_deadline_; }
 
+  grpc_call* call() { return call_; }
+
  private:
   AsyncServerContext(const AsyncServerContext&);
   AsyncServerContext& operator=(const AsyncServerContext&);
diff --git a/include/grpc++/client_context.h b/include/grpc++/client_context.h
index 8301b3c..ec1a4c2 100644
--- a/include/grpc++/client_context.h
+++ b/include/grpc++/client_context.h
@@ -39,6 +39,7 @@
 #include <vector>
 
 #include <grpc++/config.h>
+#include <grpc/support/log.h>
 
 using std::chrono::system_clock;
 
@@ -69,7 +70,10 @@
   friend class StreamContext;
 
   grpc_call *call() { return call_; }
-  void set_call(grpc_call *call) { call_ = call; }
+  void set_call(grpc_call *call) {
+    GPR_ASSERT(call_ == nullptr);
+    call_ = call;
+  }
 
   grpc_completion_queue *cq() { return cq_; }
   void set_cq(grpc_completion_queue *cq) { cq_ = cq; }
diff --git a/include/grpc++/server_credentials.h b/include/grpc++/server_credentials.h
new file mode 100644
index 0000000..f758ad5
--- /dev/null
+++ b/include/grpc++/server_credentials.h
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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 __GRPCPP_SERVER_CREDENTIALS_H_
+#define __GRPCPP_SERVER_CREDENTIALS_H_
+
+#include <memory>
+
+#include <grpc++/config.h>
+
+struct grpc_server_credentials;
+
+namespace grpc {
+
+// grpc_server_credentials wrapper class.
+class ServerCredentials final {
+ public:
+  ~ServerCredentials();
+
+ private:
+  explicit ServerCredentials(grpc_server_credentials* c_creds);
+
+  grpc_server_credentials* GetRawCreds();
+
+  friend class ServerCredentialsFactory;
+
+  grpc_server_credentials* creds_;
+};
+
+// Options to create ServerCredentials with SSL
+struct SslServerCredentialsOptions {
+  grpc::string pem_root_certs;
+  grpc::string pem_private_key;
+  grpc::string pem_cert_chain;
+};
+
+// Factory for building different types of ServerCredentials
+class ServerCredentialsFactory {
+ public:
+  // Builds SSL ServerCredentials given SSL specific options
+  static std::shared_ptr<ServerCredentials> SslCredentials(
+      const SslServerCredentialsOptions& options);
+};
+
+}  // namespace grpc
+
+#endif  // __GRPCPP_SERVER_CREDENTIALS_H_
diff --git a/include/grpc++/stream.h b/include/grpc++/stream.h
index 50c2156..49f88a6 100644
--- a/include/grpc++/stream.h
+++ b/include/grpc++/stream.h
@@ -173,6 +173,58 @@
   StreamContextInterface* const context_;
 };
 
+template <class R>
+class ServerReader : public ReaderInterface<R> {
+ public:
+  explicit ServerReader(StreamContextInterface* context) : context_(context) {
+    GPR_ASSERT(context_);
+    context_->Start(true);
+  }
+
+  virtual bool Read(R* msg) { return context_->Read(msg); }
+
+ private:
+  StreamContextInterface* const context_;  // not owned
+};
+
+template <class W>
+class ServerWriter : public WriterInterface<W> {
+ public:
+  explicit ServerWriter(StreamContextInterface* context) : context_(context) {
+    GPR_ASSERT(context_);
+    context_->Start(true);
+    context_->Read(context_->request());
+  }
+
+  virtual bool Write(const W& msg) {
+    return context_->Write(const_cast<W*>(&msg), false);
+  }
+
+ private:
+  StreamContextInterface* const context_;  // not owned
+};
+
+// Server-side interface for bi-directional streaming.
+template <class W, class R>
+class ServerReaderWriter : public WriterInterface<W>,
+                           public ReaderInterface<R> {
+ public:
+  explicit ServerReaderWriter(StreamContextInterface* context)
+      : context_(context) {
+    GPR_ASSERT(context_);
+    context_->Start(true);
+  }
+
+  virtual bool Read(R* msg) { return context_->Read(msg); }
+
+  virtual bool Write(const W& msg) {
+    return context_->Write(const_cast<W*>(&msg), false);
+  }
+
+ private:
+  StreamContextInterface* const context_;  // not owned
+};
+
 }  // namespace grpc
 
 #endif  // __GRPCPP_STREAM_H__
diff --git a/include/grpc++/stream_context_interface.h b/include/grpc++/stream_context_interface.h
index eb5c092..535c004 100644
--- a/include/grpc++/stream_context_interface.h
+++ b/include/grpc++/stream_context_interface.h
@@ -55,7 +55,7 @@
   virtual const Status& Wait() = 0;
   virtual void FinishStream(const Status& status, bool send) = 0;
 
-  virtual const google::protobuf::Message* request() = 0;
+  virtual google::protobuf::Message* request() = 0;
   virtual google::protobuf::Message* response() = 0;
 };
 
diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h
index 0db59d7..27ca63e 100644
--- a/include/grpc/byte_buffer.h
+++ b/include/grpc/byte_buffer.h
@@ -39,7 +39,7 @@
 
 typedef enum { GRPC_BB_SLICE_BUFFER } grpc_byte_buffer_type;
 
-/* byte buffers are what meesages are passed in as from the public api's */
+/* byte buffers are containers for messages passed in from the public api's */
 struct grpc_byte_buffer {
   grpc_byte_buffer_type type;
   union {
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 17e155a..ac9f0d8 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -126,6 +126,8 @@
   GRPC_CALL_ERROR_NOT_ON_SERVER,
   /* this method is not available on the client */
   GRPC_CALL_ERROR_NOT_ON_CLIENT,
+  /* this method must be called before server_accept */
+  GRPC_CALL_ERROR_ALREADY_ACCEPTED,
   /* this method must be called before invoke */
   GRPC_CALL_ERROR_ALREADY_INVOKED,
   /* this method must be called after invoke */
@@ -324,7 +326,11 @@
                                        void *metadata_read_tag,
                                        void *finished_tag, gpr_uint32 flags);
 
-/* Accept an incoming RPC, binding a completion queue to it.
+/* DEPRECATED: users should use grpc_call_server_accept, and
+   grpc_call_server_end_initial_metadata instead now.
+
+
+   Accept an incoming RPC, binding a completion queue to it.
    To be called after adding metadata to the call, but before sending
    messages.
    flags is a bit-field combination of the write flags defined above.
@@ -336,6 +342,27 @@
 grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
                                  void *finished_tag, gpr_uint32 flags);
 
+/* Accept an incoming RPC, binding a completion queue to it.
+   To be called before sending or receiving messages.
+   flags is a bit-field combination of the write flags defined above.
+   REQUIRES: Can be called at most once per call.
+             Can only be called on the server.
+   Produces a GRPC_FINISHED event with finished_tag when the call has been
+       completed (there may be other events for the call pending at this
+       time) */
+grpc_call_error grpc_call_server_accept(grpc_call *call,
+                                        grpc_completion_queue *cq,
+                                        void *finished_tag);
+
+/* Accept an incoming RPC, binding a completion queue to it.
+   To be called before sending messages.
+   flags is a bit-field combination of the write flags defined above.
+   REQUIRES: Can be called at most once per call.
+             Can only be called on the server.
+             Must be called after grpc_call_server_accept */
+grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call,
+                                                      gpr_uint32 flags);
+
 /* Called by clients to cancel an RPC on the server.
    Can be called multiple times, from any thread. */
 grpc_call_error grpc_call_cancel(grpc_call *call);
@@ -350,7 +377,8 @@
              is received.
              GRPC_INVOKE_ACCEPTED must have been received by the application
              prior to calling this on the client. On the server,
-             grpc_call_accept must have been called successfully.
+             grpc_call_server_end_of_initial_metadata must have been called
+             successfully.
    Produces a GRPC_WRITE_ACCEPTED event. */
 grpc_call_error grpc_call_start_write(grpc_call *call,
                                       grpc_byte_buffer *byte_buffer, void *tag,
@@ -376,8 +404,11 @@
    result of the read.
    REQUIRES: No other reads are pending on the call. It is only safe to start
              the next read after the corresponding read event is received.
-             GRPC_INVOKE_ACCEPTED must have been received by the application
-             prior to calling this.
+             On the client:
+               GRPC_INVOKE_ACCEPTED must have been received by the application
+               prior to calling this.
+             On the server:
+               grpc_call_server_accept must be called before calling this.
    Produces a single GRPC_READ event. */
 grpc_call_error grpc_call_start_read(grpc_call *call, void *tag);
 
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index cc1d74c..737c811 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -37,6 +37,10 @@
 #include "grpc.h"
 #include "status.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* --- grpc_credentials object. ---
 
    A credentials object represents a way to authenticate a client.  */
@@ -77,6 +81,10 @@
 /* Creates a fake transport security credentials object for testing. */
 grpc_credentials *grpc_fake_transport_security_credentials_create(void);
 
+/* Creates an IAM credentials object. */
+grpc_credentials *grpc_iam_credentials_create(const char *authorization_token,
+                                              const char *authority_selector);
+
 
 /* --- Secure channel creation. --- */
 
@@ -140,4 +148,8 @@
                                        grpc_completion_queue *cq,
                                        const grpc_channel_args *args);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_SECURITY_H_ */
diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h
index 592fce6..404543d 100644
--- a/include/grpc/support/port_platform.h
+++ b/include/grpc/support/port_platform.h
@@ -53,6 +53,7 @@
 #elif defined(ANDROID) || defined(__ANDROID__)
 #define GPR_POSIX_TIME 1
 #define GPR_POSIX_SYNC 1
+#define GPR_POSIX_STRING 1
 #define GPR_POSIX_SOCKETUTILS 1
 #define GPR_ANDROID 1
 #define GPR_GCC_SYNC 1
@@ -60,6 +61,7 @@
 #elif defined(__linux__)
 #define GPR_POSIX_TIME 1
 #define GPR_POSIX_SYNC 1
+#define GPR_POSIX_STRING 1
 #define GPR_LINUX 1
 #define GPR_GCC_ATOMIC 1
 #ifdef _LP64
@@ -70,6 +72,7 @@
 #elif defined(__APPLE__)
 #define GPR_POSIX_TIME 1
 #define GPR_POSIX_SYNC 1
+#define GPR_POSIX_STRING 1
 #define GPR_POSIX_LOG 1
 #define GPR_POSIX_SOCKETUTILS 1
 #define GPR_GCC_ATOMIC 1
diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h
index 0ae1005..67cd356 100644
--- a/src/core/channel/channel_stack.h
+++ b/src/core/channel/channel_stack.h
@@ -124,9 +124,17 @@
 char *grpc_call_op_string(grpc_call_op *op);
 
 typedef enum {
-  GRPC_CHANNEL_SHUTDOWN,
+  /* send a goaway message to remote channels indicating that we are going
+     to disconnect in the future */
+  GRPC_CHANNEL_GOAWAY,
+  /* disconnect any underlying transports */
+  GRPC_CHANNEL_DISCONNECT,
+  /* transport received a new call */
   GRPC_ACCEPT_CALL,
-  GRPC_TRANSPORT_CLOSED
+  /* an underlying transport was closed */
+  GRPC_TRANSPORT_CLOSED,
+  /* an underlying transport is about to be closed */
+  GRPC_TRANSPORT_GOAWAY
 } grpc_channel_op_type;
 
 /* A single filterable operation to be performed on a channel */
@@ -142,6 +150,10 @@
       grpc_transport *transport;
       const void *transport_server_data;
     } accept_call;
+    struct {
+      grpc_status_code status;
+      gpr_slice message;
+    } goaway;
   } data;
 } grpc_channel_op;
 
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index 9056368..0ceffba 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -392,8 +392,14 @@
   /* send the message down */
   for (i = 0; i < child_count; i++) {
     child_elem = grpc_channel_stack_element(children[i], 0);
+    if (op->type == GRPC_CHANNEL_GOAWAY) {
+      gpr_slice_ref(op->data.goaway.message);
+    }
     child_elem->filter->channel_op(child_elem, op);
   }
+  if (op->type == GRPC_CHANNEL_GOAWAY) {
+    gpr_slice_unref(op->data.goaway.message);
+  }
 
   /* unmark the inflight requests */
   gpr_mu_lock(&chand->mu);
diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c
index 336472e..fb2d5ad 100644
--- a/src/core/channel/connected_channel.c
+++ b/src/core/channel/connected_channel.c
@@ -169,7 +169,11 @@
   GPR_ASSERT(elem->filter == &grpc_connected_channel_filter);
 
   switch (op->type) {
-    case GRPC_CHANNEL_SHUTDOWN:
+    case GRPC_CHANNEL_GOAWAY:
+      grpc_transport_goaway(chand->transport, op->data.goaway.status,
+                            op->data.goaway.message);
+      break;
+    case GRPC_CHANNEL_DISCONNECT:
       grpc_transport_close(chand->transport);
       break;
     default:
@@ -439,7 +443,6 @@
                  "Last message truncated; read %d bytes, expected %d",
                  calld->incoming_message.length,
                  calld->incoming_message_length);
-      return;
     }
     call_op.type = GRPC_RECV_HALF_CLOSE;
     call_op.dir = GRPC_CALL_UP;
@@ -458,6 +461,29 @@
   }
 }
 
+static void transport_goaway(void *user_data, grpc_transport *transport,
+                             grpc_status_code status, gpr_slice debug) {
+  /* transport got goaway ==> call up and handle it */
+  grpc_channel_element *elem = user_data;
+  channel_data *chand = elem->channel_data;
+  char *msg;
+  grpc_channel_op op;
+
+  GPR_ASSERT(elem->filter == &grpc_connected_channel_filter);
+  GPR_ASSERT(chand->transport == transport);
+
+  msg = gpr_hexdump((const char *)GPR_SLICE_START_PTR(debug),
+                    GPR_SLICE_LENGTH(debug), GPR_HEXDUMP_PLAINTEXT);
+  gpr_log(GPR_DEBUG, "got goaway: status=%d, message=%s", status, msg);
+  gpr_free(msg);
+
+  op.type = GRPC_TRANSPORT_GOAWAY;
+  op.dir = GRPC_CALL_UP;
+  op.data.goaway.status = status;
+  op.data.goaway.message = debug;
+  channel_op(elem, &op);
+}
+
 static void transport_closed(void *user_data, grpc_transport *transport) {
   /* transport was closed ==> call up and handle it */
   grpc_channel_element *elem = user_data;
@@ -473,7 +499,8 @@
 }
 
 const grpc_transport_callbacks connected_channel_transport_callbacks = {
-    alloc_recv_buffer, accept_stream, recv_batch, transport_closed,
+    alloc_recv_buffer, accept_stream,    recv_batch,
+    transport_goaway,  transport_closed,
 };
 
 grpc_transport_setup_result grpc_connected_channel_bind_transport(
diff --git a/src/core/endpoint/resolve_address.c b/src/core/endpoint/resolve_address.c
index aa21954..1993b9b 100644
--- a/src/core/endpoint/resolve_address.c
+++ b/src/core/endpoint/resolve_address.c
@@ -41,10 +41,12 @@
 #include <unistd.h>
 #include <string.h>
 
+#include "src/core/endpoint/socket_utils.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/string.h>
 #include <grpc/support/log.h>
 #include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 
 typedef struct {
   char *name;
@@ -119,6 +121,7 @@
   int s;
   size_t i;
   grpc_resolved_addresses *addrs = NULL;
+  const gpr_timespec start_time = gpr_now();
 
   /* parse name, splitting it into host and port parts */
   split_host_port(name, &host, &port);
@@ -160,6 +163,22 @@
     i++;
   }
 
+  /* Temporary logging, to help identify flakiness in dualstack_socket_test. */
+  {
+    const gpr_timespec delay = gpr_time_sub(gpr_now(), start_time);
+    const int delay_ms =
+        delay.tv_sec * GPR_MS_PER_SEC + delay.tv_nsec / GPR_NS_PER_MS;
+    gpr_log(GPR_INFO, "logspam: getaddrinfo(%s, %s) resolved %d addrs in %dms:",
+            host, port, addrs->naddrs, delay_ms);
+    for (i = 0; i < addrs->naddrs; i++) {
+      char *buf;
+      grpc_sockaddr_to_string(&buf, (struct sockaddr *)&addrs->addrs[i].addr,
+                              0);
+      gpr_log(GPR_INFO, "logspam:   [%d] %s", i, buf);
+      gpr_free(buf);
+    }
+  }
+
 done:
   gpr_free(host);
   gpr_free(port);
diff --git a/src/core/endpoint/secure_endpoint.c b/src/core/endpoint/secure_endpoint.c
index 4fab0fa..ecf41d7 100644
--- a/src/core/endpoint/secure_endpoint.c
+++ b/src/core/endpoint/secure_endpoint.c
@@ -50,6 +50,8 @@
   /* saved upper level callbacks and user_data. */
   grpc_endpoint_read_cb read_cb;
   void *read_user_data;
+  grpc_endpoint_write_cb write_cb;
+  void *write_user_data;
   /* saved handshaker leftover data to unprotect. */
   gpr_slice_buffer leftover_bytes;
   /* buffers for read and write */
@@ -208,6 +210,12 @@
   *end = GPR_SLICE_END_PTR(ep->write_staging_buffer);
 }
 
+static void on_write(void *data, grpc_endpoint_cb_status error) {
+  secure_endpoint *ep = data;
+  ep->write_cb(ep->write_user_data, error);
+  secure_endpoint_unref(ep);
+}
+
 static grpc_endpoint_write_status write(grpc_endpoint *secure_ep,
                                         gpr_slice *slices, size_t nslices,
                                         grpc_endpoint_write_cb cb,
@@ -219,6 +227,7 @@
   secure_endpoint *ep = (secure_endpoint *)secure_ep;
   gpr_uint8 *cur = GPR_SLICE_START_PTR(ep->write_staging_buffer);
   gpr_uint8 *end = GPR_SLICE_END_PTR(ep->write_staging_buffer);
+  grpc_endpoint_write_status status;
   GPR_ASSERT(ep->output_buffer.count == 0);
 
 #ifdef GRPC_TRACE_SECURE_TRANSPORT
@@ -295,8 +304,16 @@
   /* clear output_buffer and let the lower level handle its slices. */
   output_buffer_count = ep->output_buffer.count;
   ep->output_buffer.count = 0;
-  return grpc_endpoint_write(ep->wrapped_ep, ep->output_buffer.slices,
-                             output_buffer_count, cb, user_data, deadline);
+  ep->write_cb = cb;
+  ep->write_user_data = user_data;
+  /* Need to keep the endpoint alive across a transport */
+  secure_endpoint_ref(ep);
+  status = grpc_endpoint_write(ep->wrapped_ep, ep->output_buffer.slices,
+                               output_buffer_count, on_write, ep, deadline);
+  if (status != GRPC_ENDPOINT_WRITE_PENDING) {
+    secure_endpoint_unref(ep);
+  }
+  return status;
 }
 
 static void shutdown(grpc_endpoint *secure_ep) {
diff --git a/src/core/endpoint/socket_utils.c b/src/core/endpoint/socket_utils.c
index 9c2540b..ef160d7 100644
--- a/src/core/endpoint/socket_utils.c
+++ b/src/core/endpoint/socket_utils.c
@@ -33,6 +33,7 @@
 
 #include "src/core/endpoint/socket_utils.h"
 
+#include <arpa/inet.h>
 #include <limits.h>
 #include <fcntl.h>
 #include <netinet/in.h>
@@ -44,6 +45,11 @@
 #include <string.h>
 #include <errno.h>
 
+#include <grpc/support/host_port.h>
+#include <grpc/support/string.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+
 /* set a socket to non blocking mode */
 int grpc_set_socket_nonblocking(int fd, int non_blocking) {
   int oldflags = fcntl(fd, F_GETFL, 0);
@@ -103,3 +109,157 @@
          0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) &&
          newval == val;
 }
+
+/* This should be 0 in production, but it may be enabled for testing or
+   debugging purposes, to simulate an environment where IPv6 sockets can't
+   also speak IPv4. */
+int grpc_forbid_dualstack_sockets_for_testing = 0;
+
+static int set_socket_dualstack(int fd) {
+  if (!grpc_forbid_dualstack_sockets_for_testing) {
+    const int off = 0;
+    return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
+  } else {
+    /* Force an IPv6-only socket, for testing purposes. */
+    const int on = 1;
+    setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+    return 0;
+  }
+}
+
+int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+                                 int protocol, grpc_dualstack_mode *dsmode) {
+  int family = addr->sa_family;
+  if (family == AF_INET6) {
+    int fd = socket(family, type, protocol);
+    /* Check if we've got a valid dualstack socket. */
+    if (fd >= 0 && set_socket_dualstack(fd)) {
+      *dsmode = GRPC_DSMODE_DUALSTACK;
+      return fd;
+    }
+    /* If this isn't an IPv4 address, then return whatever we've got. */
+    if (!grpc_sockaddr_is_v4mapped(addr, NULL)) {
+      *dsmode = GRPC_DSMODE_IPV6;
+      return fd;
+    }
+    /* Fall back to AF_INET. */
+    if (fd >= 0) {
+      close(fd);
+    }
+    family = AF_INET;
+  }
+  *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
+  return socket(family, type, protocol);
+}
+
+static const gpr_uint8 kV4MappedPrefix[] = {0, 0, 0, 0, 0,    0,
+                                            0, 0, 0, 0, 0xff, 0xff};
+
+int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr,
+                              struct sockaddr_in *addr4_out) {
+  GPR_ASSERT(addr != (struct sockaddr *)addr4_out);
+  if (addr->sa_family == AF_INET6) {
+    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
+    if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix,
+               sizeof(kV4MappedPrefix)) == 0) {
+      if (addr4_out != NULL) {
+        /* Normalize ::ffff:0.0.0.0/96 to IPv4. */
+        memset(addr4_out, 0, sizeof(*addr4_out));
+        addr4_out->sin_family = AF_INET;
+        /* s6_addr32 would be nice, but it's non-standard. */
+        memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4);
+        addr4_out->sin_port = addr6->sin6_port;
+      }
+      return 1;
+    }
+  }
+  return 0;
+}
+
+int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr,
+                              struct sockaddr_in6 *addr6_out) {
+  GPR_ASSERT(addr != (struct sockaddr *)addr6_out);
+  if (addr->sa_family == AF_INET) {
+    const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
+    memset(addr6_out, 0, sizeof(*addr6_out));
+    addr6_out->sin6_family = AF_INET6;
+    memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12);
+    memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4);
+    addr6_out->sin6_port = addr4->sin_port;
+    return 1;
+  }
+  return 0;
+}
+
+int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) {
+  struct sockaddr_in addr4_normalized;
+  if (grpc_sockaddr_is_v4mapped(addr, &addr4_normalized)) {
+    addr = (struct sockaddr *)&addr4_normalized;
+  }
+  if (addr->sa_family == AF_INET) {
+    /* Check for 0.0.0.0 */
+    const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
+    if (addr4->sin_addr.s_addr != 0) {
+      return 0;
+    }
+    *port_out = ntohs(addr4->sin_port);
+    return 1;
+  } else if (addr->sa_family == AF_INET6) {
+    /* Check for :: */
+    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
+    int i;
+    for (i = 0; i < 16; i++) {
+      if (addr6->sin6_addr.s6_addr[i] != 0) {
+        return 0;
+      }
+    }
+    *port_out = ntohs(addr6->sin6_port);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
+                                  struct sockaddr_in6 *wild6_out) {
+  memset(wild4_out, 0, sizeof(*wild4_out));
+  wild4_out->sin_family = AF_INET;
+  wild4_out->sin_port = htons(port);
+
+  memset(wild6_out, 0, sizeof(*wild6_out));
+  wild6_out->sin6_family = AF_INET6;
+  wild6_out->sin6_port = htons(port);
+}
+
+int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,
+                            int normalize) {
+  const int save_errno = errno;
+  struct sockaddr_in addr_normalized;
+  char ntop_buf[INET6_ADDRSTRLEN];
+  const void *ip = NULL;
+  int port;
+  int ret;
+
+  *out = NULL;
+  if (normalize && grpc_sockaddr_is_v4mapped(addr, &addr_normalized)) {
+    addr = (const struct sockaddr *)&addr_normalized;
+  }
+  if (addr->sa_family == AF_INET) {
+    const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
+    ip = &addr4->sin_addr;
+    port = ntohs(addr4->sin_port);
+  } else if (addr->sa_family == AF_INET6) {
+    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
+    ip = &addr6->sin6_addr;
+    port = ntohs(addr6->sin6_port);
+  }
+  if (ip != NULL &&
+      inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) {
+    ret = gpr_join_host_port(out, ntop_buf, port);
+  } else {
+    ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family);
+  }
+  /* This is probably redundant, but we wouldn't want to log the wrong error. */
+  errno = save_errno;
+  return ret;
+}
diff --git a/src/core/endpoint/socket_utils.h b/src/core/endpoint/socket_utils.h
index 545d678..23fa192 100644
--- a/src/core/endpoint/socket_utils.h
+++ b/src/core/endpoint/socket_utils.h
@@ -38,6 +38,8 @@
 #include <sys/socket.h>
 
 struct sockaddr;
+struct sockaddr_in;
+struct sockaddr_in6;
 
 /* a wrapper for accept or accept4 */
 int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
@@ -55,4 +57,82 @@
 /* disable nagle */
 int grpc_set_socket_low_latency(int fd, int low_latency);
 
+/* An enum to keep track of IPv4/IPv6 socket modes.
+
+   Currently, this information is only used when a socket is first created, but
+   in the future we may wish to store it alongside the fd.  This would let calls
+   like sendto() know which family to use without asking the kernel first. */
+typedef enum grpc_dualstack_mode {
+  /* Uninitialized, or a non-IP socket. */
+  GRPC_DSMODE_NONE,
+  /* AF_INET only. */
+  GRPC_DSMODE_IPV4,
+  /* AF_INET6 only, because IPV6_V6ONLY could not be cleared. */
+  GRPC_DSMODE_IPV6,
+  /* AF_INET6, which also supports ::ffff-mapped IPv4 addresses. */
+  GRPC_DSMODE_DUALSTACK
+} grpc_dualstack_mode;
+
+/* Only tests should use this flag. */
+extern int grpc_forbid_dualstack_sockets_for_testing;
+
+/* Creates a new socket for connecting to (or listening on) an address.
+
+   If addr is AF_INET6, this creates an IPv6 socket first.  If that fails,
+   and addr is within ::ffff:0.0.0.0/96, then it automatically falls back to
+   an IPv4 socket.
+
+   If addr is AF_INET, AF_UNIX, or anything else, then this is similar to
+   calling socket() directly.
+
+   Returns an fd on success, otherwise returns -1 with errno set to the result
+   of a failed socket() call.
+
+   The *dsmode output indicates which address family was actually created.
+   The recommended way to use this is:
+   - First convert to IPv6 using grpc_sockaddr_to_v4mapped().
+   - Create the socket.
+   - If *dsmode is IPV4, use grpc_sockaddr_is_v4mapped() to convert back to
+     IPv4, so that bind() or connect() see the correct family.
+   Also, it's important to distinguish between DUALSTACK and IPV6 when
+   listening on the [::] wildcard address. */
+int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+                                 int protocol, grpc_dualstack_mode *dsmode);
+
+/* Returns true if addr is an IPv4-mapped IPv6 address within the
+   ::ffff:0.0.0.0/96 range, or false otherwise.
+
+   If addr4_out is non-NULL, the inner IPv4 address will be copied here when
+   returning true. */
+int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr,
+                              struct sockaddr_in *addr4_out);
+
+/* If addr is an AF_INET address, writes the corresponding ::ffff:0.0.0.0/96
+   address to addr6_out and returns true.  Otherwise returns false. */
+int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr,
+                              struct sockaddr_in6 *addr6_out);
+
+/* If addr is ::, 0.0.0.0, or ::ffff:0.0.0.0, writes the port number to
+   *port_out (if not NULL) and returns true, otherwise returns false. */
+int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out);
+
+/* Writes 0.0.0.0:port and [::]:port to separate sockaddrs. */
+void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
+                                  struct sockaddr_in6 *wild6_out);
+
+/* Converts a sockaddr into a newly-allocated human-readable string.
+
+   Currently, only the AF_INET and AF_INET6 families are recognized.
+   If the normalize flag is enabled, ::ffff:0.0.0.0/96 IPv6 addresses are
+   displayed as plain IPv4.
+
+   Usage is similar to gpr_asprintf: returns the number of bytes written
+   (excluding the final '\0'), and *out points to a string which must later be
+   destroyed using gpr_free().
+
+   In the unlikely event of an error, returns -1 and sets *out to NULL.
+   The existing value of errno is always preserved. */
+int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,
+                            int normalize);
+
 #endif  /* __GRPC_INTERNAL_ENDPOINT_SOCKET_UTILS_H__ */
diff --git a/src/core/endpoint/tcp.c b/src/core/endpoint/tcp.c
index 39367e8..482344d 100644
--- a/src/core/endpoint/tcp.c
+++ b/src/core/endpoint/tcp.c
@@ -250,7 +250,7 @@
 typedef struct {
   grpc_endpoint base;
   grpc_em *em;
-  grpc_em_fd em_fd;
+  grpc_em_fd *em_fd;
   int fd;
   size_t slice_size;
   gpr_refcount refcount;
@@ -277,14 +277,14 @@
 
 static void grpc_tcp_shutdown(grpc_endpoint *ep) {
   grpc_tcp *tcp = (grpc_tcp *)ep;
-  grpc_em_fd_shutdown(&tcp->em_fd);
+  grpc_em_fd_shutdown(tcp->em_fd);
 }
 
 static void grpc_tcp_unref(grpc_tcp *tcp) {
   int refcount_zero = gpr_unref(&tcp->refcount);
   if (refcount_zero) {
-    grpc_em_fd_destroy(&tcp->em_fd);
-    close(tcp->fd);
+    grpc_em_fd_destroy(tcp->em_fd);
+    gpr_free(tcp->em_fd);
     gpr_free(tcp);
   }
 }
@@ -385,7 +385,7 @@
         } else {
           /* Spurious read event, consume it here */
           slice_state_destroy(&read_state);
-          grpc_em_fd_notify_on_read(&tcp->em_fd, grpc_tcp_handle_read, tcp,
+          grpc_em_fd_notify_on_read(tcp->em_fd, grpc_tcp_handle_read, tcp,
                                     tcp->read_deadline);
         }
       } else {
@@ -422,7 +422,7 @@
   tcp->read_user_data = user_data;
   tcp->read_deadline = deadline;
   gpr_ref(&tcp->refcount);
-  grpc_em_fd_notify_on_read(&tcp->em_fd, grpc_tcp_handle_read, tcp, deadline);
+  grpc_em_fd_notify_on_read(tcp->em_fd, grpc_tcp_handle_read, tcp, deadline);
 }
 
 #define MAX_WRITE_IOVEC 16
@@ -494,7 +494,7 @@
 
   write_status = grpc_tcp_flush(tcp);
   if (write_status == GRPC_ENDPOINT_WRITE_PENDING) {
-    grpc_em_fd_notify_on_write(&tcp->em_fd, grpc_tcp_handle_write, tcp,
+    grpc_em_fd_notify_on_write(tcp->em_fd, grpc_tcp_handle_write, tcp,
                                tcp->write_deadline);
   } else {
     slice_state_destroy(&tcp->write_state);
@@ -539,7 +539,7 @@
     tcp->write_cb = cb;
     tcp->write_user_data = user_data;
     tcp->write_deadline = deadline;
-    grpc_em_fd_notify_on_write(&tcp->em_fd, grpc_tcp_handle_write, tcp,
+    grpc_em_fd_notify_on_write(tcp->em_fd, grpc_tcp_handle_write, tcp,
                                tcp->write_deadline);
   }
 
@@ -550,11 +550,12 @@
                                             grpc_tcp_write, grpc_tcp_shutdown,
                                             grpc_tcp_destroy};
 
-grpc_endpoint *grpc_tcp_create_dbg(int fd, grpc_em *em, size_t slice_size) {
+static grpc_endpoint *grpc_tcp_create_generic(grpc_em_fd *em_fd,
+                                              size_t slice_size) {
   grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp));
   tcp->base.vtable = &vtable;
-  tcp->fd = fd;
-  tcp->em = em;
+  tcp->fd = grpc_em_fd_get(em_fd);
+  tcp->em = grpc_em_fd_get_em(em_fd);
   tcp->read_cb = NULL;
   tcp->write_cb = NULL;
   tcp->read_user_data = NULL;
@@ -565,6 +566,16 @@
   slice_state_init(&tcp->write_state, NULL, 0, 0);
   /* paired with unref in grpc_tcp_destroy */
   gpr_ref_init(&tcp->refcount, 1);
-  grpc_em_fd_init(&tcp->em_fd, tcp->em, fd);
+  tcp->em_fd = em_fd;
   return &tcp->base;
 }
+
+grpc_endpoint *grpc_tcp_create_dbg(int fd, grpc_em *em, size_t slice_size) {
+  grpc_em_fd *em_fd = gpr_malloc(sizeof(grpc_em_fd));
+  grpc_em_fd_init(em_fd, em, fd);
+  return grpc_tcp_create_generic(em_fd, slice_size);
+}
+
+grpc_endpoint *grpc_tcp_create_emfd(grpc_em_fd *em_fd) {
+  return grpc_tcp_create_generic(em_fd, DEFAULT_SLICE_SIZE);
+}
diff --git a/src/core/endpoint/tcp.h b/src/core/endpoint/tcp.h
index 6507b2f..f6a2a19 100644
--- a/src/core/endpoint/tcp.h
+++ b/src/core/endpoint/tcp.h
@@ -52,4 +52,8 @@
 /* Special version for debugging slice changes */
 grpc_endpoint *grpc_tcp_create_dbg(int fd, grpc_em *em, size_t slice_size);
 
+/* Special version for handing off ownership of an existing already created
+   eventmanager fd. Must not have any outstanding callbacks. */
+grpc_endpoint *grpc_tcp_create_emfd(grpc_em_fd *em_fd);
+
 #endif  /* __GRPC_INTERNAL_ENDPOINT_TCP_H__ */
diff --git a/src/core/endpoint/tcp_client.c b/src/core/endpoint/tcp_client.c
index 01a0c3f..c6f470b 100644
--- a/src/core/endpoint/tcp_client.c
+++ b/src/core/endpoint/tcp_client.c
@@ -34,6 +34,7 @@
 #include "src/core/endpoint/tcp_client.h"
 
 #include <errno.h>
+#include <netinet/in.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -45,14 +46,12 @@
 typedef struct {
   void (*cb)(void *arg, grpc_endpoint *tcp);
   void *cb_arg;
-  grpc_em_fd fd;
+  grpc_em_fd *fd;
   gpr_timespec deadline;
 } async_connect;
 
-static int create_fd(int address_family) {
-  int fd = socket(address_family, SOCK_STREAM, 0);
+static int prepare_socket(int fd) {
   if (fd < 0) {
-    gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
     goto error;
   }
 
@@ -63,13 +62,13 @@
     goto error;
   }
 
-  return fd;
+  return 1;
 
 error:
   if (fd >= 0) {
     close(fd);
   }
-  return -1;
+  return 0;
 }
 
 static void on_writable(void *acp, grpc_em_cb_status status) {
@@ -77,8 +76,7 @@
   int so_error = 0;
   socklen_t so_error_size;
   int err;
-  int fd = grpc_em_fd_get(&ac->fd);
-  grpc_em *em = grpc_em_fd_get_em(&ac->fd);
+  int fd = grpc_em_fd_get(ac->fd);
 
   if (status == GRPC_CALLBACK_SUCCESS) {
     do {
@@ -105,7 +103,7 @@
            opened too many network connections.  The "easy" fix:
            don't do that! */
         gpr_log(GPR_ERROR, "kernel out of buffers");
-        grpc_em_fd_notify_on_write(&ac->fd, on_writable, ac, ac->deadline);
+        grpc_em_fd_notify_on_write(ac->fd, on_writable, ac, ac->deadline);
         return;
       } else {
         goto error;
@@ -122,31 +120,50 @@
 
 error:
   ac->cb(ac->cb_arg, NULL);
-  grpc_em_fd_destroy(&ac->fd);
+  grpc_em_fd_destroy(ac->fd);
+  gpr_free(ac->fd);
   gpr_free(ac);
-  close(fd);
   return;
 
 great_success:
-  grpc_em_fd_destroy(&ac->fd);
-  ac->cb(ac->cb_arg, grpc_tcp_create(fd, em));
+  ac->cb(ac->cb_arg, grpc_tcp_create_emfd(ac->fd));
   gpr_free(ac);
 }
 
 void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep),
-                             void *arg, grpc_em *em, struct sockaddr *addr,
-                             int len, gpr_timespec deadline) {
-  int fd = create_fd(addr->sa_family);
+                             void *arg, grpc_em *em,
+                             const struct sockaddr *addr, int addr_len,
+                             gpr_timespec deadline) {
+  int fd;
+  grpc_dualstack_mode dsmode;
   int err;
   async_connect *ac;
+  struct sockaddr_in6 addr6_v4mapped;
+  struct sockaddr_in addr4_copy;
 
+  /* Use dualstack sockets where available. */
+  if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+    addr = (const struct sockaddr *)&addr6_v4mapped;
+    addr_len = sizeof(addr6_v4mapped);
+  }
+
+  fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
   if (fd < 0) {
+    gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+  }
+  if (dsmode == GRPC_DSMODE_IPV4) {
+    /* If we got an AF_INET socket, map the address back to IPv4. */
+    GPR_ASSERT(grpc_sockaddr_is_v4mapped(addr, &addr4_copy));
+    addr = (struct sockaddr *)&addr4_copy;
+    addr_len = sizeof(addr4_copy);
+  }
+  if (!prepare_socket(fd)) {
     cb(arg, NULL);
     return;
   }
 
   do {
-    err = connect(fd, addr, len);
+    err = connect(fd, addr, addr_len);
   } while (err < 0 && errno == EINTR);
 
   if (err >= 0) {
@@ -165,6 +182,7 @@
   ac->cb = cb;
   ac->cb_arg = arg;
   ac->deadline = deadline;
-  grpc_em_fd_init(&ac->fd, em, fd);
-  grpc_em_fd_notify_on_write(&ac->fd, on_writable, ac, deadline);
+  ac->fd = gpr_malloc(sizeof(grpc_em_fd));
+  grpc_em_fd_init(ac->fd, em, fd);
+  grpc_em_fd_notify_on_write(ac->fd, on_writable, ac, deadline);
 }
diff --git a/src/core/endpoint/tcp_client.h b/src/core/endpoint/tcp_client.h
index 2a8b8ee..69b1b62 100644
--- a/src/core/endpoint/tcp_client.h
+++ b/src/core/endpoint/tcp_client.h
@@ -44,7 +44,8 @@
    cb with arg and the completed connection when done (or call cb with arg and
    NULL on failure) */
 void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp),
-                             void *arg, grpc_em *em, struct sockaddr *addr,
-                             int len, gpr_timespec deadline);
+                             void *arg, grpc_em *em,
+                             const struct sockaddr *addr, int addr_len,
+                             gpr_timespec deadline);
 
 #endif  /* __GRPC_INTERNAL_ENDPOINT_TCP_CLIENT_H__ */
diff --git a/src/core/endpoint/tcp_server.c b/src/core/endpoint/tcp_server.c
index 2f386ce..efd3ded 100644
--- a/src/core/endpoint/tcp_server.c
+++ b/src/core/endpoint/tcp_server.c
@@ -114,7 +114,6 @@
     server_port *sp = &s->ports[i];
     grpc_em_fd_destroy(sp->emfd);
     gpr_free(sp->emfd);
-    close(sp->fd);
   }
   gpr_free(s->ports);
   gpr_free(s);
@@ -153,11 +152,9 @@
   return s_max_accept_queue_size;
 }
 
-/* create a socket to listen with */
-static int create_listening_socket(struct sockaddr *port, int len) {
-  int fd = socket(port->sa_family, SOCK_STREAM, 0);
+/* Prepare a recently-created socket for listening. */
+static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) {
   if (fd < 0) {
-    gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
     goto error;
   }
 
@@ -169,8 +166,11 @@
     goto error;
   }
 
-  if (bind(fd, port, len) < 0) {
-    gpr_log(GPR_ERROR, "bind: %s", strerror(errno));
+  if (bind(fd, addr, addr_len) < 0) {
+    char *addr_str;
+    grpc_sockaddr_to_string(&addr_str, addr, 0);
+    gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno));
+    gpr_free(addr_str);
     goto error;
   }
 
@@ -179,13 +179,13 @@
     goto error;
   }
 
-  return fd;
+  return 1;
 
 error:
   if (fd >= 0) {
     close(fd);
   }
-  return -1;
+  return 0;
 }
 
 /* event manager callback when reads are ready */
@@ -200,6 +200,8 @@
   for (;;) {
     struct sockaddr_storage addr;
     socklen_t addrlen = sizeof(addr);
+    /* Note: If we ever decide to return this address to the user, remember to
+             strip off the ::ffff:0.0.0.0/96 prefix first. */
     int fd = grpc_accept4(sp->fd, (struct sockaddr *)&addr, &addrlen, 1, 1);
     if (fd < 0) {
       switch (errno) {
@@ -231,13 +233,12 @@
   gpr_mu_unlock(&sp->server->mu);
 }
 
-int grpc_tcp_server_add_port(grpc_tcp_server *s, struct sockaddr *port,
-                             int len) {
+static int add_socket_to_server(grpc_tcp_server *s, int fd,
+                                const struct sockaddr *addr, int addr_len) {
   server_port *sp;
-  /* create a socket */
-  int fd = create_listening_socket(port, len);
-  if (fd < 0) {
-    return -1;
+
+  if (!prepare_socket(fd, addr, addr_len)) {
+    return 0;
   }
 
   gpr_mu_lock(&s->mu);
@@ -257,11 +258,62 @@
     gpr_free(sp->emfd);
     s->nports--;
     gpr_mu_unlock(&s->mu);
-    return -1;
+    return 0;
   }
   gpr_mu_unlock(&s->mu);
 
-  return fd;
+  return 1;
+}
+
+int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr,
+                             int addr_len) {
+  int ok = 0;
+  int fd;
+  grpc_dualstack_mode dsmode;
+  struct sockaddr_in6 addr6_v4mapped;
+  struct sockaddr_in wild4;
+  struct sockaddr_in6 wild6;
+  struct sockaddr_in addr4_copy;
+  int port;
+
+  if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+    addr = (const struct sockaddr *)&addr6_v4mapped;
+    addr_len = sizeof(addr6_v4mapped);
+  }
+
+  /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
+  if (grpc_sockaddr_is_wildcard(addr, &port)) {
+    grpc_sockaddr_make_wildcards(port, &wild4, &wild6);
+
+    /* Try listening on IPv6 first. */
+    addr = (struct sockaddr *)&wild6;
+    addr_len = sizeof(wild6);
+    fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
+    ok |= add_socket_to_server(s, fd, addr, addr_len);
+    if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
+      return ok;
+    }
+
+    /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
+    addr = (struct sockaddr *)&wild4;
+    addr_len = sizeof(wild4);
+  }
+
+  fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
+  if (fd < 0) {
+    gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+  }
+  if (dsmode == GRPC_DSMODE_IPV4 &&
+      grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
+    addr = (struct sockaddr *)&addr4_copy;
+    addr_len = sizeof(addr4_copy);
+  }
+  ok |= add_socket_to_server(s, fd, addr, addr_len);
+  return ok;
+}
+
+int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index) {
+  return (0 <= index && index < s->nports) ? s->ports[index].fd : -1;
 }
 
 void grpc_tcp_server_start(grpc_tcp_server *s, grpc_tcp_server_cb cb,
diff --git a/src/core/endpoint/tcp_server.h b/src/core/endpoint/tcp_server.h
index 99cb83e..d81cdd0 100644
--- a/src/core/endpoint/tcp_server.h
+++ b/src/core/endpoint/tcp_server.h
@@ -53,11 +53,23 @@
 void grpc_tcp_server_start(grpc_tcp_server *server, grpc_tcp_server_cb cb,
                            void *cb_arg);
 
-/* Add a port to the server, returns a file descriptor on success, or <0 on
-   failure; the file descriptor remains owned by the server and will be cleaned
-   up when grpc_tcp_server_destroy is called */
-int grpc_tcp_server_add_port(grpc_tcp_server *server, struct sockaddr *port,
-                             int len);
+/* Add a port to the server, returning true on success, or false otherwise.
+
+   The :: and 0.0.0.0 wildcard addresses are treated identically, accepting
+   both IPv4 and IPv6 connections, but :: is the preferred style.  This usually
+   creates one socket, but possibly two on systems which support IPv6,
+   but not dualstack sockets.
+
+   For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */
+int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr,
+                             int addr_len);
+
+/* Returns the file descriptor of the Nth listening socket on this server,
+   or -1 if the index is out of bounds.
+
+   The file descriptor remains owned by the server, and will be cleaned
+   up when grpc_tcp_server_destroy is called. */
+int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index);
 
 void grpc_tcp_server_destroy(grpc_tcp_server *server);
 
diff --git a/src/core/eventmanager/em.c b/src/core/eventmanager/em.c
index f16473b..36f3720 100644
--- a/src/core/eventmanager/em.c
+++ b/src/core/eventmanager/em.c
@@ -537,6 +537,8 @@
   em->num_fds--;
   gpr_cv_broadcast(&em->cv);
   gpr_mu_unlock(&em->mu);
+
+  close(em_fd->fd);
 }
 
 int grpc_em_fd_get(struct grpc_em_fd *em_fd) { return em_fd->fd; }
diff --git a/src/core/eventmanager/em.h b/src/core/eventmanager/em.h
index 6f8bded..aa439d1 100644
--- a/src/core/eventmanager/em.h
+++ b/src/core/eventmanager/em.h
@@ -146,10 +146,12 @@
    initialized *em_fd.
    fd is a non-blocking file descriptor.
 
+   This takes ownership of closing fd.
+
    Requires:  *em_fd uninitialized. fd is a non-blocking file descriptor.  */
 grpc_em_error grpc_em_fd_init(grpc_em_fd *em_fd, grpc_em *em, int fd);
 
-/* Cause *em_fd no longer to be initialized.
+/* Cause *em_fd no longer to be initialized and closes the underlying fd.
    Requires: *em_fd initialized; no outstanding notify_on_read or
    notify_on_write.  */
 void grpc_em_fd_destroy(grpc_em_fd *em_fd);
diff --git a/src/core/security/auth.c b/src/core/security/auth.c
index 6480dd2..9ce0c69 100644
--- a/src/core/security/auth.c
+++ b/src/core/security/auth.c
@@ -75,7 +75,7 @@
   switch (op->type) {
     case GRPC_SEND_START: {
       grpc_credentials *channel_creds =
-          channeld->security_context->request_metadata_only_creds;
+          channeld->security_context->request_metadata_creds;
       /* TODO(jboeuf):
          Decide on the policy in this case:
          - populate both channel and call?
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
index 969a973..7ff48f9 100644
--- a/src/core/security/credentials.c
+++ b/src/core/security/credentials.c
@@ -47,12 +47,10 @@
 #include <stdio.h>
 
 /* -- Constants. -- */
-
 #define GRPC_COMPUTE_ENGINE_TOKEN_REFRESH_THRESHOLD_SECS 60
 #define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata"
 #define GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH \
   "computeMetadata/v1/instance/service-accounts/default/token"
-#define GRPC_AUTHORIZATION_METADATA_KEY "Authorization"
 
 /* -- Common. -- */
 
@@ -108,7 +106,13 @@
 void grpc_credentials_get_request_metadata(grpc_credentials *creds,
                                            grpc_credentials_metadata_cb cb,
                                            void *user_data) {
-  if (creds == NULL || !grpc_credentials_has_request_metadata(creds)) return;
+  if (creds == NULL || !grpc_credentials_has_request_metadata(creds) ||
+      creds->vtable->get_request_metadata == NULL) {
+    if (cb != NULL) {
+      cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+    }
+    return;
+  }
   creds->vtable->get_request_metadata(creds, cb, user_data);
 }
 
@@ -521,14 +525,235 @@
   return c;
 }
 
+/* -- Composite credentials. -- */
 
-/* -- Composite credentials TODO(jboeuf). -- */
+typedef struct {
+  grpc_credentials base;
+  grpc_credentials_array inner;
+} grpc_composite_credentials;
+
+typedef struct {
+  grpc_composite_credentials *composite_creds;
+  size_t creds_index;
+  grpc_mdelem **md_elems;
+  size_t num_md;
+  void *user_data;
+  grpc_credentials_metadata_cb cb;
+} grpc_composite_credentials_metadata_context;
+
+static void composite_destroy(grpc_credentials *creds) {
+  grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
+  size_t i;
+  for (i = 0; i < c->inner.num_creds; i++) {
+    grpc_credentials_unref(c->inner.creds_array[i]);
+  }
+  gpr_free(c->inner.creds_array);
+  gpr_free(creds);
+}
+
+static int composite_has_request_metadata(const grpc_credentials *creds) {
+  const grpc_composite_credentials *c =
+      (const grpc_composite_credentials *)creds;
+  size_t i;
+  for (i = 0; i < c->inner.num_creds; i++) {
+    if (grpc_credentials_has_request_metadata(c->inner.creds_array[i])) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static int composite_has_request_metadata_only(const grpc_credentials *creds) {
+  const grpc_composite_credentials *c =
+      (const grpc_composite_credentials *)creds;
+  size_t i;
+  for (i = 0; i < c->inner.num_creds; i++) {
+    if (!grpc_credentials_has_request_metadata_only(c->inner.creds_array[i])) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+static void composite_md_context_destroy(
+    grpc_composite_credentials_metadata_context *ctx) {
+  size_t i;
+  for (i = 0; i < ctx->num_md; i++) {
+    grpc_mdelem_unref(ctx->md_elems[i]);
+  }
+  gpr_free(ctx->md_elems);
+  gpr_free(ctx);
+}
+
+static void composite_metadata_cb(void *user_data, grpc_mdelem **md_elems,
+                                  size_t num_md,
+                                  grpc_credentials_status status) {
+  grpc_composite_credentials_metadata_context *ctx =
+      (grpc_composite_credentials_metadata_context *)user_data;
+  size_t i;
+  if (status != GRPC_CREDENTIALS_OK) {
+    ctx->cb(ctx->user_data, NULL, 0, status);
+    return;
+  }
+
+  /* Copy the metadata in the context. */
+  if (num_md > 0) {
+    ctx->md_elems = gpr_realloc(ctx->md_elems,
+                                (ctx->num_md + num_md) * sizeof(grpc_mdelem *));
+    for (i = 0; i < num_md; i++) {
+      ctx->md_elems[i + ctx->num_md] = grpc_mdelem_ref(md_elems[i]);
+    }
+    ctx->num_md += num_md;
+  }
+
+  /* See if we need to get some more metadata. */
+  while (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
+    grpc_credentials *inner_creds =
+        ctx->composite_creds->inner.creds_array[ctx->creds_index++];
+    if (grpc_credentials_has_request_metadata(inner_creds)) {
+      grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
+                                            ctx);
+      return;
+    }
+  }
+
+  /* We're done!. */
+  ctx->cb(ctx->user_data, ctx->md_elems, ctx->num_md, GRPC_CREDENTIALS_OK);
+  composite_md_context_destroy(ctx);
+}
+
+static void composite_get_request_metadata(grpc_credentials *creds,
+                                           grpc_credentials_metadata_cb cb,
+                                           void *user_data) {
+  grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
+  grpc_composite_credentials_metadata_context *ctx;
+  if (!grpc_credentials_has_request_metadata(creds)) {
+    cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+    return;
+  }
+  ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context));
+  memset(ctx, 0, sizeof(grpc_composite_credentials_metadata_context));
+  ctx->user_data = user_data;
+  ctx->cb = cb;
+  ctx->composite_creds = c;
+  while (ctx->creds_index < c->inner.num_creds) {
+    grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++];
+    if (grpc_credentials_has_request_metadata(inner_creds)) {
+      grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
+                                            ctx);
+      return;
+    }
+  }
+  GPR_ASSERT(0); /* Should have exited before. */
+}
+
+static grpc_credentials_vtable composite_credentials_vtable = {
+    composite_destroy, composite_has_request_metadata,
+    composite_has_request_metadata_only, composite_get_request_metadata};
+
+static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
+  grpc_credentials_array result;
+  grpc_credentials *creds = *creds_addr;
+  result.creds_array = creds_addr;
+  result.num_creds = 1;
+  if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
+    result = *grpc_composite_credentials_get_credentials(creds);
+  }
+  return result;
+}
 
 grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
                                                     grpc_credentials *creds2) {
-  return NULL;
+  size_t i;
+  grpc_credentials_array creds1_array;
+  grpc_credentials_array creds2_array;
+  grpc_composite_credentials *c;
+  GPR_ASSERT(creds1 != NULL);
+  GPR_ASSERT(creds2 != NULL);
+  c = gpr_malloc(sizeof(grpc_composite_credentials));
+  memset(c, 0, sizeof(grpc_composite_credentials));
+  c->base.type = GRPC_CREDENTIALS_TYPE_COMPOSITE;
+  c->base.vtable = &composite_credentials_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  creds1_array = get_creds_array(&creds1);
+  creds2_array = get_creds_array(&creds2);
+  c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
+  c->inner.creds_array =
+      gpr_malloc(c->inner.num_creds * sizeof(grpc_credentials *));
+  for (i = 0; i < creds1_array.num_creds; i++) {
+    c->inner.creds_array[i] = grpc_credentials_ref(creds1_array.creds_array[i]);
+  }
+  for (i = 0; i < creds2_array.num_creds; i++) {
+    c->inner.creds_array[i + creds1_array.num_creds] =
+        grpc_credentials_ref(creds2_array.creds_array[i]);
+  }
+  return &c->base;
+}
+
+const grpc_credentials_array *grpc_composite_credentials_get_credentials(
+    grpc_credentials *creds) {
+  const grpc_composite_credentials *c =
+      (const grpc_composite_credentials *)creds;
+  GPR_ASSERT(!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
+  return &c->inner;
+}
+
+/* -- IAM credentials. -- */
+
+typedef struct {
+  grpc_credentials base;
+  grpc_mdctx *md_ctx;
+  grpc_mdelem *token_md;
+  grpc_mdelem *authority_selector_md;
+} grpc_iam_credentials;
+
+static void iam_destroy(grpc_credentials *creds) {
+  grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
+  grpc_mdelem_unref(c->token_md);
+  grpc_mdelem_unref(c->authority_selector_md);
+  grpc_mdctx_orphan(c->md_ctx);
+  gpr_free(c);
+}
+
+static int iam_has_request_metadata(const grpc_credentials *creds) { return 1; }
+
+static int iam_has_request_metadata_only(const grpc_credentials *creds) {
+  return 1;
+}
+
+static void iam_get_request_metadata(grpc_credentials *creds,
+                                     grpc_credentials_metadata_cb cb,
+                                     void *user_data) {
+  grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
+  grpc_mdelem *md_array[2];
+  md_array[0] = c->token_md;
+  md_array[1] = c->authority_selector_md;
+  cb(user_data, md_array, 2, GRPC_CREDENTIALS_OK);
+}
+
+static grpc_credentials_vtable iam_vtable = {
+    iam_destroy, iam_has_request_metadata, iam_has_request_metadata_only,
+    iam_get_request_metadata};
+
+grpc_credentials *grpc_iam_credentials_create(const char *token,
+                                              const char *authority_selector) {
+  grpc_iam_credentials *c;
+  GPR_ASSERT(token != NULL);
+  GPR_ASSERT(authority_selector != NULL);
+  c = gpr_malloc(sizeof(grpc_iam_credentials));
+  memset(c, 0, sizeof(grpc_iam_credentials));
+  c->base.type = GRPC_CREDENTIALS_TYPE_IAM;
+  c->base.vtable = &iam_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->md_ctx = grpc_mdctx_create();
+  c->token_md = grpc_mdelem_from_strings(
+      c->md_ctx, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
+  c->authority_selector_md = grpc_mdelem_from_strings(
+      c->md_ctx, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
+  return &c->base;
 }
 
 /* -- Default credentials TODO(jboeuf). -- */
 
 grpc_credentials *grpc_default_credentials_create(void) { return NULL; }
+
diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h
index 1432611..9fb82e1 100644
--- a/src/core/security/credentials.h
+++ b/src/core/security/credentials.h
@@ -50,9 +50,15 @@
 
 #define GRPC_CREDENTIALS_TYPE_SSL "Ssl"
 #define GRPC_CREDENTIALS_TYPE_OAUTH2 "Oauth2"
+#define GRPC_CREDENTIALS_TYPE_IAM "Iam"
 #define GRPC_CREDENTIALS_TYPE_COMPOSITE "Composite"
 #define GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY "FakeTransportSecurity"
 
+#define GRPC_AUTHORIZATION_METADATA_KEY "Authorization"
+#define GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY \
+  "x-goog-iam-authorization-token"
+#define GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY "x-goog-iam-authority-selector"
+
 /* --- grpc_credentials. --- */
 
 typedef void (*grpc_credentials_metadata_cb)(void *user_data,
@@ -94,6 +100,14 @@
 const grpc_ssl_config *grpc_ssl_credentials_get_config(
     const grpc_credentials *ssl_creds);
 
+typedef struct {
+  grpc_credentials **creds_array;
+  size_t num_creds;
+} grpc_credentials_array;
+
+const grpc_credentials_array *grpc_composite_credentials_get_credentials(
+    grpc_credentials *composite_creds);
+
 /* Exposed for testing only. */
 grpc_credentials_status grpc_compute_engine_credentials_parse_server_response(
     const struct grpc_httpcli_response *response, grpc_mdctx *ctx,
diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c
index beda64c..c56692a 100644
--- a/src/core/security/security_context.c
+++ b/src/core/security/security_context.c
@@ -37,6 +37,8 @@
 
 #include "src/core/endpoint/secure_endpoint.h"
 #include "src/core/security/credentials.h"
+#include "src/core/surface/lame_client.h"
+#include "src/core/transport/chttp2/alpn.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice_buffer.h>
@@ -47,7 +49,6 @@
 
 /* -- Constants. -- */
 
-#define GRPC_ALPN_PROTOCOL_STRING "h2-15"
 /* Defines the cipher suites that we accept. All these cipher suites are
    compliant with TLS 1.2 and use an RSA public key. We prefer GCM over CBC
    and ECDHE-RSA over just RSA. */
@@ -122,11 +123,11 @@
   return NULL;
 }
 
-static int check_request_metadata_only_creds(grpc_credentials *creds) {
-  if (creds != NULL && !grpc_credentials_has_request_metadata_only(creds)) {
+static int check_request_metadata_creds(grpc_credentials *creds) {
+  if (creds != NULL && !grpc_credentials_has_request_metadata(creds)) {
     gpr_log(GPR_ERROR,
             "Incompatible credentials for channel security context: needs to "
-            "only set request metadata.");
+            "set request metadata.");
     return 0;
   }
   return 1;
@@ -136,7 +137,7 @@
 
 static void fake_channel_destroy(grpc_security_context *ctx) {
   grpc_channel_security_context *c = (grpc_channel_security_context *)ctx;
-  grpc_credentials_unref(c->request_metadata_only_creds);
+  grpc_credentials_unref(c->request_metadata_creds);
   gpr_free(ctx);
 }
 
@@ -191,15 +192,14 @@
     fake_server_destroy, fake_server_create_handshaker, fake_check_peer};
 
 grpc_channel_security_context *grpc_fake_channel_security_context_create(
-    grpc_credentials *request_metadata_only_creds) {
+    grpc_credentials *request_metadata_creds) {
   grpc_channel_security_context *c =
       gpr_malloc(sizeof(grpc_channel_security_context));
   gpr_ref_init(&c->base.refcount, 1);
   c->base.is_client_side = 1;
   c->base.vtable = &fake_channel_vtable;
-  GPR_ASSERT(check_request_metadata_only_creds(request_metadata_only_creds));
-  c->request_metadata_only_creds =
-      grpc_credentials_ref(request_metadata_only_creds);
+  GPR_ASSERT(check_request_metadata_creds(request_metadata_creds));
+  c->request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
   return c;
 }
 
@@ -226,7 +226,7 @@
 static void ssl_channel_destroy(grpc_security_context *ctx) {
   grpc_ssl_channel_security_context *c =
       (grpc_ssl_channel_security_context *)ctx;
-  grpc_credentials_unref(c->base.request_metadata_only_creds);
+  grpc_credentials_unref(c->base.request_metadata_creds);
   if (c->handshaker_factory != NULL) {
     tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
   }
@@ -282,8 +282,8 @@
     gpr_log(GPR_ERROR, "Invalid or missing selected ALPN property.");
     return GRPC_SECURITY_ERROR;
   }
-  if (strncmp(GRPC_ALPN_PROTOCOL_STRING, p->value.string.data,
-              p->value.string.length)) {
+  if (!grpc_chttp2_is_alpn_version_supported(p->value.string.data,
+                                             p->value.string.length)) {
     gpr_log(GPR_ERROR, "Invalid ALPN value.");
     return GRPC_SECURITY_ERROR;
   }
@@ -320,10 +320,9 @@
     ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer};
 
 grpc_security_status grpc_ssl_channel_security_context_create(
-    grpc_credentials *request_metadata_only_creds,
-    const grpc_ssl_config *config, const char *secure_peer_name,
-    grpc_channel_security_context **ctx) {
-  const char *alpn_protocol_string = GRPC_ALPN_PROTOCOL_STRING;
+    grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
+    const char *secure_peer_name, grpc_channel_security_context **ctx) {
+  const char *alpn_protocol_string = GRPC_CHTTP2_ALPN_VERSION;
   unsigned char alpn_protocol_string_len =
       (unsigned char)strlen(alpn_protocol_string);
   tsi_result result = TSI_OK;
@@ -334,7 +333,7 @@
     gpr_log(GPR_ERROR, "An ssl channel needs a secure name and root certs.");
     return GRPC_SECURITY_ERROR;
   }
-  if (!check_request_metadata_only_creds(request_metadata_only_creds)) {
+  if (!check_request_metadata_creds(request_metadata_creds)) {
     return GRPC_SECURITY_ERROR;
   }
 
@@ -344,8 +343,7 @@
   gpr_ref_init(&c->base.base.refcount, 1);
   c->base.base.vtable = &ssl_channel_vtable;
   c->base.base.is_client_side = 1;
-  c->base.request_metadata_only_creds =
-      grpc_credentials_ref(request_metadata_only_creds);
+  c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
   if (secure_peer_name != NULL) {
     c->secure_peer_name = gpr_strdup(secure_peer_name);
   }
@@ -368,7 +366,7 @@
 
 grpc_security_status grpc_ssl_server_security_context_create(
     const grpc_ssl_config *config, grpc_security_context **ctx) {
-  const char *alpn_protocol_string = GRPC_ALPN_PROTOCOL_STRING;
+  const char *alpn_protocol_string = GRPC_CHTTP2_ALPN_VERSION;
   unsigned char alpn_protocol_string_len =
       (unsigned char)strlen(alpn_protocol_string);
   tsi_result result = TSI_OK;
@@ -427,7 +425,7 @@
   status = grpc_ssl_channel_security_context_create(creds, config,
                                                     secure_peer_name, &ctx);
   if (status != GRPC_SECURITY_OK) {
-    return NULL; /* TODO(ctiller): return lame channel. */
+    return grpc_lame_client_channel_create();
   }
   channel = grpc_secure_channel_create_internal(target, args, ctx);
   grpc_security_context_unref(&ctx->base);
@@ -435,13 +433,38 @@
 }
 
 
+static grpc_credentials *get_creds_from_composite(
+    grpc_credentials *composite_creds, const char *type) {
+  size_t i;
+  const grpc_credentials_array *inner_creds_array =
+      grpc_composite_credentials_get_credentials(composite_creds);
+  for (i = 0; i < inner_creds_array->num_creds; i++) {
+    if (!strcmp(type, inner_creds_array->creds_array[i]->type)) {
+      return inner_creds_array->creds_array[i];
+    }
+  }
+  return NULL;
+}
+
+static grpc_channel *grpc_channel_create_from_composite_creds(
+    grpc_credentials *composite_creds, const char *target,
+    const grpc_channel_args *args) {
+  grpc_credentials *creds =
+      get_creds_from_composite(composite_creds, GRPC_CREDENTIALS_TYPE_SSL);
+  if (creds != NULL) {
+    return grpc_ssl_channel_create(
+        composite_creds, grpc_ssl_credentials_get_config(creds), target, args);
+  }
+  return NULL; /* TODO(ctiller): return lame channel. */
+}
+
 grpc_channel *grpc_secure_channel_create(grpc_credentials *creds,
                                          const char *target,
                                          const grpc_channel_args *args) {
   if (grpc_credentials_has_request_metadata_only(creds)) {
     gpr_log(GPR_ERROR,
             "Credentials is insufficient to create a secure channel.");
-    return NULL; /* TODO(ctiller): return lame channel. */
+    return grpc_lame_client_channel_create();
   }
   if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
     return grpc_ssl_channel_create(NULL, grpc_ssl_credentials_get_config(creds),
@@ -455,11 +478,11 @@
     grpc_security_context_unref(&ctx->base);
     return channel;
   } else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
-    return NULL; /* TODO(jboeuf) Implement. */
+    return grpc_channel_create_from_composite_creds(creds, target, args);
   } else {
     gpr_log(GPR_ERROR,
             "Unknown credentials type %s for creating a secure channel.");
-    return NULL; /* TODO(ctiller): return lame channel. */
+    return grpc_lame_client_channel_create();
   }
 }
 
diff --git a/src/core/security/security_context.h b/src/core/security/security_context.h
index 59c9bbd..0c60256 100644
--- a/src/core/security/security_context.h
+++ b/src/core/security/security_context.h
@@ -119,7 +119,7 @@
 
 struct grpc_channel_security_context {
   grpc_security_context base;  /* requires is_client_side to be non 0. */
-  grpc_credentials *request_metadata_only_creds;
+  grpc_credentials *request_metadata_creds;
 };
 
 /* --- Creation security contexts. --- */
@@ -127,14 +127,14 @@
 /* For TESTING ONLY!
    Creates a fake context that emulates real channel security.  */
 grpc_channel_security_context *grpc_fake_channel_security_context_create(
-    grpc_credentials *request_metadata_only_creds);
+    grpc_credentials *request_metadata_creds);
 
 /* For TESTING ONLY!
    Creates a fake context that emulates real server security.  */
 grpc_security_context *grpc_fake_server_security_context_create(void);
 
 /* Creates an SSL channel_security_context.
-   - request_metadata_only_creds is the credentials object which metadata
+   - request_metadata_creds is the credentials object which metadata
      will be sent with each request. This parameter can be NULL.
    - config is the SSL config to be used for the SSL channel establishment.
    - is_client should be 0 for a server or a non-0 value for a client.
@@ -147,9 +147,8 @@
   specific error code otherwise.
 */
 grpc_security_status grpc_ssl_channel_security_context_create(
-    grpc_credentials *request_metadata_only_creds,
-    const grpc_ssl_config *config, const char *secure_peer_name,
-    grpc_channel_security_context **ctx);
+    grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
+    const char *secure_peer_name, grpc_channel_security_context **ctx);
 
 /* Creates an SSL server_security_context.
    - config is the SSL config to be used for the SSL channel establishment.
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index bce27ec..335d502 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -109,7 +109,7 @@
   for (i = 0; i < resolved->naddrs; i++) {
     if (grpc_tcp_server_add_port(tcp,
                                  (struct sockaddr *)&resolved->addrs[i].addr,
-                                 resolved->addrs[i].len) >= 0) {
+                                 resolved->addrs[i].len)) {
       count++;
     }
   }
diff --git a/src/core/support/cpu_posix.c b/src/core/support/cpu_posix.c
index 82d58de..7f9cb8b 100644
--- a/src/core/support/cpu_posix.c
+++ b/src/core/support/cpu_posix.c
@@ -34,8 +34,6 @@
 #include "src/core/support/cpu.h"
 
 #ifdef __linux__
-#include <errno.h>
-#include <unistd.h>
 #define _GNU_SOURCE
 #define __USE_GNU
 #define __USE_MISC
@@ -43,6 +41,9 @@
 #undef _GNU_SOURCE
 #undef __USE_GNU
 #undef __USE_MISC
+
+#include <errno.h>
+#include <unistd.h>
 #include <string.h>
 
 #include <grpc/support/log.h>
diff --git a/src/core/support/string_posix.c b/src/core/support/string_posix.c
index d1da379..7b7e82e 100644
--- a/src/core/support/string_posix.c
+++ b/src/core/support/string_posix.c
@@ -37,6 +37,10 @@
 #define _POSIX_C_SOURCE 200112L
 #endif
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_STRING
+
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
@@ -84,3 +88,5 @@
   *strp = NULL;
   return -1;
 }
+
+#endif /* GPR_POSIX_STRING */
diff --git a/src/core/support/string_win32.c b/src/core/support/string_win32.c
new file mode 100644
index 0000000..74b1028
--- /dev/null
+++ b/src/core/support/string_win32.c
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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 snprintf support. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+int gpr_asprintf(char **strp, const char *format, ...) {
+  va_list args;
+  int ret;
+  size_t strp_buflen;
+
+  /* Determine the length. */
+  va_start(args, format);
+  ret = vscprintf(format, args);
+  va_end(args);
+  if (!(0 <= ret && ret < ~(size_t)0)) {
+    *strp = NULL;
+    return -1;
+  }
+
+  /* Allocate a new buffer, with space for the NUL terminator. */
+  strp_buflen = (size_t)ret + 1;
+  if ((*strp = gpr_malloc(strp_buflen)) == NULL) {
+    /* This shouldn't happen, because gpr_malloc() calls abort(). */
+    return -1;
+  }
+
+  /* Print to the buffer. */
+  va_start(args, format);
+  ret = vsnprintf_s(*strp, strp_buflen, _TRUNCATE, format, args);
+  va_end(args);
+  if (ret == strp_buflen - 1) {
+    return ret;
+  }
+
+  /* This should never happen. */
+  gpr_free(*strp);
+  *strp = NULL;
+  return -1;
+}
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/support/thd_posix.c b/src/core/support/thd_posix.c
index c86eea4..1189e0c 100644
--- a/src/core/support/thd_posix.c
+++ b/src/core/support/thd_posix.c
@@ -33,6 +33,10 @@
 
 /* Posix implementation for gpr threads. */
 
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SYNC
+
 #include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
@@ -76,3 +80,5 @@
   memset(&options, 0, sizeof(options));
   return options;
 }
+
+#endif /* GPR_POSIX_SYNC */
diff --git a/src/core/support/thd_win32.c b/src/core/support/thd_win32.c
index 8440479..1762f87 100644
--- a/src/core/support/thd_win32.c
+++ b/src/core/support/thd_win32.c
@@ -48,7 +48,7 @@
 };
 
 /* Body of every thread started via gpr_thd_new. */
-static DWORD thread_body(void *v) {
+static DWORD WINAPI thread_body(void *v) {
   struct thd_arg a = *(struct thd_arg *)v;
   gpr_free(v);
   (*a.body)(a.arg);
@@ -62,7 +62,7 @@
   a->body = thd_body;
   a->arg = arg;
   *t = 0;
-  handle = CreateThread(NULL, 64 * 1024, &thread_body, a, 0, NULL);
+  handle = CreateThread(NULL, 64 * 1024, thread_body, a, 0, NULL);
   if (handle == NULL) {
     gpr_free(a);
   } else {
diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c
index e7b79d1..78d4c3b 100644
--- a/src/core/support/time_posix.c
+++ b/src/core/support/time_posix.c
@@ -37,6 +37,11 @@
 #ifndef _POSIX_C_SOURCE
 #define _POSIX_C_SOURCE 199309L
 #endif
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_TIME
+
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
@@ -79,3 +84,5 @@
     }
   }
 }
+
+#endif /* GPR_POSIX_TIME */
diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c
index 4258091..d9abe2d 100644
--- a/src/core/support/time_win32.c
+++ b/src/core/support/time_win32.c
@@ -38,12 +38,12 @@
 #ifdef GPR_WIN32
 
 #include <grpc/support/time.h>
-#include <windows.h>
+#include <sys/timeb.h>
 
 gpr_timespec gpr_now(void) {
   gpr_timespec now_tv;
-  struct _timeb64 now_tb;
-  _ftime64(&now_tb);
+  struct __timeb64 now_tb;
+  _ftime64_s(&now_tb);
   now_tv.tv_sec = now_tb.time;
   now_tv.tv_nsec = now_tb.millitm * 1000000;
   return now_tv;
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 02c224d..a731c7c 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -154,7 +154,12 @@
 
 /* the state of a call, based upon which functions have been called against
    said call */
-typedef enum { CALL_CREATED, CALL_STARTED, CALL_FINISHED } call_state;
+typedef enum {
+  CALL_CREATED,
+  CALL_BOUNDCQ,
+  CALL_STARTED,
+  CALL_FINISHED
+} call_state;
 
 struct grpc_call {
   grpc_completion_queue *cq;
@@ -404,24 +409,18 @@
   return GRPC_CALL_OK;
 }
 
-grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
-                                 void *finished_tag, gpr_uint32 flags) {
-  grpc_call_element *elem;
-  grpc_call_op op;
-
+grpc_call_error grpc_call_server_accept(grpc_call *call,
+                                        grpc_completion_queue *cq,
+                                        void *finished_tag) {
   /* validate preconditions */
   if (call->is_client) {
     gpr_log(GPR_ERROR, "can only call %s on servers", __FUNCTION__);
     return GRPC_CALL_ERROR_NOT_ON_CLIENT;
   }
 
-  if (call->state >= CALL_STARTED) {
-    gpr_log(GPR_ERROR, "call is already invoked");
-    return GRPC_CALL_ERROR_ALREADY_INVOKED;
-  }
-
-  if (flags & GRPC_WRITE_NO_COMPRESS) {
-    return GRPC_CALL_ERROR_INVALID_FLAGS;
+  if (call->state >= CALL_BOUNDCQ) {
+    gpr_log(GPR_ERROR, "call is already accepted");
+    return GRPC_CALL_ERROR_ALREADY_ACCEPTED;
   }
 
   /* inform the completion queue of an incoming operation (corresponding to
@@ -430,7 +429,7 @@
 
   /* update state */
   gpr_mu_lock(&call->read_mu);
-  call->state = CALL_STARTED;
+  call->state = CALL_BOUNDCQ;
   call->cq = cq;
   call->finished_tag = finished_tag;
   if (prq_is_empty(&call->prq) && call->received_finish) {
@@ -442,6 +441,32 @@
   }
   gpr_mu_unlock(&call->read_mu);
 
+  return GRPC_CALL_OK;
+}
+
+grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call,
+                                                      gpr_uint32 flags) {
+  grpc_call_element *elem;
+  grpc_call_op op;
+
+  /* validate preconditions */
+  if (call->is_client) {
+    gpr_log(GPR_ERROR, "can only call %s on servers", __FUNCTION__);
+    return GRPC_CALL_ERROR_NOT_ON_CLIENT;
+  }
+
+  if (call->state >= CALL_STARTED) {
+    gpr_log(GPR_ERROR, "call is already started");
+    return GRPC_CALL_ERROR_ALREADY_INVOKED;
+  }
+
+  if (flags & GRPC_WRITE_NO_COMPRESS) {
+    return GRPC_CALL_ERROR_INVALID_FLAGS;
+  }
+
+  /* update state */
+  call->state = CALL_STARTED;
+
   /* call down */
   op.type = GRPC_SEND_START;
   op.dir = GRPC_CALL_DOWN;
@@ -455,6 +480,17 @@
   return GRPC_CALL_OK;
 }
 
+grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
+                                 void *finished_tag, gpr_uint32 flags) {
+  grpc_call_error err;
+
+  err = grpc_call_server_accept(call, cq, finished_tag);
+  if (err != GRPC_CALL_OK) return err;
+  err = grpc_call_server_end_initial_metadata(call, flags);
+  if (err != GRPC_CALL_OK) return err;
+  return GRPC_CALL_OK;
+}
+
 static void done_writes_done(void *user_data, grpc_op_error error) {
   grpc_call *call = user_data;
   void *tag = call->write_tag;
@@ -515,6 +551,7 @@
   switch (call->state) {
     case CALL_CREATED:
       return GRPC_CALL_ERROR_NOT_INVOKED;
+    case CALL_BOUNDCQ:
     case CALL_STARTED:
       break;
     case CALL_FINISHED:
@@ -559,6 +596,7 @@
 
   switch (call->state) {
     case CALL_CREATED:
+    case CALL_BOUNDCQ:
       return GRPC_CALL_ERROR_NOT_INVOKED;
     case CALL_STARTED:
       break;
@@ -607,6 +645,7 @@
 
   switch (call->state) {
     case CALL_CREATED:
+    case CALL_BOUNDCQ:
       return GRPC_CALL_ERROR_NOT_INVOKED;
     case CALL_FINISHED:
       return GRPC_CALL_ERROR_ALREADY_FINISHED;
@@ -646,6 +685,7 @@
 
   switch (call->state) {
     case CALL_CREATED:
+    case CALL_BOUNDCQ:
       return GRPC_CALL_ERROR_NOT_INVOKED;
     case CALL_FINISHED:
       return GRPC_CALL_ERROR_ALREADY_FINISHED;
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index ff99425..dfb47b3 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -127,9 +127,16 @@
   grpc_channel_op op;
   grpc_channel_element *elem;
 
-  op.type = GRPC_CHANNEL_SHUTDOWN;
-  op.dir = GRPC_CALL_DOWN;
   elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
+
+  op.type = GRPC_CHANNEL_GOAWAY;
+  op.dir = GRPC_CALL_DOWN;
+  op.data.goaway.status = GRPC_STATUS_OK;
+  op.data.goaway.message = gpr_slice_from_copied_string("Client disconnect");
+  elem->filter->channel_op(elem, &op);
+
+  op.type = GRPC_CHANNEL_DISCONNECT;
+  op.dir = GRPC_CALL_DOWN;
   elem->filter->channel_op(elem, &op);
 
   grpc_channel_internal_unref(channel);
diff --git a/src/core/surface/client.c b/src/core/surface/client.c
index 26abffa..f78a45f 100644
--- a/src/core/surface/client.c
+++ b/src/core/surface/client.c
@@ -83,6 +83,9 @@
     case GRPC_TRANSPORT_CLOSED:
       gpr_log(GPR_ERROR, "Transport closed");
       break;
+    case GRPC_TRANSPORT_GOAWAY:
+      gpr_slice_unref(op->data.goaway.message);
+      break;
     default:
       GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
       grpc_channel_next_op(elem, op);
diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c
index a7d6115..2002476 100644
--- a/src/core/surface/completion_queue.c
+++ b/src/core/surface/completion_queue.c
@@ -105,7 +105,7 @@
                          void *tag, grpc_call *call,
                          grpc_event_finish_func on_finish, void *user_data) {
   event *ev = gpr_malloc(sizeof(event));
-  gpr_intptr bucket = ((gpr_intptr)tag) % NUM_TAG_BUCKETS;
+  gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS;
   GPR_ASSERT(!cc->shutdown);
   ev->base.type = type;
   ev->base.tag = tag;
@@ -260,9 +260,9 @@
   gpr_mu_lock(&cc->em->mu);
   for (;;) {
     if (cc->queue != NULL) {
-      gpr_intptr bucket;
+      gpr_uintptr bucket;
       ev = cc->queue;
-      bucket = ((gpr_intptr)ev->base.tag) % NUM_TAG_BUCKETS;
+      bucket = ((gpr_uintptr)ev->base.tag) % NUM_TAG_BUCKETS;
       cc->queue = ev->queue_next;
       ev->queue_next->queue_prev = ev->queue_prev;
       ev->queue_prev->queue_next = ev->queue_next;
@@ -297,7 +297,7 @@
 }
 
 static event *pluck_event(grpc_completion_queue *cc, void *tag) {
-  gpr_intptr bucket = ((gpr_intptr)tag) % NUM_TAG_BUCKETS;
+  gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS;
   event *ev = cc->buckets[bucket];
   if (ev == NULL) return NULL;
   do {
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index 18921c4..3744b5b 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -61,7 +61,15 @@
   op->done_cb(op->user_data, GRPC_OP_ERROR);
 }
 
-static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {}
+static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {
+  switch (op->type) {
+    case GRPC_CHANNEL_GOAWAY:
+      gpr_slice_unref(op->data.goaway.message);
+      break;
+    default:
+      break;
+  }
+}
 
 static void init_call_elem(grpc_call_element *elem,
                            const void *transport_server_data) {}
@@ -87,7 +95,7 @@
     "lame-client",
 };
 
-grpc_channel *grpc_lame_client_channel_create() {
+grpc_channel *grpc_lame_client_channel_create(void) {
   static const grpc_channel_filter *filters[] = {&lame_filter};
   return grpc_channel_create_from_filters(filters, 1, NULL, grpc_mdctx_create(),
                                           1);
diff --git a/src/core/surface/lame_client.h b/src/core/surface/lame_client.h
index 74b9707..3cfbf7b 100644
--- a/src/core/surface/lame_client.h
+++ b/src/core/surface/lame_client.h
@@ -37,6 +37,6 @@
 #include <grpc/grpc.h>
 
 /* Create a lame client: this client fails every operation attempted on it. */
-grpc_channel *grpc_lame_client_channel_create();
+grpc_channel *grpc_lame_client_channel_create(void);
 
 #endif /* __GRPC_INTERNAL_SURFACE_LAME_CLIENT_H_ */
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index 99d66ff..d8d5a7a 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -331,6 +331,9 @@
       gpr_mu_unlock(&chand->server->mu);
       server_unref(chand->server);
       break;
+    case GRPC_TRANSPORT_GOAWAY:
+      gpr_slice_unref(op->data.goaway.message);
+      break;
     default:
       GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
       grpc_channel_next_op(elem, op);
@@ -341,7 +344,7 @@
 static void finish_shutdown_channel(void *cd, grpc_em_cb_status status) {
   channel_data *chand = cd;
   grpc_channel_op op;
-  op.type = GRPC_CHANNEL_SHUTDOWN;
+  op.type = GRPC_CHANNEL_DISCONNECT;
   op.dir = GRPC_CALL_DOWN;
   channel_op(grpc_channel_stack_element(
                  grpc_channel_get_channel_stack(chand->channel), 0),
@@ -515,10 +518,15 @@
 }
 
 void grpc_server_shutdown(grpc_server *server) {
-  /* TODO(ctiller): send goaway, etc */
   listener *l;
   void **tags;
   size_t ntags;
+  channel_data **channels;
+  channel_data *c;
+  size_t nchannels;
+  size_t i;
+  grpc_channel_op op;
+  grpc_channel_element *elem;
 
   /* lock, and gather up some stuff to do */
   gpr_mu_lock(&server->mu);
@@ -527,6 +535,20 @@
     return;
   }
 
+  nchannels = 0;
+  for (c = server->root_channel_data.next; c != &server->root_channel_data;
+       c = c->next) {
+    nchannels++;
+  }
+  channels = gpr_malloc(sizeof(channel_data *) * nchannels);
+  i = 0;
+  for (c = server->root_channel_data.next; c != &server->root_channel_data;
+       c = c->next) {
+    grpc_channel_internal_ref(c->channel);
+    channels[i] = c;
+    i++;
+  }
+
   tags = server->tags;
   ntags = server->ntags;
   server->tags = NULL;
@@ -535,6 +557,21 @@
   server->shutdown = 1;
   gpr_mu_unlock(&server->mu);
 
+  for (i = 0; i < nchannels; i++) {
+    c = channels[i];
+    elem = grpc_channel_stack_element(
+        grpc_channel_get_channel_stack(c->channel), 0);
+
+    op.type = GRPC_CHANNEL_GOAWAY;
+    op.dir = GRPC_CALL_DOWN;
+    op.data.goaway.status = GRPC_STATUS_OK;
+    op.data.goaway.message = gpr_slice_from_copied_string("Server shutdown");
+    elem->filter->channel_op(elem, &op);
+
+    grpc_channel_internal_unref(c->channel);
+  }
+  gpr_free(channels);
+
   /* terminate all the requested calls */
   early_terminate_requested_calls(server->cq, tags, ntags);
   gpr_free(tags);
diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c
index 24c5757..db8924e 100644
--- a/src/core/surface/server_chttp2.c
+++ b/src/core/surface/server_chttp2.c
@@ -91,7 +91,7 @@
   for (i = 0; i < resolved->naddrs; i++) {
     if (grpc_tcp_server_add_port(tcp,
                                  (struct sockaddr *)&resolved->addrs[i].addr,
-                                 resolved->addrs[i].len) >= 0) {
+                                 resolved->addrs[i].len)) {
       count++;
     }
   }
diff --git a/src/core/transport/chttp2/alpn.c b/src/core/transport/chttp2/alpn.c
new file mode 100644
index 0000000..cd9cf67
--- /dev/null
+++ b/src/core/transport/chttp2/alpn.c
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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/transport/chttp2/alpn.h"
+
+static const char *const supported_versions[] = {GRPC_CHTTP2_ALPN_VERSION,
+                                                 "h2-15", "h2-14"};
+
+int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size) {
+  size_t i;
+  for (i = 0; i < sizeof(supported_versions) / sizeof(const char *); i++) {
+    if (!strncmp(version, supported_versions[i], size)) return 1;
+  }
+  return 0;
+}
diff --git a/src/core/transport/chttp2/alpn.h b/src/core/transport/chttp2/alpn.h
new file mode 100644
index 0000000..1353a18
--- /dev/null
+++ b/src/core/transport/chttp2/alpn.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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_INTERNAL_TRANSPORT_CHTTP2_ALPN_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_ALPN_H_
+
+#include <string.h>
+
+#define GRPC_CHTTP2_ALPN_VERSION "h2-15"
+
+/* Retuns 1 if the version is supported, 0 otherwise. */
+int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size);
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_ALPN_H_ */
diff --git a/src/core/transport/chttp2/bin_encoder.c b/src/core/transport/chttp2/bin_encoder.c
new file mode 100644
index 0000000..39a303b
--- /dev/null
+++ b/src/core/transport/chttp2/bin_encoder.c
@@ -0,0 +1,271 @@
+/*
+ *
+ * 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/transport/chttp2/bin_encoder.h"
+#include "src/core/transport/chttp2/huffsyms.h"
+#include <grpc/support/log.h>
+
+static const char alphabet[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+typedef struct {
+  gpr_uint16 bits;
+  gpr_uint8 length;
+} b64_huff_sym;
+
+static const b64_huff_sym huff_alphabet[64] = {{0x21, 6},
+                                               {0x5d, 7},
+                                               {0x5e, 7},
+                                               {0x5f, 7},
+                                               {0x60, 7},
+                                               {0x61, 7},
+                                               {0x62, 7},
+                                               {0x63, 7},
+                                               {0x64, 7},
+                                               {0x65, 7},
+                                               {0x66, 7},
+                                               {0x67, 7},
+                                               {0x68, 7},
+                                               {0x69, 7},
+                                               {0x6a, 7},
+                                               {0x6b, 7},
+                                               {0x6c, 7},
+                                               {0x6d, 7},
+                                               {0x6e, 7},
+                                               {0x6f, 7},
+                                               {0x70, 7},
+                                               {0x71, 7},
+                                               {0x72, 7},
+                                               {0xfc, 8},
+                                               {0x73, 7},
+                                               {0xfd, 8},
+                                               {0x3, 5},
+                                               {0x23, 6},
+                                               {0x4, 5},
+                                               {0x24, 6},
+                                               {0x5, 5},
+                                               {0x25, 6},
+                                               {0x26, 6},
+                                               {0x27, 6},
+                                               {0x6, 5},
+                                               {0x74, 7},
+                                               {0x75, 7},
+                                               {0x28, 6},
+                                               {0x29, 6},
+                                               {0x2a, 6},
+                                               {0x7, 5},
+                                               {0x2b, 6},
+                                               {0x76, 7},
+                                               {0x2c, 6},
+                                               {0x8, 5},
+                                               {0x9, 5},
+                                               {0x2d, 6},
+                                               {0x77, 7},
+                                               {0x78, 7},
+                                               {0x79, 7},
+                                               {0x7a, 7},
+                                               {0x7b, 7},
+                                               {0x0, 5},
+                                               {0x1, 5},
+                                               {0x2, 5},
+                                               {0x19, 6},
+                                               {0x1a, 6},
+                                               {0x1b, 6},
+                                               {0x1c, 6},
+                                               {0x1d, 6},
+                                               {0x1e, 6},
+                                               {0x1f, 6},
+                                               {0x7fb, 11},
+                                               {0x18, 6}};
+
+static const gpr_uint8 tail_xtra[3] = {0, 2, 3};
+
+gpr_slice grpc_chttp2_base64_encode(gpr_slice input) {
+  size_t input_length = GPR_SLICE_LENGTH(input);
+  size_t input_triplets = input_length / 3;
+  size_t tail_case = input_length % 3;
+  size_t output_length = input_triplets * 4 + tail_xtra[tail_case];
+  gpr_slice output = gpr_slice_malloc(output_length);
+  gpr_uint8 *in = GPR_SLICE_START_PTR(input);
+  gpr_uint8 *out = GPR_SLICE_START_PTR(output);
+  size_t i;
+
+  /* encode full triplets */
+  for (i = 0; i < input_triplets; i++) {
+    out[0] = alphabet[in[0] >> 2];
+    out[1] = alphabet[((in[0] & 0x2) << 4) | (in[1] >> 4)];
+    out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)];
+    out[3] = alphabet[in[2] & 0x3f];
+    out += 4;
+    in += 3;
+  }
+
+  /* encode the remaining bytes */
+  switch (tail_case) {
+    case 0:
+      break;
+    case 1:
+      out[0] = alphabet[in[0] >> 2];
+      out[1] = alphabet[(in[0] & 0x2) << 4];
+      out += 2;
+      in += 1;
+      break;
+    case 2:
+      out[0] = alphabet[in[0] >> 2];
+      out[1] = alphabet[((in[0] & 0x2) << 4) | (in[1] >> 4)];
+      out[2] = alphabet[(in[1] & 0xf) << 2];
+      out += 3;
+      in += 2;
+      break;
+  }
+
+  GPR_ASSERT(out == GPR_SLICE_END_PTR(output));
+  GPR_ASSERT(in == GPR_SLICE_END_PTR(input));
+  return output;
+}
+
+gpr_slice grpc_chttp2_huffman_compress(gpr_slice input) {
+  size_t nbits;
+  gpr_uint8 *in;
+  gpr_uint8 *out;
+  gpr_slice output;
+  gpr_uint32 temp = 0;
+  gpr_uint32 temp_length = 0;
+
+  nbits = 0;
+  for (in = GPR_SLICE_START_PTR(input); in != GPR_SLICE_END_PTR(input); ++in) {
+    nbits += grpc_chttp2_huffsyms[*in].length;
+  }
+
+  output = gpr_slice_malloc(nbits / 8 + (nbits % 8 != 0));
+  out = GPR_SLICE_START_PTR(output);
+  for (in = GPR_SLICE_START_PTR(input); in != GPR_SLICE_END_PTR(input); ++in) {
+    int sym = *in;
+    temp <<= grpc_chttp2_huffsyms[sym].length;
+    temp |= grpc_chttp2_huffsyms[sym].bits;
+    temp_length += grpc_chttp2_huffsyms[sym].length;
+
+    while (temp_length > 8) {
+      temp_length -= 8;
+      *out++ = temp >> temp_length;
+    }
+  }
+
+  if (temp_length) {
+    *out++ = (temp << (8 - temp_length)) | (0xff >> temp_length);
+  }
+
+  GPR_ASSERT(out == GPR_SLICE_END_PTR(output));
+
+  return output;
+}
+
+typedef struct {
+  gpr_uint32 temp;
+  gpr_uint32 temp_length;
+  gpr_uint8 *out;
+} huff_out;
+
+static void enc_flush_some(huff_out *out) {
+  while (out->temp_length > 8) {
+    out->temp_length -= 8;
+    *out->out++ = out->temp >> out->temp_length;
+  }
+}
+
+static void enc_add2(huff_out *out, gpr_uint8 a, gpr_uint8 b) {
+  b64_huff_sym sa = huff_alphabet[a];
+  b64_huff_sym sb = huff_alphabet[b];
+  out->temp =
+      (out->temp << (sa.length + sb.length)) | (sa.bits << sb.length) | sb.bits;
+  out->temp_length += sa.length + sb.length;
+  enc_flush_some(out);
+}
+
+static void enc_add1(huff_out *out, gpr_uint8 a) {
+  b64_huff_sym sa = huff_alphabet[a];
+  out->temp = (out->temp << sa.length) | sa.bits;
+  out->temp_length += sa.length;
+  enc_flush_some(out);
+}
+
+gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) {
+  size_t input_length = GPR_SLICE_LENGTH(input);
+  size_t input_triplets = input_length / 3;
+  size_t tail_case = input_length % 3;
+  size_t output_syms = input_triplets * 4 + tail_xtra[tail_case];
+  size_t max_output_bits = 11 * output_syms;
+  size_t max_output_length = max_output_bits / 8 + (max_output_bits % 8 != 0);
+  gpr_slice output = gpr_slice_malloc(max_output_length);
+  gpr_uint8 *in = GPR_SLICE_START_PTR(input);
+  gpr_uint8 *start_out = GPR_SLICE_START_PTR(output);
+  huff_out out;
+  size_t i;
+
+  out.temp = 0;
+  out.temp_length = 0;
+  out.out = start_out;
+
+  /* encode full triplets */
+  for (i = 0; i < input_triplets; i++) {
+    enc_add2(&out, in[0] >> 2, ((in[0] & 0x2) << 4) | (in[1] >> 4));
+    enc_add2(&out, ((in[1] & 0xf) << 2) | (in[2] >> 6), in[2] & 0x3f);
+    in += 3;
+  }
+
+  /* encode the remaining bytes */
+  switch (tail_case) {
+    case 0:
+      break;
+    case 1:
+      enc_add2(&out, in[0] >> 2, (in[0] & 0x2) << 4);
+      in += 1;
+      break;
+    case 2:
+      enc_add2(&out, in[0] >> 2, ((in[0] & 0x2) << 4) | (in[1] >> 4));
+      enc_add1(&out, (in[1] & 0xf) << 2);
+      in += 2;
+      break;
+  }
+
+  if (out.temp_length) {
+    *out.out++ =
+        (out.temp << (8 - out.temp_length)) | (0xff >> out.temp_length);
+  }
+
+  GPR_ASSERT(out.out <= GPR_SLICE_END_PTR(output));
+  GPR_SLICE_SET_LENGTH(output, out.out - start_out);
+
+  GPR_ASSERT(in == GPR_SLICE_END_PTR(input));
+  return output;
+}
diff --git a/src/core/transport/chttp2/bin_encoder.h b/src/core/transport/chttp2/bin_encoder.h
new file mode 100644
index 0000000..8603113
--- /dev/null
+++ b/src/core/transport/chttp2/bin_encoder.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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_INTERNAL_TRANSPORT_CHTTP2_BIN_ENCODER_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_BIN_ENCODER_H_
+
+#include <grpc/support/slice.h>
+
+/* base64 encode a slice. Returns a new slice, does not take ownership of the
+   input */
+gpr_slice grpc_chttp2_base64_encode(gpr_slice input);
+
+/* Compress a slice with the static huffman encoder detailed in the hpack
+   standard. Returns a new slice, does not take ownership of the input */
+gpr_slice grpc_chttp2_huffman_compress(gpr_slice input);
+
+/* equivalent to:
+   gpr_slice x = grpc_chttp2_base64_encode(input);
+   gpr_slice y = grpc_chttp2_huffman_compress(x);
+   gpr_slice_unref(x);
+   return y; */
+gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input);
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_BIN_ENCODER_H_ */
diff --git a/src/core/transport/chttp2/frame.h b/src/core/transport/chttp2/frame.h
index 7c0bbe0..a04e442 100644
--- a/src/core/transport/chttp2/frame.h
+++ b/src/core/transport/chttp2/frame.h
@@ -35,6 +35,7 @@
 #define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_H__
 
 #include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
 
 /* Common definitions for frame handling in the chttp2 transport */
 
@@ -51,8 +52,12 @@
   gpr_uint8 ack_settings;
   gpr_uint8 send_ping_ack;
   gpr_uint8 process_ping_reply;
+  gpr_uint8 goaway;
 
   gpr_uint32 window_update;
+  gpr_uint32 goaway_last_stream_index;
+  gpr_uint32 goaway_error;
+  gpr_slice goaway_text;
 } grpc_chttp2_parse_state;
 
 #define GRPC_CHTTP2_FRAME_DATA 0
@@ -61,6 +66,7 @@
 #define GRPC_CHTTP2_FRAME_RST_STREAM 3
 #define GRPC_CHTTP2_FRAME_SETTINGS 4
 #define GRPC_CHTTP2_FRAME_PING 6
+#define GRPC_CHTTP2_FRAME_GOAWAY 7
 #define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8
 
 #define GRPC_CHTTP2_MAX_PAYLOAD_LENGTH ((1 << 14) - 1)
diff --git a/src/core/transport/chttp2/frame_goaway.c b/src/core/transport/chttp2/frame_goaway.c
new file mode 100644
index 0000000..de7c0b0
--- /dev/null
+++ b/src/core/transport/chttp2/frame_goaway.c
@@ -0,0 +1,189 @@
+/*
+ *
+ * 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/transport/chttp2/frame_goaway.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p) {
+  p->debug_data = NULL;
+}
+
+void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p) {
+  gpr_free(p->debug_data);
+}
+
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame(
+    grpc_chttp2_goaway_parser *p, gpr_uint32 length, gpr_uint8 flags) {
+  if (length < 8) {
+    gpr_log(GPR_ERROR, "goaway frame too short (%d bytes)", length);
+    return GRPC_CHTTP2_CONNECTION_ERROR;
+  }
+
+  gpr_free(p->debug_data);
+  p->debug_length = length - 8;
+  p->debug_data = gpr_malloc(p->debug_length);
+  p->debug_pos = 0;
+  p->state = GRPC_CHTTP2_GOAWAY_LSI0;
+  return GRPC_CHTTP2_PARSE_OK;
+}
+
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse(
+    void *parser, grpc_chttp2_parse_state *state, gpr_slice slice,
+    int is_last) {
+  gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice);
+  gpr_uint8 *const end = GPR_SLICE_END_PTR(slice);
+  gpr_uint8 *cur = beg;
+  grpc_chttp2_goaway_parser *p = parser;
+
+  switch (p->state) {
+    case GRPC_CHTTP2_GOAWAY_LSI0:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI0;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->last_stream_id = ((gpr_uint32)*cur) << 24;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_LSI1:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI1;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->last_stream_id |= ((gpr_uint32)*cur) << 16;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_LSI2:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI2;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->last_stream_id |= ((gpr_uint32)*cur) << 8;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_LSI3:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI3;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->last_stream_id |= ((gpr_uint32)*cur);
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR0:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR0;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->error_code = ((gpr_uint32)*cur) << 24;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR1:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR1;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->error_code |= ((gpr_uint32)*cur) << 16;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR2:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR2;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->error_code |= ((gpr_uint32)*cur) << 8;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR3:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR3;
+        return GRPC_CHTTP2_PARSE_OK;
+      }
+      p->error_code |= ((gpr_uint32)*cur);
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_DEBUG:
+      memcpy(p->debug_data + p->debug_pos, cur, end - cur);
+      p->debug_pos += end - cur;
+      p->state = GRPC_CHTTP2_GOAWAY_DEBUG;
+      if (is_last) {
+        state->goaway = 1;
+        state->goaway_last_stream_index = p->last_stream_id;
+        state->goaway_error = p->error_code;
+        state->goaway_text =
+            gpr_slice_new(p->debug_data, p->debug_length, gpr_free);
+        p->debug_data = NULL;
+      }
+      return GRPC_CHTTP2_PARSE_OK;
+  }
+  gpr_log(GPR_ERROR, "Should never end up here");
+  abort();
+  return GRPC_CHTTP2_CONNECTION_ERROR;
+}
+
+void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code,
+                               gpr_slice debug_data,
+                               gpr_slice_buffer *slice_buffer) {
+  gpr_slice header = gpr_slice_malloc(9 + 4 + 4);
+  gpr_uint8 *p = GPR_SLICE_START_PTR(header);
+  gpr_uint32 frame_length = 4 + 4 + GPR_SLICE_LENGTH(debug_data);
+
+  /* frame header: length */
+  *p++ = frame_length >> 16;
+  *p++ = frame_length >> 8;
+  *p++ = frame_length;
+  /* frame header: type */
+  *p++ = GRPC_CHTTP2_FRAME_GOAWAY;
+  /* frame header: flags */
+  *p++ = 0;
+  /* frame header: stream id */
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  /* payload: last stream id */
+  *p++ = last_stream_id >> 24;
+  *p++ = last_stream_id >> 16;
+  *p++ = last_stream_id >> 8;
+  *p++ = last_stream_id;
+  /* payload: error code */
+  *p++ = error_code >> 24;
+  *p++ = error_code >> 16;
+  *p++ = error_code >> 8;
+  *p++ = error_code;
+  GPR_ASSERT(p == GPR_SLICE_END_PTR(header));
+  gpr_slice_buffer_add(slice_buffer, header);
+  gpr_slice_buffer_add(slice_buffer, debug_data);
+}
diff --git a/src/core/transport/chttp2/frame_goaway.h b/src/core/transport/chttp2/frame_goaway.h
new file mode 100644
index 0000000..9a3f8e6
--- /dev/null
+++ b/src/core/transport/chttp2/frame_goaway.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * 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_INTERNAL_TRANSPORT_CHTTP2_FRAME_GOAWAY_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_GOAWAY_H_
+
+#include "src/core/transport/chttp2/frame.h"
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+
+typedef enum {
+  GRPC_CHTTP2_GOAWAY_LSI0,
+  GRPC_CHTTP2_GOAWAY_LSI1,
+  GRPC_CHTTP2_GOAWAY_LSI2,
+  GRPC_CHTTP2_GOAWAY_LSI3,
+  GRPC_CHTTP2_GOAWAY_ERR0,
+  GRPC_CHTTP2_GOAWAY_ERR1,
+  GRPC_CHTTP2_GOAWAY_ERR2,
+  GRPC_CHTTP2_GOAWAY_ERR3,
+  GRPC_CHTTP2_GOAWAY_DEBUG
+} grpc_chttp2_goaway_parse_state;
+
+typedef struct {
+  grpc_chttp2_goaway_parse_state state;
+  gpr_uint32 last_stream_id;
+  gpr_uint32 error_code;
+  char *debug_data;
+  gpr_uint32 debug_length;
+  gpr_uint32 debug_pos;
+} grpc_chttp2_goaway_parser;
+
+void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p);
+void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p);
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame(
+    grpc_chttp2_goaway_parser *parser, gpr_uint32 length, gpr_uint8 flags);
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse(
+    void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last);
+
+void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code,
+                               gpr_slice debug_data,
+                               gpr_slice_buffer *slice_buffer);
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_GOAWAY_H_ */
diff --git a/src/core/transport/chttp2/gen_hpack_tables.c b/src/core/transport/chttp2/gen_hpack_tables.c
index cc94a73..301b79e 100644
--- a/src/core/transport/chttp2/gen_hpack_tables.c
+++ b/src/core/transport/chttp2/gen_hpack_tables.c
@@ -39,6 +39,7 @@
 #include <assert.h>
 
 #include <grpc/support/log.h>
+#include "src/core/transport/chttp2/huffsyms.h"
 
 /*
  * first byte LUT generation
@@ -127,277 +128,10 @@
  * Huffman decoder table generation
  */
 
-#define NSYMS 257
 #define MAXHUFFSTATES 1024
 
-/* Constants pulled from the HPACK spec, and converted to C using the vim
-   command:
-   :%s/.*   \([0-9a-f]\+\)  \[ *\([0-9]\+\)\]/{0x\1, \2},/g */
-static const struct {
-  unsigned bits;
-  unsigned length;
-} huffsyms[NSYMS] = {
-      {0x1ff8, 13},
-      {0x7fffd8, 23},
-      {0xfffffe2, 28},
-      {0xfffffe3, 28},
-      {0xfffffe4, 28},
-      {0xfffffe5, 28},
-      {0xfffffe6, 28},
-      {0xfffffe7, 28},
-      {0xfffffe8, 28},
-      {0xffffea, 24},
-      {0x3ffffffc, 30},
-      {0xfffffe9, 28},
-      {0xfffffea, 28},
-      {0x3ffffffd, 30},
-      {0xfffffeb, 28},
-      {0xfffffec, 28},
-      {0xfffffed, 28},
-      {0xfffffee, 28},
-      {0xfffffef, 28},
-      {0xffffff0, 28},
-      {0xffffff1, 28},
-      {0xffffff2, 28},
-      {0x3ffffffe, 30},
-      {0xffffff3, 28},
-      {0xffffff4, 28},
-      {0xffffff5, 28},
-      {0xffffff6, 28},
-      {0xffffff7, 28},
-      {0xffffff8, 28},
-      {0xffffff9, 28},
-      {0xffffffa, 28},
-      {0xffffffb, 28},
-      {0x14, 6},
-      {0x3f8, 10},
-      {0x3f9, 10},
-      {0xffa, 12},
-      {0x1ff9, 13},
-      {0x15, 6},
-      {0xf8, 8},
-      {0x7fa, 11},
-      {0x3fa, 10},
-      {0x3fb, 10},
-      {0xf9, 8},
-      {0x7fb, 11},
-      {0xfa, 8},
-      {0x16, 6},
-      {0x17, 6},
-      {0x18, 6},
-      {0x0, 5},
-      {0x1, 5},
-      {0x2, 5},
-      {0x19, 6},
-      {0x1a, 6},
-      {0x1b, 6},
-      {0x1c, 6},
-      {0x1d, 6},
-      {0x1e, 6},
-      {0x1f, 6},
-      {0x5c, 7},
-      {0xfb, 8},
-      {0x7ffc, 15},
-      {0x20, 6},
-      {0xffb, 12},
-      {0x3fc, 10},
-      {0x1ffa, 13},
-      {0x21, 6},
-      {0x5d, 7},
-      {0x5e, 7},
-      {0x5f, 7},
-      {0x60, 7},
-      {0x61, 7},
-      {0x62, 7},
-      {0x63, 7},
-      {0x64, 7},
-      {0x65, 7},
-      {0x66, 7},
-      {0x67, 7},
-      {0x68, 7},
-      {0x69, 7},
-      {0x6a, 7},
-      {0x6b, 7},
-      {0x6c, 7},
-      {0x6d, 7},
-      {0x6e, 7},
-      {0x6f, 7},
-      {0x70, 7},
-      {0x71, 7},
-      {0x72, 7},
-      {0xfc, 8},
-      {0x73, 7},
-      {0xfd, 8},
-      {0x1ffb, 13},
-      {0x7fff0, 19},
-      {0x1ffc, 13},
-      {0x3ffc, 14},
-      {0x22, 6},
-      {0x7ffd, 15},
-      {0x3, 5},
-      {0x23, 6},
-      {0x4, 5},
-      {0x24, 6},
-      {0x5, 5},
-      {0x25, 6},
-      {0x26, 6},
-      {0x27, 6},
-      {0x6, 5},
-      {0x74, 7},
-      {0x75, 7},
-      {0x28, 6},
-      {0x29, 6},
-      {0x2a, 6},
-      {0x7, 5},
-      {0x2b, 6},
-      {0x76, 7},
-      {0x2c, 6},
-      {0x8, 5},
-      {0x9, 5},
-      {0x2d, 6},
-      {0x77, 7},
-      {0x78, 7},
-      {0x79, 7},
-      {0x7a, 7},
-      {0x7b, 7},
-      {0x7ffe, 15},
-      {0x7fc, 11},
-      {0x3ffd, 14},
-      {0x1ffd, 13},
-      {0xffffffc, 28},
-      {0xfffe6, 20},
-      {0x3fffd2, 22},
-      {0xfffe7, 20},
-      {0xfffe8, 20},
-      {0x3fffd3, 22},
-      {0x3fffd4, 22},
-      {0x3fffd5, 22},
-      {0x7fffd9, 23},
-      {0x3fffd6, 22},
-      {0x7fffda, 23},
-      {0x7fffdb, 23},
-      {0x7fffdc, 23},
-      {0x7fffdd, 23},
-      {0x7fffde, 23},
-      {0xffffeb, 24},
-      {0x7fffdf, 23},
-      {0xffffec, 24},
-      {0xffffed, 24},
-      {0x3fffd7, 22},
-      {0x7fffe0, 23},
-      {0xffffee, 24},
-      {0x7fffe1, 23},
-      {0x7fffe2, 23},
-      {0x7fffe3, 23},
-      {0x7fffe4, 23},
-      {0x1fffdc, 21},
-      {0x3fffd8, 22},
-      {0x7fffe5, 23},
-      {0x3fffd9, 22},
-      {0x7fffe6, 23},
-      {0x7fffe7, 23},
-      {0xffffef, 24},
-      {0x3fffda, 22},
-      {0x1fffdd, 21},
-      {0xfffe9, 20},
-      {0x3fffdb, 22},
-      {0x3fffdc, 22},
-      {0x7fffe8, 23},
-      {0x7fffe9, 23},
-      {0x1fffde, 21},
-      {0x7fffea, 23},
-      {0x3fffdd, 22},
-      {0x3fffde, 22},
-      {0xfffff0, 24},
-      {0x1fffdf, 21},
-      {0x3fffdf, 22},
-      {0x7fffeb, 23},
-      {0x7fffec, 23},
-      {0x1fffe0, 21},
-      {0x1fffe1, 21},
-      {0x3fffe0, 22},
-      {0x1fffe2, 21},
-      {0x7fffed, 23},
-      {0x3fffe1, 22},
-      {0x7fffee, 23},
-      {0x7fffef, 23},
-      {0xfffea, 20},
-      {0x3fffe2, 22},
-      {0x3fffe3, 22},
-      {0x3fffe4, 22},
-      {0x7ffff0, 23},
-      {0x3fffe5, 22},
-      {0x3fffe6, 22},
-      {0x7ffff1, 23},
-      {0x3ffffe0, 26},
-      {0x3ffffe1, 26},
-      {0xfffeb, 20},
-      {0x7fff1, 19},
-      {0x3fffe7, 22},
-      {0x7ffff2, 23},
-      {0x3fffe8, 22},
-      {0x1ffffec, 25},
-      {0x3ffffe2, 26},
-      {0x3ffffe3, 26},
-      {0x3ffffe4, 26},
-      {0x7ffffde, 27},
-      {0x7ffffdf, 27},
-      {0x3ffffe5, 26},
-      {0xfffff1, 24},
-      {0x1ffffed, 25},
-      {0x7fff2, 19},
-      {0x1fffe3, 21},
-      {0x3ffffe6, 26},
-      {0x7ffffe0, 27},
-      {0x7ffffe1, 27},
-      {0x3ffffe7, 26},
-      {0x7ffffe2, 27},
-      {0xfffff2, 24},
-      {0x1fffe4, 21},
-      {0x1fffe5, 21},
-      {0x3ffffe8, 26},
-      {0x3ffffe9, 26},
-      {0xffffffd, 28},
-      {0x7ffffe3, 27},
-      {0x7ffffe4, 27},
-      {0x7ffffe5, 27},
-      {0xfffec, 20},
-      {0xfffff3, 24},
-      {0xfffed, 20},
-      {0x1fffe6, 21},
-      {0x3fffe9, 22},
-      {0x1fffe7, 21},
-      {0x1fffe8, 21},
-      {0x7ffff3, 23},
-      {0x3fffea, 22},
-      {0x3fffeb, 22},
-      {0x1ffffee, 25},
-      {0x1ffffef, 25},
-      {0xfffff4, 24},
-      {0xfffff5, 24},
-      {0x3ffffea, 26},
-      {0x7ffff4, 23},
-      {0x3ffffeb, 26},
-      {0x7ffffe6, 27},
-      {0x3ffffec, 26},
-      {0x3ffffed, 26},
-      {0x7ffffe7, 27},
-      {0x7ffffe8, 27},
-      {0x7ffffe9, 27},
-      {0x7ffffea, 27},
-      {0x7ffffeb, 27},
-      {0xffffffe, 28},
-      {0x7ffffec, 27},
-      {0x7ffffed, 27},
-      {0x7ffffee, 27},
-      {0x7ffffef, 27},
-      {0x7fffff0, 27},
-      {0x3ffffee, 26},
-      {0x3fffffff, 30},
-};
-
 /* represents a set of symbols as an array of booleans indicating inclusion */
-typedef struct { char included[NSYMS]; } symset;
+typedef struct { char included[GRPC_CHTTP2_NUM_HUFFSYMS]; } symset;
 /* represents a lookup table indexed by a nibble */
 typedef struct { int values[16]; } nibblelut;
 
@@ -430,7 +164,7 @@
 static int nsyms(symset s) {
   int i;
   int c = 0;
-  for (i = 0; i < NSYMS; i++) {
+  for (i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) {
     c += s.included[i] != 0;
   }
   return c;
@@ -458,7 +192,8 @@
   int i;
   for (i = 0; i < nhuffstates; i++) {
     if (huffstates[i].bitofs != bitofs) continue;
-    if (0 != memcmp(huffstates[i].syms.included, syms.included, NSYMS))
+    if (0 != memcmp(huffstates[i].syms.included, syms.included,
+                    GRPC_CHTTP2_NUM_HUFFSYMS))
       continue;
     *isnew = 0;
     return i;
@@ -510,12 +245,13 @@
   for (bit = 0; bit < 2; bit++) {
     /* walk over active symbols and see if they have this bit set */
     symset nextsyms = symset_none();
-    for (i = 0; i < NSYMS; i++) {
+    for (i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) {
       if (!syms.included[i]) continue; /* disregard inactive symbols */
-      if (((huffsyms[i].bits >> (huffsyms[i].length - bitofs - 1)) & 1) ==
-          bit) {
+      if (((grpc_chttp2_huffsyms[i].bits >>
+            (grpc_chttp2_huffsyms[i].length - bitofs - 1)) &
+           1) == bit) {
         /* the bit is set, include it in the next recursive set */
-        if (huffsyms[i].length == bitofs + 1) {
+        if (grpc_chttp2_huffsyms[i].length == bitofs + 1) {
           /* additionally, we've gotten to the end of a symbol - this is a
              special recursion step: re-activate all the symbols, reset
              bitofs to zero, and recurse */
@@ -581,9 +317,25 @@
   dump_ctbl("emit_sub_tbl");
 }
 
+static void generate_base64_huff_encoder_table() {
+  static const char alphabet[] =
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  int i;
+
+  printf(
+      "static const struct { gpr_uint16 bits, gpr_uint8 length } "
+      "base64_syms[64] = {\n");
+  for (i = 0; i < 64; i++) {
+    printf("{0x%x, %d},", grpc_chttp2_huffsyms[(unsigned char)alphabet[i]].bits,
+           grpc_chttp2_huffsyms[(unsigned char)alphabet[i]].length);
+  }
+  printf("};\n");
+}
+
 int main(void) {
   generate_huff_tables();
   generate_first_byte_lut();
+  generate_base64_huff_encoder_table();
 
   return 0;
 }
diff --git a/src/core/transport/chttp2/huffsyms.c b/src/core/transport/chttp2/huffsyms.c
new file mode 100644
index 0000000..0f86f1b
--- /dev/null
+++ b/src/core/transport/chttp2/huffsyms.c
@@ -0,0 +1,297 @@
+/*
+ *
+ * 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/transport/chttp2/huffsyms.h"
+
+/* Constants pulled from the HPACK spec, and converted to C using the vim
+   command:
+   :%s/.*   \([0-9a-f]\+\)  \[ *\([0-9]\+\)\]/{0x\1, \2},/g */
+const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = {
+    {0x1ff8, 13},
+    {0x7fffd8, 23},
+    {0xfffffe2, 28},
+    {0xfffffe3, 28},
+    {0xfffffe4, 28},
+    {0xfffffe5, 28},
+    {0xfffffe6, 28},
+    {0xfffffe7, 28},
+    {0xfffffe8, 28},
+    {0xffffea, 24},
+    {0x3ffffffc, 30},
+    {0xfffffe9, 28},
+    {0xfffffea, 28},
+    {0x3ffffffd, 30},
+    {0xfffffeb, 28},
+    {0xfffffec, 28},
+    {0xfffffed, 28},
+    {0xfffffee, 28},
+    {0xfffffef, 28},
+    {0xffffff0, 28},
+    {0xffffff1, 28},
+    {0xffffff2, 28},
+    {0x3ffffffe, 30},
+    {0xffffff3, 28},
+    {0xffffff4, 28},
+    {0xffffff5, 28},
+    {0xffffff6, 28},
+    {0xffffff7, 28},
+    {0xffffff8, 28},
+    {0xffffff9, 28},
+    {0xffffffa, 28},
+    {0xffffffb, 28},
+    {0x14, 6},
+    {0x3f8, 10},
+    {0x3f9, 10},
+    {0xffa, 12},
+    {0x1ff9, 13},
+    {0x15, 6},
+    {0xf8, 8},
+    {0x7fa, 11},
+    {0x3fa, 10},
+    {0x3fb, 10},
+    {0xf9, 8},
+    {0x7fb, 11},
+    {0xfa, 8},
+    {0x16, 6},
+    {0x17, 6},
+    {0x18, 6},
+    {0x0, 5},
+    {0x1, 5},
+    {0x2, 5},
+    {0x19, 6},
+    {0x1a, 6},
+    {0x1b, 6},
+    {0x1c, 6},
+    {0x1d, 6},
+    {0x1e, 6},
+    {0x1f, 6},
+    {0x5c, 7},
+    {0xfb, 8},
+    {0x7ffc, 15},
+    {0x20, 6},
+    {0xffb, 12},
+    {0x3fc, 10},
+    {0x1ffa, 13},
+    {0x21, 6},
+    {0x5d, 7},
+    {0x5e, 7},
+    {0x5f, 7},
+    {0x60, 7},
+    {0x61, 7},
+    {0x62, 7},
+    {0x63, 7},
+    {0x64, 7},
+    {0x65, 7},
+    {0x66, 7},
+    {0x67, 7},
+    {0x68, 7},
+    {0x69, 7},
+    {0x6a, 7},
+    {0x6b, 7},
+    {0x6c, 7},
+    {0x6d, 7},
+    {0x6e, 7},
+    {0x6f, 7},
+    {0x70, 7},
+    {0x71, 7},
+    {0x72, 7},
+    {0xfc, 8},
+    {0x73, 7},
+    {0xfd, 8},
+    {0x1ffb, 13},
+    {0x7fff0, 19},
+    {0x1ffc, 13},
+    {0x3ffc, 14},
+    {0x22, 6},
+    {0x7ffd, 15},
+    {0x3, 5},
+    {0x23, 6},
+    {0x4, 5},
+    {0x24, 6},
+    {0x5, 5},
+    {0x25, 6},
+    {0x26, 6},
+    {0x27, 6},
+    {0x6, 5},
+    {0x74, 7},
+    {0x75, 7},
+    {0x28, 6},
+    {0x29, 6},
+    {0x2a, 6},
+    {0x7, 5},
+    {0x2b, 6},
+    {0x76, 7},
+    {0x2c, 6},
+    {0x8, 5},
+    {0x9, 5},
+    {0x2d, 6},
+    {0x77, 7},
+    {0x78, 7},
+    {0x79, 7},
+    {0x7a, 7},
+    {0x7b, 7},
+    {0x7ffe, 15},
+    {0x7fc, 11},
+    {0x3ffd, 14},
+    {0x1ffd, 13},
+    {0xffffffc, 28},
+    {0xfffe6, 20},
+    {0x3fffd2, 22},
+    {0xfffe7, 20},
+    {0xfffe8, 20},
+    {0x3fffd3, 22},
+    {0x3fffd4, 22},
+    {0x3fffd5, 22},
+    {0x7fffd9, 23},
+    {0x3fffd6, 22},
+    {0x7fffda, 23},
+    {0x7fffdb, 23},
+    {0x7fffdc, 23},
+    {0x7fffdd, 23},
+    {0x7fffde, 23},
+    {0xffffeb, 24},
+    {0x7fffdf, 23},
+    {0xffffec, 24},
+    {0xffffed, 24},
+    {0x3fffd7, 22},
+    {0x7fffe0, 23},
+    {0xffffee, 24},
+    {0x7fffe1, 23},
+    {0x7fffe2, 23},
+    {0x7fffe3, 23},
+    {0x7fffe4, 23},
+    {0x1fffdc, 21},
+    {0x3fffd8, 22},
+    {0x7fffe5, 23},
+    {0x3fffd9, 22},
+    {0x7fffe6, 23},
+    {0x7fffe7, 23},
+    {0xffffef, 24},
+    {0x3fffda, 22},
+    {0x1fffdd, 21},
+    {0xfffe9, 20},
+    {0x3fffdb, 22},
+    {0x3fffdc, 22},
+    {0x7fffe8, 23},
+    {0x7fffe9, 23},
+    {0x1fffde, 21},
+    {0x7fffea, 23},
+    {0x3fffdd, 22},
+    {0x3fffde, 22},
+    {0xfffff0, 24},
+    {0x1fffdf, 21},
+    {0x3fffdf, 22},
+    {0x7fffeb, 23},
+    {0x7fffec, 23},
+    {0x1fffe0, 21},
+    {0x1fffe1, 21},
+    {0x3fffe0, 22},
+    {0x1fffe2, 21},
+    {0x7fffed, 23},
+    {0x3fffe1, 22},
+    {0x7fffee, 23},
+    {0x7fffef, 23},
+    {0xfffea, 20},
+    {0x3fffe2, 22},
+    {0x3fffe3, 22},
+    {0x3fffe4, 22},
+    {0x7ffff0, 23},
+    {0x3fffe5, 22},
+    {0x3fffe6, 22},
+    {0x7ffff1, 23},
+    {0x3ffffe0, 26},
+    {0x3ffffe1, 26},
+    {0xfffeb, 20},
+    {0x7fff1, 19},
+    {0x3fffe7, 22},
+    {0x7ffff2, 23},
+    {0x3fffe8, 22},
+    {0x1ffffec, 25},
+    {0x3ffffe2, 26},
+    {0x3ffffe3, 26},
+    {0x3ffffe4, 26},
+    {0x7ffffde, 27},
+    {0x7ffffdf, 27},
+    {0x3ffffe5, 26},
+    {0xfffff1, 24},
+    {0x1ffffed, 25},
+    {0x7fff2, 19},
+    {0x1fffe3, 21},
+    {0x3ffffe6, 26},
+    {0x7ffffe0, 27},
+    {0x7ffffe1, 27},
+    {0x3ffffe7, 26},
+    {0x7ffffe2, 27},
+    {0xfffff2, 24},
+    {0x1fffe4, 21},
+    {0x1fffe5, 21},
+    {0x3ffffe8, 26},
+    {0x3ffffe9, 26},
+    {0xffffffd, 28},
+    {0x7ffffe3, 27},
+    {0x7ffffe4, 27},
+    {0x7ffffe5, 27},
+    {0xfffec, 20},
+    {0xfffff3, 24},
+    {0xfffed, 20},
+    {0x1fffe6, 21},
+    {0x3fffe9, 22},
+    {0x1fffe7, 21},
+    {0x1fffe8, 21},
+    {0x7ffff3, 23},
+    {0x3fffea, 22},
+    {0x3fffeb, 22},
+    {0x1ffffee, 25},
+    {0x1ffffef, 25},
+    {0xfffff4, 24},
+    {0xfffff5, 24},
+    {0x3ffffea, 26},
+    {0x7ffff4, 23},
+    {0x3ffffeb, 26},
+    {0x7ffffe6, 27},
+    {0x3ffffec, 26},
+    {0x3ffffed, 26},
+    {0x7ffffe7, 27},
+    {0x7ffffe8, 27},
+    {0x7ffffe9, 27},
+    {0x7ffffea, 27},
+    {0x7ffffeb, 27},
+    {0xffffffe, 28},
+    {0x7ffffec, 27},
+    {0x7ffffed, 27},
+    {0x7ffffee, 27},
+    {0x7ffffef, 27},
+    {0x7fffff0, 27},
+    {0x3ffffee, 26},
+    {0x3fffffff, 30},
+};
diff --git a/src/core/transport/chttp2/huffsyms.h b/src/core/transport/chttp2/huffsyms.h
new file mode 100644
index 0000000..02d0e53
--- /dev/null
+++ b/src/core/transport/chttp2/huffsyms.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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_INTERNAL_TRANSPORT_CHTTP2_HUFFSYMS_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_HUFFSYMS_H_
+
+/* HPACK static huffman table */
+
+#define GRPC_CHTTP2_NUM_HUFFSYMS 257
+
+typedef struct {
+  unsigned bits;
+  unsigned length;
+} grpc_chttp2_huffsym;
+
+extern const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS];
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HUFFSYMS_H_ */
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index 8a6b427..e6629ac 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -37,23 +37,24 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "src/core/transport/chttp2/frame_data.h"
+#include "src/core/transport/chttp2/frame_goaway.h"
+#include "src/core/transport/chttp2/frame_ping.h"
+#include "src/core/transport/chttp2/frame_rst_stream.h"
+#include "src/core/transport/chttp2/frame_settings.h"
+#include "src/core/transport/chttp2/frame_window_update.h"
+#include "src/core/transport/chttp2/hpack_parser.h"
+#include "src/core/transport/chttp2/http2_errors.h"
+#include "src/core/transport/chttp2/status_conversion.h"
+#include "src/core/transport/chttp2/stream_encoder.h"
+#include "src/core/transport/chttp2/stream_map.h"
+#include "src/core/transport/chttp2/timeout_encoding.h"
+#include "src/core/transport/transport_impl.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice_buffer.h>
 #include <grpc/support/string.h>
 #include <grpc/support/useful.h>
-#include "src/core/transport/transport_impl.h"
-#include "src/core/transport/chttp2/http2_errors.h"
-#include "src/core/transport/chttp2/hpack_parser.h"
-#include "src/core/transport/chttp2/frame_data.h"
-#include "src/core/transport/chttp2/frame_ping.h"
-#include "src/core/transport/chttp2/frame_rst_stream.h"
-#include "src/core/transport/chttp2/frame_settings.h"
-#include "src/core/transport/chttp2/frame_window_update.h"
-#include "src/core/transport/chttp2/status_conversion.h"
-#include "src/core/transport/chttp2/stream_encoder.h"
-#include "src/core/transport/chttp2/stream_map.h"
-#include "src/core/transport/chttp2/timeout_encoding.h"
 
 #define DEFAULT_WINDOW 65536
 #define MAX_WINDOW 0x7fffffffu
@@ -160,6 +161,11 @@
   void *user_data;
 } outstanding_ping;
 
+typedef struct {
+  grpc_status_code status;
+  gpr_slice debug;
+} pending_goaway;
+
 struct transport {
   grpc_transport base; /* must be first */
   const grpc_transport_callbacks *cb;
@@ -180,6 +186,7 @@
 
   /* stream indexing */
   gpr_uint32 next_stream_id;
+  gpr_uint32 last_incoming_stream_id;
 
   /* settings */
   gpr_uint32 settings[NUM_SETTING_SETS][GRPC_CHTTP2_NUM_SETTINGS];
@@ -211,6 +218,12 @@
     grpc_chttp2_ping_parser ping;
   } simple_parsers;
 
+  /* goaway */
+  grpc_chttp2_goaway_parser goaway_parser;
+  pending_goaway *pending_goaways;
+  size_t num_pending_goaways;
+  size_t cap_pending_goaways;
+
   /* state for a stream that's not yet been created */
   grpc_stream_op_buffer new_stream_sopb;
 
@@ -310,6 +323,7 @@
   gpr_slice_buffer_destroy(&t->qbuf);
   grpc_chttp2_hpack_parser_destroy(&t->hpack_parser);
   grpc_chttp2_hpack_compressor_destroy(&t->hpack_compressor);
+  grpc_chttp2_goaway_parser_destroy(&t->goaway_parser);
 
   grpc_mdstr_unref(t->str_grpc_timeout);
 
@@ -332,6 +346,11 @@
   }
   gpr_free(t->pings);
 
+  for (i = 0; i < t->num_pending_goaways; i++) {
+    gpr_slice_unref(t->pending_goaways[i].debug);
+  }
+  gpr_free(t->pending_goaways);
+
   gpr_free(t);
 }
 
@@ -360,6 +379,7 @@
   t->writing = 0;
   t->error_state = ERROR_STATE_NONE;
   t->next_stream_id = is_client ? 1 : 2;
+  t->last_incoming_stream_id = 0;
   t->is_client = is_client;
   t->outgoing_window = DEFAULT_WINDOW;
   t->incoming_window = DEFAULT_WINDOW;
@@ -370,6 +390,10 @@
   t->ping_capacity = 0;
   t->ping_counter = gpr_now().tv_nsec;
   grpc_chttp2_hpack_compressor_init(&t->hpack_compressor, mdctx);
+  grpc_chttp2_goaway_parser_init(&t->goaway_parser);
+  t->pending_goaways = NULL;
+  t->num_pending_goaways = 0;
+  t->cap_pending_goaways = 0;
   gpr_slice_buffer_init(&t->outbuf);
   gpr_slice_buffer_init(&t->qbuf);
   if (is_client) {
@@ -456,6 +480,16 @@
   gpr_mu_unlock(&t->mu);
 }
 
+static void goaway(grpc_transport *gt, grpc_status_code status,
+                   gpr_slice debug_data) {
+  transport *t = (transport *)gt;
+  lock(t);
+  grpc_chttp2_goaway_append(t->last_incoming_stream_id,
+                            grpc_chttp2_grpc_status_to_http2_error(status),
+                            debug_data, &t->qbuf);
+  unlock(t);
+}
+
 static int init_stream(grpc_transport *gt, grpc_stream *gs,
                        const void *server_data) {
   transport *t = (transport *)gt;
@@ -609,6 +643,9 @@
   int start_write = 0;
   int perform_callbacks = 0;
   int call_closed = 0;
+  int num_goaways = 0;
+  int i;
+  pending_goaway *goaways = NULL;
   grpc_endpoint *ep = t->ep;
 
   /* see if we need to trigger a write - and if so, get the data ready */
@@ -630,9 +667,16 @@
       t->calling_back = 1;
       t->error_state = ERROR_STATE_NOTIFIED;
     }
+    if (t->num_pending_goaways) {
+      goaways = t->pending_goaways;
+      num_goaways = t->num_pending_goaways;
+      t->pending_goaways = NULL;
+      t->num_pending_goaways = 0;
+      t->calling_back = 1;
+    }
   }
 
-  if (perform_callbacks || call_closed) {
+  if (perform_callbacks || call_closed || num_goaways) {
     ref_transport(t);
   }
 
@@ -640,6 +684,11 @@
   gpr_mu_unlock(&t->mu);
 
   /* perform some callbacks if necessary */
+  for (i = 0; i < num_goaways; i++) {
+    t->cb->goaway(t->cb_user_data, &t->base, goaways[i].status,
+                  goaways[i].debug);
+  }
+
   if (perform_callbacks) {
     run_callbacks(t);
   }
@@ -698,13 +747,15 @@
     }
   }
 
-  if (perform_callbacks || call_closed) {
+  if (perform_callbacks || call_closed || num_goaways) {
     lock(t);
     t->calling_back = 0;
     gpr_cv_broadcast(&t->cv);
     unlock(t);
     unref_transport(t);
   }
+
+  gpr_free(goaways);
 }
 
 /*
@@ -1130,6 +1181,12 @@
         gpr_log(GPR_ERROR, "ignoring new stream creation on client");
       }
       return init_skip_frame(t, 1);
+    } else if (t->last_incoming_stream_id > t->incoming_stream_id) {
+      gpr_log(GPR_ERROR,
+              "ignoring out of order new stream request on server; last stream "
+              "id=%d, new stream id=%d",
+              t->last_incoming_stream_id, t->incoming_stream);
+      return init_skip_frame(t, 1);
     }
     t->incoming_stream = NULL;
     /* if stream is accepted, we set incoming_stream in init_stream */
@@ -1187,6 +1244,19 @@
   return ok;
 }
 
+static int init_goaway_parser(transport *t) {
+  int ok =
+      GRPC_CHTTP2_PARSE_OK ==
+      grpc_chttp2_goaway_parser_begin_frame(
+          &t->goaway_parser, t->incoming_frame_size, t->incoming_frame_flags);
+  if (!ok) {
+    drop_connection(t);
+  }
+  t->parser = grpc_chttp2_goaway_parser_parse;
+  t->parser_data = &t->goaway_parser;
+  return ok;
+}
+
 static int init_settings_frame_parser(transport *t) {
   int ok = GRPC_CHTTP2_PARSE_OK ==
            grpc_chttp2_settings_parser_begin_frame(
@@ -1240,6 +1310,8 @@
       return init_window_update_frame_parser(t);
     case GRPC_CHTTP2_FRAME_PING:
       return init_ping_parser(t);
+    case GRPC_CHTTP2_FRAME_GOAWAY:
+      return init_goaway_parser(t);
     default:
       gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type);
       return init_skip_frame(t, 0);
@@ -1277,6 +1349,18 @@
             &t->qbuf,
             grpc_chttp2_ping_create(1, t->simple_parsers.ping.opaque_8bytes));
       }
+      if (st.goaway) {
+        if (t->num_pending_goaways == t->cap_pending_goaways) {
+          t->cap_pending_goaways = GPR_MAX(1, t->cap_pending_goaways * 2);
+          t->pending_goaways =
+              gpr_realloc(t->pending_goaways,
+                          sizeof(pending_goaway) * t->cap_pending_goaways);
+        }
+        t->pending_goaways[t->num_pending_goaways].status =
+            grpc_chttp2_http2_error_to_grpc_status(st.goaway_error);
+        t->pending_goaways[t->num_pending_goaways].debug = st.goaway_text;
+        t->num_pending_goaways++;
+      }
       if (st.process_ping_reply) {
         for (i = 0; i < t->ping_count; i++) {
           if (0 ==
@@ -1455,6 +1539,7 @@
       if (!init_frame_parser(t)) {
         return 0;
       }
+      t->last_incoming_stream_id = t->incoming_stream_id;
       if (t->incoming_frame_size == 0) {
         if (!parse_frame_slice(t, gpr_empty_slice(), 1)) {
           return 0;
@@ -1599,7 +1684,7 @@
 
 static const grpc_transport_vtable vtable = {
     sizeof(stream), init_stream, send_batch, set_allow_window_updates,
-    destroy_stream, abort_stream, close_transport, send_ping,
+    destroy_stream, abort_stream, goaway, close_transport, send_ping,
     destroy_transport};
 
 void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
diff --git a/src/core/transport/transport.c b/src/core/transport/transport.c
index d3291bb..1c44abc 100644
--- a/src/core/transport/transport.c
+++ b/src/core/transport/transport.c
@@ -38,6 +38,11 @@
   return transport->vtable->sizeof_stream;
 }
 
+void grpc_transport_goaway(grpc_transport *transport, grpc_status_code status,
+                           gpr_slice message) {
+  transport->vtable->goaway(transport, status, message);
+}
+
 void grpc_transport_close(grpc_transport *transport) {
   transport->vtable->close(transport);
 }
diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h
index 1872947..6a089a2 100644
--- a/src/core/transport/transport.h
+++ b/src/core/transport/transport.h
@@ -116,6 +116,10 @@
                      grpc_stream *stream, grpc_stream_op *ops, size_t ops_count,
                      grpc_stream_state final_state);
 
+  /* The transport received a goaway */
+  void (*goaway)(void *user_data, grpc_transport *transport,
+                 grpc_status_code status, gpr_slice debug);
+
   /* The transport has been closed */
   void (*closed)(void *user_data, grpc_transport *transport);
 };
@@ -198,6 +202,10 @@
 void grpc_transport_abort_stream(grpc_transport *transport, grpc_stream *stream,
                                  grpc_status_code status);
 
+/* Advise peer of pending connection termination. */
+void grpc_transport_goaway(struct grpc_transport *transport,
+                           grpc_status_code status, gpr_slice debug_data);
+
 /* Close a transport. Aborts all open streams. */
 void grpc_transport_close(struct grpc_transport *transport);
 
diff --git a/src/core/transport/transport_impl.h b/src/core/transport/transport_impl.h
index 6acdbf2..328ead2 100644
--- a/src/core/transport/transport_impl.h
+++ b/src/core/transport/transport_impl.h
@@ -60,6 +60,10 @@
   void (*abort_stream)(grpc_transport *self, grpc_stream *stream,
                        grpc_status_code status);
 
+  /* implementation of grpc_transport_goaway */
+  void (*goaway)(grpc_transport *self, grpc_status_code status,
+                 gpr_slice debug_data);
+
   /* implementation of grpc_transport_close */
   void (*close)(grpc_transport *self);
 
diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c
index 7bd178b..2c74b36 100644
--- a/src/core/tsi/ssl_transport_security.c
+++ b/src/core/tsi/ssl_transport_security.c
@@ -289,6 +289,17 @@
   return result;
 }
 
+/* Logs the SSL error stack. */
+static void log_ssl_error_stack(void) {
+  unsigned long err;
+  while ((err = ERR_get_error()) != 0) {
+    char details[256];
+    ERR_error_string_n(err, details, sizeof(details));
+    gpr_log(GPR_ERROR, "%s", details);
+  }
+}
+
+
 /* Performs an SSL_read and handle errors. */
 static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes,
                               uint32_t* unprotected_bytes_size) {
@@ -312,6 +323,7 @@
         return TSI_UNIMPLEMENTED;
       case SSL_ERROR_SSL:
         gpr_log(GPR_ERROR, "Corruption detected.");
+        log_ssl_error_stack();
         return TSI_DATA_CORRUPTED;
       default:
         gpr_log(GPR_ERROR, "SSL_read failed with error %s.",
@@ -364,7 +376,10 @@
     }
     while (1) {
       X509* certificate_authority = PEM_read_bio_X509(pem, NULL, NULL, "");
-      if (certificate_authority == NULL) break;  /* Done reading. */
+      if (certificate_authority == NULL) {
+        ERR_clear_error();
+        break;  /* Done reading. */
+      }
       if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) {
         X509_free(certificate_authority);
         result = TSI_INVALID_ARGUMENT;
@@ -425,7 +440,10 @@
 
   while (1) {
     root = PEM_read_bio_X509_AUX(pem, NULL, NULL, "");
-    if (root == NULL) break;  /* We're at the end of stream. */
+    if (root == NULL) {
+      ERR_clear_error();
+      break;  /* We're at the end of stream. */
+    }
     if (root_names != NULL) {
       root_name = X509_get_subject_name(root);
       if (root_name == NULL) {
diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc
index 7a75291..3792869 100644
--- a/src/cpp/client/channel.cc
+++ b/src/cpp/client/channel.cc
@@ -59,67 +59,19 @@
 Channel::~Channel() { grpc_channel_destroy(c_channel_); }
 
 namespace {
-// Poll one event from the compeletion queue. Return false when an error
-// occured or the polled type is not expected. If a finished event has been
-// polled, set finished and set status if it has not been set.
-bool NextEvent(grpc_completion_queue* cq, grpc_completion_type expected_type,
-               bool* finished, bool* status_set, Status* status,
-               google::protobuf::Message* result) {
-  // We rely on the c layer to enforce deadline and thus do not use deadline
-  // here.
-  grpc_event* ev = grpc_completion_queue_next(cq, gpr_inf_future);
-  if (!ev) {
-    return false;
-  }
-  bool ret = ev->type == expected_type;
-  switch (ev->type) {
-    case GRPC_INVOKE_ACCEPTED:
-      ret = ret && (ev->data.invoke_accepted == GRPC_OP_OK);
-      break;
-    case GRPC_READ:
-      ret = ret && (ev->data.read != nullptr);
-      if (ret && !DeserializeProto(ev->data.read, result)) {
-        *status_set = true;
-        *status =
-            Status(StatusCode::DATA_LOSS, "Failed to parse response proto.");
-        ret = false;
-      }
-      break;
-    case GRPC_WRITE_ACCEPTED:
-      ret = ret && (ev->data.write_accepted == GRPC_OP_OK);
-      break;
-    case GRPC_FINISH_ACCEPTED:
-      ret = ret && (ev->data.finish_accepted == GRPC_OP_OK);
-      break;
-    case GRPC_CLIENT_METADATA_READ:
-      break;
-    case GRPC_FINISHED:
-      *finished = true;
-      if (!*status_set) {
-        *status_set = true;
-        StatusCode error_code = static_cast<StatusCode>(ev->data.finished.code);
-        grpc::string details(
-            ev->data.finished.details ? ev->data.finished.details : "");
-        *status = Status(error_code, details);
-      }
-      break;
-    default:
-      gpr_log(GPR_ERROR, "Dropping unhandled event with type %d", ev->type);
-      break;
+// Pluck the finished event and set to status when it is not nullptr.
+void GetFinalStatus(grpc_completion_queue* cq, void* finished_tag,
+                    Status* status) {
+  grpc_event* ev =
+      grpc_completion_queue_pluck(cq, finished_tag, gpr_inf_future);
+  if (status) {
+    StatusCode error_code = static_cast<StatusCode>(ev->data.finished.code);
+    grpc::string details(ev->data.finished.details ? ev->data.finished.details
+                                                   : "");
+    *status = Status(error_code, details);
   }
   grpc_event_finish(ev);
-  return ret;
 }
-
-// If finished is not true, get final status by polling until a finished
-// event is obtained.
-void GetFinalStatus(grpc_completion_queue* cq, bool status_set, bool finished,
-                    Status* status) {
-  while (!finished) {
-    NextEvent(cq, GRPC_FINISHED, &finished, &status_set, status, nullptr);
-  }
-}
-
 }  // namespace
 
 // TODO(yangg) more error handling
@@ -128,8 +80,6 @@
                                  const google::protobuf::Message& request,
                                  google::protobuf::Message* result) {
   Status status;
-  bool status_set = false;
-  bool finished = false;
   gpr_timespec absolute_deadline;
   AbsoluteDeadlineTimepoint2Timespec(context->absolute_deadline(),
                                      &absolute_deadline);
@@ -137,59 +87,68 @@
                                              // FIXME(yangg)
                                              "localhost", absolute_deadline);
   context->set_call(call);
+  grpc_event* ev;
+  void* finished_tag = reinterpret_cast<char*>(call);
+  void* invoke_tag = reinterpret_cast<char*>(call) + 1;
+  void* metadata_read_tag = reinterpret_cast<char*>(call) + 2;
+  void* write_tag = reinterpret_cast<char*>(call) + 3;
+  void* halfclose_tag = reinterpret_cast<char*>(call) + 4;
+  void* read_tag = reinterpret_cast<char*>(call) + 5;
+
   grpc_completion_queue* cq = grpc_completion_queue_create();
   context->set_cq(cq);
   // add_metadata from context
   //
   // invoke
-  GPR_ASSERT(grpc_call_start_invoke(call, cq, call, call, call,
+  GPR_ASSERT(grpc_call_start_invoke(call, cq, invoke_tag, metadata_read_tag,
+                                    finished_tag,
                                     GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
-  if (!NextEvent(cq, GRPC_INVOKE_ACCEPTED, &status_set, &finished, &status,
-                 nullptr)) {
-    GetFinalStatus(cq, finished, status_set, &status);
-    return status;
-  }
+  ev = grpc_completion_queue_pluck(cq, invoke_tag, gpr_inf_future);
+  grpc_event_finish(ev);
   // write request
   grpc_byte_buffer* write_buffer = nullptr;
   bool success = SerializeProto(request, &write_buffer);
   if (!success) {
     grpc_call_cancel(call);
-    status_set = true;
     status =
         Status(StatusCode::DATA_LOSS, "Failed to serialize request proto.");
-    GetFinalStatus(cq, finished, status_set, &status);
+    GetFinalStatus(cq, finished_tag, nullptr);
     return status;
   }
-  GPR_ASSERT(grpc_call_start_write(call, write_buffer, call,
+  GPR_ASSERT(grpc_call_start_write(call, write_buffer, write_tag,
                                    GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
   grpc_byte_buffer_destroy(write_buffer);
-  if (!NextEvent(cq, GRPC_WRITE_ACCEPTED, &finished, &status_set, &status,
-                 nullptr)) {
-    GetFinalStatus(cq, finished, status_set, &status);
+  ev = grpc_completion_queue_pluck(cq, write_tag, gpr_inf_future);
+
+  success = ev->data.write_accepted == GRPC_OP_OK;
+  grpc_event_finish(ev);
+  if (!success) {
+    GetFinalStatus(cq, finished_tag, &status);
     return status;
   }
   // writes done
-  GPR_ASSERT(grpc_call_writes_done(call, call) == GRPC_CALL_OK);
-  if (!NextEvent(cq, GRPC_FINISH_ACCEPTED, &finished, &status_set, &status,
-                 nullptr)) {
-    GetFinalStatus(cq, finished, status_set, &status);
-    return status;
-  }
+  GPR_ASSERT(grpc_call_writes_done(call, halfclose_tag) == GRPC_CALL_OK);
+  ev = grpc_completion_queue_pluck(cq, halfclose_tag, gpr_inf_future);
+  grpc_event_finish(ev);
   // start read metadata
   //
-  if (!NextEvent(cq, GRPC_CLIENT_METADATA_READ, &finished, &status_set, &status,
-                 nullptr)) {
-    GetFinalStatus(cq, finished, status_set, &status);
-    return status;
-  }
+  ev = grpc_completion_queue_pluck(cq, metadata_read_tag, gpr_inf_future);
+  grpc_event_finish(ev);
   // start read
-  GPR_ASSERT(grpc_call_start_read(call, call) == GRPC_CALL_OK);
-  if (!NextEvent(cq, GRPC_READ, &finished, &status_set, &status, result)) {
-    GetFinalStatus(cq, finished, status_set, &status);
-    return status;
+  GPR_ASSERT(grpc_call_start_read(call, read_tag) == GRPC_CALL_OK);
+  ev = grpc_completion_queue_pluck(cq, read_tag, gpr_inf_future);
+  if (ev->data.read) {
+    if (!DeserializeProto(ev->data.read, result)) {
+      grpc_event_finish(ev);
+      status = Status(StatusCode::DATA_LOSS, "Failed to parse response proto.");
+      GetFinalStatus(cq, finished_tag, nullptr);
+      return status;
+    }
   }
+  grpc_event_finish(ev);
+
   // wait status
-  GetFinalStatus(cq, finished, status_set, &status);
+  GetFinalStatus(cq, finished_tag, &status);
   return status;
 }
 
diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc
index 78774a7..58a8ad2 100644
--- a/src/cpp/client/client_context.cc
+++ b/src/cpp/client/client_context.cc
@@ -50,6 +50,14 @@
   }
   if (cq_) {
     grpc_completion_queue_shutdown(cq_);
+    // Drain cq_.
+    grpc_event* ev;
+    grpc_completion_type t;
+    do {
+      ev = grpc_completion_queue_next(cq_, gpr_inf_future);
+      t = ev->type;
+      grpc_event_finish(ev);
+    } while (t != GRPC_QUEUE_SHUTDOWN);
     grpc_completion_queue_destroy(cq_);
   }
 }
diff --git a/src/cpp/server/async_server_context.cc b/src/cpp/server/async_server_context.cc
index b231f4b..0a9c07f 100644
--- a/src/cpp/server/async_server_context.cc
+++ b/src/cpp/server/async_server_context.cc
@@ -75,18 +75,11 @@
   return err == GRPC_CALL_OK;
 }
 
-namespace {
-grpc_status TranslateStatus(const Status& status) {
-  grpc_status c_status;
-  // TODO(yangg)
-  c_status.code = GRPC_STATUS_OK;
-  c_status.details = nullptr;
-  return c_status;
-}
-}  // namespace
-
 bool AsyncServerContext::StartWriteStatus(const Status& status) {
-  grpc_status c_status = TranslateStatus(status);
+  grpc_status c_status = {static_cast<grpc_status_code>(status.code()),
+                          status.details().empty()
+                              ? nullptr
+                              : const_cast<char*>(status.details().c_str())};
   grpc_call_error err = grpc_call_start_write_status(call_, c_status, this);
   return err == GRPC_CALL_OK;
 }
diff --git a/src/cpp/server/rpc_service_method.h b/src/cpp/server/rpc_service_method.h
index ac2badd..425545f 100644
--- a/src/cpp/server/rpc_service_method.h
+++ b/src/cpp/server/rpc_service_method.h
@@ -42,8 +42,10 @@
 #include "src/cpp/rpc_method.h"
 #include <google/protobuf/message.h>
 #include <grpc++/status.h>
+#include <grpc++/stream.h>
 
 namespace grpc {
+class StreamContextInterface;
 
 // TODO(rocking): we might need to split this file into multiple ones.
 
@@ -53,23 +55,27 @@
   virtual ~MethodHandler() {}
   struct HandlerParameter {
     HandlerParameter(const google::protobuf::Message* req, google::protobuf::Message* resp)
-        : request(req), response(resp) {}
+        : request(req), response(resp), stream_context(nullptr) {}
+    HandlerParameter(const google::protobuf::Message* req, google::protobuf::Message* resp,
+                     StreamContextInterface* context)
+        : request(req), response(resp), stream_context(context) {}
     const google::protobuf::Message* request;
     google::protobuf::Message* response;
+    StreamContextInterface* stream_context;
   };
-  virtual ::grpc::Status RunHandler(const HandlerParameter& param) = 0;
+  virtual Status RunHandler(const HandlerParameter& param) = 0;
 };
 
 // A wrapper class of an application provided rpc method handler.
 template <class ServiceType, class RequestType, class ResponseType>
 class RpcMethodHandler : public MethodHandler {
  public:
-  RpcMethodHandler(std::function<::grpc::Status(
-                       ServiceType*, const RequestType*, ResponseType*)> func,
+  RpcMethodHandler(std::function<Status(ServiceType*, const RequestType*,
+                                        ResponseType*)> func,
                    ServiceType* service)
       : func_(func), service_(service) {}
 
-  ::grpc::Status RunHandler(const HandlerParameter& param) final {
+  Status RunHandler(const HandlerParameter& param) final {
     // Invoke application function, cast proto messages to their actual types.
     return func_(service_, dynamic_cast<const RequestType*>(param.request),
                  dynamic_cast<ResponseType*>(param.response));
@@ -77,20 +83,84 @@
 
  private:
   // Application provided rpc handler function.
-  std::function<::grpc::Status(ServiceType*, const RequestType*, ResponseType*)>
-      func_;
+  std::function<Status(ServiceType*, const RequestType*, ResponseType*)> func_;
   // The class the above handler function lives in.
   ServiceType* service_;
 };
 
+// A wrapper class of an application provided client streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler : public MethodHandler {
+ public:
+  ClientStreamingHandler(
+      std::function<Status(ServiceType*, ServerReader<RequestType>*,
+                           ResponseType*)> func,
+      ServiceType* service)
+      : func_(func), service_(service) {}
+
+  Status RunHandler(const HandlerParameter& param) final {
+    ServerReader<RequestType> reader(param.stream_context);
+    return func_(service_, &reader,
+                 dynamic_cast<ResponseType*>(param.response));
+  }
+
+ private:
+  std::function<Status(ServiceType*, ServerReader<RequestType>*, ResponseType*)>
+      func_;
+  ServiceType* service_;
+};
+
+// A wrapper class of an application provided server streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler : public MethodHandler {
+ public:
+  ServerStreamingHandler(
+      std::function<Status(ServiceType*, const RequestType*,
+                           ServerWriter<ResponseType>*)> func,
+      ServiceType* service)
+      : func_(func), service_(service) {}
+
+  Status RunHandler(const HandlerParameter& param) final {
+    ServerWriter<ResponseType> writer(param.stream_context);
+    return func_(service_, dynamic_cast<const RequestType*>(param.request),
+                 &writer);
+  }
+
+ private:
+  std::function<Status(ServiceType*, const RequestType*,
+                       ServerWriter<ResponseType>*)> func_;
+  ServiceType* service_;
+};
+
+// A wrapper class of an application provided bidi-streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler : public MethodHandler {
+ public:
+  BidiStreamingHandler(
+      std::function<Status(
+          ServiceType*, ServerReaderWriter<ResponseType, RequestType>*)> func,
+      ServiceType* service)
+      : func_(func), service_(service) {}
+
+  Status RunHandler(const HandlerParameter& param) final {
+    ServerReaderWriter<ResponseType, RequestType> stream(param.stream_context);
+    return func_(service_, &stream);
+  }
+
+ private:
+  std::function<Status(ServiceType*,
+                       ServerReaderWriter<ResponseType, RequestType>*)> func_;
+  ServiceType* service_;
+};
+
 // Server side rpc method class
 class RpcServiceMethod : public RpcMethod {
  public:
   // Takes ownership of the handler and two prototype objects.
-  RpcServiceMethod(const char* name, MethodHandler* handler,
-                   google::protobuf::Message* request_prototype,
+  RpcServiceMethod(const char* name, RpcMethod::RpcType type,
+                   MethodHandler* handler, google::protobuf::Message* request_prototype,
                    google::protobuf::Message* response_prototype)
-      : RpcMethod(name),
+      : RpcMethod(name, type),
         handler_(handler),
         request_prototype_(request_prototype),
         response_prototype_(response_prototype) {}
diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc
new file mode 100644
index 0000000..d23a09f
--- /dev/null
+++ b/src/cpp/server/server_credentials.cc
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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/grpc_security.h>
+
+#include <grpc++/server_credentials.h>
+
+namespace grpc {
+
+ServerCredentials::ServerCredentials(grpc_server_credentials* c_creds)
+    : creds_(c_creds) {}
+
+ServerCredentials::~ServerCredentials() {
+  grpc_server_credentials_release(creds_);
+}
+
+grpc_server_credentials* ServerCredentials::GetRawCreds() { return creds_; }
+
+std::shared_ptr<ServerCredentials> ServerCredentialsFactory::SslCredentials(
+    const SslServerCredentialsOptions& options) {
+  grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create(
+      reinterpret_cast<const unsigned char*>(options.pem_root_certs.c_str()),
+      options.pem_root_certs.size(),
+      reinterpret_cast<const unsigned char*>(options.pem_private_key.c_str()),
+      options.pem_private_key.size(),
+      reinterpret_cast<const unsigned char*>(options.pem_cert_chain.c_str()),
+      options.pem_cert_chain.size());
+  return std::shared_ptr<ServerCredentials>(new ServerCredentials(c_creds));
+}
+
+}  // namespace grpc
diff --git a/src/cpp/server/server_rpc_handler.cc b/src/cpp/server/server_rpc_handler.cc
index 2d5a081..4c8d0cd 100644
--- a/src/cpp/server/server_rpc_handler.cc
+++ b/src/cpp/server/server_rpc_handler.cc
@@ -35,20 +35,16 @@
 
 #include <grpc/support/log.h>
 #include "src/cpp/server/rpc_service_method.h"
+#include "src/cpp/stream/stream_context.h"
 #include <grpc++/async_server_context.h>
 
 namespace grpc {
 
 ServerRpcHandler::ServerRpcHandler(AsyncServerContext* server_context,
                                    RpcServiceMethod* method)
-    : server_context_(server_context),
-      method_(method) {
-}
+    : server_context_(server_context), method_(method) {}
 
 void ServerRpcHandler::StartRpc() {
-  // Start the rpc on this dedicated completion queue.
-  server_context_->Accept(cq_.cq());
-
   if (method_ == nullptr) {
     // Method not supported, finish the rpc with error.
     // TODO(rocking): do we need to call read to consume the request?
@@ -56,30 +52,54 @@
     return;
   }
 
-  // Allocate request and response.
-  std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto());
-  std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto());
+  if (method_->method_type() == RpcMethod::NORMAL_RPC) {
+    // Start the rpc on this dedicated completion queue.
+    server_context_->Accept(cq_.cq());
 
-  // Read request
-  server_context_->StartRead(request.get());
-  auto type = WaitForNextEvent();
-  GPR_ASSERT(type == CompletionQueue::SERVER_READ_OK);
+    // Allocate request and response.
+    std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto());
+    std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto());
 
-  // Run the application's rpc handler
-  MethodHandler* handler = method_->handler();
-  Status status = handler->RunHandler(
-      MethodHandler::HandlerParameter(request.get(), response.get()));
+    // Read request
+    server_context_->StartRead(request.get());
+    auto type = WaitForNextEvent();
+    GPR_ASSERT(type == CompletionQueue::SERVER_READ_OK);
 
-  if (status.IsOk()) {
-    // Send the response if we get an ok status.
-    server_context_->StartWrite(*response, 0);
-    type = WaitForNextEvent();
-    if (type != CompletionQueue::SERVER_WRITE_OK) {
-      status = Status(StatusCode::INTERNAL, "Error writing response.");
+    // Run the application's rpc handler
+    MethodHandler* handler = method_->handler();
+    Status status = handler->RunHandler(
+        MethodHandler::HandlerParameter(request.get(), response.get()));
+
+    if (status.IsOk()) {
+      // Send the response if we get an ok status.
+      server_context_->StartWrite(*response, 0);
+      type = WaitForNextEvent();
+      if (type != CompletionQueue::SERVER_WRITE_OK) {
+        status = Status(StatusCode::INTERNAL, "Error writing response.");
+      }
     }
-  }
 
-  FinishRpc(status);
+    FinishRpc(status);
+  } else {
+    // Allocate request and response.
+    // TODO(yangg) maybe not allocate both when not needed?
+    std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto());
+    std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto());
+
+    StreamContext stream_context(*method_, server_context_->call(), cq_.cq(),
+                                 request.get(), response.get());
+
+    // Run the application's rpc handler
+    MethodHandler* handler = method_->handler();
+    Status status = handler->RunHandler(MethodHandler::HandlerParameter(
+        request.get(), response.get(), &stream_context));
+    if (status.IsOk() &&
+        method_->method_type() == RpcMethod::CLIENT_STREAMING) {
+      stream_context.Write(response.get(), false);
+    }
+    // TODO(yangg) Do we need to consider the status in stream_context?
+    FinishRpc(status);
+  }
 }
 
 CompletionQueue::CompletionType ServerRpcHandler::WaitForNextEvent() {
@@ -94,11 +114,15 @@
 
 void ServerRpcHandler::FinishRpc(const Status& status) {
   server_context_->StartWriteStatus(status);
-  CompletionQueue::CompletionType type = WaitForNextEvent();
-  // TODO(rocking): do we care about this return type?
+  CompletionQueue::CompletionType type;
 
+  // HALFCLOSE_OK and RPC_END events come in either order.
   type = WaitForNextEvent();
-  GPR_ASSERT(type == CompletionQueue::RPC_END);
+  GPR_ASSERT(type == CompletionQueue::HALFCLOSE_OK ||
+             type == CompletionQueue::RPC_END);
+  type = WaitForNextEvent();
+  GPR_ASSERT(type == CompletionQueue::HALFCLOSE_OK ||
+             type == CompletionQueue::RPC_END);
 
   cq_.Shutdown();
   type = WaitForNextEvent();
diff --git a/src/cpp/stream/stream_context.cc b/src/cpp/stream/stream_context.cc
index 07e771f..706e90c 100644
--- a/src/cpp/stream/stream_context.cc
+++ b/src/cpp/stream/stream_context.cc
@@ -33,7 +33,6 @@
 
 #include "src/cpp/stream/stream_context.h"
 
-#include <grpc/grpc.h>
 #include <grpc/support/log.h>
 #include "src/cpp/rpc_method.h"
 #include "src/cpp/proto/proto_utils.h"
@@ -50,227 +49,146 @@
                              google::protobuf::Message* result)
     : is_client_(true),
       method_(&method),
-      context_(context),
-      request_(request),
+      call_(context->call()),
+      cq_(context->cq()),
+      request_(const_cast<google::protobuf::Message*>(request)),
       result_(result),
-      invoke_ev_(nullptr),
-      read_ev_(nullptr),
-      write_ev_(nullptr),
-      reading_(false),
-      writing_(false),
-      got_read_(false),
-      got_write_(false),
       peer_halfclosed_(false),
-      self_halfclosed_(false),
-      stream_finished_(false),
-      waiting_(false) {
+      self_halfclosed_(false) {
   GPR_ASSERT(method_->method_type() != RpcMethod::RpcType::NORMAL_RPC);
 }
 
-StreamContext::~StreamContext() { cq_poller_.join(); }
-
-void StreamContext::PollingLoop() {
-  grpc_event* ev = nullptr;
-  gpr_timespec absolute_deadline;
-  AbsoluteDeadlineTimepoint2Timespec(context_->absolute_deadline(),
-                                     &absolute_deadline);
-  std::condition_variable* cv_to_notify = nullptr;
-  std::unique_lock<std::mutex> lock(mu_, std::defer_lock);
-  while (1) {
-    cv_to_notify = nullptr;
-    lock.lock();
-    if (stream_finished_ && !reading_ && !writing_) {
-      return;
-    }
-    lock.unlock();
-    ev = grpc_completion_queue_next(context_->cq(), absolute_deadline);
-    lock.lock();
-    if (!ev) {
-      stream_finished_ = true;
-      final_status_ = Status(StatusCode::DEADLINE_EXCEEDED);
-      std::condition_variable* cvs[3] = {reading_ ? &read_cv_ : nullptr,
-                                         writing_ ? &write_cv_ : nullptr,
-                                         waiting_ ? &finish_cv_ : nullptr};
-      got_read_ = reading_;
-      got_write_ = writing_;
-      read_ev_ = nullptr;
-      write_ev_ = nullptr;
-      lock.unlock();
-      for (int i = 0; i < 3; i++) {
-        if (cvs[i]) cvs[i]->notify_one();
-      }
-      break;
-    }
-    switch (ev->type) {
-      case GRPC_READ:
-        GPR_ASSERT(reading_);
-        got_read_ = true;
-        read_ev_ = ev;
-        cv_to_notify = &read_cv_;
-        reading_ = false;
-        break;
-      case GRPC_FINISH_ACCEPTED:
-      case GRPC_WRITE_ACCEPTED:
-        got_write_ = true;
-        write_ev_ = ev;
-        cv_to_notify = &write_cv_;
-        writing_ = false;
-        break;
-      case GRPC_FINISHED: {
-        grpc::string error_details(
-            ev->data.finished.details ? ev->data.finished.details : "");
-        final_status_ = Status(static_cast<StatusCode>(ev->data.finished.code),
-                               error_details);
-        grpc_event_finish(ev);
-        stream_finished_ = true;
-        if (waiting_) {
-          cv_to_notify = &finish_cv_;
-        }
-        break;
-      }
-      case GRPC_INVOKE_ACCEPTED:
-        invoke_ev_ = ev;
-        cv_to_notify = &invoke_cv_;
-        break;
-      case GRPC_CLIENT_METADATA_READ:
-        grpc_event_finish(ev);
-        break;
-      default:
-        grpc_event_finish(ev);
-        // not handling other types now
-        gpr_log(GPR_ERROR, "unknown event type");
-        abort();
-    }
-    lock.unlock();
-    if (cv_to_notify) {
-      cv_to_notify->notify_one();
-    }
-  }
+// Server only ctor
+StreamContext::StreamContext(const RpcMethod& method, grpc_call* call,
+                             grpc_completion_queue* cq,
+                             google::protobuf::Message* request, google::protobuf::Message* result)
+    : is_client_(false),
+      method_(&method),
+      call_(call),
+      cq_(cq),
+      request_(request),
+      result_(result),
+      peer_halfclosed_(false),
+      self_halfclosed_(false) {
+  GPR_ASSERT(method_->method_type() != RpcMethod::RpcType::NORMAL_RPC);
 }
 
+StreamContext::~StreamContext() {}
+
 void StreamContext::Start(bool buffered) {
-  // TODO(yangg) handle metadata send path
-  int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0;
-  grpc_call_error error = grpc_call_start_invoke(
-      context_->call(), context_->cq(), this, this, this, flag);
-  GPR_ASSERT(GRPC_CALL_OK == error);
-  // kicks off the poller thread
-  cq_poller_ = std::thread(&StreamContext::PollingLoop, this);
-  std::unique_lock<std::mutex> lock(mu_);
-  while (!invoke_ev_) {
-    invoke_cv_.wait(lock);
+  if (is_client_) {
+    // TODO(yangg) handle metadata send path
+    int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0;
+    grpc_call_error error = grpc_call_start_invoke(call(), cq(), invoke_tag(),
+                                                   client_metadata_read_tag(),
+                                                   finished_tag(), flag);
+    GPR_ASSERT(GRPC_CALL_OK == error);
+    grpc_event* invoke_ev =
+        grpc_completion_queue_pluck(cq(), invoke_tag(), gpr_inf_future);
+    grpc_event_finish(invoke_ev);
+  } else {
+    // TODO(yangg) metadata needs to be added before accept
+    // TODO(yangg) correctly set flag to accept
+    grpc_call_error error = grpc_call_accept(call(), cq(), finished_tag(), 0);
+    GPR_ASSERT(GRPC_CALL_OK == error);
   }
-  lock.unlock();
-  GPR_ASSERT(invoke_ev_->data.invoke_accepted == GRPC_OP_OK);
-  grpc_event_finish(invoke_ev_);
 }
 
-namespace {
-// Wait for got_event with event_cv protected by mu, return event.
-grpc_event* WaitForEvent(bool* got_event, std::condition_variable* event_cv,
-                         std::mutex* mu, grpc_event** event) {
-  std::unique_lock<std::mutex> lock(*mu);
-  while (*got_event == false) {
-    event_cv->wait(lock);
-  }
-  *got_event = false;
-  return *event;
-}
-}  // namespace
-
 bool StreamContext::Read(google::protobuf::Message* msg) {
-  std::unique_lock<std::mutex> lock(mu_);
-  if (stream_finished_) {
-    peer_halfclosed_ = true;
-    return false;
-  }
-  reading_ = true;
-  lock.unlock();
-
-  grpc_call_error err = grpc_call_start_read(context_->call(), this);
+  // TODO(yangg) check peer_halfclosed_ here for possible early return.
+  grpc_call_error err = grpc_call_start_read(call(), read_tag());
   GPR_ASSERT(err == GRPC_CALL_OK);
-
-  grpc_event* ev = WaitForEvent(&got_read_, &read_cv_, &mu_, &read_ev_);
-  if (!ev) {
-    return false;
-  }
-  GPR_ASSERT(ev->type == GRPC_READ);
+  grpc_event* read_ev =
+      grpc_completion_queue_pluck(cq(), read_tag(), gpr_inf_future);
+  GPR_ASSERT(read_ev->type == GRPC_READ);
   bool ret = true;
-  if (ev->data.read) {
-    if (!DeserializeProto(ev->data.read, msg)) {
-      ret = false;  // parse error
-                    // TODO(yangg) cancel the stream.
+  if (read_ev->data.read) {
+    if (!DeserializeProto(read_ev->data.read, msg)) {
+      ret = false;
+      FinishStream(
+          Status(StatusCode::DATA_LOSS, "Failed to parse incoming proto"),
+          true);
     }
   } else {
     ret = false;
     peer_halfclosed_ = true;
   }
-  grpc_event_finish(ev);
+  grpc_event_finish(read_ev);
   return ret;
 }
 
 bool StreamContext::Write(const google::protobuf::Message* msg, bool is_last) {
+  // TODO(yangg) check self_halfclosed_ for possible early return.
   bool ret = true;
   grpc_event* ev = nullptr;
 
-  std::unique_lock<std::mutex> lock(mu_);
-  if (stream_finished_) {
-    self_halfclosed_ = true;
-    return false;
-  }
-  writing_ = true;
-  lock.unlock();
-
   if (msg) {
     grpc_byte_buffer* out_buf = nullptr;
     if (!SerializeProto(*msg, &out_buf)) {
       FinishStream(Status(StatusCode::INVALID_ARGUMENT,
-                          "Failed to serialize request proto"),
+                          "Failed to serialize outgoing proto"),
                    true);
       return false;
     }
     int flag = is_last ? GRPC_WRITE_BUFFER_HINT : 0;
     grpc_call_error err =
-        grpc_call_start_write(context_->call(), out_buf, this, flag);
+        grpc_call_start_write(call(), out_buf, write_tag(), flag);
     grpc_byte_buffer_destroy(out_buf);
     GPR_ASSERT(err == GRPC_CALL_OK);
 
-    ev = WaitForEvent(&got_write_, &write_cv_, &mu_, &write_ev_);
-    if (!ev) {
-      return false;
-    }
+    ev = grpc_completion_queue_pluck(cq(), write_tag(), gpr_inf_future);
     GPR_ASSERT(ev->type == GRPC_WRITE_ACCEPTED);
 
     ret = ev->data.write_accepted == GRPC_OP_OK;
     grpc_event_finish(ev);
   }
-  if (is_last) {
-    grpc_call_error err = grpc_call_writes_done(context_->call(), this);
+  if (ret && is_last) {
+    grpc_call_error err = grpc_call_writes_done(call(), halfclose_tag());
     GPR_ASSERT(err == GRPC_CALL_OK);
-    ev = WaitForEvent(&got_write_, &write_cv_, &mu_, &write_ev_);
-    if (!ev) {
-      return false;
-    }
+    ev = grpc_completion_queue_pluck(cq(), halfclose_tag(), gpr_inf_future);
     GPR_ASSERT(ev->type == GRPC_FINISH_ACCEPTED);
     grpc_event_finish(ev);
+
     self_halfclosed_ = true;
+  } else if (!ret) {  // Stream broken
+    self_halfclosed_ = true;
+    peer_halfclosed_ = true;
   }
+
   return ret;
 }
 
 const Status& StreamContext::Wait() {
-  std::unique_lock<std::mutex> lock(mu_);
-  // TODO(yangg) if not halfclosed cancel the stream
-  GPR_ASSERT(self_halfclosed_);
-  GPR_ASSERT(peer_halfclosed_);
-  GPR_ASSERT(!waiting_);
-  waiting_ = true;
-  while (!stream_finished_) {
-    finish_cv_.wait(lock);
+  // TODO(yangg) properly support metadata
+  grpc_event* metadata_ev = grpc_completion_queue_pluck(
+      cq(), client_metadata_read_tag(), gpr_inf_future);
+  grpc_event_finish(metadata_ev);
+  // TODO(yangg) protect states by a mutex, including other places.
+  if (!self_halfclosed_ || !peer_halfclosed_) {
+    FinishStream(Status::Cancelled, true);
+  } else {
+    grpc_event* finish_ev =
+        grpc_completion_queue_pluck(cq(), finished_tag(), gpr_inf_future);
+    GPR_ASSERT(finish_ev->type == GRPC_FINISHED);
+    std::string error_details(finish_ev->data.finished.details
+                                  ? finish_ev->data.finished.details
+                                  : "");
+    final_status_ = Status(
+        static_cast<StatusCode>(finish_ev->data.finished.code), error_details);
+    grpc_event_finish(finish_ev);
   }
   return final_status_;
 }
 
-void StreamContext::FinishStream(const Status& status, bool send) { return; }
+void StreamContext::FinishStream(const Status& status, bool send) {
+  if (send) {
+    grpc_call_cancel(call());
+  }
+  grpc_event* finish_ev =
+      grpc_completion_queue_pluck(cq(), finished_tag(), gpr_inf_future);
+  GPR_ASSERT(finish_ev->type == GRPC_FINISHED);
+  grpc_event_finish(finish_ev);
+  final_status_ = status;
+}
 
 }  // namespace grpc
diff --git a/src/cpp/stream/stream_context.h b/src/cpp/stream/stream_context.h
index b7f462f..6c31095 100644
--- a/src/cpp/stream/stream_context.h
+++ b/src/cpp/stream/stream_context.h
@@ -34,10 +34,7 @@
 #ifndef __GRPCPP_INTERNAL_STREAM_STREAM_CONTEXT_H__
 #define __GRPCPP_INTERNAL_STREAM_STREAM_CONTEXT_H__
 
-#include <condition_variable>
-#include <mutex>
-#include <thread>
-
+#include <grpc/grpc.h>
 #include <grpc++/status.h>
 #include <grpc++/stream_context_interface.h>
 
@@ -47,8 +44,6 @@
 }
 }
 
-struct grpc_event;
-
 namespace grpc {
 class ClientContext;
 class RpcMethod;
@@ -57,6 +52,9 @@
  public:
   StreamContext(const RpcMethod& method, ClientContext* context,
                 const google::protobuf::Message* request, google::protobuf::Message* result);
+  StreamContext(const RpcMethod& method, grpc_call* call,
+                grpc_completion_queue* cq, google::protobuf::Message* request,
+                google::protobuf::Message* result);
   ~StreamContext();
   // Start the stream, if there is a final write following immediately, set
   // buffered so that the messages can be sent in batch.
@@ -66,37 +64,31 @@
   const Status& Wait() override;
   void FinishStream(const Status& status, bool send) override;
 
-  const google::protobuf::Message* request() override { return request_; }
+  google::protobuf::Message* request() override { return request_; }
   google::protobuf::Message* response() override { return result_; }
 
  private:
-  void PollingLoop();
-  bool BlockingStart();
+  // Unique tags for plucking events from the c layer. this pointer is casted
+  // to char* to create single byte step between tags. It implicitly relies on
+  // that StreamContext is large enough to contain all the pointers.
+  void* finished_tag() { return reinterpret_cast<char*>(this); }
+  void* read_tag() { return reinterpret_cast<char*>(this) + 1; }
+  void* write_tag() { return reinterpret_cast<char*>(this) + 2; }
+  void* halfclose_tag() { return reinterpret_cast<char*>(this) + 3; }
+  void* invoke_tag() { return reinterpret_cast<char*>(this) + 4; }
+  void* client_metadata_read_tag() { return reinterpret_cast<char*>(this) + 5; }
+  grpc_call* call() { return call_; }
+  grpc_completion_queue* cq() { return cq_; }
+
   bool is_client_;
   const RpcMethod* method_;         // not owned
-  ClientContext* context_;          // now owned
-  const google::protobuf::Message* request_;  // not owned
-  google::protobuf::Message* result_;         // not owned
+  grpc_call* call_;                 // not owned
+  grpc_completion_queue* cq_;       // not owned
+  google::protobuf::Message* request_;        // first request, not owned
+  google::protobuf::Message* result_;         // last response, not owned
 
-  std::thread cq_poller_;
-  std::mutex mu_;
-  std::condition_variable invoke_cv_;
-  std::condition_variable read_cv_;
-  std::condition_variable write_cv_;
-  std::condition_variable finish_cv_;
-  grpc_event* invoke_ev_;
-  // TODO(yangg) make these two into queues to support concurrent reads and
-  // writes
-  grpc_event* read_ev_;
-  grpc_event* write_ev_;
-  bool reading_;
-  bool writing_;
-  bool got_read_;
-  bool got_write_;
   bool peer_halfclosed_;
   bool self_halfclosed_;
-  bool stream_finished_;
-  bool waiting_;
   Status final_status_;
 };
 
diff --git a/src/ruby/README.md b/src/ruby/README.md
index 8377866..3a5c508 100755
--- a/src/ruby/README.md
+++ b/src/ruby/README.md
@@ -52,11 +52,25 @@
 $ make
 $ sudo make install
 
+Install an update to OpenSSL with ALPN support
+
+$ wget https://www.openssl.org/source/openssl-1.0.2-beta3.tar.gz
+$ tar -zxvf openssl-1.0.2-beta3.tar.gz
+$ cd openssl-1.0.2-beta3
+$ ./config shared
+$ make
+$ sudo make install
+
 Install RVM
 
+$ # the -with-openssl-dir ensures that ruby uses the updated version of SSL
+$ command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
 $ \curl -sSL https://get.rvm.io | bash -s stable --ruby
-$ # follow the instructions to ensure that your're using the latest stable version of Ruby
 $
+$ # follow the instructions to ensure that your're using the latest stable version of Ruby
+$ # and that the rvm command is installed
+$
+$ rvm reinstall 2.1.5 --with-openssl-dir=/usr/local/ssl
 $ gem install bundler  # install bundler, the standard ruby package manager
 
 Install the patched beefcake, and update the Gemfile to reference
@@ -90,4 +104,3 @@
 $ # the patched beefcake dir
 $
 $ bundle install
-
diff --git a/src/ruby/Rakefile b/src/ruby/Rakefile
index 11b3d04..0a0fbce 100755
--- a/src/ruby/Rakefile
+++ b/src/ruby/Rakefile
@@ -9,7 +9,10 @@
 
 SPEC_SUITES = [
   { :id => :wrapper, :title => 'wrapper layer', :files => %w(spec/*.rb) },
-  { :id => :idiomatic, :title => 'idiomatic layer', :dir => %w(spec/generic) }
+  { :id => :idiomatic, :title => 'idiomatic layer', :dir => %w(spec/generic),
+    :tag => '~bidi' },
+  { :id => :bidi, :title => 'bidi tests', :dir => %w(spec/generic),
+    :tag => 'bidi' }
 ]
 
 desc "Run all RSpec tests"
@@ -28,11 +31,16 @@
         end
 
         t.pattern = spec_files
+
+        if suite[:tag]
+          t.rspec_opts = "--tag #{suite[:tag]}"
+        end
       end
     end
   end
 end
 
-desc "Run tests"
-task :default => [ "spec:suite:wrapper", "spec:suite:idiomatic"]
-task :spec => :compile
+task :default => "spec:suite:idiomatic"  # this should be spec:suite:bidi
+task "spec:suite:wrapper" => :compile
+task "spec:suite:idiomatic" => "spec:suite:wrapper"
+task "spec:suite:bidi" => "spec:suite:idiomatic"
diff --git a/src/ruby/bin/math.pb.rb b/src/ruby/bin/math.pb.rb
index 9278a84..f6976be 100755
--- a/src/ruby/bin/math.pb.rb
+++ b/src/ruby/bin/math.pb.rb
@@ -51,7 +51,7 @@
     class Service
       include GRPC::GenericService
 
-      self.marshal_instance_method = :encode
+      self.marshal_class_method = :encode
       self.unmarshal_class_method = :decode
 
       rpc :Div, DivArgs, DivReply
diff --git a/src/ruby/bin/math_client.rb b/src/ruby/bin/math_client.rb
index f8cf858..947e86a 100644
--- a/src/ruby/bin/math_client.rb
+++ b/src/ruby/bin/math_client.rb
@@ -43,13 +43,16 @@
 require 'grpc/generic/client_stub'
 require 'grpc/generic/service'
 require 'math.pb'
+require 'optparse'
+
+include GRPC::Core::TimeConsts
 
 def do_div(stub)
   logger.info('request_response')
   logger.info('----------------')
   req = Math::DivArgs.new(:dividend => 7, :divisor => 3)
   logger.info("div(7/3): req=#{req.inspect}")
-  resp = stub.div(req, deadline=GRPC::TimeConsts::INFINITE_FUTURE)
+  resp = stub.div(req, deadline=INFINITE_FUTURE)
   logger.info("Answer: #{resp.inspect}")
   logger.info('----------------')
 end
@@ -70,7 +73,7 @@
   logger.info('----------------')
   req = Math::FibArgs.new(:limit => 11)
   logger.info("fib(11): req=#{req.inspect}")
-  resp = stub.fib(req, deadline=GRPC::TimeConsts::INFINITE_FUTURE)
+  resp = stub.fib(req, deadline=INFINITE_FUTURE)
   resp.each do |r|
     logger.info("Answer: #{r.inspect}")
   end
@@ -92,15 +95,51 @@
   logger.info('----------------')
 end
 
+def load_test_certs
+  this_dir = File.expand_path(File.dirname(__FILE__))
+  data_dir = File.join(File.dirname(this_dir), 'spec/testdata')
+  files = ['ca.pem', 'server1.key', 'server1.pem']
+  files.map { |f| File.open(File.join(data_dir, f)).read }
+end
+
+def test_creds
+  certs = load_test_certs
+  creds = GRPC::Core::Credentials.new(certs[0])
+end
 
 def main
-  host_port = 'localhost:7070'
-  if ARGV.size > 0
-    host_port = ARGV[0]
-  end
+  options = {
+    'host' => 'localhost:7071',
+    'secure' => false
+  }
+  OptionParser.new do |opts|
+    opts.banner = 'Usage: [--host|-h <hostname>:<port>] [--secure|-s]'
+    opts.on('-h', '--host', '<hostname>:<port>') do |v|
+      options['host'] = v
+    end
+    opts.on('-s', '--secure', 'access using test creds') do |v|
+      options['secure'] = true
+    end
+  end.parse!
+
   # The Math::Math:: module occurs because the service has the same name as its
   # package. That practice should be avoided by defining real services.
-  stub = Math::Math::Stub.new(host_port)
+
+  p options
+  if options['secure']
+    stub_opts = {
+      :creds => test_creds,
+      GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com',
+    }
+    p stub_opts
+    p options['host']
+    stub = Math::Math::Stub.new(options['host'], **stub_opts)
+    logger.info("... connecting securely on #{options['host']}")
+  else
+    stub = Math::Math::Stub.new(options['host'])
+    logger.info("... connecting insecurely on #{options['host']}")
+  end
+
   do_div(stub)
   do_sum(stub)
   do_fib(stub)
diff --git a/src/ruby/bin/math_server.rb b/src/ruby/bin/math_server.rb
index 72a1f6b..d21ae7e 100644
--- a/src/ruby/bin/math_server.rb
+++ b/src/ruby/bin/math_server.rb
@@ -44,6 +44,7 @@
 require 'grpc/generic/service'
 require 'grpc/generic/rpc_server'
 require 'math.pb'
+require 'optparse'
 
 # Holds state for a fibonacci series
 class Fibber
@@ -151,14 +152,43 @@
 
 end
 
+def load_test_certs
+  this_dir = File.expand_path(File.dirname(__FILE__))
+  data_dir = File.join(File.dirname(this_dir), 'spec/testdata')
+  files = ['ca.pem', 'server1.key', 'server1.pem']
+  files.map { |f| File.open(File.join(data_dir, f)).read }
+end
+
+def test_server_creds
+  certs = load_test_certs
+  server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
+end
+
 def main
-  host_port = 'localhost:7070'
-  if ARGV.size > 0
-    host_port = ARGV[0]
+  options = {
+    'host' => 'localhost:7071',
+    'secure' => false
+  }
+  OptionParser.new do |opts|
+    opts.banner = 'Usage: [--host|-h <hostname>:<port>] [--secure|-s]'
+    opts.on('-h', '--host', '<hostname>:<port>') do |v|
+      options['host'] = v
+    end
+    opts.on('-s', '--secure', 'access using test creds') do |v|
+      options['secure'] = true
+    end
+  end.parse!
+
+  if options['secure']
+    s = GRPC::RpcServer.new(creds: test_server_creds)
+    s.add_http2_port(options['host'], true)
+    logger.info("... running securely on #{options['host']}")
+  else
+    s = GRPC::RpcServer.new
+    s.add_http2_port(options['host'])
+    logger.info("... running insecurely on #{options['host']}")
   end
 
-  s = GRPC::RpcServer.new()
-  s.add_http2_port(host_port)
   s.handle(Calculator)
   s.run
 end
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index 06bfad9..9a181f5 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -80,7 +80,9 @@
 $CFLAGS << ' -Wall '
 $CFLAGS << ' -pedantic '
 
-$LDFLAGS << ' -lgrpc -lgpr -levent -levent_pthreads -levent_core'
+$LDFLAGS << ' -lgrpc -lgpr -levent -levent_pthreads -levent_core '
+$LDFLAGS << ' -lssl -lcrypto '
+$DLDFLAGS << ' -Wl,-rpath,/usr/local/ssl/lib '
 
 # crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
 #
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c
index a520ca4..b75e853 100644
--- a/src/ruby/ext/grpc/rb_byte_buffer.c
+++ b/src/ruby/ext/grpc/rb_byte_buffer.c
@@ -205,7 +205,7 @@
 VALUE rb_cByteBuffer = Qnil;
 
 void Init_google_rpc_byte_buffer() {
-  rb_cByteBuffer = rb_define_class_under(rb_mGoogleRPC, "ByteBuffer",
+  rb_cByteBuffer = rb_define_class_under(rb_mGoogleRpcCore, "ByteBuffer",
                                          rb_cObject);
 
   /* Allocates an object managed by the ruby runtime */
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index 07f70e0..5ea66e6 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -429,7 +429,7 @@
 
 void Init_google_rpc_error_codes() {
   /* Constants representing the error codes of grpc_call_error in grpc.h */
-  VALUE rb_RpcErrors = rb_define_module_under(rb_mGoogleRPC, "RpcErrors");
+  VALUE rb_RpcErrors = rb_define_module_under(rb_mGoogleRpcCore, "RpcErrors");
   rb_define_const(rb_RpcErrors, "OK", UINT2NUM(GRPC_CALL_OK));
   rb_define_const(rb_RpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR));
   rb_define_const(rb_RpcErrors, "NOT_ON_SERVER",
@@ -475,9 +475,9 @@
 
 void Init_google_rpc_call() {
   /* CallError inherits from Exception to signal that it is non-recoverable */
-  rb_eCallError = rb_define_class_under(rb_mGoogleRPC, "CallError",
+  rb_eCallError = rb_define_class_under(rb_mGoogleRpcCore, "CallError",
                                         rb_eException);
-  rb_cCall = rb_define_class_under(rb_mGoogleRPC, "Call", rb_cObject);
+  rb_cCall = rb_define_class_under(rb_mGoogleRpcCore, "Call", rb_cObject);
 
   /* Prevent allocation or inialization of the Call class */
   rb_define_alloc_func(rb_cCall, grpc_rb_cannot_alloc);
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index f4c09a3..dbced1c 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -36,10 +36,12 @@
 #include <ruby.h>
 
 #include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
 #include "rb_grpc.h"
 #include "rb_call.h"
 #include "rb_channel_args.h"
 #include "rb_completion_queue.h"
+#include "rb_credentials.h"
 #include "rb_server.h"
 
 /* id_channel is the name of the hidden ivar that preserves a reference to the
@@ -104,18 +106,36 @@
                           wrapper);
 }
 
-/* Initializes channel instances */
-static VALUE grpc_rb_channel_init(VALUE self, VALUE target,
-                                  VALUE channel_args) {
+/*
+  call-seq:
+    insecure_channel = Channel:new("myhost:8080", {'arg1': 'value1'})
+    creds = ...
+    secure_channel = Channel:new("myhost:443", {'arg1': 'value1'}, creds)
+
+  Creates channel instances. */
+static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
+  VALUE channel_args = Qnil;
+  VALUE credentials = Qnil;
+  VALUE target = Qnil;
   grpc_rb_channel *wrapper = NULL;
+  grpc_credentials *creds = NULL;
   grpc_channel *ch = NULL;
-  char *target_chars = StringValueCStr(target);
+  char *target_chars = NULL;
   grpc_channel_args args;
   MEMZERO(&args, grpc_channel_args, 1);
 
+  /* "21" == 2 mandatory args, 1 (credentials) is optional */
+  rb_scan_args(argc, argv, "21", &target, &channel_args, &credentials);
+
   Data_Get_Struct(self, grpc_rb_channel, wrapper);
+  target_chars = StringValueCStr(target);
   grpc_rb_hash_convert_to_channel_args(channel_args, &args);
-  ch = grpc_channel_create(target_chars, &args);
+  if (credentials == Qnil) {
+    ch = grpc_channel_create(target_chars, &args);
+  } else {
+    creds = grpc_rb_get_wrapped_credentials(credentials);
+    ch = grpc_secure_channel_create(creds, target_chars, &args);
+  }
   if (args.args != NULL) {
     xfree(args.args);   /* Allocated by grpc_rb_hash_convert_to_channel_args */
   }
@@ -208,13 +228,13 @@
 
 void Init_google_rpc_channel() {
   rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
-  rb_cChannel = rb_define_class_under(rb_mGoogleRPC, "Channel", rb_cObject);
+  rb_cChannel = rb_define_class_under(rb_mGoogleRpcCore, "Channel", rb_cObject);
 
   /* Allocates an object managed by the ruby runtime */
   rb_define_alloc_func(rb_cChannel, grpc_rb_channel_alloc);
 
   /* Provides a ruby constructor and support for dup/clone. */
-  rb_define_method(rb_cChannel, "initialize", grpc_rb_channel_init, 2);
+  rb_define_method(rb_cChannel, "initialize", grpc_rb_channel_init, -1);
   rb_define_method(rb_cChannel, "initialize_copy", grpc_rb_channel_init_copy,
                    1);
 
@@ -225,6 +245,8 @@
 
   id_channel = rb_intern("__channel");
   id_target = rb_intern("__target");
+  rb_define_const(rb_cChannel, "SSL_TARGET",
+                  ID2SYM(rb_intern(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)));
 }
 
 /* Gets the wrapped channel from the ruby wrapper */
diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
index 62d045e..dfde442 100644
--- a/src/ruby/ext/grpc/rb_completion_queue.c
+++ b/src/ruby/ext/grpc/rb_completion_queue.c
@@ -168,7 +168,7 @@
 VALUE rb_cCompletionQueue = Qnil;
 
 void Init_google_rpc_completion_queue() {
-  rb_cCompletionQueue = rb_define_class_under(rb_mGoogleRPC,
+  rb_cCompletionQueue = rb_define_class_under(rb_mGoogleRpcCore,
                                               "CompletionQueue",
                                               rb_cObject);
 
diff --git a/src/ruby/ext/grpc/rb_credentials.c b/src/ruby/ext/grpc/rb_credentials.c
new file mode 100644
index 0000000..658adac
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_credentials.c
@@ -0,0 +1,301 @@
+/*
+ *
+ * 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 "rb_credentials.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+
+#include "rb_grpc.h"
+
+
+/* grpc_rb_credentials wraps a grpc_credentials.  It provides a
+ * peer ruby object, 'mark' to minimize copying when a credential is
+ * created from ruby. */
+typedef struct grpc_rb_credentials {
+  /* Holder of ruby objects involved in constructing the credentials */
+  VALUE mark;
+
+  /* The actual credentials */
+  grpc_credentials *wrapped;
+} grpc_rb_credentials;
+
+/* Destroys the credentials instances. */
+static void grpc_rb_credentials_free(void *p) {
+  grpc_rb_credentials *wrapper = NULL;
+  if (p == NULL) {
+    return;
+  };
+  wrapper = (grpc_rb_credentials *)p;
+
+  /* Delete the wrapped object if the mark object is Qnil, which indicates that
+   * no other object is the actual owner. */
+  if (wrapper->wrapped != NULL && wrapper->mark == Qnil) {
+    grpc_credentials_release(wrapper->wrapped);
+    wrapper->wrapped = NULL;
+  }
+
+  xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_credentials_mark(void *p) {
+  grpc_rb_credentials *wrapper = NULL;
+  if (p == NULL) {
+    return;
+  }
+  wrapper = (grpc_rb_credentials *)p;
+
+  /* If it's not already cleaned up, mark the mark object */
+  if (wrapper->mark != Qnil) {
+    rb_gc_mark(wrapper->mark);
+  }
+}
+
+/* Allocates Credential instances.
+
+   Provides safe initial defaults for the instance fields. */
+static VALUE grpc_rb_credentials_alloc(VALUE cls) {
+  grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
+  wrapper->wrapped = NULL;
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
+                          grpc_rb_credentials_free,
+                          wrapper);
+}
+
+/* Clones Credentials instances.
+
+   Gives Credentials a consistent implementation of Ruby's object copy/dup
+   protocol. */
+static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) {
+  grpc_rb_credentials *orig_cred = NULL;
+  grpc_rb_credentials *copy_cred = NULL;
+
+  if (copy == orig) {
+    return copy;
+  }
+
+  /* Raise an error if orig is not a credentials object or a subclass. */
+  if (TYPE(orig) != T_DATA ||
+      RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_credentials_free) {
+    rb_raise(rb_eTypeError, "not a %s",
+             rb_obj_classname(rb_cCredentials));
+  }
+
+  Data_Get_Struct(orig, grpc_rb_credentials, orig_cred);
+  Data_Get_Struct(copy, grpc_rb_credentials, copy_cred);
+
+  /* use ruby's MEMCPY to make a byte-for-byte copy of the credentials
+   * wrapper object. */
+  MEMCPY(copy_cred, orig_cred, grpc_rb_credentials, 1);
+  return copy;
+}
+
+/*
+  call-seq:
+    creds = Credentials.default()
+
+    Creates the default credential instances. */
+static VALUE grpc_rb_default_credentials_create(VALUE cls) {
+  grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
+  wrapper->wrapped = grpc_default_credentials_create();
+  if (wrapper->wrapped == NULL) {
+    rb_raise(rb_eRuntimeError,
+             "could not create default credentials, not sure why");
+    return Qnil;
+  }
+
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
+                          grpc_rb_credentials_free, wrapper);
+}
+
+/*
+  call-seq:
+    creds = Credentials.compute_engine()
+
+    Creates the default credential instances. */
+static VALUE grpc_rb_compute_engine_credentials_create(VALUE cls) {
+  grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
+  wrapper->wrapped = grpc_compute_engine_credentials_create();
+  if (wrapper->wrapped == NULL) {
+    rb_raise(rb_eRuntimeError,
+             "could not create composite engine credentials, not sure why");
+    return Qnil;
+  }
+
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
+                          grpc_rb_credentials_free, wrapper);
+}
+
+/*
+  call-seq:
+    creds1 = ...
+    creds2 = ...
+    creds3 = creds1.add(creds2)
+
+    Creates the default credential instances. */
+static VALUE grpc_rb_composite_credentials_create(VALUE self, VALUE other) {
+  grpc_rb_credentials *self_wrapper = NULL;
+  grpc_rb_credentials *other_wrapper = NULL;
+  grpc_rb_credentials *wrapper = NULL;
+
+  Data_Get_Struct(self, grpc_rb_credentials, self_wrapper);
+  Data_Get_Struct(other, grpc_rb_credentials, other_wrapper);
+  wrapper = ALLOC(grpc_rb_credentials);
+  wrapper->wrapped = grpc_composite_credentials_create(self_wrapper->wrapped,
+                                                       other_wrapper->wrapped);
+  if (wrapper->wrapped == NULL) {
+    rb_raise(rb_eRuntimeError,
+             "could not create composite credentials, not sure why");
+    return Qnil;
+  }
+
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(rb_cCredentials, grpc_rb_credentials_mark,
+                          grpc_rb_credentials_free, wrapper);
+}
+
+/* The attribute used on the mark object to hold the pem_root_certs. */
+static ID id_pem_root_certs;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_private_key;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_cert_chain;
+
+/*
+  call-seq:
+    creds1 = Credentials.new(pem_root_certs)
+    ...
+    creds2 = Credentials.new(pem_root_certs, pem_private_key,
+                             pem_cert_chain)
+
+    pem_root_certs: (required) PEM encoding of the server root certificate
+    pem_private_key: (optional) PEM encoding of the client's private key
+    pem_cert_chain: (optional) PEM encoding of the client's cert chain
+
+    Initializes Credential instances. */
+static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
+  VALUE pem_root_certs = Qnil;
+  VALUE pem_private_key = Qnil;
+  VALUE pem_cert_chain = Qnil;
+  grpc_rb_credentials *wrapper = NULL;
+  grpc_credentials *creds = NULL;
+  /* "12" == 1 mandatory arg, 2 (credentials) is optional */
+  rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key,
+               &pem_cert_chain);
+
+  Data_Get_Struct(self, grpc_rb_credentials, wrapper);
+  if (pem_root_certs == Qnil) {
+    rb_raise(rb_eRuntimeError,
+             "could not create a credential: nil pem_root_certs");
+    return Qnil;
+  }
+  if (pem_private_key == Qnil && pem_cert_chain == Qnil) {
+    creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs),
+                                        RSTRING_LEN(pem_root_certs), NULL, 0,
+                                        NULL, 0);
+  } else if (pem_cert_chain == Qnil) {
+    creds = grpc_ssl_credentials_create(
+        RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+        RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+        RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+  } else if (pem_private_key == Qnil) {
+    creds = grpc_ssl_credentials_create(
+        RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+        NULL, 0,
+        RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+  } else {
+    creds = grpc_ssl_credentials_create(
+        RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+        RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+        NULL, 0);
+  }
+  if (creds == NULL) {
+    rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
+    return Qnil;
+  }
+  wrapper->wrapped = creds;
+
+  /* Add the input objects as hidden fields to preserve them. */
+  rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain);
+  rb_ivar_set(self, id_pem_private_key, pem_private_key);
+  rb_ivar_set(self, id_pem_root_certs, pem_root_certs);
+
+  return self;
+}
+
+/* rb_cCredentials is the ruby class that proxies grpc_credentials. */
+VALUE rb_cCredentials = Qnil;
+
+void Init_google_rpc_credentials() {
+  rb_cCredentials = rb_define_class_under(rb_mGoogleRpcCore, "Credentials",
+                                          rb_cObject);
+
+  /* Allocates an object managed by the ruby runtime */
+  rb_define_alloc_func(rb_cCredentials, grpc_rb_credentials_alloc);
+
+  /* Provides a ruby constructor and support for dup/clone. */
+  rb_define_method(rb_cCredentials, "initialize",
+                   grpc_rb_credentials_init, -1);
+  rb_define_method(rb_cCredentials, "initialize_copy",
+                   grpc_rb_credentials_init_copy, 1);
+
+  /* Provide static funcs that create new special instances. */
+  rb_define_singleton_method(rb_cCredentials, "default",
+                             grpc_rb_default_credentials_create, 0);
+
+  rb_define_singleton_method(rb_cCredentials, "compute_engine",
+                             grpc_rb_compute_engine_credentials_create, 0);
+
+  /* Provide other methods. */
+  rb_define_method(rb_cCredentials, "compose",
+                   grpc_rb_composite_credentials_create, 1);
+
+  id_pem_cert_chain = rb_intern("__pem_cert_chain");
+  id_pem_private_key = rb_intern("__pem_private_key");
+  id_pem_root_certs = rb_intern("__pem_root_certs");
+}
+
+/* Gets the wrapped grpc_credentials from the ruby wrapper */
+grpc_credentials* grpc_rb_get_wrapped_credentials(VALUE v) {
+  grpc_rb_credentials *wrapper = NULL;
+  Data_Get_Struct(v, grpc_rb_credentials, wrapper);
+  return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_credentials.h b/src/ruby/ext/grpc/rb_credentials.h
new file mode 100644
index 0000000..d18c88a
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_credentials.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_RB_CREDENTIALS_H_
+#define GRPC_RB_CREDENTIALS_H_
+
+#include <ruby.h>
+#include <grpc/grpc_security.h>
+
+/* rb_cCredentials is the ruby class whose instances proxy
+   grpc_credentials. */
+extern VALUE rb_cCredentials;
+
+/* Initializes the ruby Credentials class. */
+void Init_google_rpc_credentials();
+
+/* Gets the wrapped credentials from the ruby wrapper */
+grpc_credentials* grpc_rb_get_wrapped_credentials(VALUE v);
+
+#endif  /* GRPC_RB_CREDENTIALS_H_ */
diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c
index 6f542f9..6708ea3 100644
--- a/src/ruby/ext/grpc/rb_event.c
+++ b/src/ruby/ext/grpc/rb_event.c
@@ -245,9 +245,9 @@
 VALUE rb_eEventError = Qnil;
 
 void Init_google_rpc_event() {
-  rb_eEventError = rb_define_class_under(rb_mGoogleRPC, "EventError",
+  rb_eEventError = rb_define_class_under(rb_mGoogleRpcCore, "EventError",
                                          rb_eStandardError);
-  rb_cEvent = rb_define_class_under(rb_mGoogleRPC, "Event", rb_cObject);
+  rb_cEvent = rb_define_class_under(rb_mGoogleRpcCore, "Event", rb_cObject);
   rb_sNewServerRpc = rb_struct_define("NewServerRpc", "method", "host",
                                       "deadline", "metadata", NULL);
 
@@ -263,7 +263,7 @@
   rb_define_method(rb_cEvent, "type", grpc_rb_event_type, 0);
 
   /* Constants representing the completion types */
-  rb_mCompletionType = rb_define_module_under(rb_mGoogleRPC, "CompletionType");
+  rb_mCompletionType = rb_define_module_under(rb_mGoogleRpcCore, "CompletionType");
   rb_define_const(rb_mCompletionType, "QUEUE_SHUTDOWN",
                   INT2NUM(GRPC_QUEUE_SHUTDOWN));
   rb_define_const(rb_mCompletionType, "READ", INT2NUM(GRPC_READ));
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
index 5cc45cf..9c54a05 100644
--- a/src/ruby/ext/grpc/rb_grpc.c
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -46,6 +46,8 @@
 #include "rb_event.h"
 #include "rb_metadata.h"
 #include "rb_server.h"
+#include "rb_credentials.h"
+#include "rb_server_credentials.h"
 #include "rb_status.h"
 
 /* Define common vars and funcs declared in rb.h */
@@ -184,8 +186,10 @@
 
 /* Adds a module with constants that map to gpr's static timeval structs. */
 void Init_google_time_consts() {
-  VALUE rb_mTimeConsts = rb_define_module_under(rb_mGoogleRPC, "TimeConsts");
-  rb_cTimeVal = rb_define_class_under(rb_mGoogleRPC, "TimeSpec", rb_cObject);
+  VALUE rb_mTimeConsts = rb_define_module_under(rb_mGoogleRpcCore,
+                                                "TimeConsts");
+  rb_cTimeVal = rb_define_class_under(rb_mGoogleRpcCore, "TimeSpec",
+                                      rb_cObject);
   rb_define_const(rb_mTimeConsts, "ZERO",
                   Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
                                    GC_DONT_FREE, (void *)&gpr_time_0));
@@ -212,19 +216,23 @@
 /* Initialize the Google RPC module. */
 VALUE rb_mGoogle = Qnil;
 VALUE rb_mGoogleRPC = Qnil;
+VALUE rb_mGoogleRpcCore = Qnil;
 void Init_grpc() {
   grpc_init();
   ruby_vm_at_exit(grpc_rb_shutdown);
   rb_mGoogle = rb_define_module("Google");
   rb_mGoogleRPC = rb_define_module_under(rb_mGoogle, "RPC");
+  rb_mGoogleRpcCore = rb_define_module_under(rb_mGoogleRPC, "Core");
 
   Init_google_rpc_byte_buffer();
   Init_google_rpc_event();
   Init_google_rpc_channel();
   Init_google_rpc_completion_queue();
   Init_google_rpc_call();
+  Init_google_rpc_credentials();
   Init_google_rpc_metadata();
   Init_google_rpc_server();
+  Init_google_rpc_server_credentials();
   Init_google_rpc_status();
   Init_google_time_consts();
 }
diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h
index fd43c37..68f8a06 100644
--- a/src/ruby/ext/grpc/rb_grpc.h
+++ b/src/ruby/ext/grpc/rb_grpc.h
@@ -41,8 +41,8 @@
 /* rb_mGoogle is the top-level Google module. */
 extern VALUE rb_mGoogle;
 
-/* rb_mGoogleRPC is the module containing all the ruby wrapper GRPC classes. */
-extern VALUE rb_mGoogleRPC;
+/* rb_mGoogleRpcCore is the module containing the ruby wrapper GRPC classes. */
+extern VALUE rb_mGoogleRpcCore;
 
 /* Class used to wrap timeval structs. */
 extern VALUE rb_cTimeVal;
diff --git a/src/ruby/ext/grpc/rb_metadata.c b/src/ruby/ext/grpc/rb_metadata.c
index 13d515a..733a53a 100644
--- a/src/ruby/ext/grpc/rb_metadata.c
+++ b/src/ruby/ext/grpc/rb_metadata.c
@@ -189,7 +189,7 @@
 /* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */
 VALUE rb_cMetadata = Qnil;
 void Init_google_rpc_metadata() {
-  rb_cMetadata = rb_define_class_under(rb_mGoogleRPC, "Metadata", rb_cObject);
+  rb_cMetadata = rb_define_class_under(rb_mGoogleRpcCore, "Metadata", rb_cObject);
 
   /* Allocates an object managed by the ruby runtime */
   rb_define_alloc_func(rb_cMetadata, grpc_rb_metadata_alloc);
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index f4230bd..84fba5b 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -36,16 +36,18 @@
 #include <ruby.h>
 
 #include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
 #include "rb_call.h"
 #include "rb_channel_args.h"
 #include "rb_completion_queue.h"
+#include "rb_server_credentials.h"
 #include "rb_grpc.h"
 
 /* rb_cServer is the ruby class that proxies grpc_server. */
 VALUE rb_cServer = Qnil;
 
 /* grpc_rb_server wraps a grpc_server.  It provides a peer ruby object,
- * 'mark' to minimize copying when a server is created from ruby. */
+  'mark' to minimize copying when a server is created from ruby. */
 typedef struct grpc_rb_server {
   /* Holder of ruby objects involved in constructing the server */
   VALUE mark;
@@ -62,7 +64,7 @@
   svr = (grpc_rb_server *)p;
 
   /* Deletes the wrapped object if the mark object is Qnil, which indicates
-   * that no other object is the actual owner. */
+     that no other object is the actual owner. */
   if (svr->wrapped != NULL && svr->mark == Qnil) {
     grpc_server_shutdown(svr->wrapped);
     grpc_server_destroy(svr->wrapped);
@@ -92,17 +94,39 @@
                           wrapper);
 }
 
-/* Initializes Server instances. */
-static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
-  grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+/*
+  call-seq:
+    cq = CompletionQueue.new
+    insecure_server = Server.new(cq, {'arg1': 'value1'})
+    server_creds = ...
+    secure_server = Server.new(cq, {'arg1': 'value1'}, server_creds)
+
+  Initializes server instances. */
+static VALUE grpc_rb_server_init(int argc, VALUE *argv, VALUE self) {
+  VALUE cqueue = Qnil;
+  VALUE credentials = Qnil;
+  VALUE channel_args = Qnil;
+  grpc_completion_queue *cq = NULL;
+  grpc_server_credentials *creds = NULL;
   grpc_rb_server *wrapper = NULL;
   grpc_server *srv = NULL;
   grpc_channel_args args;
   MEMZERO(&args, grpc_channel_args, 1);
 
+  /* "21" == 2 mandatory args, 1 (credentials) is optional */
+  rb_scan_args(argc, argv, "21", &cqueue, &channel_args, &credentials);
+  cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+
   Data_Get_Struct(self, grpc_rb_server, wrapper);
   grpc_rb_hash_convert_to_channel_args(channel_args, &args);
   srv = grpc_server_create(cq, &args);
+  if (credentials == Qnil) {
+    srv = grpc_server_create(cq, &args);
+  } else {
+    creds = grpc_rb_get_wrapped_server_credentials(credentials);
+    srv = grpc_secure_server_create(creds, cq, &args);
+  }
+
   if (args.args != NULL) {
     xfree(args.args);  /* Allocated by grpc_rb_hash_convert_to_channel_args */
   }
@@ -112,7 +136,7 @@
   wrapper->wrapped = srv;
 
   /* Add the cq as the server's mark object. This ensures the ruby cq can't be
-   * GCed before the server */
+     GCed before the server */
   wrapper->mark = cqueue;
   return self;
 }
@@ -139,7 +163,7 @@
   Data_Get_Struct(copy, grpc_rb_server, copy_srv);
 
   /* use ruby's MEMCPY to make a byte-for-byte copy of the server wrapper
-   * object. */
+     object. */
   MEMCPY(copy_srv, orig_srv, grpc_rb_server, 1);
   return copy;
 }
@@ -183,16 +207,44 @@
   return Qnil;
 }
 
-static VALUE grpc_rb_server_add_http2_port(VALUE self, VALUE port) {
+/*
+  call-seq:
+    // insecure port
+    insecure_server = Server.new(cq, {'arg1': 'value1'})
+    insecure_server.add_http2_port('mydomain:7575')
+
+    // secure port
+    server_creds = ...
+    secure_server = Server.new(cq, {'arg1': 'value1'}, creds)
+    secure_server.add_http_port('mydomain:7575', True)
+
+    Adds a http2 port to server */
+static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
+  VALUE port = Qnil;
+  VALUE is_secure = Qnil;
   grpc_rb_server *s = NULL;
   int added_ok = 0;
+
+  /* "11" == 1 mandatory args, 1 (is_secure) is optional */
+  rb_scan_args(argc, argv, "11", &port, &is_secure);
+
   Data_Get_Struct(self, grpc_rb_server, s);
   if (s->wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
-  } else {
+    return Qnil;
+  } else if (is_secure == Qnil || TYPE(is_secure) != T_TRUE) {
     added_ok = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
     if (added_ok == 0) {
-      rb_raise(rb_eRuntimeError, "could not add port %s to server, not sure why",
+      rb_raise(rb_eRuntimeError,
+               "could not add port %s to server, not sure why",
+               StringValueCStr(port));
+    }
+  } else if (TYPE(is_secure) != T_FALSE) {
+    added_ok = grpc_server_add_secure_http2_port(s->wrapped,
+                                                 StringValueCStr(port));
+    if (added_ok == 0) {
+      rb_raise(rb_eRuntimeError,
+               "could not add secure port %s to server, not sure why",
                StringValueCStr(port));
     }
   }
@@ -200,13 +252,13 @@
 }
 
 void Init_google_rpc_server() {
-  rb_cServer = rb_define_class_under(rb_mGoogleRPC, "Server", rb_cObject);
+  rb_cServer = rb_define_class_under(rb_mGoogleRpcCore, "Server", rb_cObject);
 
   /* Allocates an object managed by the ruby runtime */
   rb_define_alloc_func(rb_cServer, grpc_rb_server_alloc);
 
   /* Provides a ruby constructor and support for dup/clone. */
-  rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, 2);
+  rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, -1);
   rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1);
 
   /* Add the server methods. */
@@ -215,7 +267,7 @@
   rb_define_method(rb_cServer, "destroy", grpc_rb_server_destroy, 0);
   rb_define_alias(rb_cServer, "close", "destroy");
   rb_define_method(rb_cServer, "add_http2_port", grpc_rb_server_add_http2_port,
-                   1);
+                   -1);
 }
 
 /* Gets the wrapped server from the ruby wrapper */
diff --git a/src/ruby/ext/grpc/rb_server.h b/src/ruby/ext/grpc/rb_server.h
index 4619203..7fcd32c 100644
--- a/src/ruby/ext/grpc/rb_server.h
+++ b/src/ruby/ext/grpc/rb_server.h
@@ -31,7 +31,7 @@
  *
  */
 
-#ifndef GRPC_RB_BYTE_BUFFER_H_
+#ifndef GRPC_RB_SERVER_H_
 #define GRPC_RB_SERVER_H_
 
 #include <ruby.h>
diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c
new file mode 100644
index 0000000..fa0d6f2
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server_credentials.c
@@ -0,0 +1,215 @@
+/*
+ *
+ * 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 "rb_server_credentials.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+
+#include "rb_grpc.h"
+
+
+/* grpc_rb_server_credentials wraps a grpc_server_credentials.  It provides a
+   peer ruby object, 'mark' to minimize copying when a server credential is
+   created from ruby. */
+typedef struct grpc_rb_server_credentials {
+  /* Holder of ruby objects involved in constructing the server credentials */
+  VALUE mark;
+  /* The actual server credentials */
+  grpc_server_credentials *wrapped;
+} grpc_rb_server_credentials;
+
+/* Destroys the server credentials instances. */
+static void grpc_rb_server_credentials_free(void *p) {
+  grpc_rb_server_credentials *wrapper = NULL;
+  if (p == NULL) {
+    return;
+  };
+  wrapper = (grpc_rb_server_credentials *)p;
+
+  /* Delete the wrapped object if the mark object is Qnil, which indicates that
+     no other object is the actual owner. */
+  if (wrapper->wrapped != NULL && wrapper->mark == Qnil) {
+    grpc_server_credentials_release(wrapper->wrapped);
+    wrapper->wrapped = NULL;
+  }
+
+  xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_server_credentials_mark(void *p) {
+  grpc_rb_server_credentials *wrapper = NULL;
+  if (p == NULL) {
+    return;
+  }
+  wrapper = (grpc_rb_server_credentials *)p;
+
+  /* If it's not already cleaned up, mark the mark object */
+  if (wrapper->mark != Qnil) {
+    rb_gc_mark(wrapper->mark);
+  }
+}
+
+/* Allocates ServerCredential instances.
+
+   Provides safe initial defaults for the instance fields. */
+static VALUE grpc_rb_server_credentials_alloc(VALUE cls) {
+  grpc_rb_server_credentials *wrapper = ALLOC(grpc_rb_server_credentials);
+  wrapper->wrapped = NULL;
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_server_credentials_mark,
+                          grpc_rb_server_credentials_free,
+                          wrapper);
+}
+
+/* Clones ServerCredentials instances.
+
+   Gives ServerCredentials a consistent implementation of Ruby's object copy/dup
+   protocol. */
+static VALUE grpc_rb_server_credentials_init_copy(VALUE copy, VALUE orig) {
+  grpc_rb_server_credentials *orig_ch = NULL;
+  grpc_rb_server_credentials *copy_ch = NULL;
+
+  if (copy == orig) {
+    return copy;
+  }
+
+  /* Raise an error if orig is not a server_credentials object or a subclass. */
+  if (TYPE(orig) != T_DATA ||
+      RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_credentials_free) {
+    rb_raise(rb_eTypeError, "not a %s",
+             rb_obj_classname(rb_cServerCredentials));
+  }
+
+  Data_Get_Struct(orig, grpc_rb_server_credentials, orig_ch);
+  Data_Get_Struct(copy, grpc_rb_server_credentials, copy_ch);
+
+  /* use ruby's MEMCPY to make a byte-for-byte copy of the server_credentials
+     wrapper object. */
+  MEMCPY(copy_ch, orig_ch, grpc_rb_server_credentials, 1);
+  return copy;
+}
+
+
+/* The attribute used on the mark object to hold the pem_root_certs. */
+static ID id_pem_root_certs;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_private_key;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_cert_chain;
+
+/*
+  call-seq:
+    creds = ServerCredentials.new(pem_root_certs, pem_private_key,
+                                  pem_cert_chain)
+    creds = ServerCredentials.new(nil, pem_private_key,
+                                  pem_cert_chain)
+
+    pem_root_certs: (required) PEM encoding of the server root certificate
+    pem_private_key: (optional) PEM encoding of the server's private key
+    pem_cert_chain: (optional) PEM encoding of the server's cert chain
+
+    Initializes ServerCredential instances. */
+static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
+                                             VALUE pem_private_key,
+                                             VALUE pem_cert_chain) {
+  grpc_rb_server_credentials *wrapper = NULL;
+  grpc_server_credentials *creds = NULL;
+  Data_Get_Struct(self, grpc_rb_server_credentials, wrapper);
+  if (pem_cert_chain == Qnil) {
+    rb_raise(rb_eRuntimeError,
+             "could not create a server credential: nil pem_cert_chain");
+    return Qnil;
+  } else if (pem_private_key == Qnil) {
+    rb_raise(rb_eRuntimeError,
+             "could not create a server credential: nil pem_private_key");
+    return Qnil;
+  }
+  if (pem_root_certs == Qnil) {
+    creds = grpc_ssl_server_credentials_create(
+        NULL, 0, RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+        RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+  } else {
+    creds = grpc_ssl_server_credentials_create(
+        RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+        RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+        RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+  }
+  if (creds == NULL) {
+    rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
+  }
+  wrapper->wrapped = creds;
+
+  /* Add the input objects as hidden fields to preserve them. */
+  rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain);
+  rb_ivar_set(self, id_pem_private_key, pem_private_key);
+  rb_ivar_set(self, id_pem_root_certs, pem_root_certs);
+
+  return self;
+}
+
+/* rb_cServerCredentials is the ruby class that proxies
+   grpc_server_credentials. */
+VALUE rb_cServerCredentials = Qnil;
+
+void Init_google_rpc_server_credentials() {
+  rb_cServerCredentials = rb_define_class_under(rb_mGoogleRpcCore,
+                                                "ServerCredentials",
+                                                rb_cObject);
+
+  /* Allocates an object managed by the ruby runtime */
+  rb_define_alloc_func(rb_cServerCredentials, grpc_rb_server_credentials_alloc);
+
+  /* Provides a ruby constructor and support for dup/clone. */
+  rb_define_method(rb_cServerCredentials, "initialize",
+                   grpc_rb_server_credentials_init, 3);
+  rb_define_method(rb_cServerCredentials, "initialize_copy",
+                   grpc_rb_server_credentials_init_copy,
+                   1);
+
+  id_pem_cert_chain = rb_intern("__pem_cert_chain");
+  id_pem_private_key = rb_intern("__pem_private_key");
+  id_pem_root_certs = rb_intern("__pem_root_certs");
+}
+
+/* Gets the wrapped grpc_server_credentials from the ruby wrapper */
+grpc_server_credentials* grpc_rb_get_wrapped_server_credentials(VALUE v) {
+  grpc_rb_server_credentials *wrapper = NULL;
+  Data_Get_Struct(v, grpc_rb_server_credentials, wrapper);
+  return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_server_credentials.h b/src/ruby/ext/grpc/rb_server_credentials.h
new file mode 100644
index 0000000..3918951
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server_credentials.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_RB_SERVER_CREDENTIALS_H_
+#define GRPC_RB_SERVER_CREDENTIALS_H_
+
+#include <ruby.h>
+#include <grpc/grpc_security.h>
+
+/* rb_cServerCredentials is the ruby class whose instances proxy
+   grpc_server_credentials. */
+extern VALUE rb_cServerCredentials;
+
+/* Initializes the ruby ServerCredentials class. */
+void Init_google_rpc_server_credentials();
+
+/* Gets the wrapped server_credentials from the ruby wrapper */
+grpc_server_credentials* grpc_rb_get_wrapped_server_credentials(VALUE v);
+
+#endif  /* GRPC_RB_SERVER_CREDENTIALS_H_ */
diff --git a/src/ruby/ext/grpc/rb_status.c b/src/ruby/ext/grpc/rb_status.c
index 747c47c..4c1b6c7 100644
--- a/src/ruby/ext/grpc/rb_status.c
+++ b/src/ruby/ext/grpc/rb_status.c
@@ -172,7 +172,8 @@
 
 void Init_google_status_codes() {
   /* Constants representing the status codes or grpc_status_code in status.h */
-  VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRPC, "StatusCodes");
+  VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRpcCore,
+                                                 "StatusCodes");
   rb_define_const(rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
   rb_define_const(rb_mStatusCodes, "CANCELLED", INT2NUM(GRPC_STATUS_CANCELLED));
   rb_define_const(rb_mStatusCodes, "UNKNOWN", INT2NUM(GRPC_STATUS_UNKNOWN));
@@ -207,7 +208,7 @@
 
 /* Initializes the Status class. */
 void Init_google_rpc_status() {
-  rb_cStatus = rb_define_class_under(rb_mGoogleRPC, "Status", rb_cObject);
+  rb_cStatus = rb_define_class_under(rb_mGoogleRpcCore, "Status", rb_cObject);
 
   /* Allocates an object whose memory is managed by the Ruby. */
   rb_define_alloc_func(rb_cStatus, grpc_rb_status_alloc);
diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb
index 60a3b96..1012a83 100644
--- a/src/ruby/lib/grpc.rb
+++ b/src/ruby/lib/grpc.rb
@@ -27,12 +27,13 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-require 'grpc/event'
+require 'grpc/beefcake'  # extends beefcake
 require 'grpc/errors'
 require 'grpc/grpc'
 require 'grpc/logconfig'
-require 'grpc/time_consts'
 require 'grpc/version'
+require 'grpc/core/event'
+require 'grpc/core/time_consts'
 
 # alias GRPC
 GRPC = Google::RPC
diff --git a/src/ruby/lib/grpc/event.rb b/src/ruby/lib/grpc/beefcake.rb
similarity index 64%
copy from src/ruby/lib/grpc/event.rb
copy to src/ruby/lib/grpc/beefcake.rb
index c108cd4..e8d7f0c 100644
--- a/src/ruby/lib/grpc/event.rb
+++ b/src/ruby/lib/grpc/beefcake.rb
@@ -27,12 +27,36 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-module Google
-  module RPC
-    class Event  # Add an inspect method to C-defined Event class.
-      def inspect
-        '<%s: type:%s, tag:%s result:%s>' % [self.class, type, tag, result]
+require 'beefcake'
+
+# Re-open the beefcake message module to add a static encode
+#
+# This is a temporary measure while beefcake is used as the default proto
+# library for developing grpc ruby.  Once that changes to the official proto
+# library this can be removed.  It's necessary to allow the update the service
+# module to assume a static encode method.
+#
+# TODO(temiola): remove me, once official code generation is available in protoc
+module Beefcake
+  module Message
+
+    # additional mixin module that adds static encode method when include
+    module StaticEncode
+
+      # encodes o with its instance#encode method
+      def encode(o)
+        o.encode
       end
+
     end
+
+    # extend self.included in Beefcake::Message to include StaticEncode
+    def self.included(o)
+      o.extend StaticEncode
+      o.extend Dsl
+      o.extend Decode
+      o.send(:include, Encode)
+    end
+
   end
 end
diff --git a/src/ruby/lib/grpc/event.rb b/src/ruby/lib/grpc/core/event.rb
similarity index 88%
rename from src/ruby/lib/grpc/event.rb
rename to src/ruby/lib/grpc/core/event.rb
index c108cd4..2948676 100644
--- a/src/ruby/lib/grpc/event.rb
+++ b/src/ruby/lib/grpc/core/event.rb
@@ -29,9 +29,11 @@
 
 module Google
   module RPC
-    class Event  # Add an inspect method to C-defined Event class.
-      def inspect
-        '<%s: type:%s, tag:%s result:%s>' % [self.class, type, tag, result]
+    module Core
+      class Event  # Add an inspect method to C-defined Event class.
+        def inspect
+          '<%s: type:%s, tag:%s result:%s>' % [self.class, type, tag, result]
+        end
       end
     end
   end
diff --git a/src/ruby/lib/grpc/core/time_consts.rb b/src/ruby/lib/grpc/core/time_consts.rb
new file mode 100644
index 0000000..52e4c3f
--- /dev/null
+++ b/src/ruby/lib/grpc/core/time_consts.rb
@@ -0,0 +1,72 @@
+# 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.
+
+require 'grpc'
+
+module Google
+  module RPC
+    module Core
+
+      module TimeConsts  # re-opens a module in the C extension.
+
+        # Converts a time delta to an absolute deadline.
+        #
+        # Assumes timeish is a relative time, and converts its to an absolute,
+        # with following exceptions:
+        #
+        # * if timish is one of the TimeConsts.TimeSpec constants the value is
+        # preserved.
+        # * timish < 0 => TimeConsts.INFINITE_FUTURE
+        # * timish == 0 => TimeConsts.ZERO
+        #
+        # @param timeish [Number|TimeSpec]
+        # @return timeish [Number|TimeSpec]
+        def from_relative_time(timeish)
+          if timeish.is_a?TimeSpec
+            timeish
+          elsif timeish.nil?
+            TimeConsts::ZERO
+          elsif !timeish.is_a?Numeric
+            raise TypeError('Cannot make an absolute deadline from %s',
+                            timeish.inspect)
+          elsif timeish < 0
+            TimeConsts::INFINITE_FUTURE
+          elsif timeish == 0
+            TimeConsts::ZERO
+          else !timeish.nil?
+            Time.now + timeish
+          end
+        end
+
+        module_function :from_relative_time
+
+      end
+    end
+  end
+end
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index d987b39..de35f27 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -40,8 +40,9 @@
   # The ActiveCall class provides simple methods for sending marshallable
   # data to a call
   class ActiveCall
-    include CompletionType
-    include StatusCodes
+    include Core::CompletionType
+    include Core::StatusCodes
+    include Core::TimeConsts
     attr_reader(:deadline)
 
     # client_start_invoke begins a client invocation.
@@ -55,15 +56,15 @@
     # @param q [CompletionQueue] used to wait for INVOKE_ACCEPTED
     # @param deadline [Fixnum,TimeSpec] the deadline for INVOKE_ACCEPTED
     def self.client_start_invoke(call, q, deadline)
-      raise ArgumentError.new('not a call') unless call.is_a?Call
-      if !q.is_a?CompletionQueue
+      raise ArgumentError.new('not a call') unless call.is_a?Core::Call
+      if !q.is_a?Core::CompletionQueue
         raise ArgumentError.new('not a CompletionQueue')
       end
       invoke_accepted, client_metadata_read = Object.new, Object.new
       finished_tag = Object.new
       call.start_invoke(q, invoke_accepted, client_metadata_read, finished_tag)
       # wait for the invocation to be accepted
-      ev = q.pluck(invoke_accepted, TimeConsts::INFINITE_FUTURE)
+      ev = q.pluck(invoke_accepted, INFINITE_FUTURE)
       raise OutOfTime if ev.nil?
       finished_tag
     end
@@ -93,8 +94,8 @@
     # @param started [true|false] (default true) indicates if the call has begun
     def initialize(call, q, marshal, unmarshal, deadline, finished_tag: nil,
                    started: true)
-      raise ArgumentError.new('not a call') unless call.is_a?Call
-      if !q.is_a?CompletionQueue
+      raise ArgumentError.new('not a call') unless call.is_a?Core::Call
+      if !q.is_a?Core::CompletionQueue
         raise ArgumentError.new('not a CompletionQueue')
       end
       @call = call
@@ -178,11 +179,11 @@
     # FINISHED.
     def writes_done(assert_finished=true)
       @call.writes_done(self)
-      ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+      ev = @cq.pluck(self, INFINITE_FUTURE)
       assert_event_type(ev.type, FINISH_ACCEPTED)
       logger.debug("Writes done: waiting for finish? #{assert_finished}")
       if assert_finished
-        ev = @cq.pluck(@finished_tag, TimeConsts::INFINITE_FUTURE)
+        ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
         raise "unexpected event: #{ev.inspect}" if ev.nil?
         return @call.status
       end
@@ -193,9 +194,9 @@
     # It blocks until the remote endpoint acknowledges by sending a FINISHED
     # event.
     def finished
-      ev = @cq.pluck(@finished_tag, TimeConsts::INFINITE_FUTURE)
+      ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
       raise "unexpected event: #{ev.inspect}" unless ev.type == FINISHED
-      if ev.result.code != StatusCodes::OK
+      if ev.result.code != Core::StatusCodes::OK
         raise BadStatus.new(ev.result.code, ev.result.details)
       end
       res = ev.result
@@ -223,11 +224,11 @@
       else
         payload = @marshal.call(req)
       end
-      @call.start_write(ByteBuffer.new(payload), self)
+      @call.start_write(Core::ByteBuffer.new(payload), self)
 
       # call queue#pluck, and wait for WRITE_ACCEPTED, so as not to return
       # until the flow control allows another send on this call.
-      ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+      ev = @cq.pluck(self, INFINITE_FUTURE)
       assert_event_type(ev.type, WRITE_ACCEPTED)
       ev = nil
     end
@@ -240,8 +241,8 @@
     # FINISHED.
     def send_status(code=OK, details='', assert_finished=false)
       assert_queue_is_ready
-      @call.start_write_status(Status.new(code, details), self)
-      ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+      @call.start_write_status(Core::Status.new(code, details), self)
+      ev = @cq.pluck(self, INFINITE_FUTURE)
       assert_event_type(ev.type, FINISH_ACCEPTED)
       logger.debug("Status sent: #{code}:'#{details}'")
       if assert_finished
@@ -257,7 +258,7 @@
     # FINISHED, it returns nil if the status is OK, otherwise raising BadStatus
     def remote_read
       @call.start_read(self)
-      ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+      ev = @cq.pluck(self, INFINITE_FUTURE)
       assert_event_type(ev.type, READ)
       logger.debug("received req: #{ev.result.inspect}")
       if !ev.result.nil?
@@ -291,7 +292,7 @@
       return enum_for(:each_remote_read) if !block_given?
       loop do
         resp = remote_read()
-        break if resp.is_a?Status  # this will be an OK status, bad statii raise
+        break if resp.is_a?Core::Status  # is an OK status, bad statii raise
         break if resp.nil?  # the last response was received
         yield resp
       end
@@ -321,7 +322,7 @@
       return enum_for(:each_remote_read_then_finish) if !block_given?
       loop do
         resp = remote_read
-        break if resp.is_a?Status  # this will be an OK status, bad statii raise
+        break if resp.is_a?Core::Status  # is an OK status, bad statii raise
         if resp.nil?  # the last response was received, but not finished yet
           finished
           break
@@ -339,7 +340,7 @@
       remote_send(req)
       writes_done(false)
       response = remote_read
-      if !response.is_a?(Status)  # finish if status not yet received
+      if !response.is_a?(Core::Status)  # finish if status not yet received
         finished
       end
       response
@@ -360,7 +361,7 @@
       requests.each { |r| remote_send(r) }
       writes_done(false)
       response = remote_read
-      if !response.is_a?(Status)  # finish if status not yet received
+      if !response.is_a?(Core::Status)  # finish if status not yet received
         finished
       end
       response
@@ -472,7 +473,7 @@
     # shutdown.
     def assert_queue_is_ready
       begin
-        ev = @cq.pluck(self, TimeConsts::ZERO)
+        ev = @cq.pluck(self, ZERO)
         raise "unexpected event #{ev.inspect}" unless ev.nil?
       rescue OutOfTime
         # expected, nothing should be on the queue and the deadline was ZERO,
diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb
index a3566e1..91ceaa9 100644
--- a/src/ruby/lib/grpc/generic/bidi_call.rb
+++ b/src/ruby/lib/grpc/generic/bidi_call.rb
@@ -35,8 +35,9 @@
   # The BiDiCall class orchestrates exection of a BiDi stream on a client or
   # server.
   class BidiCall
-    include CompletionType
-    include StatusCodes
+    include Core::CompletionType
+    include Core::StatusCodes
+    include Core::TimeConsts
 
     # Creates a BidiCall.
     #
@@ -59,8 +60,8 @@
     # @param deadline [Fixnum] the deadline for the call to complete
     # @param finished_tag [Object] the object used as the call's finish tag,
     def initialize(call, q, marshal, unmarshal, deadline, finished_tag)
-      raise ArgumentError.new('not a call') unless call.is_a?Call
-      if !q.is_a?CompletionQueue
+      raise ArgumentError.new('not a call') unless call.is_a?Core::Call
+      if !q.is_a?Core::CompletionQueue
         raise ArgumentError.new('not a CompletionQueue')
       end
       @call = call
@@ -210,7 +211,7 @@
 
       # send the payload
       payload = @marshal.call(req)
-      @call.start_write(ByteBuffer.new(payload), self)
+      @call.start_write(Core::ByteBuffer.new(payload), self)
       logger.debug("rwloop: sent payload #{req.inspect}")
       in_write = true
       return [in_write, done_writing]
@@ -259,10 +260,10 @@
           logger.debug('waiting for another event')
           if in_write or !done_reading or !pre_finished
             logger.debug('waiting for another event')
-            ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+            ev = @cq.pluck(self, INFINITE_FUTURE)
           elsif !finished
             logger.debug('waiting for another event')
-            ev = @cq.pluck(@finish_tag, TimeConsts::INFINITE_FUTURE)
+            ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
           else
             next  # no events to wait on, but not done writing
           end
@@ -270,7 +271,7 @@
           break if done_writing and done_reading
           if in_write or !done_reading
             logger.debug('waiting for another event')
-            ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+            ev = @cq.pluck(self, INFINITE_FUTURE)
           else
             next  # no events to wait on, but not done writing
           end
diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb
index fee31e3..144f9d9 100644
--- a/src/ruby/lib/grpc/generic/client_stub.rb
+++ b/src/ruby/lib/grpc/generic/client_stub.rb
@@ -35,7 +35,7 @@
 
   # ClientStub represents an endpoint used to send requests to GRPC servers.
   class ClientStub
-    include StatusCodes
+    include Core::StatusCodes
 
     # Default deadline is 5 seconds.
     DEFAULT_DEADLINE = 5
@@ -62,23 +62,29 @@
     # when present, this is the default deadline used for calls
     #
     # @param host [String] the host the stub connects to
-    # @param q [TaggedCompletionQueue] used to wait for events
-    # @param channel_override [Channel] a pre-created channel
+    # @param q [Core::CompletionQueue] used to wait for events
+    # @param channel_override [Core::Channel] a pre-created channel
     # @param deadline [Number] the default deadline to use in requests
+    # @param creds [Core::Credentials] secures and/or authenticates the channel
     # @param kw [KeywordArgs] the channel arguments
     def initialize(host, q,
                    channel_override:nil,
                    deadline:DEFAULT_DEADLINE,
+                   creds:nil,
                    **kw)
-      if !q.is_a?CompletionQueue
+      if !q.is_a?Core::CompletionQueue
         raise ArgumentError.new('not a CompletionQueue')
       end
       @host = host
       if !channel_override.nil?
         ch = channel_override
-        raise ArgumentError.new('not a Channel') unless ch.is_a?(Channel)
+        raise ArgumentError.new('not a Channel') unless ch.is_a?(Core::Channel)
+      elsif creds.nil?
+        ch = Core::Channel.new(host, kw)
+      elsif !creds.is_a?(Core::Credentials)
+        raise ArgumentError.new('not a Credentials')
       else
-        ch = Channel.new(host, **kw)
+        ch = Core::Channel.new(host, kw, creds)
       end
 
       @deadline = deadline
@@ -347,7 +353,7 @@
     # @param unmarshal [Function] f(string)->obj that unmarshals responses
     # @param deadline [TimeConst]
     def new_active_call(ch, marshal, unmarshal, deadline=nil)
-      absolute_deadline = TimeConsts.from_relative_time(deadline)
+      absolute_deadline = Core::TimeConsts.from_relative_time(deadline)
       call = @ch.create_call(ch, @host, absolute_deadline)
       ActiveCall.new(call, @queue, marshal, unmarshal, absolute_deadline,
                      started:false)
diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb
index 43b4d4f..eef886a 100644
--- a/src/ruby/lib/grpc/generic/rpc_desc.rb
+++ b/src/ruby/lib/grpc/generic/rpc_desc.rb
@@ -34,6 +34,7 @@
   # RpcDesc is a Descriptor of an RPC method.
   class RpcDesc < Struct.new(:name, :input, :output, :marshal_method,
                              :unmarshal_method)
+    include Core::StatusCodes
 
     # Used to wrap a message class to indicate that it needs to be streamed.
     class Stream
@@ -46,7 +47,7 @@
 
     # @return [Proc] { |instance| marshalled(instance) }
     def marshal_proc
-      Proc.new { |o| o.method(marshal_method).call.to_s }
+      Proc.new { |o| o.class.method(marshal_method).call(o).to_s }
     end
 
     # @param [:input, :output] target determines whether to produce the an
@@ -82,7 +83,7 @@
         else  # is a bidi_stream
           active_call.run_server_bidi(mth)
         end
-        send_status(active_call, StatusCodes::OK, 'OK')
+        send_status(active_call, OK, 'OK')
         active_call.finished
       rescue BadStatus => e
         # this is raised by handlers that want GRPC to send an application
@@ -97,7 +98,7 @@
         # This is raised when active_call#method.call exceeeds the deadline
         # event.  Send a status of deadline exceeded
         logger.warn("late call: #{active_call}")
-        send_status(active_call, StatusCodes::DEADLINE_EXCEEDED, 'late')
+        send_status(active_call, DEADLINE_EXCEEDED, 'late')
       rescue EventError => e
         # This is raised by GRPC internals but should rarely, if ever happen.
         # Log it, but don't notify the other endpoint..
@@ -107,7 +108,7 @@
         # Send back a UNKNOWN status to the client
         logger.warn("failed handler: #{active_call}; sending status:UNKNOWN")
         logger.warn(e)
-        send_status(active_call, StatusCodes::UNKNOWN, 'no reason given')
+        send_status(active_call, UNKNOWN, 'no reason given')
       end
     end
 
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index e6efdc3..ebbf3f9 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -38,7 +38,8 @@
   # RpcServer hosts a number of services and makes them available on the
   # network.
   class RpcServer
-    include CompletionType
+    include Core::CompletionType
+    include Core::TimeConsts
     extend ::Forwardable
 
     def_delegators :@server, :add_http2_port
@@ -57,7 +58,7 @@
     # instance, however other arbitrary are allowed and when present are used
     # to configure the listeninng connection set up by the RpcServer.
     #
-    # * server_override: which if passed must be a [GRPC::Server].  When
+    # * server_override: which if passed must be a [GRPC::Core::Server].  When
     # present.
     #
     # * poll_period: when present, the server polls for new events with this
@@ -70,30 +71,38 @@
     # completion_queue that the server uses to receive network events,
     # otherwise its creates a new instance itself
     #
+    # * creds: [GRPC::Core::ServerCredentials]
+    # the credentials used to secure the server
+    #
     # * max_waiting_requests: the maximum number of requests that are not
     # being handled to allow. When this limit is exceeded, the server responds
     # with not available to new requests
     def initialize(pool_size:DEFAULT_POOL_SIZE,
                    max_waiting_requests:DEFAULT_MAX_WAITING_REQUESTS,
-                   poll_period:TimeConsts::INFINITE_FUTURE,
+                   poll_period:INFINITE_FUTURE,
                    completion_queue_override:nil,
+                   creds:nil,
                    server_override:nil,
                    **kw)
       if !completion_queue_override.nil?
         cq = completion_queue_override
-        if !cq.is_a?(CompletionQueue)
+        if !cq.is_a?(Core::CompletionQueue)
           raise ArgumentError.new('not a CompletionQueue')
         end
       else
-        cq = CompletionQueue.new
+        cq = Core::CompletionQueue.new
       end
       @cq = cq
 
       if !server_override.nil?
         srv = server_override
-        raise ArgumentError.new('not a Server') unless srv.is_a?(Server)
+        raise ArgumentError.new('not a Server') unless srv.is_a?(Core::Server)
+      elsif creds.nil?
+        srv = Core::Server.new(@cq, kw)
+      elsif !creds.is_a?(Core::ServerCredentials)
+        raise ArgumentError.new('not a ServerCredentials')
       else
-        srv = Server.new(@cq, **kw)
+        srv = Core::Server.new(@cq, kw, creds)
       end
       @server = srv
 
@@ -236,7 +245,7 @@
       # Accept the call.  This is necessary even if a status is to be sent back
       # immediately
       finished_tag = Object.new
-      call_queue = CompletionQueue.new
+      call_queue = Core::CompletionQueue.new
       call.accept(call_queue, finished_tag)
 
       # Send UNAVAILABLE if there are too many unprocessed jobs
diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb
index 1a3d0dc..05bb0af 100644
--- a/src/ruby/lib/grpc/generic/service.rb
+++ b/src/ruby/lib/grpc/generic/service.rb
@@ -88,12 +88,12 @@
       # - unmarshal_class method must be a class method on the serializable
       # message type that takes a string (byte stream) and produces and object
       #
-      # - marshal_instance_method is called on a serializable message instance
+      # - marshal_class_method is called on a serializable message instance
       # and produces a serialized string.
       #
       # The Dsl verifies that the types in the descriptor have both the
       # unmarshal and marshal methods.
-      attr_writer(:marshal_instance_method, :unmarshal_class_method)
+      attr_writer(:marshal_class_method, :unmarshal_class_method)
       attr_accessor(:service_name)
 
       # Adds an RPC spec.
@@ -113,7 +113,7 @@
         assert_can_marshal(input)
         assert_can_marshal(output)
         rpc_descs[name] = RpcDesc.new(name, input, output,
-                                      marshal_instance_method,
+                                      marshal_class_method,
                                       unmarshal_class_method)
       end
 
@@ -125,8 +125,8 @@
       end
 
       # the name of the instance method used to marshal events to a byte stream.
-      def marshal_instance_method
-        @marshal_instance_method ||= :marshal
+      def marshal_class_method
+        @marshal_class_method ||= :marshal
       end
 
       # the name of the class method used to unmarshal from a byte stream.
@@ -144,9 +144,9 @@
           raise ArgumentError, "#{cls} needs #{cls}.#{mth}"
         end
 
-        mth = marshal_instance_method
-        if !cls.instance_methods.include?(mth)
-          raise ArgumentError, "#{cls} needs #{cls}.new.#{mth}"
+        mth = marshal_class_method
+        if !cls.methods.include?(mth)
+          raise ArgumentError, "#{cls} needs #{cls}.#{mth}"
         end
       end
 
@@ -173,7 +173,7 @@
           # @param kw [KeywordArgs] the channel arguments, plus any optional
           #                         args for configuring the client's channel
           def initialize(host, **kw)
-            super(host, CompletionQueue.new, **kw)
+            super(host, Core::CompletionQueue.new, **kw)
           end
 
           # Used define_method to add a method for each rpc_desc.  Each method
diff --git a/src/ruby/lib/grpc/time_consts.rb b/src/ruby/lib/grpc/time_consts.rb
deleted file mode 100644
index 2cbab5d..0000000
--- a/src/ruby/lib/grpc/time_consts.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# 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.
-
-require 'grpc'
-
-module Google
-  module RPC
-    module TimeConsts  # re-opens a module in the C extension.
-
-      # Converts a time delta to an absolute deadline.
-      #
-      # Assumes timeish is a relative time, and converts its to an absolute,
-      # with following exceptions:
-      #
-      # * if timish is one of the TimeConsts.TimeSpec constants the value is
-      # preserved.
-      # * timish < 0 => TimeConsts.INFINITE_FUTURE
-      # * timish == 0 => TimeConsts.ZERO
-      #
-      # @param timeish [Number|TimeSpec]
-      # @return timeish [Number|TimeSpec]
-      def from_relative_time(timeish)
-        if timeish.is_a?TimeSpec
-          timeish
-        elsif timeish.nil?
-          TimeConsts::ZERO
-        elsif !timeish.is_a?Numeric
-          raise TypeError('Cannot make an absolute deadline from %s',
-                          timeish.inspect)
-        elsif timeish < 0
-          TimeConsts::INFINITE_FUTURE
-        elsif timeish == 0
-          TimeConsts::ZERO
-        else !timeish.nil?
-          Time.now + timeish
-        end
-      end
-
-      module_function :from_relative_time
-
-    end
-  end
-end
diff --git a/src/ruby/spec/alloc_spec.rb b/src/ruby/spec/alloc_spec.rb
index 99cc39d..305405e 100644
--- a/src/ruby/spec/alloc_spec.rb
+++ b/src/ruby/spec/alloc_spec.rb
@@ -31,15 +31,15 @@
 
 describe 'Wrapped classes where .new cannot create an instance' do
 
-  describe GRPC::Event do
+  describe GRPC::Core::Event do
     it 'should fail .new fail with a runtime error' do
-      expect { GRPC::Event.new }.to raise_error(TypeError)
+      expect { GRPC::Core::Event.new }.to raise_error(TypeError)
     end
   end
 
-  describe GRPC::Call do
+  describe GRPC::Core::Call do
     it 'should fail .new fail with a runtime error' do
-      expect { GRPC::Event.new }.to raise_error(TypeError)
+      expect { GRPC::Core::Event.new }.to raise_error(TypeError)
     end
   end
 
diff --git a/src/ruby/spec/byte_buffer_spec.rb b/src/ruby/spec/byte_buffer_spec.rb
index d4d3a69..b89d7f3 100644
--- a/src/ruby/spec/byte_buffer_spec.rb
+++ b/src/ruby/spec/byte_buffer_spec.rb
@@ -29,25 +29,25 @@
 
 require 'grpc'
 
-describe GRPC::ByteBuffer do
+describe GRPC::Core::ByteBuffer do
 
   describe '#new' do
 
     it 'is constructed from a string' do
-      expect { GRPC::ByteBuffer.new('#new') }.not_to raise_error
+      expect { GRPC::Core::ByteBuffer.new('#new') }.not_to raise_error
     end
 
     it 'can be constructed from the empty string' do
-      expect { GRPC::ByteBuffer.new('') }.not_to raise_error
+      expect { GRPC::Core::ByteBuffer.new('') }.not_to raise_error
     end
 
     it 'cannot be constructed from nil' do
-      expect { GRPC::ByteBuffer.new(nil) }.to raise_error TypeError
+      expect { GRPC::Core::ByteBuffer.new(nil) }.to raise_error TypeError
     end
 
     it 'cannot be constructed from non-strings' do
       [1, Object.new, :a_symbol].each do |x|
-        expect { GRPC::ByteBuffer.new(x) }.to raise_error TypeError
+        expect { GRPC::Core::ByteBuffer.new(x) }.to raise_error TypeError
       end
     end
 
@@ -55,13 +55,13 @@
 
   describe '#to_s' do
     it 'is the string value the ByteBuffer was constructed with' do
-      expect(GRPC::ByteBuffer.new('#to_s').to_s).to eq('#to_s')
+      expect(GRPC::Core::ByteBuffer.new('#to_s').to_s).to eq('#to_s')
     end
   end
 
   describe '#dup' do
     it 'makes an instance whose #to_s is the original string value' do
-      bb = GRPC::ByteBuffer.new('#dup')
+      bb = GRPC::Core::ByteBuffer.new('#dup')
       a_copy = bb.dup
       expect(a_copy.to_s).to eq('#dup')
       expect(a_copy.dup.to_s).to eq('#dup')
diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb
index 339f2c1..9228f7d 100644
--- a/src/ruby/spec/call_spec.rb
+++ b/src/ruby/spec/call_spec.rb
@@ -30,9 +30,9 @@
 require 'grpc'
 require 'port_picker'
 
-include GRPC::StatusCodes
+include GRPC::Core::StatusCodes
 
-describe GRPC::RpcErrors do
+describe GRPC::Core::RpcErrors do
 
   before(:each) do
     @known_types = {
@@ -60,24 +60,24 @@
   end
 
   it 'should have symbols for all the known error codes' do
-    m = GRPC::RpcErrors
+    m = GRPC::Core::RpcErrors
     syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
     expect(Hash[syms_and_codes]).to eq(@known_types)
   end
 
 end
 
-describe GRPC::Call do
+describe GRPC::Core::Call do
 
   before(:each) do
     @tag = Object.new
-    @client_queue = GRPC::CompletionQueue.new
-    @server_queue = GRPC::CompletionQueue.new
+    @client_queue = GRPC::Core::CompletionQueue.new
+    @server_queue = GRPC::Core::CompletionQueue.new
     port = find_unused_tcp_port
     host = "localhost:#{port}"
-    @server = GRPC::Server.new(@server_queue, nil)
+    @server = GRPC::Core::Server.new(@server_queue, nil)
     @server.add_http2_port(host)
-    @ch = GRPC::Channel.new(host, nil)
+    @ch = GRPC::Core::Channel.new(host, nil)
   end
 
   after(:each) do
@@ -86,29 +86,29 @@
 
   describe '#start_read' do
     it 'should fail if called immediately' do
-      expect { make_test_call.start_read(@tag) }.to raise_error GRPC::CallError
+      expect { make_test_call.start_read(@tag) }.to raise_error GRPC::Core::CallError
     end
   end
 
   describe '#start_write' do
     it 'should fail if called immediately' do
-      bytes = GRPC::ByteBuffer.new('test string')
+      bytes = GRPC::Core::ByteBuffer.new('test string')
       expect { make_test_call.start_write(bytes, @tag) }
-          .to raise_error GRPC::CallError
+          .to raise_error GRPC::Core::CallError
     end
   end
 
   describe '#start_write_status' do
     it 'should fail if called immediately' do
-      sts = GRPC::Status.new(153, 'test detail')
+      sts = GRPC::Core::Status.new(153, 'test detail')
       expect { make_test_call.start_write_status(sts, @tag) }
-          .to raise_error GRPC::CallError
+          .to raise_error GRPC::Core::CallError
     end
   end
 
   describe '#writes_done' do
     it 'should fail if called immediately' do
-      expect { make_test_call.writes_done(@tag) }.to raise_error GRPC::CallError
+      expect { make_test_call.writes_done(@tag) }.to raise_error GRPC::Core::CallError
     end
   end
 
@@ -126,9 +126,9 @@
       call = make_test_call
       expect(call.start_invoke(@client_queue, @tag, @tag, @tag)).to be_nil
       ev = @client_queue.next(deadline)
-      expect(ev.call).to be_a(GRPC::Call)
+      expect(ev.call).to be_a(GRPC::Core::Call)
       expect(ev.tag).to be(@tag)
-      expect(ev.type).to be(GRPC::CompletionType::INVOKE_ACCEPTED)
+      expect(ev.type).to be(GRPC::Core::CompletionType::INVOKE_ACCEPTED)
       expect(ev.call).to_not be(call)
     end
   end
@@ -138,12 +138,12 @@
       call = make_test_call
       call.start_invoke(@client_queue, @tag, @tag, @tag)
       ev = @client_queue.next(deadline)
-      expect(ev.type).to be(GRPC::CompletionType::INVOKE_ACCEPTED)
-      expect(call.start_write(GRPC::ByteBuffer.new('test_start_write'),
+      expect(ev.type).to be(GRPC::Core::CompletionType::INVOKE_ACCEPTED)
+      expect(call.start_write(GRPC::Core::ByteBuffer.new('test_start_write'),
                               @tag)).to be_nil
       ev = @client_queue.next(deadline)
-      expect(ev.call).to be_a(GRPC::Call)
-      expect(ev.type).to be(GRPC::CompletionType::WRITE_ACCEPTED)
+      expect(ev.call).to be_a(GRPC::Core::Call)
+      expect(ev.type).to be(GRPC::Core::CompletionType::WRITE_ACCEPTED)
       expect(ev.tag).to be(@tag)
     end
   end
@@ -151,7 +151,7 @@
   describe '#status' do
     it 'can save the status and read it back' do
       call = make_test_call
-      sts = GRPC::Status.new(OK, 'OK')
+      sts = GRPC::Core::Status.new(OK, 'OK')
       expect { call.status = sts }.not_to raise_error
       expect(call.status).to be(sts)
     end
diff --git a/src/ruby/spec/channel_spec.rb b/src/ruby/spec/channel_spec.rb
index bd46bff..d268612 100644
--- a/src/ruby/spec/channel_spec.rb
+++ b/src/ruby/spec/channel_spec.rb
@@ -30,135 +30,166 @@
 require 'grpc'
 require 'port_picker'
 
-module GRPC
+def load_test_certs
+  test_root = File.join(File.dirname(__FILE__), 'testdata')
+  files = ['ca.pem', 'server1.key', 'server1.pem']
+  files.map { |f| File.open(File.join(test_root, f)).read }
+end
 
-  describe Channel do
+describe GRPC::Core::Channel do
 
-    before(:each) do
-      @cq = CompletionQueue.new
+
+  def create_test_cert
+    GRPC::Core::Credentials.new(load_test_certs[0])
+  end
+
+  before(:each) do
+    @cq = GRPC::Core::CompletionQueue.new
+  end
+
+  shared_examples '#new' do
+
+    it 'take a host name without channel args' do
+      expect { GRPC::Core::Channel.new('dummy_host', nil) }.not_to raise_error
     end
 
-    describe '#new' do
-
-      it 'take a host name without channel args' do
-        expect { Channel.new('dummy_host', nil) }.not_to raise_error
-      end
-
-      it 'does not take a hash with bad keys as channel args' do
-        blk = construct_with_args(Object.new => 1)
-        expect(&blk).to raise_error TypeError
-        blk = construct_with_args(1 => 1)
-        expect(&blk).to raise_error TypeError
-      end
-
-      it 'does not take a hash with bad values as channel args' do
-        blk = construct_with_args(:symbol => Object.new)
-        expect(&blk).to raise_error TypeError
-        blk = construct_with_args('1' => Hash.new)
-        expect(&blk).to raise_error TypeError
-      end
-
-      it 'can take a hash with a symbol key as channel args' do
-        blk = construct_with_args(:a_symbol => 1)
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with a string key as channel args' do
-        blk = construct_with_args('a_symbol' => 1)
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with a string value as channel args' do
-        blk = construct_with_args(:a_symbol => '1')
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with a symbol value as channel args' do
-        blk = construct_with_args(:a_symbol => :another_symbol)
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with a numeric value as channel args' do
-        blk = construct_with_args(:a_symbol => 1)
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with many args as channel args' do
-        args = Hash[127.times.collect { |x| [x.to_s, x] } ]
-        blk = construct_with_args(args)
-        expect(&blk).to_not raise_error
-      end
-
+    it 'does not take a hash with bad keys as channel args' do
+      blk = construct_with_args(Object.new => 1)
+      expect(&blk).to raise_error TypeError
+      blk = construct_with_args(1 => 1)
+      expect(&blk).to raise_error TypeError
     end
 
-    describe '#create_call' do
-      it 'creates a call OK' do
-        port = find_unused_tcp_port
-        host = "localhost:#{port}"
-        ch = Channel.new(host, nil)
-
-        deadline = Time.now + 5
-        expect(ch.create_call('dummy_method', 'dummy_host', deadline))
-          .not_to be(nil)
-      end
-
-      it 'raises an error if called on a closed channel' do
-        port = find_unused_tcp_port
-        host = "localhost:#{port}"
-        ch = Channel.new(host, nil)
-        ch.close
-
-        deadline = Time.now + 5
-        blk = Proc.new do
-          ch.create_call('dummy_method', 'dummy_host', deadline)
-        end
-        expect(&blk).to raise_error(RuntimeError)
-      end
-
+    it 'does not take a hash with bad values as channel args' do
+      blk = construct_with_args(:symbol => Object.new)
+      expect(&blk).to raise_error TypeError
+      blk = construct_with_args('1' => Hash.new)
+      expect(&blk).to raise_error TypeError
     end
 
-    describe '#destroy' do
-      it 'destroys a channel ok' do
-        port = find_unused_tcp_port
-        host = "localhost:#{port}"
-        ch = Channel.new(host, nil)
-        blk = Proc.new { ch.destroy }
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can be called more than once without error' do
-        port = find_unused_tcp_port
-        host = "localhost:#{port}"
-        ch = Channel.new(host, nil)
-        blk = Proc.new { ch.destroy }
-        blk.call
-        expect(&blk).to_not raise_error
-      end
+    it 'can take a hash with a symbol key as channel args' do
+      blk = construct_with_args(:a_symbol => 1)
+      expect(&blk).to_not raise_error
     end
 
-    describe '#close' do
-      it 'closes a channel ok' do
-        port = find_unused_tcp_port
-        host = "localhost:#{port}"
-        ch = Channel.new(host, nil)
-        blk = Proc.new { ch.close }
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can be called more than once without error' do
-        port = find_unused_tcp_port
-        host = "localhost:#{port}"
-        ch = Channel.new(host, nil)
-        blk = Proc.new { ch.close }
-        blk.call
-        expect(&blk).to_not raise_error
-      end
+    it 'can take a hash with a string key as channel args' do
+      blk = construct_with_args('a_symbol' => 1)
+      expect(&blk).to_not raise_error
     end
 
+    it 'can take a hash with a string value as channel args' do
+      blk = construct_with_args(:a_symbol => '1')
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with a symbol value as channel args' do
+      blk = construct_with_args(:a_symbol => :another_symbol)
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with a numeric value as channel args' do
+      blk = construct_with_args(:a_symbol => 1)
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with many args as channel args' do
+      args = Hash[127.times.collect { |x| [x.to_s, x] } ]
+      blk = construct_with_args(args)
+      expect(&blk).to_not raise_error
+    end
+
+  end
+
+  describe '#new for secure channels' do
+
     def construct_with_args(a)
-      Proc.new {Channel.new('dummy_host', a)}
+      Proc.new { GRPC::Core::Channel.new('dummy_host', a, create_test_cert) }
     end
 
+    it_behaves_like '#new'
+  end
+
+  describe '#new for insecure channels' do
+    it_behaves_like '#new'
+
+    def construct_with_args(a)
+      Proc.new { GRPC::Core::Channel.new('dummy_host', a) }
+    end
+  end
+
+  describe '#create_call' do
+    it 'creates a call OK' do
+      port = find_unused_tcp_port
+      host = "localhost:#{port}"
+      ch = GRPC::Core::Channel.new(host, nil)
+
+      deadline = Time.now + 5
+
+      blk = Proc.new do
+        ch.create_call('dummy_method', 'dummy_host', deadline)
+      end
+      expect(&blk).to_not raise_error
+    end
+
+    it 'raises an error if called on a closed channel' do
+      port = find_unused_tcp_port
+      host = "localhost:#{port}"
+      ch = GRPC::Core::Channel.new(host, nil)
+      ch.close
+
+      deadline = Time.now + 5
+      blk = Proc.new do
+        ch.create_call('dummy_method', 'dummy_host', deadline)
+      end
+      expect(&blk).to raise_error(RuntimeError)
+    end
+
+  end
+
+  describe '#destroy' do
+    it 'destroys a channel ok' do
+      port = find_unused_tcp_port
+      host = "localhost:#{port}"
+      ch = GRPC::Core::Channel.new(host, nil)
+      blk = Proc.new { ch.destroy }
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can be called more than once without error' do
+      port = find_unused_tcp_port
+      host = "localhost:#{port}"
+      ch = GRPC::Core::Channel.new(host, nil)
+      blk = Proc.new { ch.destroy }
+      blk.call
+      expect(&blk).to_not raise_error
+    end
+  end
+
+  describe '::SSL_TARGET' do
+
+    it 'is a symbol' do
+      expect(GRPC::Core::Channel::SSL_TARGET).to be_a(Symbol)
+    end
+
+  end
+
+  describe '#close' do
+    it 'closes a channel ok' do
+      port = find_unused_tcp_port
+      host = "localhost:#{port}"
+      ch = GRPC::Core::Channel.new(host, nil)
+      blk = Proc.new { ch.close }
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can be called more than once without error' do
+      port = find_unused_tcp_port
+      host = "localhost:#{port}"
+      ch = GRPC::Core::Channel.new(host, nil)
+      blk = Proc.new { ch.close }
+      blk.call
+      expect(&blk).to_not raise_error
+    end
   end
 
 end
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 64068ab..c96fe97 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -31,8 +31,14 @@
 require 'port_picker'
 require 'spec_helper'
 
-include GRPC::CompletionType
-include GRPC
+include GRPC::Core::CompletionType
+include GRPC::Core
+
+def load_test_certs
+  test_root = File.join(File.dirname(__FILE__), 'testdata')
+  files = ['ca.pem', 'server1.key', 'server1.pem']
+  files.map { |f| File.open(File.join(test_root, f)).read }
+end
 
 shared_context 'setup: tags' do
 
@@ -327,9 +333,9 @@
   before(:example) do
     port = find_unused_tcp_port
     host = "localhost:#{port}"
-    @client_queue = GRPC::CompletionQueue.new
-    @server_queue = GRPC::CompletionQueue.new
-    @server = GRPC::Server.new(@server_queue, nil)
+    @client_queue = GRPC::Core::CompletionQueue.new
+    @server_queue = GRPC::Core::CompletionQueue.new
+    @server = GRPC::Core::Server.new(@server_queue, nil)
     @server.add_http2_port(host)
     @server.start
     @ch = Channel.new(host, nil)
@@ -339,6 +345,34 @@
     @server.close
   end
 
+  it_behaves_like 'basic GRPC message delivery is OK' do
+  end
+
+  it_behaves_like 'GRPC metadata delivery works OK' do
+  end
+
+end
+
+describe 'the secure http client/server' do
+
+  before(:example) do
+    certs = load_test_certs
+    port = find_unused_tcp_port
+    host = "localhost:#{port}"
+    @client_queue = GRPC::Core::CompletionQueue.new
+    @server_queue = GRPC::Core::CompletionQueue.new
+    server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
+    @server = GRPC::Core::Server.new(@server_queue, nil, server_creds)
+    @server.add_http2_port(host, true)
+    @server.start
+    args = {Channel::SSL_TARGET => 'foo.test.google.com'}
+    @ch = Channel.new(host, args,
+                      GRPC::Core::Credentials.new(certs[0], nil, nil))
+  end
+
+  after(:example) do
+    @server.close
+  end
 
   it_behaves_like 'basic GRPC message delivery is OK' do
   end
diff --git a/src/ruby/spec/completion_queue_spec.rb b/src/ruby/spec/completion_queue_spec.rb
index 3743244..50f74b5 100644
--- a/src/ruby/spec/completion_queue_spec.rb
+++ b/src/ruby/spec/completion_queue_spec.rb
@@ -29,26 +29,25 @@
 
 require 'grpc'
 
-describe GRPC::CompletionQueue do
+describe GRPC::Core::CompletionQueue do
 
   describe '#new' do
     it 'is constructed successufully' do
-      expect { GRPC::CompletionQueue.new }.not_to raise_error
-      expect(GRPC::CompletionQueue.new).to be_a(GRPC::CompletionQueue)
+      expect { GRPC::Core::CompletionQueue.new }.not_to raise_error
     end
   end
 
   describe '#next' do
     it 'can be called without failing' do
-      ch = GRPC::CompletionQueue.new
+      ch = GRPC::Core::CompletionQueue.new
       expect { ch.next(3) }.not_to raise_error
     end
 
     it 'can be called with the time constants' do
-      ch = GRPC::CompletionQueue.new
+      ch = GRPC::Core::CompletionQueue.new
       # don't use INFINITE_FUTURE, as there we have no events.
       non_blocking_consts = [:ZERO, :INFINITE_PAST]
-      m = GRPC::TimeConsts
+      m = GRPC::Core::TimeConsts
       non_blocking_consts.each do |c|
         a_time = m.const_get(c)
         expect { ch.next(a_time) }.not_to raise_error
@@ -59,16 +58,16 @@
 
   describe '#pluck' do
     it 'can be called without failing' do
-      ch = GRPC::CompletionQueue.new
+      ch = GRPC::Core::CompletionQueue.new
       tag = Object.new
       expect { ch.pluck(tag, 3) }.not_to raise_error
     end
 
     it 'can be called with the time constants' do
-      ch = GRPC::CompletionQueue.new
+      ch = GRPC::Core::CompletionQueue.new
       # don't use INFINITE_FUTURE, as there we have no events.
       non_blocking_consts = [:ZERO, :INFINITE_PAST]
-      m = GRPC::TimeConsts
+      m = GRPC::Core::TimeConsts
       tag = Object.new
       non_blocking_consts.each do |c|
         a_time = m.const_get(c)
diff --git a/src/ruby/spec/credentials_spec.rb b/src/ruby/spec/credentials_spec.rb
new file mode 100644
index 0000000..4d932db
--- /dev/null
+++ b/src/ruby/spec/credentials_spec.rb
@@ -0,0 +1,87 @@
+# 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.
+
+require 'grpc'
+
+
+def load_test_certs
+  test_root = File.join(File.dirname(__FILE__), 'testdata')
+  files = ['ca.pem', 'server1.pem', 'server1.key']
+  files.map { |f| File.open(File.join(test_root, f)).read }
+end
+
+Credentials = GRPC::Core::Credentials
+
+describe Credentials do
+
+  describe '#new' do
+
+    it 'can be constructed with fake inputs' do
+      expect { Credentials.new('root_certs', 'key', 'cert') }.not_to raise_error
+    end
+
+    it 'it can be constructed using specific test certificates' do
+      certs = load_test_certs
+      expect { Credentials.new(*certs) }.not_to raise_error
+    end
+
+    it 'can be constructed with server roots certs only' do
+      root_cert, _, _ = load_test_certs
+      expect { Credentials.new(root_cert) }.not_to raise_error
+    end
+
+    it 'cannot be constructed with a nil server roots' do
+      _, client_key, client_chain = load_test_certs
+      blk = Proc.new { Credentials.new(nil, client_key, client_chain) }
+      expect(&blk).to raise_error
+    end
+
+  end
+
+  describe '#compose' do
+
+    it 'can be completed OK' do
+      certs = load_test_certs
+      cred1 = Credentials.new(*certs)
+      cred2 = Credentials.new(*certs)
+      expect { cred1.compose(cred2) }.to_not raise_error
+    end
+
+  end
+
+  describe 'Credentials#default' do
+
+    it 'is not implemented yet' do
+      expect { Credentials.default() }.to raise_error RuntimeError
+    end
+
+  end
+
+
+end
diff --git a/src/ruby/spec/event_spec.rb b/src/ruby/spec/event_spec.rb
index 19b9754..a61b926 100644
--- a/src/ruby/spec/event_spec.rb
+++ b/src/ruby/spec/event_spec.rb
@@ -29,7 +29,7 @@
 
 require 'grpc'
 
-describe GRPC::CompletionType do
+describe GRPC::Core::CompletionType do
 
   before(:each) do
     @known_types = {
@@ -46,9 +46,9 @@
   end
 
   it 'should have all the known types' do
-    mod = GRPC::CompletionType
-    expect(Hash[mod.constants.collect { |c| [c, mod.const_get(c)] }])
-        .to eq(@known_types)
+    mod = GRPC::Core::CompletionType
+    blk = Proc.new { Hash[mod.constants.collect { |c| [c, mod.const_get(c)] }] }
+    expect(blk.call).to eq(@known_types)
   end
 
 end
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index 872625c..ceeef2a 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -31,291 +31,289 @@
 require 'grpc/generic/active_call'
 require_relative '../port_picker'
 
-module GRPC
+ActiveCall = GRPC::ActiveCall
 
-  describe ActiveCall do
+describe GRPC::ActiveCall do
 
+  before(:each) do
+    @pass_through = Proc.new { |x| x }
+    @server_tag = Object.new
+    @server_finished_tag = Object.new
+    @tag = Object.new
+
+    @client_queue = GRPC::Core::CompletionQueue.new
+    @server_queue = GRPC::Core::CompletionQueue.new
+    port = find_unused_tcp_port
+    host = "localhost:#{port}"
+    @server = GRPC::Core::Server.new(@server_queue, nil)
+    @server.add_http2_port(host)
+    @server.start
+    @ch = GRPC::Core::Channel.new(host, nil)
+  end
+
+  after(:each) do
+    @server.close
+  end
+
+  describe 'restricted view methods' do
     before(:each) do
-      @pass_through = Proc.new { |x| x }
-      @server_tag = Object.new
-      @server_finished_tag = Object.new
-      @tag = Object.new
-
-      @client_queue = CompletionQueue.new
-      @server_queue = CompletionQueue.new
-      port = find_unused_tcp_port
-      host = "localhost:#{port}"
-      @server = GRPC::Server.new(@server_queue, nil)
-      @server.add_http2_port(host)
-      @server.start
-      @ch = GRPC::Channel.new(host, nil)
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      @client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                    @pass_through, deadline,
+                                    finished_tag: finished_tag)
     end
 
-    after(:each) do
-      @server.close
-    end
-
-    describe 'restricted view methods' do
-      before(:each) do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        @client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                      @pass_through, deadline,
-                                      finished_tag: finished_tag)
-      end
-
-      describe '#multi_req_view' do
-        it 'exposes a fixed subset of the ActiveCall methods' do
-          want = ['cancelled', 'deadline', 'each_remote_read', 'shutdown']
-          v = @client_call.multi_req_view
-          want.each do |w|
-            expect(v.methods.include?(w))
-          end
-        end
-      end
-
-      describe '#single_req_view' do
-        it 'exposes a fixed subset of the ActiveCall methods' do
-          want = ['cancelled', 'deadline', 'shutdown']
-          v = @client_call.single_req_view
-          want.each do |w|
-            expect(v.methods.include?(w))
-          end
+    describe '#multi_req_view' do
+      it 'exposes a fixed subset of the ActiveCall methods' do
+        want = ['cancelled', 'deadline', 'each_remote_read', 'shutdown']
+        v = @client_call.multi_req_view
+        want.each do |w|
+          expect(v.methods.include?(w))
         end
       end
     end
 
-    describe '#remote_send' do
-      it 'allows a client to send a payload to the server' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        @client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                      @pass_through, deadline,
-                                      finished_tag: finished_tag)
-        msg = 'message is a string'
-        @client_call.remote_send(msg)
-
-        # check that server rpc new was received
-        @server.request_call(@server_tag)
-        ev = @server_queue.next(deadline)
-        expect(ev.type).to be(CompletionType::SERVER_RPC_NEW)
-        expect(ev.call).to be_a(Call)
-        expect(ev.tag).to be(@server_tag)
-
-        # Accept the call, and verify that the server reads the response ok.
-        ev.call.accept(@client_queue, @server_tag)
-        server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
-                                     @pass_through, deadline)
-        expect(server_call.remote_read).to eq(msg)
-      end
-
-      it 'marshals the payload using the marshal func' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        marshal = Proc.new { |x| 'marshalled:' + x }
-        client_call = ActiveCall.new(call, @client_queue, marshal,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is a string'
-        client_call.remote_send(msg)
-
-        # confirm that the message was marshalled
-        @server.request_call(@server_tag)
-        ev = @server_queue.next(deadline)
-        ev.call.accept(@client_queue, @server_tag)
-        server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
-                                     @pass_through, deadline)
-        expect(server_call.remote_read).to eq('marshalled:' + msg)
-      end
-
-    end
-
-    describe '#remote_read' do
-      it 'reads the response sent by a server' do
-        call, pass_through = make_test_call, Proc.new { |x| x }
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is a string'
-        client_call.remote_send(msg)
-        server_call = expect_server_to_receive(msg)
-        server_call.remote_send('server_response')
-        expect(client_call.remote_read).to eq('server_response')
-      end
-
-      it 'get a nil msg before a status when an OK status is sent' do
-        call, pass_through = make_test_call, Proc.new { |x| x }
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is a string'
-        client_call.remote_send(msg)
-        client_call.writes_done(false)
-        server_call = expect_server_to_receive(msg)
-        server_call.remote_send('server_response')
-        server_call.send_status(StatusCodes::OK, 'OK')
-        expect(client_call.remote_read).to eq('server_response')
-        res = client_call.remote_read
-        expect(res).to be_nil
-      end
-
-
-      it 'unmarshals the response using the unmarshal func' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        unmarshal = Proc.new { |x| 'unmarshalled:' + x }
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     unmarshal, deadline,
-                                     finished_tag: finished_tag)
-
-        # confirm the client receives the unmarshalled message
-        msg = 'message is a string'
-        client_call.remote_send(msg)
-        server_call = expect_server_to_receive(msg)
-        server_call.remote_send('server_response')
-        expect(client_call.remote_read).to eq('unmarshalled:server_response')
-      end
-
-    end
-
-    describe '#each_remote_read' do
-      it 'creates an Enumerator' do
-        call = make_test_call
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline)
-        expect(client_call.each_remote_read).to be_a(Enumerator)
-      end
-
-      it 'the returns an enumerator that can read n responses' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is 4a string'
-        reply = 'server_response'
-        client_call.remote_send(msg)
-        server_call = expect_server_to_receive(msg)
-        e = client_call.each_remote_read
-        n = 3  # arbitrary value > 1
-        n.times do
-          server_call.remote_send(reply)
-          expect(e.next).to eq(reply)
+    describe '#single_req_view' do
+      it 'exposes a fixed subset of the ActiveCall methods' do
+        want = ['cancelled', 'deadline', 'shutdown']
+        v = @client_call.single_req_view
+        want.each do |w|
+          expect(v.methods.include?(w))
         end
       end
-
-      it 'the returns an enumerator that stops after an OK Status' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is a string'
-        reply = 'server_response'
-        client_call.remote_send(msg)
-        client_call.writes_done(false)
-        server_call = expect_server_to_receive(msg)
-        e = client_call.each_remote_read
-        n = 3  # arbitrary value > 1
-        n.times do
-          server_call.remote_send(reply)
-          expect(e.next).to eq(reply)
-        end
-        server_call.send_status(StatusCodes::OK, 'OK')
-        expect { e.next }.to raise_error(StopIteration)
-      end
-
     end
+  end
 
-    describe '#writes_done' do
-      it 'finishes ok if the server sends a status response' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is a string'
-        client_call.remote_send(msg)
-        expect { client_call.writes_done(false) }.to_not raise_error
-        server_call = expect_server_to_receive(msg)
-        server_call.remote_send('server_response')
-        expect(client_call.remote_read).to eq('server_response')
-        server_call.send_status(StatusCodes::OK, 'status code is OK')
-        expect { server_call.finished }.to_not raise_error
-        expect { client_call.finished }.to_not raise_error
-      end
+  describe '#remote_send' do
+    it 'allows a client to send a payload to the server' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      @client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                    @pass_through, deadline,
+                                    finished_tag: finished_tag)
+      msg = 'message is a string'
+      @client_call.remote_send(msg)
 
-      it 'finishes ok if the server sends an early status response' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is a string'
-        client_call.remote_send(msg)
-        server_call = expect_server_to_receive(msg)
-        server_call.remote_send('server_response')
-        server_call.send_status(StatusCodes::OK, 'status code is OK')
-        expect(client_call.remote_read).to eq('server_response')
-        expect { client_call.writes_done(false) }.to_not raise_error
-        expect { server_call.finished }.to_not raise_error
-        expect { client_call.finished }.to_not raise_error
-      end
-
-      it 'finishes ok if writes_done is true' do
-        call = make_test_call
-        finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
-                                                      deadline)
-        client_call = ActiveCall.new(call, @client_queue, @pass_through,
-                                     @pass_through, deadline,
-                                     finished_tag: finished_tag)
-        msg = 'message is a string'
-        client_call.remote_send(msg)
-        server_call = expect_server_to_receive(msg)
-        server_call.remote_send('server_response')
-        server_call.send_status(StatusCodes::OK, 'status code is OK')
-        expect(client_call.remote_read).to eq('server_response')
-        expect { client_call.writes_done(true) }.to_not raise_error
-        expect { server_call.finished }.to_not raise_error
-      end
-
-    end
-
-    def expect_server_to_receive(sent_text)
-      c = expect_server_to_be_invoked
-      expect(c.remote_read).to eq(sent_text)
-      c
-    end
-
-    def expect_server_to_be_invoked()
+      # check that server rpc new was received
       @server.request_call(@server_tag)
       ev = @server_queue.next(deadline)
-      ev.call.accept(@client_queue, @server_finished_tag)
-      ActiveCall.new(ev.call, @client_queue, @pass_through,
-                     @pass_through, deadline,
-                     finished_tag: @server_finished_tag)
+      expect(ev.type).to be(CompletionType::SERVER_RPC_NEW)
+      expect(ev.call).to be_a(Call)
+      expect(ev.tag).to be(@server_tag)
+
+      # Accept the call, and verify that the server reads the response ok.
+      ev.call.accept(@client_queue, @server_tag)
+      server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
+                                   @pass_through, deadline)
+      expect(server_call.remote_read).to eq(msg)
     end
 
-    def make_test_call
-      @ch.create_call('dummy_method', 'dummy_host', deadline)
+    it 'marshals the payload using the marshal func' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      marshal = Proc.new { |x| 'marshalled:' + x }
+      client_call = ActiveCall.new(call, @client_queue, marshal,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is a string'
+      client_call.remote_send(msg)
+
+      # confirm that the message was marshalled
+      @server.request_call(@server_tag)
+      ev = @server_queue.next(deadline)
+      ev.call.accept(@client_queue, @server_tag)
+      server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
+                                   @pass_through, deadline)
+      expect(server_call.remote_read).to eq('marshalled:' + msg)
     end
 
-    def deadline
-      Time.now + 0.25  # in 0.25 seconds; arbitrary
+  end
+
+  describe '#remote_read' do
+    it 'reads the response sent by a server' do
+      call, pass_through = make_test_call, Proc.new { |x| x }
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is a string'
+      client_call.remote_send(msg)
+      server_call = expect_server_to_receive(msg)
+      server_call.remote_send('server_response')
+      expect(client_call.remote_read).to eq('server_response')
     end
 
+    it 'get a nil msg before a status when an OK status is sent' do
+      call, pass_through = make_test_call, Proc.new { |x| x }
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is a string'
+      client_call.remote_send(msg)
+      client_call.writes_done(false)
+      server_call = expect_server_to_receive(msg)
+      server_call.remote_send('server_response')
+      server_call.send_status(StatusCodes::OK, 'OK')
+      expect(client_call.remote_read).to eq('server_response')
+      res = client_call.remote_read
+      expect(res).to be_nil
+    end
+
+
+    it 'unmarshals the response using the unmarshal func' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      unmarshal = Proc.new { |x| 'unmarshalled:' + x }
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   unmarshal, deadline,
+                                   finished_tag: finished_tag)
+
+      # confirm the client receives the unmarshalled message
+      msg = 'message is a string'
+      client_call.remote_send(msg)
+      server_call = expect_server_to_receive(msg)
+      server_call.remote_send('server_response')
+      expect(client_call.remote_read).to eq('unmarshalled:server_response')
+    end
+
+  end
+
+  describe '#each_remote_read' do
+    it 'creates an Enumerator' do
+      call = make_test_call
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline)
+      expect(client_call.each_remote_read).to be_a(Enumerator)
+    end
+
+    it 'the returns an enumerator that can read n responses' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is 4a string'
+      reply = 'server_response'
+      client_call.remote_send(msg)
+      server_call = expect_server_to_receive(msg)
+      e = client_call.each_remote_read
+      n = 3  # arbitrary value > 1
+      n.times do
+        server_call.remote_send(reply)
+        expect(e.next).to eq(reply)
+      end
+    end
+
+    it 'the returns an enumerator that stops after an OK Status' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is a string'
+      reply = 'server_response'
+      client_call.remote_send(msg)
+      client_call.writes_done(false)
+      server_call = expect_server_to_receive(msg)
+      e = client_call.each_remote_read
+      n = 3  # arbitrary value > 1
+      n.times do
+        server_call.remote_send(reply)
+        expect(e.next).to eq(reply)
+      end
+      server_call.send_status(StatusCodes::OK, 'OK')
+      expect { e.next }.to raise_error(StopIteration)
+    end
+
+  end
+
+  describe '#writes_done' do
+    it 'finishes ok if the server sends a status response' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is a string'
+      client_call.remote_send(msg)
+      expect { client_call.writes_done(false) }.to_not raise_error
+      server_call = expect_server_to_receive(msg)
+      server_call.remote_send('server_response')
+      expect(client_call.remote_read).to eq('server_response')
+      server_call.send_status(StatusCodes::OK, 'status code is OK')
+      expect { server_call.finished }.to_not raise_error
+      expect { client_call.finished }.to_not raise_error
+    end
+
+    it 'finishes ok if the server sends an early status response' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is a string'
+      client_call.remote_send(msg)
+      server_call = expect_server_to_receive(msg)
+      server_call.remote_send('server_response')
+      server_call.send_status(StatusCodes::OK, 'status code is OK')
+      expect(client_call.remote_read).to eq('server_response')
+      expect { client_call.writes_done(false) }.to_not raise_error
+      expect { server_call.finished }.to_not raise_error
+      expect { client_call.finished }.to_not raise_error
+    end
+
+    it 'finishes ok if writes_done is true' do
+      call = make_test_call
+      finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+                                                    deadline)
+      client_call = ActiveCall.new(call, @client_queue, @pass_through,
+                                   @pass_through, deadline,
+                                   finished_tag: finished_tag)
+      msg = 'message is a string'
+      client_call.remote_send(msg)
+      server_call = expect_server_to_receive(msg)
+      server_call.remote_send('server_response')
+      server_call.send_status(StatusCodes::OK, 'status code is OK')
+      expect(client_call.remote_read).to eq('server_response')
+      expect { client_call.writes_done(true) }.to_not raise_error
+      expect { server_call.finished }.to_not raise_error
+    end
+
+  end
+
+  def expect_server_to_receive(sent_text)
+    c = expect_server_to_be_invoked
+    expect(c.remote_read).to eq(sent_text)
+    c
+  end
+
+  def expect_server_to_be_invoked()
+    @server.request_call(@server_tag)
+    ev = @server_queue.next(deadline)
+    ev.call.accept(@client_queue, @server_finished_tag)
+    ActiveCall.new(ev.call, @client_queue, @pass_through,
+                   @pass_through, deadline,
+                   finished_tag: @server_finished_tag)
+  end
+
+  def make_test_call
+    @ch.create_call('dummy_method', 'dummy_host', deadline)
+  end
+
+  def deadline
+    Time.now + 0.25  # in 0.25 seconds; arbitrary
   end
 
 end
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index c8dee74..4b01af9 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -44,12 +44,16 @@
   t
 end
 
+def load_test_certs
+  test_root = File.join(File.parent(File.dirname(__FILE__)), 'testdata')
+  files = ['ca.pem', 'server1.key', 'server1.pem']
+  files.map { |f| File.open(File.join(test_root, f)).read }
+end
 
-include GRPC::StatusCodes
+include GRPC::Core::StatusCodes
+include GRPC::Core::TimeConsts
 
 describe 'ClientStub' do
-  BadStatus = GRPC::BadStatus
-  TimeConsts = GRPC::TimeConsts
 
   before(:each) do
     Thread.abort_on_exception = true
@@ -57,7 +61,7 @@
     @method = 'an_rpc_method'
     @pass = OK
     @fail = INTERNAL
-    @cq = GRPC::CompletionQueue.new
+    @cq = GRPC::Core::CompletionQueue.new
   end
 
   after(:each) do
@@ -102,6 +106,29 @@
       expect(&blk).to raise_error
     end
 
+    it 'cannot be created with bad credentials' do
+      host = new_test_host
+      blk = Proc.new do
+        opts = {:a_channel_arg => 'an_arg', :creds => Object.new}
+        GRPC::ClientStub.new(host, @cq, **opts)
+      end
+      expect(&blk).to raise_error
+    end
+
+    it 'can be created with test test credentials' do
+      certs = load_test_certs
+      host = new_test_host
+      blk = Proc.new do
+        opts = {
+          GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com',
+          :a_channel_arg => 'an_arg',
+          :creds => GRPC::Core::Credentials.new(certs[0], nil, nil)
+        }
+        GRPC::ClientStub.new(host, @cq, **opts)
+      end
+      expect(&blk).to_not raise_error
+    end
+
   end
 
   describe '#request_response' do
@@ -123,7 +150,7 @@
       it 'should send a request when configured using an override channel' do
         alt_host = new_test_host
         th = run_request_response(alt_host, @sent_msg, @resp, @pass)
-        ch = GRPC::Channel.new(alt_host, nil)
+        ch = GRPC::Core::Channel.new(alt_host, nil)
         stub = GRPC::ClientStub.new('ignored-host', @cq,
                                     channel_override:ch)
         resp = stub.request_response(@method, @sent_msg, NOOP, NOOP)
@@ -138,7 +165,7 @@
         blk = Proc.new do
           stub.request_response(@method, @sent_msg, NOOP, NOOP)
         end
-        expect(&blk).to raise_error(BadStatus)
+        expect(&blk).to raise_error(GRPC::BadStatus)
         th.join
       end
 
@@ -168,7 +195,7 @@
         blk = Proc.new do
           op.execute()
         end
-        expect(&blk).to raise_error(BadStatus)
+        expect(&blk).to raise_error(GRPC::BadStatus)
         th.join
       end
 
@@ -309,7 +336,7 @@
 
     describe 'without a call operation' do
 
-      it 'supports a simple scenario with all requests sent first' do
+      it 'supports sending all the requests first', :bidi => true do
         host = new_test_host
         th = run_bidi_streamer_handle_inputs_first(host, @sent_msgs, @replys,
                                                    @pass)
@@ -320,7 +347,7 @@
         th.join
       end
 
-      it 'supports a simple scenario with a client-initiated ping pong' do
+      it 'supports client-initiated ping pong', :bidi => true do
         host = new_test_host
         th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, true)
         stub = GRPC::ClientStub.new(host, @cq)
@@ -336,7 +363,7 @@
       # servers don't know if all the client metadata has been sent until
       # they receive a message from the client.  Without receiving all the
       # metadata, the server does not accept the call, so this test hangs.
-      xit 'supports a simple scenario with a server-initiated ping pong' do
+      xit 'supports a server-initiated ping pong', :bidi => true do
         host = new_test_host
         th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, false)
         stub = GRPC::ClientStub.new(host, @cq)
@@ -350,7 +377,7 @@
 
     describe 'via a call operation' do
 
-      it 'supports a simple scenario with all requests sent first' do
+      it 'supports sending all the requests first', :bidi => true do
         host = new_test_host
         th = run_bidi_streamer_handle_inputs_first(host, @sent_msgs, @replys,
                                                    @pass)
@@ -364,7 +391,7 @@
         th.join
       end
 
-      it 'supports a simple scenario with a client-initiated ping pong' do
+      it 'supports client-initiated ping pong', :bidi => true  do
         host = new_test_host
         th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, true)
         stub = GRPC::ClientStub.new(host, @cq)
@@ -383,7 +410,7 @@
       # servers don't know if all the client metadata has been sent until
       # they receive a message from the client.  Without receiving all the
       # metadata, the server does not accept the call, so this test hangs.
-      xit 'supports a simple scenario with a server-initiated ping pong' do
+      xit 'supports server-initiated ping pong', :bidi => true do
         th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, false)
         stub = GRPC::ClientStub.new(host, @cq)
         op = stub.bidi_streamer(@method, @sent_msgs, NOOP, NOOP,
@@ -454,8 +481,8 @@
   end
 
   def start_test_server(hostname, awake_mutex, awake_cond)
-    server_queue = GRPC::CompletionQueue.new
-    @server = GRPC::Server.new(server_queue, nil)
+    server_queue = GRPC::Core::CompletionQueue.new
+    @server = GRPC::Core::Server.new(server_queue, nil)
     @server.add_http2_port(hostname)
     @server.start
     @server_tag = Object.new
@@ -467,12 +494,11 @@
   def expect_server_to_be_invoked(hostname, awake_mutex, awake_cond)
     server_queue = start_test_server(hostname, awake_mutex, awake_cond)
     test_deadline = Time.now + 10  # fail tests after 10 seconds
-    ev = server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)
+    ev = server_queue.pluck(@server_tag, INFINITE_FUTURE)
     raise OutOfTime if ev.nil?
     finished_tag = Object.new
     ev.call.accept(server_queue, finished_tag)
-    GRPC::ActiveCall.new(ev.call, server_queue, NOOP,
-                         NOOP, TimeConsts::INFINITE_FUTURE,
+    GRPC::ActiveCall.new(ev.call, server_queue, NOOP, NOOP, INFINITE_FUTURE,
                          finished_tag: finished_tag)
   end
 
diff --git a/src/ruby/spec/generic/rpc_desc_spec.rb b/src/ruby/spec/generic/rpc_desc_spec.rb
index 141fb11..efef7e4 100644
--- a/src/ruby/spec/generic/rpc_desc_spec.rb
+++ b/src/ruby/spec/generic/rpc_desc_spec.rb
@@ -35,8 +35,11 @@
 
   RpcDesc = GRPC::RpcDesc
   Stream = RpcDesc::Stream
-  OK = GRPC::StatusCodes::OK
-  UNKNOWN = GRPC::StatusCodes::UNKNOWN
+  OK = GRPC::Core::StatusCodes::OK
+  INTERNAL = GRPC::Core::StatusCodes::INTERNAL
+  UNKNOWN = GRPC::Core::StatusCodes::UNKNOWN
+  CallError = GRPC::Core::CallError
+  EventError = GRPC::Core::EventError
 
   before(:each) do
     @request_response = RpcDesc.new('rr', Object.new, Object.new, 'encode',
@@ -47,7 +50,7 @@
                                    'encode', 'decode')
     @bidi_streamer = RpcDesc.new('ss', Stream.new(Object.new),
                                  Stream.new(Object.new), 'encode', 'decode')
-    @bs_code = GRPC::StatusCodes::INTERNAL
+    @bs_code = INTERNAL
     @no_reason = 'no reason given'
     @ok_response = Object.new
   end
@@ -74,7 +77,7 @@
       end
 
       it 'absorbs EventError  with no further action' do
-        expect(@call).to receive(:remote_read).once.and_raise(GRPC::EventError)
+        expect(@call).to receive(:remote_read).once.and_raise(EventError)
         blk = Proc.new do
           @request_response.run_server_method(@call, method(:fake_reqresp))
         end
@@ -82,7 +85,7 @@
       end
 
       it 'absorbs CallError with no further action' do
-        expect(@call).to receive(:remote_read).once.and_raise(GRPC::CallError)
+        expect(@call).to receive(:remote_read).once.and_raise(CallError)
         blk = Proc.new do
           @request_response.run_server_method(@call, method(:fake_reqresp))
         end
@@ -118,7 +121,7 @@
       end
 
       it 'absorbs EventError  with no further action' do
-        expect(@call).to receive(:remote_send).once.and_raise(GRPC::EventError)
+        expect(@call).to receive(:remote_send).once.and_raise(EventError)
         blk = Proc.new do
           @client_streamer.run_server_method(@call, method(:fake_clstream))
         end
@@ -126,7 +129,7 @@
       end
 
       it 'absorbs CallError with no further action' do
-        expect(@call).to receive(:remote_send).once.and_raise(GRPC::CallError)
+        expect(@call).to receive(:remote_send).once.and_raise(CallError)
         blk = Proc.new do
           @client_streamer.run_server_method(@call, method(:fake_clstream))
         end
@@ -163,7 +166,7 @@
       end
 
       it 'absorbs EventError  with no further action' do
-        expect(@call).to receive(:remote_read).once.and_raise(GRPC::EventError)
+        expect(@call).to receive(:remote_read).once.and_raise(EventError)
         blk = Proc.new do
           @server_streamer.run_server_method(@call, method(:fake_svstream))
         end
@@ -171,7 +174,7 @@
       end
 
       it 'absorbs CallError with no further action' do
-        expect(@call).to receive(:remote_read).once.and_raise(GRPC::CallError)
+        expect(@call).to receive(:remote_read).once.and_raise(CallError)
         blk = Proc.new do
           @server_streamer.run_server_method(@call, method(:fake_svstream))
         end
@@ -377,4 +380,3 @@
   end
 
 end
-
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index 4e7379b..fc579a6 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -35,8 +35,14 @@
 require 'xray/thread_dump_signal_handler'
 require_relative '../port_picker'
 
+def load_test_certs
+  test_root = File.join(File.parent(File.dirname(__FILE__)), 'testdata')
+  files = ['ca.pem', 'server1.key', 'server1.pem']
+  files.map { |f| File.open(File.join(test_root, f)).read }
+end
+
 class EchoMsg
-  def marshal
+  def self.marshal(o)
     ''
   end
 
@@ -86,302 +92,324 @@
 
 SlowStub = SlowService.rpc_stub_class
 
-module GRPC
+describe GRPC::RpcServer do
 
-  describe RpcServer do
+  RpcServer = GRPC::RpcServer
 
-    before(:each) do
-      @method = 'an_rpc_method'
-      @pass = 0
-      @fail = 1
-      @noop = Proc.new { |x| x }
+  before(:each) do
+    @method = 'an_rpc_method'
+    @pass = 0
+    @fail = 1
+    @noop = Proc.new { |x| x }
 
-      @server_queue = CompletionQueue.new
-      port = find_unused_tcp_port
-      @host = "localhost:#{port}"
-      @server = GRPC::Server.new(@server_queue, nil)
-      @server.add_http2_port(@host)
-      @ch = GRPC::Channel.new(@host, nil)
+    @server_queue = GRPC::Core::CompletionQueue.new
+    port = find_unused_tcp_port
+    @host = "localhost:#{port}"
+    @server = GRPC::Core::Server.new(@server_queue, nil)
+    @server.add_http2_port(@host)
+    @ch = GRPC::Core::Channel.new(@host, nil)
+  end
+
+  after(:each) do
+    @server.close
+  end
+
+  describe '#new' do
+
+    it 'can be created with just some args' do
+      opts = {:a_channel_arg => 'an_arg'}
+      blk = Proc.new do
+        RpcServer.new(**opts)
+      end
+      expect(&blk).not_to raise_error
     end
 
-    after(:each) do
-      @server.close
+    it 'can be created with a default deadline' do
+      opts = {:a_channel_arg => 'an_arg', :deadline => 5}
+      blk = Proc.new do
+        RpcServer.new(**opts)
+      end
+      expect(&blk).not_to raise_error
     end
 
-    describe '#new' do
-
-      it 'can be created with just some args' do
-        opts = {:a_channel_arg => 'an_arg'}
-        blk = Proc.new do
-          RpcServer.new(**opts)
-        end
-        expect(&blk).not_to raise_error
+    it 'can be created with a completion queue override' do
+      opts = {
+        :a_channel_arg => 'an_arg',
+        :completion_queue_override => @server_queue
+      }
+      blk = Proc.new do
+        RpcServer.new(**opts)
       end
+      expect(&blk).not_to raise_error
+    end
 
-      it 'can be created with a default deadline' do
-        opts = {:a_channel_arg => 'an_arg', :deadline => 5}
-        blk = Proc.new do
-          RpcServer.new(**opts)
-        end
-        expect(&blk).not_to raise_error
-      end
-
-      it 'can be created with a completion queue override' do
+    it 'cannot be created with a bad completion queue override' do
+      blk = Proc.new do
         opts = {
           :a_channel_arg => 'an_arg',
-          :completion_queue_override => @server_queue
+          :completion_queue_override => Object.new
         }
-        blk = Proc.new do
-          RpcServer.new(**opts)
-        end
-        expect(&blk).not_to raise_error
+        RpcServer.new(**opts)
       end
-
-      it 'cannot be created with a bad completion queue override' do
-        blk = Proc.new do
-          opts = {
-            :a_channel_arg => 'an_arg',
-            :completion_queue_override => Object.new
-          }
-          RpcServer.new(**opts)
-        end
-        expect(&blk).to raise_error
-      end
-
-      it 'can be created with a server override' do
-        opts = {:a_channel_arg => 'an_arg', :server_override => @server}
-        blk = Proc.new do
-          RpcServer.new(**opts)
-        end
-        expect(&blk).not_to raise_error
-      end
-
-      it 'cannot be created with a bad server override' do
-        blk = Proc.new do
-          opts = {
-            :a_channel_arg => 'an_arg',
-            :server_override => Object.new
-          }
-          RpcServer.new(**opts)
-        end
-        expect(&blk).to raise_error
-      end
-
+      expect(&blk).to raise_error
     end
 
-    describe '#stopped?' do
-
-      before(:each) do
-        opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
-        @srv = RpcServer.new(**opts)
+    it 'cannot be created with invalid ServerCredentials' do
+      blk = Proc.new do
+        opts = {
+          :a_channel_arg => 'an_arg',
+          :creds => Object.new
+        }
+        RpcServer.new(**opts)
       end
+      expect(&blk).to raise_error
+    end
 
-      it 'starts out false' do
-        expect(@srv.stopped?).to be(false)
+    it 'can be created with the creds as valid ServerCedentials' do
+      certs = load_test_certs
+      server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
+      blk = Proc.new do
+        opts = {
+          :a_channel_arg => 'an_arg',
+          :creds => server_creds
+        }
+        RpcServer.new(**opts)
       end
+      expect(&blk).to_not raise_error
+    end
 
-      it 'stays false after a #stop is called before #run' do
-        @srv.stop
-        expect(@srv.stopped?).to be(false)
+    it 'can be created with a server override' do
+      opts = {:a_channel_arg => 'an_arg', :server_override => @server}
+      blk = Proc.new do
+        RpcServer.new(**opts)
       end
+      expect(&blk).not_to raise_error
+    end
 
-      it 'stays false after the server starts running' do
+    it 'cannot be created with a bad server override' do
+      blk = Proc.new do
+        opts = {
+          :a_channel_arg => 'an_arg',
+          :server_override => Object.new
+        }
+        RpcServer.new(**opts)
+      end
+      expect(&blk).to raise_error
+    end
+
+  end
+
+  describe '#stopped?' do
+
+    before(:each) do
+      opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
+      @srv = RpcServer.new(**opts)
+    end
+
+    it 'starts out false' do
+      expect(@srv.stopped?).to be(false)
+    end
+
+    it 'stays false after a #stop is called before #run' do
+      @srv.stop
+      expect(@srv.stopped?).to be(false)
+    end
+
+    it 'stays false after the server starts running' do
+      @srv.handle(EchoService)
+      t = Thread.new { @srv.run }
+      @srv.wait_till_running
+      expect(@srv.stopped?).to be(false)
+      @srv.stop
+      t.join
+    end
+
+    it 'is true after a running server is stopped' do
+      @srv.handle(EchoService)
+      t = Thread.new { @srv.run }
+      @srv.wait_till_running
+      @srv.stop
+      expect(@srv.stopped?).to be(true)
+      t.join
+    end
+
+  end
+
+  describe '#running?' do
+
+    it 'starts out false' do
+      opts = {:a_channel_arg => 'an_arg', :server_override => @server}
+      r = RpcServer.new(**opts)
+      expect(r.running?).to be(false)
+    end
+
+    it 'is false after run is called with no services registered' do
+      opts = {
+          :a_channel_arg => 'an_arg',
+          :poll_period => 1,
+          :server_override => @server
+      }
+      r = RpcServer.new(**opts)
+      r.run()
+      expect(r.running?).to be(false)
+    end
+
+    it 'is true after run is called with a registered service' do
+      opts = {
+          :a_channel_arg => 'an_arg',
+          :poll_period => 1,
+          :server_override => @server
+      }
+      r = RpcServer.new(**opts)
+      r.handle(EchoService)
+      t = Thread.new { r.run }
+      r.wait_till_running
+      expect(r.running?).to be(true)
+      r.stop
+      t.join
+    end
+
+  end
+
+  describe '#handle' do
+
+    before(:each) do
+      @opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
+      @srv = RpcServer.new(**@opts)
+    end
+
+    it 'raises if #run has already been called' do
+      @srv.handle(EchoService)
+      t = Thread.new { @srv.run }
+      @srv.wait_till_running
+      expect { @srv.handle(EchoService) }.to raise_error
+      @srv.stop
+      t.join
+    end
+
+    it 'raises if the server has been run and stopped' do
+      @srv.handle(EchoService)
+      t = Thread.new { @srv.run }
+      @srv.wait_till_running
+      @srv.stop
+      t.join
+      expect { @srv.handle(EchoService) }.to raise_error
+    end
+
+    it 'raises if the service does not include GenericService ' do
+      expect { @srv.handle(Object) }.to raise_error
+    end
+
+    it 'raises if the service does not declare any rpc methods' do
+      expect { @srv.handle(EmptyService) }.to raise_error
+    end
+
+    it 'raises if the service does not define its rpc methods' do
+      expect { @srv.handle(NoRpcImplementation) }.to raise_error
+    end
+
+    it 'raises if a handler method is already registered' do
+      @srv.handle(EchoService)
+      expect { r.handle(EchoService) }.to raise_error
+    end
+
+  end
+
+  describe '#run' do
+
+    before(:each) do
+      @client_opts = {
+          :channel_override => @ch
+      }
+      @marshal = EchoService.rpc_descs[:an_rpc].marshal_proc
+      @unmarshal = EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output)
+      server_opts = {
+          :server_override => @server,
+          :completion_queue_override => @server_queue,
+          :poll_period => 1
+      }
+      @srv = RpcServer.new(**server_opts)
+    end
+
+    describe 'when running' do
+
+      it 'should return NOT_FOUND status for requests on unknown methods' do
         @srv.handle(EchoService)
         t = Thread.new { @srv.run }
         @srv.wait_till_running
-        expect(@srv.stopped?).to be(false)
+        req = EchoMsg.new
+        blk = Proc.new do
+          cq = GRPC::Core::CompletionQueue.new
+          stub = GRPC::ClientStub.new(@host, cq, **@client_opts)
+          stub.request_response('/unknown', req, @marshal, @unmarshal)
+        end
+        expect(&blk).to raise_error BadStatus
         @srv.stop
         t.join
       end
 
-      it 'is true after a running server is stopped' do
+      it 'should obtain responses for multiple sequential requests' do
         @srv.handle(EchoService)
         t = Thread.new { @srv.run }
         @srv.wait_till_running
+        req = EchoMsg.new
+        n = 5  # arbitrary
+        stub = EchoStub.new(@host, **@client_opts)
+        n.times { |x|  expect(stub.an_rpc(req)).to be_a(EchoMsg) }
         @srv.stop
-        expect(@srv.stopped?).to be(true)
         t.join
       end
 
-    end
-
-    describe '#running?' do
-
-      it 'starts out false' do
-        opts = {:a_channel_arg => 'an_arg', :server_override => @server}
-        r = RpcServer.new(**opts)
-        expect(r.running?).to be(false)
+      it 'should obtain responses for multiple parallel requests' do
+        @srv.handle(EchoService)
+        t = Thread.new { @srv.run }
+        @srv.wait_till_running
+        req, q = EchoMsg.new, Queue.new
+        n = 5  # arbitrary
+        threads = []
+        n.times do |x|
+          cq = GRPC::Core::CompletionQueue.new
+          threads << Thread.new do
+            stub = EchoStub.new(@host, **@client_opts)
+            q << stub.an_rpc(req)
+          end
+        end
+        n.times { expect(q.pop).to be_a(EchoMsg) }
+        @srv.stop
+        threads.each { |t| t.join }
       end
 
-      it 'is false after run is called with no services registered' do
+      it 'should return UNAVAILABLE status if there too many jobs' do
         opts = {
             :a_channel_arg => 'an_arg',
-            :poll_period => 1,
-            :server_override => @server
-        }
-        r = RpcServer.new(**opts)
-        r.run()
-        expect(r.running?).to be(false)
-      end
-
-      it 'is true after run is called with a registered service' do
-        opts = {
-            :a_channel_arg => 'an_arg',
-            :poll_period => 1,
-            :server_override => @server
-        }
-        r = RpcServer.new(**opts)
-        r.handle(EchoService)
-        t = Thread.new { r.run }
-        r.wait_till_running
-        expect(r.running?).to be(true)
-        r.stop
-        t.join
-      end
-
-    end
-
-    describe '#handle' do
-
-      before(:each) do
-        @opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
-        @srv = RpcServer.new(**@opts)
-      end
-
-      it 'raises if #run has already been called' do
-        @srv.handle(EchoService)
-        t = Thread.new { @srv.run }
-        @srv.wait_till_running
-        expect { @srv.handle(EchoService) }.to raise_error
-        @srv.stop
-        t.join
-      end
-
-      it 'raises if the server has been run and stopped' do
-        @srv.handle(EchoService)
-        t = Thread.new { @srv.run }
-        @srv.wait_till_running
-        @srv.stop
-        t.join
-        expect { @srv.handle(EchoService) }.to raise_error
-      end
-
-      it 'raises if the service does not include GenericService ' do
-        expect { @srv.handle(Object) }.to raise_error
-      end
-
-      it 'raises if the service does not declare any rpc methods' do
-        expect { @srv.handle(EmptyService) }.to raise_error
-      end
-
-      it 'raises if the service does not define its rpc methods' do
-        expect { @srv.handle(NoRpcImplementation) }.to raise_error
-      end
-
-      it 'raises if a handler method is already registered' do
-        @srv.handle(EchoService)
-        expect { r.handle(EchoService) }.to raise_error
-      end
-
-    end
-
-    describe '#run' do
-
-      before(:each) do
-        @client_opts = {
-            :channel_override => @ch
-        }
-        @marshal = EchoService.rpc_descs[:an_rpc].marshal_proc
-        @unmarshal = EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output)
-        server_opts = {
             :server_override => @server,
             :completion_queue_override => @server_queue,
-            :poll_period => 1
+            :pool_size => 1,
+            :poll_period => 1,
+            :max_waiting_requests => 0
         }
-        @srv = RpcServer.new(**server_opts)
-      end
-
-      describe 'when running' do
-
-        it 'should return NOT_FOUND status for requests on unknown methods' do
-          @srv.handle(EchoService)
-          t = Thread.new { @srv.run }
-          @srv.wait_till_running
-          req = EchoMsg.new
-          blk = Proc.new do
-            cq = CompletionQueue.new
-            stub = ClientStub.new(@host, cq, **@client_opts)
-            stub.request_response('/unknown', req, @marshal, @unmarshal)
-          end
-          expect(&blk).to raise_error BadStatus
-          @srv.stop
-          t.join
-        end
-
-        it 'should obtain responses for multiple sequential requests' do
-          @srv.handle(EchoService)
-          t = Thread.new { @srv.run }
-          @srv.wait_till_running
-          req = EchoMsg.new
-          n = 5  # arbitrary
-          stub = EchoStub.new(@host, **@client_opts)
-          n.times { |x|  expect(stub.an_rpc(req)).to be_a(EchoMsg) }
-          @srv.stop
-          t.join
-        end
-
-        it 'should obtain responses for multiple parallel requests' do
-          @srv.handle(EchoService)
-          t = Thread.new { @srv.run }
-          @srv.wait_till_running
-          req, q = EchoMsg.new, Queue.new
-          n = 5  # arbitrary
-          threads = []
-          n.times do |x|
-            cq = CompletionQueue.new
-            threads << Thread.new do
-              stub = EchoStub.new(@host, **@client_opts)
-              q << stub.an_rpc(req)
+        alt_srv = RpcServer.new(**opts)
+        alt_srv.handle(SlowService)
+        t = Thread.new { alt_srv.run }
+        alt_srv.wait_till_running
+        req = EchoMsg.new
+        n = 5  # arbitrary, use as many to ensure the server pool is exceeded
+        threads = []
+        _1_failed_as_unavailable = false
+        n.times do |x|
+          threads << Thread.new do
+            cq = GRPC::Core::CompletionQueue.new
+            stub = SlowStub.new(@host, **@client_opts)
+            begin
+              stub.an_rpc(req)
+            rescue BadStatus => e
+              _1_failed_as_unavailable = e.code == StatusCodes::UNAVAILABLE
             end
           end
-          n.times { expect(q.pop).to be_a(EchoMsg) }
-          @srv.stop
-          threads.each { |t| t.join }
         end
-
-        it 'should return UNAVAILABLE status if there too many jobs' do
-          opts = {
-              :a_channel_arg => 'an_arg',
-              :server_override => @server,
-              :completion_queue_override => @server_queue,
-              :pool_size => 1,
-              :poll_period => 1,
-              :max_waiting_requests => 0
-          }
-          alt_srv = RpcServer.new(**opts)
-          alt_srv.handle(SlowService)
-          t = Thread.new { alt_srv.run }
-          alt_srv.wait_till_running
-          req = EchoMsg.new
-          n = 5  # arbitrary, use as many to ensure the server pool is exceeded
-          threads = []
-          _1_failed_as_unavailable = false
-          n.times do |x|
-            threads << Thread.new do
-              cq = CompletionQueue.new
-              stub = SlowStub.new(@host, **@client_opts)
-              begin
-                stub.an_rpc(req)
-              rescue BadStatus => e
-                _1_failed_as_unavailable = e.code == StatusCodes::UNAVAILABLE
-              end
-            end
-          end
-          threads.each { |t| t.join }
-          alt_srv.stop
-          expect(_1_failed_as_unavailable).to be(true)
-        end
-
+        threads.each { |t| t.join }
+        alt_srv.stop
+        expect(_1_failed_as_unavailable).to be(true)
       end
 
     end
diff --git a/src/ruby/spec/generic/service_spec.rb b/src/ruby/spec/generic/service_spec.rb
index 4c76881..dc921d8 100644
--- a/src/ruby/spec/generic/service_spec.rb
+++ b/src/ruby/spec/generic/service_spec.rb
@@ -33,7 +33,7 @@
 
 
 class GoodMsg
-  def marshal
+  def self.marshal(o)
     ''
   end
 
@@ -43,7 +43,7 @@
 end
 
 class EncodeDecodeMsg
-  def encode
+  def self.encode(o)
     ''
   end
 
@@ -53,7 +53,6 @@
 end
 
 GenericService = GRPC::GenericService
-RpcDesc = GRPC::RpcDesc
 Dsl = GenericService::Dsl
 
 
@@ -95,7 +94,7 @@
       end
 
       expect(c.rpc_descs).to include(:AnRpc)
-      expect(c.rpc_descs[:AnRpc]).to be_a(RpcDesc)
+      expect(c.rpc_descs[:AnRpc]).to be_a(GRPC::RpcDesc)
     end
 
     it 'give subclasses access to #rpc_descs' do
@@ -106,7 +105,7 @@
       c = Class.new(base) do
       end
       expect(c.rpc_descs).to include(:AnRpc)
-      expect(c.rpc_descs[:AnRpc]).to be_a(RpcDesc)
+      expect(c.rpc_descs[:AnRpc]).to be_a(GRPC::RpcDesc)
     end
 
   end
@@ -189,7 +188,7 @@
       blk = Proc.new do
         Class.new do
           include GenericService
-          self.marshal_instance_method = :encode
+          self.marshal_class_method = :encode
           self.unmarshal_class_method = :decode
           rpc :AnRpc, EncodeDecodeMsg, EncodeDecodeMsg
         end
diff --git a/src/ruby/spec/metadata_spec.rb b/src/ruby/spec/metadata_spec.rb
index 8465a40..d5dc8b2 100644
--- a/src/ruby/spec/metadata_spec.rb
+++ b/src/ruby/spec/metadata_spec.rb
@@ -29,24 +29,23 @@
 
 require 'grpc'
 
-describe GRPC::Metadata do
+describe GRPC::Core::Metadata do
 
   describe '#new' do
     it 'should create instances' do
-      expect { GRPC::Metadata.new('a key', 'a value') }.to_not raise_error
-      expect(GRPC::Metadata.new('a key', 'a value')).to be_a(GRPC::Metadata)
+      expect { GRPC::Core::Metadata.new('a key', 'a value') }.to_not raise_error
     end
   end
 
   describe '#key' do
-    md = GRPC::Metadata.new('a key', 'a value')
+    md = GRPC::Core::Metadata.new('a key', 'a value')
     it 'should be the constructor value' do
       expect(md.key).to eq('a key')
     end
   end
 
   describe '#value' do
-    md = GRPC::Metadata.new('a key', 'a value')
+    md = GRPC::Core::Metadata.new('a key', 'a value')
     it 'should be the constuctor value' do
       expect(md.value).to eq('a value')
     end
@@ -54,12 +53,12 @@
 
   describe '#dup' do
     it 'should create a copy that returns the correct key' do
-      md = GRPC::Metadata.new('a key', 'a value')
+      md = GRPC::Core::Metadata.new('a key', 'a value')
       expect(md.dup.key).to eq('a key')
     end
 
     it 'should create a copy that returns the correct value' do
-      md = GRPC::Metadata.new('a key', 'a value')
+      md = GRPC::Core::Metadata.new('a key', 'a value')
       expect(md.dup.value).to eq('a value')
     end
   end
diff --git a/src/ruby/spec/server_credentials_spec.rb b/src/ruby/spec/server_credentials_spec.rb
new file mode 100644
index 0000000..bcc2cae
--- /dev/null
+++ b/src/ruby/spec/server_credentials_spec.rb
@@ -0,0 +1,74 @@
+# 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.
+
+require 'grpc'
+
+def load_test_certs
+  test_root = File.join(File.dirname(__FILE__), 'testdata')
+  files = ['ca.pem', 'server1.pem', 'server1.key']
+  files.map { |f| File.open(File.join(test_root, f)).read }
+end
+
+
+describe GRPC::Core::ServerCredentials do
+
+  Creds = GRPC::Core::ServerCredentials
+
+  describe '#new' do
+
+    it 'can be constructed from a fake CA PEM, server PEM and a server key' do
+      expect { Creds.new('a', 'b', 'c') }.not_to raise_error
+    end
+
+    it 'can be constructed using the test certificates' do
+      certs = load_test_certs
+      expect { Creds.new(*certs) }.not_to raise_error
+    end
+
+    it 'cannot be constructed without a server cert chain' do
+      root_cert, server_key, _ = load_test_certs
+      blk = Proc.new { Creds.new(root_cert, server_key, nil) }
+      expect(&blk).to raise_error
+    end
+
+    it 'cannot be constructed without a server key' do
+      root_cert, server_key, _ = load_test_certs
+      blk = Proc.new { Creds.new(root_cert, _, cert_chain) }
+      expect(&blk).to raise_error
+    end
+
+    it 'can be constructed without a root_cret' do
+      _, server_key, cert_chain = load_test_certs
+      blk = Proc.new { Creds.new(_, server_key, cert_chain) }
+      expect(&blk).to_not raise_error
+    end
+
+  end
+
+end
diff --git a/src/ruby/spec/server_spec.rb b/src/ruby/spec/server_spec.rb
index 598b7cf..28f520a 100644
--- a/src/ruby/spec/server_spec.rb
+++ b/src/ruby/spec/server_spec.rb
@@ -30,72 +30,84 @@
 require 'grpc'
 require 'port_picker'
 
-module GRPC
+def load_test_certs
+  test_root = File.join(File.dirname(__FILE__), 'testdata')
+  files = ['ca.pem', 'server1.key', 'server1.pem']
+  files.map { |f| File.open(File.join(test_root, f)).read }
+end
 
-  describe Server do
+Server = GRPC::Core::Server
 
-    before(:each) do
-      @cq = CompletionQueue.new
+describe Server do
+
+  def create_test_cert
+    GRPC::Core::ServerCredentials.new(*load_test_certs)
+  end
+
+  before(:each) do
+    @cq = GRPC::Core::CompletionQueue.new
+  end
+
+  describe '#start' do
+
+    it 'runs without failing' do
+      blk = Proc.new do
+        s = Server.new(@cq, nil).start
+      end
+      expect(&blk).to_not raise_error
     end
 
-    describe '#start' do
-
-      it 'runs without failing' do
-        blk = Proc.new do
-          s = Server.new(@cq, nil).start
-        end
-        expect(&blk).to_not raise_error
-      end
-
-      it 'fails if the server is closed' do
-        s = Server.new(@cq, nil)
-        s.close
-        expect { s.start }.to raise_error(RuntimeError)
-      end
-
+    it 'fails if the server is closed' do
+      s = Server.new(@cq, nil)
+      s.close
+      expect { s.start }.to raise_error(RuntimeError)
     end
 
-    describe '#destroy' do
-      it 'destroys a server ok' do
-        s = start_a_server
+  end
+
+  describe '#destroy' do
+    it 'destroys a server ok' do
+      s = start_a_server
+      blk = Proc.new { s.destroy }
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can be called more than once without error' do
+      s = start_a_server
+      begin
         blk = Proc.new { s.destroy }
         expect(&blk).to_not raise_error
-      end
-
-      it 'can be called more than once without error' do
-        s = start_a_server
-        begin
-          blk = Proc.new { s.destroy }
-          expect(&blk).to_not raise_error
-          blk.call
-          expect(&blk).to_not raise_error
-        ensure
-          s.close
-        end
-      end
-    end
-
-    describe '#close' do
-      it 'closes a server ok' do
-        s = start_a_server
-        begin
-          blk = Proc.new { s.close }
-          expect(&blk).to_not raise_error
-        ensure
-          s.close
-        end
-      end
-
-      it 'can be called more than once without error' do
-        s = start_a_server
-        blk = Proc.new { s.close }
-        expect(&blk).to_not raise_error
         blk.call
         expect(&blk).to_not raise_error
+      ensure
+        s.close
+      end
+    end
+  end
+
+  describe '#close' do
+    it 'closes a server ok' do
+      s = start_a_server
+      begin
+        blk = Proc.new { s.close }
+        expect(&blk).to_not raise_error
+      ensure
+        s.close
       end
     end
 
-    describe '#add_http_port' do
+    it 'can be called more than once without error' do
+      s = start_a_server
+      blk = Proc.new { s.close }
+      expect(&blk).to_not raise_error
+      blk.call
+      expect(&blk).to_not raise_error
+    end
+  end
+
+  describe '#add_http_port' do
+
+    describe 'for insecure servers' do
 
       it 'runs without failing' do
         blk = Proc.new do
@@ -114,72 +126,108 @@
 
     end
 
-    describe '#new' do
+    describe 'for secure servers' do
 
-      it 'takes a completion queue with nil channel args' do
-        expect { Server.new(@cq, nil) }.to_not raise_error
-      end
-
-      it 'does not take a hash with bad keys as channel args' do
-        blk = construct_with_args(Object.new => 1)
-        expect(&blk).to raise_error TypeError
-        blk = construct_with_args(1 => 1)
-        expect(&blk).to raise_error TypeError
-      end
-
-      it 'does not take a hash with bad values as channel args' do
-        blk = construct_with_args(:symbol => Object.new)
-        expect(&blk).to raise_error TypeError
-        blk = construct_with_args('1' => Hash.new)
-        expect(&blk).to raise_error TypeError
-      end
-
-      it 'can take a hash with a symbol key as channel args' do
-        blk = construct_with_args(:a_symbol => 1)
+      it 'runs without failing' do
+        blk = Proc.new do
+          s = Server.new(@cq, nil)
+          s.add_http2_port('localhost:0', true)
+          s.close
+        end
         expect(&blk).to_not raise_error
       end
 
-      it 'can take a hash with a string key as channel args' do
-        blk = construct_with_args('a_symbol' => 1)
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with a string value as channel args' do
-        blk = construct_with_args(:a_symbol => '1')
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with a symbol value as channel args' do
-        blk = construct_with_args(:a_symbol => :another_symbol)
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with a numeric value as channel args' do
-        blk = construct_with_args(:a_symbol => 1)
-        expect(&blk).to_not raise_error
-      end
-
-      it 'can take a hash with many args as channel args' do
-        args = Hash[127.times.collect { |x| [x.to_s, x] } ]
-        blk = construct_with_args(args)
-        expect(&blk).to_not raise_error
+      it 'fails if the server is closed' do
+        s = Server.new(@cq, nil)
+        s.close
+        blk = Proc.new { s.add_http2_port('localhost:0', true) }
+        expect(&blk).to raise_error(RuntimeError)
       end
 
     end
 
+  end
+
+  shared_examples '#new' do
+
+    it 'takes a completion queue with nil channel args' do
+      expect { Server.new(@cq, nil, create_test_cert) }.to_not raise_error
+    end
+
+    it 'does not take a hash with bad keys as channel args' do
+      blk = construct_with_args(Object.new => 1)
+      expect(&blk).to raise_error TypeError
+      blk = construct_with_args(1 => 1)
+      expect(&blk).to raise_error TypeError
+    end
+
+    it 'does not take a hash with bad values as channel args' do
+      blk = construct_with_args(:symbol => Object.new)
+      expect(&blk).to raise_error TypeError
+      blk = construct_with_args('1' => Hash.new)
+      expect(&blk).to raise_error TypeError
+    end
+
+    it 'can take a hash with a symbol key as channel args' do
+      blk = construct_with_args(:a_symbol => 1)
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with a string key as channel args' do
+      blk = construct_with_args('a_symbol' => 1)
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with a string value as channel args' do
+      blk = construct_with_args(:a_symbol => '1')
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with a symbol value as channel args' do
+      blk = construct_with_args(:a_symbol => :another_symbol)
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with a numeric value as channel args' do
+      blk = construct_with_args(:a_symbol => 1)
+      expect(&blk).to_not raise_error
+    end
+
+    it 'can take a hash with many args as channel args' do
+      args = Hash[127.times.collect { |x| [x.to_s, x] } ]
+      blk = construct_with_args(args)
+      expect(&blk).to_not raise_error
+    end
+
+  end
+
+  describe '#new with an insecure channel' do
+
     def construct_with_args(a)
       Proc.new { Server.new(@cq, a) }
     end
 
-    def start_a_server
-      port = find_unused_tcp_port
-      host = "localhost:#{port}"
-      s = Server.new(@cq, nil)
-      s.add_http2_port(host)
-      s.start
-      s
+    it_behaves_like '#new'
+
+  end
+
+  describe '#new with a secure channel' do
+
+    def construct_with_args(a)
+      Proc.new { Server.new(@cq, a, create_test_cert) }
     end
 
+    it_behaves_like '#new'
+
+  end
+
+  def start_a_server
+    port = find_unused_tcp_port
+    host = "localhost:#{port}"
+    s = Server.new(@cq, nil)
+    s.add_http2_port(host)
+    s.start
+    s
   end
 
 end
diff --git a/src/ruby/spec/status_spec.rb b/src/ruby/spec/status_spec.rb
index 83d4efc..63dcefb 100644
--- a/src/ruby/spec/status_spec.rb
+++ b/src/ruby/spec/status_spec.rb
@@ -29,133 +29,138 @@
 
 require 'grpc'
 
-module GRPC
 
-  describe StatusCodes do
+describe GRPC::Core::StatusCodes do
 
-    before(:each) do
-      @known_types = {
-        :OK => 0,
-        :CANCELLED => 1,
-        :UNKNOWN => 2,
-        :INVALID_ARGUMENT => 3,
-        :DEADLINE_EXCEEDED => 4,
-        :NOT_FOUND => 5,
-        :ALREADY_EXISTS => 6,
-        :PERMISSION_DENIED => 7,
-        :RESOURCE_EXHAUSTED => 8,
-        :FAILED_PRECONDITION => 9,
-        :ABORTED => 10,
-        :OUT_OF_RANGE => 11,
-        :UNIMPLEMENTED => 12,
-        :INTERNAL => 13,
-        :UNAVAILABLE => 14,
-        :DATA_LOSS => 15,
-        :UNAUTHENTICATED => 16
-      }
-    end
+  StatusCodes = GRPC::Core::StatusCodes
 
-    it 'should have symbols for all the known status codes' do
-      m = StatusCodes
-      syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
-      expect(Hash[syms_and_codes]).to eq(@known_types)
-    end
-
+  before(:each) do
+    @known_types = {
+      :OK => 0,
+      :CANCELLED => 1,
+      :UNKNOWN => 2,
+      :INVALID_ARGUMENT => 3,
+      :DEADLINE_EXCEEDED => 4,
+      :NOT_FOUND => 5,
+      :ALREADY_EXISTS => 6,
+      :PERMISSION_DENIED => 7,
+      :RESOURCE_EXHAUSTED => 8,
+      :FAILED_PRECONDITION => 9,
+      :ABORTED => 10,
+      :OUT_OF_RANGE => 11,
+      :UNIMPLEMENTED => 12,
+      :INTERNAL => 13,
+      :UNAVAILABLE => 14,
+      :DATA_LOSS => 15,
+      :UNAUTHENTICATED => 16
+    }
   end
 
-  describe Status do
-
-    describe '#new' do
-      it 'should create new instances' do
-        expect { Status.new(142, 'test details') }.to_not raise_error
-      end
-    end
-
-    describe '#details' do
-      it 'return the detail' do
-        sts = Status.new(142, 'test details')
-        expect(sts.details).to eq('test details')
-      end
-    end
-
-    describe '#code' do
-      it 'should return the code' do
-        sts = Status.new(142, 'test details')
-        expect(sts.code).to eq(142)
-      end
-    end
-
-    describe '#dup' do
-      it 'should create a copy that returns the correct details' do
-        sts = Status.new(142, 'test details')
-        expect(sts.dup.code).to eq(142)
-      end
-
-      it 'should create a copy that returns the correct code' do
-        sts = Status.new(142, 'test details')
-        expect(sts.dup.details).to eq('test details')
-      end
-    end
-
-
+  it 'should have symbols for all the known status codes' do
+    m = StatusCodes
+    syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
+    expect(Hash[syms_and_codes]).to eq(@known_types)
   end
 
-  describe BadStatus do
+end
 
-    describe '#new' do
-      it 'should create new instances' do
-        expect { BadStatus.new(142, 'test details') }.to_not raise_error
-      end
+
+describe GRPC::Core::Status do
+
+  Status = GRPC::Core::Status
+
+  describe '#new' do
+    it 'should create new instances' do
+      expect { Status.new(142, 'test details') }.to_not raise_error
+    end
+  end
+
+  describe '#details' do
+    it 'return the detail' do
+      sts = Status.new(142, 'test details')
+      expect(sts.details).to eq('test details')
+    end
+  end
+
+  describe '#code' do
+    it 'should return the code' do
+      sts = Status.new(142, 'test details')
+      expect(sts.code).to eq(142)
+    end
+  end
+
+  describe '#dup' do
+    it 'should create a copy that returns the correct details' do
+      sts = Status.new(142, 'test details')
+      expect(sts.dup.code).to eq(142)
     end
 
-    describe '#details' do
-      it 'return the detail' do
-        err = BadStatus.new(142, 'test details')
-        expect(err.details).to eq('test details')
-      end
+    it 'should create a copy that returns the correct code' do
+      sts = Status.new(142, 'test details')
+      expect(sts.dup.details).to eq('test details')
+    end
+  end
+
+
+end
+
+
+describe GRPC::BadStatus do
+
+  BadStatus = GRPC::BadStatus
+
+  describe '#new' do
+    it 'should create new instances' do
+      expect { BadStatus.new(142, 'test details') }.to_not raise_error
+    end
+  end
+
+  describe '#details' do
+    it 'return the detail' do
+      err = BadStatus.new(142, 'test details')
+      expect(err.details).to eq('test details')
+    end
+  end
+
+  describe '#code' do
+    it 'should return the code' do
+      err = BadStatus.new(142, 'test details')
+      expect(err.code).to eq(142)
+    end
+  end
+
+  describe '#dup' do
+    it 'should create a copy that returns the correct details' do
+      err = BadStatus.new(142, 'test details')
+      expect(err.dup.code).to eq(142)
     end
 
-    describe '#code' do
-      it 'should return the code' do
-        err = BadStatus.new(142, 'test details')
-        expect(err.code).to eq(142)
-      end
+    it 'should create a copy that returns the correct code' do
+      err = BadStatus.new(142, 'test details')
+      expect(err.dup.details).to eq('test details')
+    end
+  end
+
+  describe '#to_status' do
+    it 'should create a Status with the same code and details' do
+      err = BadStatus.new(142, 'test details')
+      sts = err.to_status
+      expect(sts.code).to eq(142)
+      expect(sts.details).to eq('test details')
     end
 
-    describe '#dup' do
-      it 'should create a copy that returns the correct details' do
-        err = BadStatus.new(142, 'test details')
-        expect(err.dup.code).to eq(142)
-      end
-
-      it 'should create a copy that returns the correct code' do
-        err = BadStatus.new(142, 'test details')
-        expect(err.dup.details).to eq('test details')
-      end
+    it 'should create a copy that returns the correct code' do
+      err = BadStatus.new(142, 'test details')
+      expect(err.dup.details).to eq('test details')
     end
+  end
 
-    describe '#to_status' do
-      it 'should create a Status with the same code and details' do
-        err = BadStatus.new(142, 'test details')
-        sts = err.to_status
-        expect(sts.code).to eq(142)
-        expect(sts.details).to eq('test details')
-      end
+  describe 'as an exception' do
 
-      it 'should create a copy that returns the correct code' do
-        err = BadStatus.new(142, 'test details')
-        expect(err.dup.details).to eq('test details')
-      end
+    it 'can be raised' do
+      blk = Proc.new { raise BadStatus.new(343, 'status 343') }
+      expect(&blk).to raise_error(BadStatus)
     end
-
-    describe 'as an exception' do
-
-      it 'can be raised' do
-        blk = Proc.new { raise BadStatus.new(343, 'status 343') }
-        expect(&blk).to raise_error(BadStatus)
-      end
-    end
-
   end
 
 end
diff --git a/src/ruby/spec/testdata/README b/src/ruby/spec/testdata/README
new file mode 100755
index 0000000..ed72661
--- /dev/null
+++ b/src/ruby/spec/testdata/README
@@ -0,0 +1,4 @@
+These are test keys *NOT* to be used in production.
+http://go/keyhunt requires this README
+
+CONFIRMEDTESTKEY
diff --git a/src/ruby/spec/testdata/ca.pem b/src/ruby/spec/testdata/ca.pem
new file mode 100755
index 0000000..6c8511a
--- /dev/null
+++ b/src/ruby/spec/testdata/ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
+Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
+BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
+g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
+Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
+sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
+oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
+Dfcog5wrJytaQ6UA0wE=
+-----END CERTIFICATE-----
diff --git a/src/ruby/spec/testdata/server1.key b/src/ruby/spec/testdata/server1.key
new file mode 100755
index 0000000..143a5b8
--- /dev/null
+++ b/src/ruby/spec/testdata/server1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
+M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
+3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
+AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
+V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
+tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
+dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
+K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
+81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
+DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
+aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
+ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
+XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
+F98XJ7tIFfJq
+-----END PRIVATE KEY-----
diff --git a/src/ruby/spec/testdata/server1.pem b/src/ruby/spec/testdata/server1.pem
new file mode 100755
index 0000000..8e582e5
--- /dev/null
+++ b/src/ruby/spec/testdata/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/src/ruby/spec/time_consts_spec.rb b/src/ruby/spec/time_consts_spec.rb
index 2bbcac0..2775507 100644
--- a/src/ruby/spec/time_consts_spec.rb
+++ b/src/ruby/spec/time_consts_spec.rb
@@ -29,67 +29,65 @@
 
 require 'grpc'
 
-module GRPC
-  describe TimeConsts do
+TimeConsts = GRPC::Core::TimeConsts
 
-    before(:each) do
-      @known_consts = [:ZERO, :INFINITE_FUTURE, :INFINITE_PAST].sort
-    end
+describe TimeConsts do
 
-    it 'should have all the known types' do
-      expect(TimeConsts.constants.collect.sort).to eq(@known_consts)
-    end
-
-    describe "#to_time" do
-      it 'converts each constant to a Time' do
-        m = TimeConsts
-        m.constants.each do |c|
-          expect(m.const_get(c).to_time).to be_a(Time)
-        end
-      end
-    end
-
+  before(:each) do
+    @known_consts = [:ZERO, :INFINITE_FUTURE, :INFINITE_PAST].sort
   end
 
-  describe '#from_relative_time' do
+  it 'should have all the known types' do
+    expect(TimeConsts.constants.collect.sort).to eq(@known_consts)
+  end
 
-    it 'cannot handle arbitrary objects' do
-      expect { TimeConsts.from_relative_time(Object.new) }.to raise_error
-    end
-
-    it 'preserves TimeConsts' do
+  describe '#to_time' do
+    it 'converts each constant to a Time' do
       m = TimeConsts
       m.constants.each do |c|
-        const = m.const_get(c)
-        expect(TimeConsts.from_relative_time(const)).to be(const)
+        expect(m.const_get(c).to_time).to be_a(Time)
       end
     end
-
-    it 'converts 0 to TimeConsts::ZERO' do
-      expect(TimeConsts.from_relative_time(0)).to eq(TimeConsts::ZERO)
-    end
-
-    it 'converts nil to TimeConsts::ZERO' do
-      expect(TimeConsts.from_relative_time(nil)).to eq(TimeConsts::ZERO)
-    end
-
-    it 'converts negative values to TimeConsts::INFINITE_FUTURE' do
-      [-1, -3.2, -1e6].each do |t|
-        y = TimeConsts.from_relative_time(t)
-        expect(y).to eq(TimeConsts::INFINITE_FUTURE)
-      end
-    end
-
-    it 'converts a positive value to an absolute time' do
-      epsilon = 1
-      [1, 3.2, 1e6].each do |t|
-        want = Time.now + t
-        abs = TimeConsts.from_relative_time(t)
-        expect(abs.to_f).to be_within(epsilon).of(want.to_f)
-      end
-    end
-
   end
 
 end
 
+describe '#from_relative_time' do
+
+  it 'cannot handle arbitrary objects' do
+    expect { TimeConsts.from_relative_time(Object.new) }.to raise_error
+  end
+
+  it 'preserves TimeConsts' do
+    m = TimeConsts
+    m.constants.each do |c|
+      const = m.const_get(c)
+      expect(TimeConsts.from_relative_time(const)).to be(const)
+    end
+  end
+
+  it 'converts 0 to TimeConsts::ZERO' do
+    expect(TimeConsts.from_relative_time(0)).to eq(TimeConsts::ZERO)
+  end
+
+  it 'converts nil to TimeConsts::ZERO' do
+    expect(TimeConsts.from_relative_time(nil)).to eq(TimeConsts::ZERO)
+  end
+
+  it 'converts negative values to TimeConsts::INFINITE_FUTURE' do
+    [-1, -3.2, -1e6].each do |t|
+      y = TimeConsts.from_relative_time(t)
+      expect(y).to eq(TimeConsts::INFINITE_FUTURE)
+    end
+  end
+
+  it 'converts a positive value to an absolute time' do
+    epsilon = 1
+    [1, 3.2, 1e6].each do |t|
+      want = Time.now + t
+      abs = TimeConsts.from_relative_time(t)
+      expect(abs.to_f).to be_within(epsilon).of(want.to_f)
+    end
+  end
+
+end
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 801344e..792482c 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -122,7 +122,7 @@
 
 static: static_c static_cxx
 
-static_c: make_dirs dep\
+static_c: make_dirs dep_c\
 % for lib in libs:
 % if lib.build == 'all' and not lib.get('c++', False):
  libs/lib${lib.name}.a\
@@ -130,7 +130,7 @@
 % endfor
 
 
-static_cxx: make_dirs dep\
+static_cxx: make_dirs dep_cxx\
 % for lib in libs:
 % if lib.build == 'all' and lib.get('c++', False):
  libs/lib${lib.name}.a\
@@ -140,7 +140,7 @@
 
 shared: shared_c shared_cxx
 
-shared_c: make_dirs dep\
+shared_c: make_dirs dep_c\
 % for lib in libs:
 % if lib.build == 'all' and not lib.get('c++', False):
  libs/lib${lib.name}.so.$(VERSION)\
@@ -148,7 +148,7 @@
 % endfor
 
 
-shared_cxx: make_dirs dep\
+shared_cxx: make_dirs dep_cxx\
 % for lib in libs:
 % if lib.build == 'all' and lib.get('c++', False):
  libs/lib${lib.name}.so.$(VERSION)\
@@ -158,7 +158,7 @@
 
 privatelibs: privatelibs_c privatelibs_cxx
 
-privatelibs_c: make_dirs dep\
+privatelibs_c: make_dirs dep_c\
 % for lib in libs:
 % if lib.build == 'private':
  libs/lib${lib.name}.a\
@@ -166,7 +166,7 @@
 % endfor
 
 
-privatelibs_cxx: make_dirs dep\
+privatelibs_cxx: make_dirs dep_cxx\
 % for lib in libs:
 % if lib.build == 'private':
  libs/lib${lib.name}.a\
@@ -288,12 +288,31 @@
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
 
-dep:\
+dep: dep_c dep_cxx
+
+dep_c:\
 % for lib in libs:
+% if not lib.get('c++', False):
  deps_lib${lib.name}\
+% endif
 % endfor
 % for tgt in targets:
+% if not tgt.get('c++', False):
  deps_${tgt.name}\
+% endif
+% endfor
+
+
+dep_cxx:\
+% for lib in libs:
+% if lib.get('c++', False):
+ deps_lib${lib.name}\
+% endif
+% endfor
+% for tgt in targets:
+% if tgt.get('c++', False):
+ deps_${tgt.name}\
+% endif
 % endfor
 
 
diff --git a/test/core/echo/echo_test.c b/test/core/echo/echo_test.c
index c699f1e..bbce9da 100644
--- a/test/core/echo/echo_test.c
+++ b/test/core/echo/echo_test.c
@@ -99,8 +99,10 @@
     if (WEXITSTATUS(status)) return WEXITSTATUS(status);
   }
   /* wait for server */
-  printf("checking server\n");
-  if (waitpid(svr, &status, WNOHANG) != 0) return 2;
-  kill(svr, SIGKILL);
+  printf("waiting for server\n");
+  kill(svr, SIGINT);
+  if (waitpid(svr, &status, 0) == -1) return 2;
+  if (!WIFEXITED(status)) return 4;
+  if (WEXITSTATUS(status)) return WEXITSTATUS(status);
   return 0;
 }
diff --git a/test/core/echo/server.c b/test/core/echo/server.c
index 77383f8..d80b55e 100644
--- a/test/core/echo/server.c
+++ b/test/core/echo/server.c
@@ -33,6 +33,7 @@
 
 #include <grpc/grpc.h>
 
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -48,6 +49,7 @@
 
 static grpc_completion_queue *cq;
 static grpc_server *server;
+static int got_sigint = 0;
 
 static const grpc_status status_ok = {GRPC_STATUS_OK, NULL};
 
@@ -79,10 +81,14 @@
   grpc_byte_buffer_reader_destroy(bb_reader);
 }
 
+static void sigint_handler(int x) { got_sigint = 1; }
+
 int main(int argc, char **argv) {
   grpc_event *ev;
   char *addr;
   call_state *s;
+  int shutdown_started = 0;
+  int shutdown_finished = 0;
 
   grpc_test_init(argc, argv);
 
@@ -104,16 +110,29 @@
 
   request_call();
 
-  for (;;) {
-    ev = grpc_completion_queue_next(cq, gpr_inf_future);
-    GPR_ASSERT(ev);
+  signal(SIGINT, sigint_handler);
+  while (!shutdown_finished) {
+    if (got_sigint && !shutdown_started) {
+      gpr_log(GPR_INFO, "Shutting down due to SIGINT");
+      grpc_server_shutdown(server);
+      grpc_completion_queue_shutdown(cq);
+      shutdown_started = 1;
+    }
+    ev = grpc_completion_queue_next(
+        cq, gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)));
+    if (!ev) continue;
     s = ev->tag;
     switch (ev->type) {
       case GRPC_SERVER_RPC_NEW:
-        /* initial ops are already started in request_call */
-        grpc_call_accept(ev->call, cq, s, GRPC_WRITE_BUFFER_HINT);
-        GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
-        request_call();
+        if (ev->call != NULL) {
+          /* initial ops are already started in request_call */
+          grpc_call_accept(ev->call, cq, s, GRPC_WRITE_BUFFER_HINT);
+          GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
+          request_call();
+        } else {
+          GPR_ASSERT(shutdown_started);
+          gpr_free(s);
+        }
         break;
       case GRPC_WRITE_ACCEPTED:
         GPR_ASSERT(ev->data.write_accepted == GRPC_OP_OK);
@@ -137,12 +156,18 @@
           gpr_free(s);
         }
         break;
+      case GRPC_QUEUE_SHUTDOWN:
+        GPR_ASSERT(shutdown_started);
+        shutdown_finished = 1;
+        break;
       default:
-        abort();
+        GPR_ASSERT(0);
     }
     grpc_event_finish(ev);
   }
 
+  grpc_server_destroy(server);
+  grpc_completion_queue_destroy(cq);
   grpc_shutdown();
 
   return 0;
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
new file mode 100644
index 0000000..fd67966
--- /dev/null
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -0,0 +1,202 @@
+/*
+ *
+ * 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/endpoint/socket_utils.h"
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+/* This test exercises IPv4, IPv6, and dualstack sockets in various ways. */
+
+static void *tag(gpr_intptr i) { return (void *)i; }
+
+static gpr_timespec ms_from_now(int ms) {
+  return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_MS * ms));
+}
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event *ev;
+  grpc_completion_type type;
+  do {
+    ev = grpc_completion_queue_next(cq, ms_from_now(5000));
+    GPR_ASSERT(ev);
+    type = ev->type;
+    grpc_event_finish(ev);
+    gpr_log(GPR_INFO, "Drained event type %d", type);
+  } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+void test_connect(const char *server_host, const char *client_host, int port,
+                  int expect_ok) {
+  const grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+  const grpc_status cancelled_status = {GRPC_STATUS_CANCELLED, NULL};
+  char *client_hostport;
+  char *server_hostport;
+  grpc_channel *client;
+  grpc_server *server;
+  grpc_completion_queue *client_cq;
+  grpc_completion_queue *server_cq;
+  grpc_call *c;
+  grpc_call *s;
+  cq_verifier *v_client;
+  cq_verifier *v_server;
+  gpr_timespec deadline;
+
+  gpr_join_host_port(&client_hostport, client_host, port);
+  gpr_join_host_port(&server_hostport, server_host, port);
+  gpr_log(GPR_INFO, "Testing with server=%s client=%s (expecting %s)",
+          server_hostport, client_hostport, expect_ok ? "success" : "failure");
+
+  /* Create server. */
+  server_cq = grpc_completion_queue_create();
+  server = grpc_server_create(server_cq, NULL);
+  GPR_ASSERT(grpc_server_add_http2_port(server, server_hostport));
+  grpc_server_start(server);
+  gpr_free(server_hostport);
+  v_server = cq_verifier_create(server_cq);
+
+  /* Create client. */
+  client_cq = grpc_completion_queue_create();
+  client = grpc_channel_create(client_hostport, NULL);
+  gpr_free(client_hostport);
+  v_client = cq_verifier_create(client_cq);
+
+  if (expect_ok) {
+    /* Normal deadline, shouldn't be reached. */
+    deadline = ms_from_now(60000);
+  } else {
+    /* Give up faster when failure is expected.
+       BUG: Setting this to 1000 reveals a memory leak (b/18608927). */
+    deadline = ms_from_now(1500);
+  }
+
+  /* Send a trivial request. */
+  c = grpc_channel_create_call(client, "/foo", "test.google.com", deadline);
+  GPR_ASSERT(c);
+
+  GPR_ASSERT(GRPC_CALL_OK ==
+             grpc_call_start_invoke(c, client_cq, tag(1), tag(2), tag(3), 0));
+  if (expect_ok) {
+    /* Check for a successful request. */
+    cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+    cq_verify(v_client);
+
+    GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+    cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+    cq_verify(v_client);
+
+    GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(server, tag(100)));
+    cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+                             deadline, NULL);
+    cq_verify(v_server);
+
+    GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, server_cq, tag(102), 0));
+    cq_expect_client_metadata_read(v_client, tag(2), NULL);
+    cq_verify(v_client);
+
+    GPR_ASSERT(GRPC_CALL_OK ==
+               grpc_call_start_write_status(s, send_status, tag(5)));
+    cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+    cq_verify(v_client);
+
+    cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+    cq_verify(v_server);
+    cq_expect_finished(v_server, tag(102), NULL);
+    cq_verify(v_server);
+
+    grpc_call_destroy(c);
+    grpc_call_destroy(s);
+  } else {
+    /* Check for a failed connection. */
+    cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_ERROR);
+    cq_expect_client_metadata_read(v_client, tag(2), NULL);
+    cq_expect_finished_with_status(v_client, tag(3), cancelled_status, NULL);
+    cq_verify(v_client);
+
+    grpc_call_destroy(c);
+  }
+
+  cq_verifier_destroy(v_client);
+  cq_verifier_destroy(v_server);
+
+  /* Destroy client. */
+  grpc_channel_destroy(client);
+  grpc_completion_queue_shutdown(client_cq);
+  drain_cq(client_cq);
+  grpc_completion_queue_destroy(client_cq);
+
+  /* Destroy server. */
+  grpc_server_shutdown(server);
+  grpc_server_destroy(server);
+  grpc_completion_queue_shutdown(server_cq);
+  drain_cq(server_cq);
+  grpc_completion_queue_destroy(server_cq);
+}
+
+int main(int argc, char **argv) {
+  int i;
+  int port = grpc_pick_unused_port_or_die();
+
+  grpc_test_init(argc, argv);
+  grpc_init();
+
+  for (i = 0; i <= 1; i++) {
+    /* For coverage, test with and without dualstack sockets. */
+    grpc_forbid_dualstack_sockets_for_testing = i;
+
+    /* :: and 0.0.0.0 are handled identically. */
+    test_connect("::", "127.0.0.1", port, 1);
+    test_connect("::", "::1", port, 1);
+    test_connect("::", "::ffff:127.0.0.1", port, 1);
+    test_connect("::", "localhost", port, 1);
+    test_connect("0.0.0.0", "127.0.0.1", port, 1);
+    test_connect("0.0.0.0", "::1", port, 1);
+    test_connect("0.0.0.0", "::ffff:127.0.0.1", port, 1);
+    test_connect("0.0.0.0", "localhost", port, 1);
+
+    /* These only work when the families agree. */
+    test_connect("::1", "::1", port, 1);
+    test_connect("::1", "127.0.0.1", port, 0);
+    test_connect("127.0.0.1", "127.0.0.1", port, 1);
+    test_connect("127.0.0.1", "::1", port, 0);
+
+  }
+
+  grpc_shutdown();
+
+  return 0;
+}
diff --git a/test/core/end2end/fixtures/chttp2_fake_security.c b/test/core/end2end/fixtures/chttp2_fake_security.c
index a1f29de..aaca563 100644
--- a/test/core/end2end/fixtures/chttp2_fake_security.c
+++ b/test/core/end2end/fixtures/chttp2_fake_security.c
@@ -47,7 +47,6 @@
 #include "test/core/util/port.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 
-
 static grpc_em em;
 
 typedef struct fullstack_secure_fixture_data {
@@ -124,7 +123,6 @@
   size_t i;
   grpc_test_init(argc, argv);
 
-
   grpc_init();
   grpc_em_init(&em);
 
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
index 2dd3ce9..57c9141 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
@@ -47,7 +47,6 @@
 #include "test/core/util/port.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 
-
 static grpc_em em;
 
 typedef struct fullstack_secure_fixture_data {
@@ -131,7 +130,6 @@
   size_t i;
   grpc_test_init(argc, argv);
 
-
   grpc_init();
   grpc_em_init(&em);
 
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
index 22720c5..8d55853 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
@@ -47,7 +47,6 @@
 #include "test/core/util/port.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 
-
 static grpc_em em;
 
 typedef struct fullstack_secure_fixture_data {
@@ -70,6 +69,15 @@
   return f;
 }
 
+static void chttp2_init_client_secure_fullstack(grpc_end2end_test_fixture *f,
+                                                grpc_channel_args *client_args,
+                                                grpc_credentials *creds) {
+  fullstack_secure_fixture_data *ffd = f->fixture_data;
+  f->client = grpc_secure_channel_create(creds, ffd->localaddr, client_args);
+  GPR_ASSERT(f->client != NULL);
+  grpc_credentials_release(creds);
+}
+
 static void chttp2_init_server_secure_fullstack(
     grpc_end2end_test_fixture *f, grpc_channel_args *server_args,
     grpc_server_credentials *server_creds) {
@@ -89,23 +97,21 @@
 
 static void chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack(
     grpc_end2end_test_fixture *f, grpc_channel_args *client_args) {
-  /* TODO(jboeuf): Replace with composite credentials when those are
-     implemented. */
-  grpc_channel_security_context *client_ctx = NULL;
-  fullstack_secure_fixture_data *ffd = f->fixture_data;
-  grpc_ssl_config config;
-  grpc_credentials *oauth2 =
+  grpc_credentials *ssl_creds = grpc_ssl_credentials_create(
+      test_ca_cert, test_ca_cert_size, NULL, 0, NULL, 0);
+  grpc_credentials *oauth2_creds =
       grpc_fake_oauth2_credentials_create("Bearer aaslkfjs424535asdf", 1);
-  memset(&config, 0, sizeof(grpc_ssl_config));
-  config.pem_root_certs = test_ca_cert;
-  config.pem_root_certs_size = test_ca_cert_size;
-  GPR_ASSERT(grpc_ssl_channel_security_context_create(
-                 oauth2, &config, "foo.test.google.com", &client_ctx) ==
-             GRPC_SECURITY_OK);
-  f->client = grpc_secure_channel_create_internal(ffd->localaddr, client_args,
-                                                  client_ctx);
-  grpc_security_context_unref(&client_ctx->base);
-  grpc_credentials_unref(oauth2);
+  grpc_credentials *ssl_oauth2_creds =
+      grpc_composite_credentials_create(ssl_creds, oauth2_creds);
+  grpc_arg ssl_name_override = {GRPC_ARG_STRING,
+                                GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,
+                                {"foo.test.google.com"}};
+  grpc_channel_args *new_client_args =
+      grpc_channel_args_copy_and_add(client_args, &ssl_name_override);
+  chttp2_init_client_secure_fullstack(f, new_client_args, ssl_oauth2_creds);
+  grpc_channel_args_destroy(new_client_args);
+  grpc_credentials_release(ssl_creds);
+  grpc_credentials_release(oauth2_creds);
 }
 
 static void chttp2_init_server_simple_ssl_secure_fullstack(
@@ -131,7 +137,6 @@
   size_t i;
   grpc_test_init(argc, argv);
 
-
   grpc_init();
   grpc_em_init(&em);
 
diff --git a/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c b/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c
new file mode 100644
index 0000000..9287364
--- /dev/null
+++ b/test/core/end2end/fixtures/chttp2_socket_pair_one_byte_at_a_time.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * 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 "test/core/end2end/end2end_tests.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "src/core/channel/client_channel.h"
+#include "src/core/channel/connected_channel.h"
+#include "src/core/channel/http_filter.h"
+#include "src/core/channel/http_server_filter.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/surface/channel.h"
+#include "src/core/surface/client.h"
+#include "src/core/surface/server.h"
+#include "src/core/surface/surface_em.h"
+#include "src/core/transport/chttp2_transport.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+static void create_sockets(int sv[2]) {
+  int flags;
+  GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+  flags = fcntl(sv[0], F_GETFL, 0);
+  GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+  flags = fcntl(sv[1], F_GETFL, 0);
+  GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+}
+
+/* chttp2 transport that is immediately available (used for testing
+   connected_channel without a client_channel */
+
+static grpc_transport_setup_result server_setup_transport(
+    void *ts, grpc_transport *transport, grpc_mdctx *mdctx) {
+  grpc_end2end_test_fixture *f = ts;
+  static grpc_channel_filter const *extra_filters[] = {&grpc_http_server_filter,
+                                                       &grpc_http_filter};
+  return grpc_server_setup_transport(f->server, transport, extra_filters,
+                                     GPR_ARRAY_SIZE(extra_filters), mdctx);
+}
+
+typedef struct {
+  grpc_end2end_test_fixture *f;
+  grpc_channel_args *client_args;
+} sp_client_setup;
+
+static grpc_transport_setup_result client_setup_transport(
+    void *ts, grpc_transport *transport, grpc_mdctx *mdctx) {
+  sp_client_setup *cs = ts;
+
+  const grpc_channel_filter *filters[] = {&grpc_client_surface_filter,
+                                          &grpc_connected_channel_filter};
+  size_t nfilters = sizeof(filters) / sizeof(*filters);
+  grpc_channel *channel = grpc_channel_create_from_filters(
+      filters, nfilters, cs->client_args, mdctx, 1);
+
+  cs->f->client = channel;
+
+  return grpc_connected_channel_bind_transport(
+      grpc_channel_get_channel_stack(channel), transport);
+}
+
+typedef struct socketpair_fixture_data { int sv[2]; } socketpair_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_socketpair(
+    grpc_channel_args *client_args, grpc_channel_args *server_args) {
+  socketpair_fixture_data *sfd = gpr_malloc(sizeof(socketpair_fixture_data));
+
+  grpc_end2end_test_fixture f;
+  f.fixture_data = sfd;
+  f.client_cq = grpc_completion_queue_create();
+  f.server_cq = grpc_completion_queue_create();
+  f.server = grpc_server_create_from_filters(f.server_cq, NULL, 0, server_args);
+  f.client = NULL;
+
+  create_sockets(sfd->sv);
+
+  return f;
+}
+
+static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
+                                          grpc_channel_args *client_args) {
+  socketpair_fixture_data *sfd = f->fixture_data;
+  grpc_endpoint *cli_tcp;
+  sp_client_setup cs;
+  cs.client_args = client_args;
+  cs.f = f;
+  cli_tcp = grpc_tcp_create_dbg(sfd->sv[0], grpc_surface_em(), 1);
+  grpc_create_chttp2_transport(client_setup_transport, &cs, client_args,
+                               cli_tcp, NULL, 0, grpc_mdctx_create(), 1);
+  GPR_ASSERT(f->client);
+}
+
+static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
+                                          grpc_channel_args *server_args) {
+  socketpair_fixture_data *sfd = f->fixture_data;
+  grpc_endpoint *svr_tcp;
+  svr_tcp = grpc_tcp_create_dbg(sfd->sv[1], grpc_surface_em(), 1);
+  grpc_create_chttp2_transport(server_setup_transport, f, server_args, svr_tcp,
+                               NULL, 0, grpc_mdctx_create(), 0);
+}
+
+static void chttp2_tear_down_socketpair(grpc_end2end_test_fixture *f) {
+  gpr_free(f->fixture_data);
+}
+
+/* All test configurations */
+static grpc_end2end_test_config configs[] = {
+    {"chttp2/socketpair_one_byte_at_a_time", 0, chttp2_create_fixture_socketpair,
+     chttp2_init_client_socketpair, chttp2_init_server_socketpair,
+     chttp2_tear_down_socketpair},
+};
+
+int main(int argc, char **argv) {
+  size_t i;
+
+  grpc_test_init(argc, argv);
+  grpc_init();
+
+  for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+    grpc_end2end_tests(configs[i]);
+  }
+
+  grpc_shutdown();
+
+  return 0;
+}
diff --git a/test/core/end2end/gen_build_json.py b/test/core/end2end/gen_build_json.py
index 0bcccbe..a333621 100755
--- a/test/core/end2end/gen_build_json.py
+++ b/test/core/end2end/gen_build_json.py
@@ -1,3 +1,8 @@
+#!/usr/bin/python
+
+"""Generates the appropriate build.json data for all the end2end tests."""
+
+
 import simplejson
 
 END2END_FIXTURES = [
@@ -6,6 +11,7 @@
     'chttp2_simple_ssl_fullstack',
     'chttp2_simple_ssl_with_oauth2_fullstack',
     'chttp2_socket_pair',
+    'chttp2_socket_pair_one_byte_at_a_time',
 ]
 
 
diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c
index 7a3cab3..5721424 100644
--- a/test/core/end2end/tests/invoke_large_request.c
+++ b/test/core/end2end/tests/invoke_large_request.c
@@ -64,13 +64,11 @@
   return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
 }
 
-static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
-
 static void drain_cq(grpc_completion_queue *cq) {
   grpc_event *ev;
   grpc_completion_type type;
   do {
-    ev = grpc_completion_queue_next(cq, five_seconds_time());
+    ev = grpc_completion_queue_next(cq, n_seconds_time(5));
     GPR_ASSERT(ev);
     type = ev->type;
     grpc_event_finish(ev);
@@ -115,7 +113,7 @@
   gpr_slice request_payload_slice = large_slice();
   grpc_byte_buffer *request_payload =
       grpc_byte_buffer_create(&request_payload_slice, 1);
-  gpr_timespec deadline = five_seconds_time();
+  gpr_timespec deadline = n_seconds_time(10);
   grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
   cq_verifier *v_client = cq_verifier_create(f.client_cq);
   cq_verifier *v_server = cq_verifier_create(f.server_cq);
diff --git a/test/core/end2end/tests/request_response_with_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_metadata_and_payload.c
index 4cb5679..f828ebb 100644
--- a/test/core/end2end/tests/request_response_with_metadata_and_payload.c
+++ b/test/core/end2end/tests/request_response_with_metadata_and_payload.c
@@ -153,11 +153,13 @@
                            deadline, "key1", "val1", "key2", "val2", NULL);
   cq_verify(v_server);
 
+  grpc_call_server_accept(s, f.server_cq, tag(102));
+
   /* add multiple metadata */
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(s, &meta3, 0));
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(s, &meta4, 0));
 
-  grpc_call_accept(s, f.server_cq, tag(102), 0);
+  grpc_call_server_end_initial_metadata(s, 0);
 
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
   cq_expect_read(v_server, tag(5), gpr_slice_from_copied_string("hello world"));
diff --git a/test/core/endpoint/socket_utils_test.c b/test/core/endpoint/socket_utils_test.c
new file mode 100644
index 0000000..ef6ac32
--- /dev/null
+++ b/test/core/endpoint/socket_utils_test.c
@@ -0,0 +1,232 @@
+/*
+ *
+ * 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/endpoint/socket_utils.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include "test/core/util/test_config.h"
+
+static struct sockaddr_in make_addr4(const gpr_uint8 *data, size_t data_len) {
+  struct sockaddr_in addr4;
+  memset(&addr4, 0, sizeof(addr4));
+  addr4.sin_family = AF_INET;
+  GPR_ASSERT(data_len == sizeof(addr4.sin_addr.s_addr));
+  memcpy(&addr4.sin_addr.s_addr, data, data_len);
+  addr4.sin_port = htons(12345);
+  return addr4;
+}
+
+static struct sockaddr_in6 make_addr6(const gpr_uint8 *data, size_t data_len) {
+  struct sockaddr_in6 addr6;
+  memset(&addr6, 0, sizeof(addr6));
+  addr6.sin6_family = AF_INET6;
+  GPR_ASSERT(data_len == sizeof(addr6.sin6_addr.s6_addr));
+  memcpy(&addr6.sin6_addr.s6_addr, data, data_len);
+  addr6.sin6_port = htons(12345);
+  return addr6;
+}
+
+static const gpr_uint8 kMapped[] = {0, 0, 0,    0,    0,   0, 0, 0,
+                                    0, 0, 0xff, 0xff, 192, 0, 2, 1};
+static const gpr_uint8 kNotQuiteMapped[] = {0, 0, 0,    0,    0,   0, 0, 0,
+                                            0, 0, 0xff, 0xfe, 192, 0, 2, 99};
+static const gpr_uint8 kIPv4[] = {192, 0, 2, 1};
+static const gpr_uint8 kIPv6[] = {0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
+                                  0,    0,    0,    0,    0, 0, 0, 1};
+
+static void test_sockaddr_is_v4mapped() {
+  struct sockaddr_in input4;
+  struct sockaddr_in6 input6;
+  struct sockaddr_in output4;
+  struct sockaddr_in expect4;
+
+  gpr_log(GPR_INFO, "%s", __FUNCTION__);
+
+  /* v4mapped input should succeed. */
+  input6 = make_addr6(kMapped, sizeof(kMapped));
+  GPR_ASSERT(grpc_sockaddr_is_v4mapped((const struct sockaddr *)&input6, NULL));
+  GPR_ASSERT(
+      grpc_sockaddr_is_v4mapped((const struct sockaddr *)&input6, &output4));
+  expect4 = make_addr4(kIPv4, sizeof(kIPv4));
+  GPR_ASSERT(memcmp(&expect4, &output4, sizeof(expect4)) == 0);
+
+  /* Non-v4mapped input should fail. */
+  input6 = make_addr6(kNotQuiteMapped, sizeof(kNotQuiteMapped));
+  GPR_ASSERT(
+      !grpc_sockaddr_is_v4mapped((const struct sockaddr *)&input6, NULL));
+  GPR_ASSERT(
+      !grpc_sockaddr_is_v4mapped((const struct sockaddr *)&input6, &output4));
+  /* Output is unchanged. */
+  GPR_ASSERT(memcmp(&expect4, &output4, sizeof(expect4)) == 0);
+
+  /* Plain IPv4 input should also fail. */
+  input4 = make_addr4(kIPv4, sizeof(kIPv4));
+  GPR_ASSERT(
+      !grpc_sockaddr_is_v4mapped((const struct sockaddr *)&input4, NULL));
+}
+
+static void test_sockaddr_to_v4mapped() {
+  struct sockaddr_in input4;
+  struct sockaddr_in6 input6;
+  struct sockaddr_in6 output6;
+  struct sockaddr_in6 expect6;
+
+  gpr_log(GPR_INFO, "%s", __FUNCTION__);
+
+  /* IPv4 input should succeed. */
+  input4 = make_addr4(kIPv4, sizeof(kIPv4));
+  GPR_ASSERT(
+      grpc_sockaddr_to_v4mapped((const struct sockaddr *)&input4, &output6));
+  expect6 = make_addr6(kMapped, sizeof(kMapped));
+  GPR_ASSERT(memcmp(&expect6, &output6, sizeof(output6)) == 0);
+
+  /* IPv6 input should fail. */
+  input6 = make_addr6(kIPv6, sizeof(kIPv6));
+  GPR_ASSERT(
+      !grpc_sockaddr_to_v4mapped((const struct sockaddr *)&input6, &output6));
+  /* Output is unchanged. */
+  GPR_ASSERT(memcmp(&expect6, &output6, sizeof(output6)) == 0);
+
+  /* Already-v4mapped input should also fail. */
+  input6 = make_addr6(kMapped, sizeof(kMapped));
+  GPR_ASSERT(
+      !grpc_sockaddr_to_v4mapped((const struct sockaddr *)&input6, &output6));
+}
+
+static void test_sockaddr_is_wildcard() {
+  struct sockaddr_in wild4;
+  struct sockaddr_in6 wild6;
+  struct sockaddr_in6 wild_mapped;
+  struct sockaddr dummy;
+  int port;
+
+  gpr_log(GPR_INFO, "%s", __FUNCTION__);
+
+  /* Generate wildcards. */
+  grpc_sockaddr_make_wildcards(555, &wild4, &wild6);
+  GPR_ASSERT(
+      grpc_sockaddr_to_v4mapped((const struct sockaddr *)&wild4, &wild_mapped));
+
+  /* Test 0.0.0.0:555 */
+  port = -1;
+  GPR_ASSERT(grpc_sockaddr_is_wildcard((const struct sockaddr *)&wild4, &port));
+  GPR_ASSERT(port == 555);
+  memset(&wild4.sin_addr.s_addr, 0xbd, 1);
+  GPR_ASSERT(
+      !grpc_sockaddr_is_wildcard((const struct sockaddr *)&wild4, &port));
+
+  /* Test [::]:555 */
+  port = -1;
+  GPR_ASSERT(grpc_sockaddr_is_wildcard((const struct sockaddr *)&wild6, &port));
+  GPR_ASSERT(port == 555);
+  memset(&wild6.sin6_addr.s6_addr, 0xbd, 1);
+  GPR_ASSERT(
+      !grpc_sockaddr_is_wildcard((const struct sockaddr *)&wild6, &port));
+
+  /* Test [::ffff:0.0.0.0]:555 */
+  port = -1;
+  GPR_ASSERT(
+      grpc_sockaddr_is_wildcard((const struct sockaddr *)&wild_mapped, &port));
+  GPR_ASSERT(port == 555);
+  memset(&wild_mapped.sin6_addr.s6_addr, 0xbd, 1);
+  GPR_ASSERT(
+      !grpc_sockaddr_is_wildcard((const struct sockaddr *)&wild_mapped, &port));
+
+  /* Test AF_UNSPEC. */
+  port = -1;
+  memset(&dummy, 0, sizeof(dummy));
+  GPR_ASSERT(!grpc_sockaddr_is_wildcard(&dummy, &port));
+  GPR_ASSERT(port == -1);
+}
+
+static void expect_sockaddr_str(const char *expected, void *addr,
+                                int normalize) {
+  int result;
+  char *str;
+  gpr_log(GPR_INFO, "  expect_sockaddr_str(%s)", expected);
+  result = grpc_sockaddr_to_string(&str, (struct sockaddr *)addr, normalize);
+  GPR_ASSERT(str != NULL);
+  GPR_ASSERT(result == strlen(str));
+  GPR_ASSERT(strcmp(expected, str) == 0);
+  gpr_free(str);
+}
+
+static void test_sockaddr_to_string() {
+  struct sockaddr_in input4;
+  struct sockaddr_in6 input6;
+  struct sockaddr dummy;
+
+  gpr_log(GPR_INFO, "%s", __FUNCTION__);
+
+  errno = 0xDEADBEEF;
+
+  input4 = make_addr4(kIPv4, sizeof(kIPv4));
+  expect_sockaddr_str("192.0.2.1:12345", &input4, 0);
+  expect_sockaddr_str("192.0.2.1:12345", &input4, 1);
+
+  input6 = make_addr6(kIPv6, sizeof(kIPv6));
+  expect_sockaddr_str("[2001:db8::1]:12345", &input6, 0);
+  expect_sockaddr_str("[2001:db8::1]:12345", &input6, 1);
+
+  input6 = make_addr6(kMapped, sizeof(kMapped));
+  expect_sockaddr_str("[::ffff:192.0.2.1]:12345", &input6, 0);
+  expect_sockaddr_str("192.0.2.1:12345", &input6, 1);
+
+  input6 = make_addr6(kNotQuiteMapped, sizeof(kNotQuiteMapped));
+  expect_sockaddr_str("[::fffe:c000:263]:12345", &input6, 0);
+  expect_sockaddr_str("[::fffe:c000:263]:12345", &input6, 1);
+
+  memset(&dummy, 0, sizeof(dummy));
+  dummy.sa_family = 999;
+  expect_sockaddr_str("(sockaddr family=999)", &dummy, 0);
+  expect_sockaddr_str("(sockaddr family=999)", &dummy, 1);
+
+  GPR_ASSERT(errno == 0xDEADBEEF);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+
+  test_sockaddr_is_v4mapped();
+  test_sockaddr_to_v4mapped();
+  test_sockaddr_is_wildcard();
+  test_sockaddr_to_string();
+
+  return 0;
+}
diff --git a/test/core/endpoint/tcp_server_test.c b/test/core/endpoint/tcp_server_test.c
index 10e2c36..6208915 100644
--- a/test/core/endpoint/tcp_server_test.c
+++ b/test/core/endpoint/tcp_server_test.c
@@ -80,7 +80,7 @@
   memset(&addr, 0, sizeof(addr));
   addr.sin_family = AF_INET;
   GPR_ASSERT(
-      grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)) >= 0);
+      grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)));
 
   grpc_tcp_server_destroy(s);
 }
@@ -93,7 +93,7 @@
   memset(&addr, 0, sizeof(addr));
   addr.sin_family = AF_INET;
   GPR_ASSERT(
-      grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)) >= 0);
+      grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)));
 
   grpc_tcp_server_start(s, on_connect, NULL);
 
@@ -101,7 +101,7 @@
 }
 
 static void test_connect(int n) {
-  struct sockaddr_in addr;
+  struct sockaddr_storage addr;
   socklen_t addr_len = sizeof(addr);
   int svrfd, clifd;
   grpc_tcp_server *s = grpc_tcp_server_create(&em);
@@ -114,12 +114,13 @@
   gpr_mu_lock(&mu);
 
   memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  svrfd = grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len);
-  GPR_ASSERT(svrfd >= 0);
+  addr.ss_family = AF_INET;
+  GPR_ASSERT(grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len));
 
+  svrfd = grpc_tcp_server_get_fd(s, 0);
+  GPR_ASSERT(svrfd >= 0);
   GPR_ASSERT(getsockname(svrfd, (struct sockaddr *)&addr, &addr_len) == 0);
-  GPR_ASSERT(addr_len == sizeof(addr));
+  GPR_ASSERT(addr_len <= sizeof(addr));
 
   grpc_tcp_server_start(s, on_connect, NULL);
 
@@ -127,7 +128,7 @@
     deadline = gpr_time_add(gpr_now(), gpr_time_from_micros(10000000));
 
     nconnects_before = nconnects;
-    clifd = socket(AF_INET, SOCK_STREAM, 0);
+    clifd = socket(addr.ss_family, SOCK_STREAM, 0);
     GPR_ASSERT(clifd >= 0);
     GPR_ASSERT(connect(clifd, (struct sockaddr *)&addr, addr_len) == 0);
 
diff --git a/test/core/eventmanager/em_pipe_test.c b/test/core/eventmanager/em_pipe_test.c
index 5411142..f2414c4 100644
--- a/test/core/eventmanager/em_pipe_test.c
+++ b/test/core/eventmanager/em_pipe_test.c
@@ -73,7 +73,6 @@
 void write_shutdown_cb(void *arg, /*async_pipe*/
                        enum grpc_em_cb_status status) {
   async_pipe *ap = arg;
-  close(ap->fd[1]);
   grpc_em_fd_destroy(&ap->write_em_fd);
 }
 
@@ -110,7 +109,6 @@
 
 void read_shutdown_cb(void *arg, /*async_pipe*/ enum grpc_em_cb_status status) {
   async_pipe *ap = arg;
-  close(ap->fd[0]);
   grpc_em_fd_destroy(&ap->read_em_fd);
   pthread_mutex_lock(&ap->mu);
   if (ap->done == 0) {
diff --git a/test/core/eventmanager/em_test.c b/test/core/eventmanager/em_test.c
index fceabff..274edc3 100644
--- a/test/core/eventmanager/em_test.c
+++ b/test/core/eventmanager/em_test.c
@@ -138,7 +138,6 @@
   ssize_t read_total = 0;
 
   if (status == GRPC_CALLBACK_CANCELLED) {
-    close(fd);
     session_shutdown_cb(arg, GRPC_CALLBACK_SUCCESS);
     return;
   }
@@ -181,7 +180,6 @@
                                enum grpc_em_cb_status status) {
   server *sv = arg;
 
-  close(grpc_em_fd_get(&sv->em_fd));
   grpc_em_fd_destroy(&sv->em_fd);
 
   gpr_mu_lock(&sv->mu);
@@ -527,7 +525,6 @@
   GPR_ASSERT(gpr_event_get(&ev) == (void *)1);
   grpc_em_fd_destroy(&em_fd);
   grpc_em_destroy(&em);
-  close(sv[0]);
   close(sv[1]);
 }
 
diff --git a/test/core/fling/fling_stream_test.c b/test/core/fling/fling_stream_test.c
index 14a31eb..a24240e 100644
--- a/test/core/fling/fling_stream_test.c
+++ b/test/core/fling/fling_stream_test.c
@@ -96,8 +96,10 @@
   if (waitpid(cli, &status, 0) == -1) return 2;
   if (!WIFEXITED(status)) return 4;
   if (WEXITSTATUS(status)) return WEXITSTATUS(status);
-  printf("checking server\n");
-  if (waitpid(svr, &status, WNOHANG) != 0) return 2;
-  kill(svr, SIGKILL);
+  printf("waiting for server\n");
+  kill(svr, SIGINT);
+  if (waitpid(svr, &status, 0) == -1) return 2;
+  if (!WIFEXITED(status)) return 4;
+  if (WEXITSTATUS(status)) return WEXITSTATUS(status);
   return 0;
 }
diff --git a/test/core/fling/fling_test.c b/test/core/fling/fling_test.c
index e5e63f9..c6b3695 100644
--- a/test/core/fling/fling_test.c
+++ b/test/core/fling/fling_test.c
@@ -96,8 +96,10 @@
   if (waitpid(cli, &status, 0) == -1) return 2;
   if (!WIFEXITED(status)) return 4;
   if (WEXITSTATUS(status)) return WEXITSTATUS(status);
-  printf("checking server\n");
-  if (waitpid(svr, &status, WNOHANG) != 0) return 2;
-  kill(svr, SIGKILL);
+  printf("waiting for server\n");
+  kill(svr, SIGINT);
+  if (waitpid(svr, &status, 0) == -1) return 2;
+  if (!WIFEXITED(status)) return 4;
+  if (WEXITSTATUS(status)) return WEXITSTATUS(status);
   return 0;
 }
diff --git a/test/core/fling/server.c b/test/core/fling/server.c
index 7a5d895..c35bab5 100644
--- a/test/core/fling/server.c
+++ b/test/core/fling/server.c
@@ -50,7 +50,7 @@
 
 static grpc_completion_queue *cq;
 static grpc_server *server;
-static int done = 0;
+static int got_sigint = 0;
 
 static const grpc_status status_ok = {GRPC_STATUS_OK, NULL};
 
@@ -65,13 +65,15 @@
   grpc_server_request_call(server, s);
 }
 
-static void sigint_handler(int x) { done = 1; }
+static void sigint_handler(int x) { got_sigint = 1; }
 
 int main(int argc, char **argv) {
   grpc_event *ev;
   call_state *s;
   char *addr_buf = NULL;
   gpr_cmdline *cl;
+  int shutdown_started = 0;
+  int shutdown_finished = 0;
 
   int secure = 0;
   char *addr = NULL;
@@ -109,23 +111,34 @@
 
   grpc_profiler_start("server.prof");
   signal(SIGINT, sigint_handler);
-  while (!done) {
+  while (!shutdown_finished) {
+    if (got_sigint && !shutdown_started) {
+      gpr_log(GPR_INFO, "Shutting down due to SIGINT");
+      grpc_server_shutdown(server);
+      grpc_completion_queue_shutdown(cq);
+      shutdown_started = 1;
+    }
     ev = grpc_completion_queue_next(
         cq, gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)));
     if (!ev) continue;
     s = ev->tag;
     switch (ev->type) {
       case GRPC_SERVER_RPC_NEW:
-        /* initial ops are already started in request_call */
-        if (0 == strcmp(ev->data.server_rpc_new.method,
-                        "/Reflector/reflectStream")) {
-          s->flags = 0;
+        if (ev->call != NULL) {
+          /* initial ops are already started in request_call */
+          if (0 == strcmp(ev->data.server_rpc_new.method,
+                          "/Reflector/reflectStream")) {
+            s->flags = 0;
+          } else {
+            s->flags = GRPC_WRITE_BUFFER_HINT;
+          }
+          grpc_call_accept(ev->call, cq, s, s->flags);
+          GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
+          request_call();
         } else {
-          s->flags = GRPC_WRITE_BUFFER_HINT;
+          GPR_ASSERT(shutdown_started);
+          gpr_free(s);
         }
-        grpc_call_accept(ev->call, cq, s, s->flags);
-        GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
-        request_call();
         break;
       case GRPC_WRITE_ACCEPTED:
         GPR_ASSERT(ev->data.write_accepted == GRPC_OP_OK);
@@ -147,13 +160,19 @@
           gpr_free(s);
         }
         break;
+      case GRPC_QUEUE_SHUTDOWN:
+        GPR_ASSERT(shutdown_started);
+        shutdown_finished = 1;
+        break;
       default:
-        abort();
+        GPR_ASSERT(0);
     }
     grpc_event_finish(ev);
   }
   grpc_profiler_stop();
 
+  grpc_server_destroy(server);
+  grpc_completion_queue_destroy(cq);
   grpc_shutdown();
   return 0;
 }
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
index 260b39b..7eca844 100644
--- a/test/core/security/credentials_test.c
+++ b/test/core/security/credentials_test.c
@@ -40,6 +40,17 @@
 
 #include <string.h>
 
+static const char test_iam_authorization_token[] = "blahblahblhahb";
+static const char test_iam_authority_selector[] = "respectmyauthoritah";
+static const char test_oauth2_bearer_token[] =
+    "Bearer blaaslkdjfaslkdfasdsfasf";
+static const unsigned char test_root_cert[] = {0xDE, 0xAD, 0xBE, 0xEF};
+
+typedef struct {
+  const char *key;
+  const char *value;
+} expected_md;
+
 static grpc_httpcli_response http_response(int status, char *body) {
   grpc_httpcli_response response;
   memset(&response, 0, sizeof(grpc_httpcli_response));
@@ -154,6 +165,126 @@
   grpc_mdctx_orphan(ctx);
 }
 
+static void check_metadata(expected_md *expected, grpc_mdelem **md_elems,
+                           size_t num_md) {
+  size_t i;
+  for (i = 0; i < num_md; i++) {
+    size_t j;
+    for (j = 0; j < num_md; j++) {
+      if (0 == gpr_slice_str_cmp(md_elems[j]->key->slice, expected[i].key)) {
+        GPR_ASSERT(0 == gpr_slice_str_cmp(md_elems[j]->value->slice,
+                                          expected[i].value));
+        break;
+      }
+    }
+    if (j == num_md) {
+      gpr_log(GPR_ERROR, "key %s not found", expected[i].key);
+      GPR_ASSERT(0);
+    }
+  }
+}
+
+static void check_iam_metadata(void *user_data, grpc_mdelem **md_elems,
+                               size_t num_md, grpc_credentials_status status) {
+  grpc_credentials *c = (grpc_credentials *)user_data;
+  expected_md emd[] = {
+      {GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, test_iam_authorization_token},
+      {GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, test_iam_authority_selector}};
+  GPR_ASSERT(status == GRPC_CREDENTIALS_OK);
+  GPR_ASSERT(num_md == 2);
+  check_metadata(emd, md_elems, num_md);
+  grpc_credentials_unref(c);
+}
+
+static void test_iam_creds(void) {
+  grpc_credentials *creds = grpc_iam_credentials_create(
+      test_iam_authorization_token, test_iam_authority_selector);
+  GPR_ASSERT(grpc_credentials_has_request_metadata(creds));
+  GPR_ASSERT(grpc_credentials_has_request_metadata_only(creds));
+  grpc_credentials_get_request_metadata(creds, check_iam_metadata, creds);
+}
+
+static void check_ssl_oauth2_composite_metadata(
+    void *user_data, grpc_mdelem **md_elems, size_t num_md,
+    grpc_credentials_status status) {
+  grpc_credentials *c = (grpc_credentials *)user_data;
+  expected_md emd[] = {
+      {GRPC_AUTHORIZATION_METADATA_KEY, test_oauth2_bearer_token}};
+  GPR_ASSERT(status == GRPC_CREDENTIALS_OK);
+  GPR_ASSERT(num_md == 1);
+  check_metadata(emd, md_elems, num_md);
+  grpc_credentials_unref(c);
+}
+
+static void test_ssl_oauth2_composite_creds(void) {
+  grpc_credentials *ssl_creds = grpc_ssl_credentials_create(
+      test_root_cert, sizeof(test_root_cert), NULL, 0, NULL, 0);
+  const grpc_credentials_array *creds_array;
+  grpc_credentials *oauth2_creds =
+      grpc_fake_oauth2_credentials_create(test_oauth2_bearer_token, 0);
+  grpc_credentials *composite_creds =
+      grpc_composite_credentials_create(ssl_creds, oauth2_creds);
+  grpc_credentials_unref(ssl_creds);
+  grpc_credentials_unref(oauth2_creds);
+  GPR_ASSERT(!strcmp(composite_creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
+  GPR_ASSERT(grpc_credentials_has_request_metadata(composite_creds));
+  GPR_ASSERT(!grpc_credentials_has_request_metadata_only(composite_creds));
+  creds_array = grpc_composite_credentials_get_credentials(composite_creds);
+  GPR_ASSERT(creds_array->num_creds == 2);
+  GPR_ASSERT(
+      !strcmp(creds_array->creds_array[0]->type, GRPC_CREDENTIALS_TYPE_SSL));
+  GPR_ASSERT(
+      !strcmp(creds_array->creds_array[1]->type, GRPC_CREDENTIALS_TYPE_OAUTH2));
+  grpc_credentials_get_request_metadata(
+      composite_creds, check_ssl_oauth2_composite_metadata, composite_creds);
+}
+
+static void check_ssl_oauth2_iam_composite_metadata(
+    void *user_data, grpc_mdelem **md_elems, size_t num_md,
+    grpc_credentials_status status) {
+  grpc_credentials *c = (grpc_credentials *)user_data;
+  expected_md emd[] = {
+      {GRPC_AUTHORIZATION_METADATA_KEY, test_oauth2_bearer_token},
+      {GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, test_iam_authorization_token},
+      {GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, test_iam_authority_selector}};
+  GPR_ASSERT(status == GRPC_CREDENTIALS_OK);
+  GPR_ASSERT(num_md == 3);
+  check_metadata(emd, md_elems, num_md);
+  grpc_credentials_unref(c);
+}
+
+static void test_ssl_oauth2_iam_composite_creds(void) {
+  grpc_credentials *ssl_creds = grpc_ssl_credentials_create(
+      test_root_cert, sizeof(test_root_cert), NULL, 0, NULL, 0);
+  const grpc_credentials_array *creds_array;
+  grpc_credentials *oauth2_creds =
+      grpc_fake_oauth2_credentials_create(test_oauth2_bearer_token, 0);
+  grpc_credentials *aux_creds =
+      grpc_composite_credentials_create(ssl_creds, oauth2_creds);
+  grpc_credentials *iam_creds = grpc_iam_credentials_create(
+      test_iam_authorization_token, test_iam_authority_selector);
+  grpc_credentials *composite_creds =
+      grpc_composite_credentials_create(aux_creds, iam_creds);
+  grpc_credentials_unref(ssl_creds);
+  grpc_credentials_unref(oauth2_creds);
+  grpc_credentials_unref(aux_creds);
+  grpc_credentials_unref(iam_creds);
+  GPR_ASSERT(!strcmp(composite_creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
+  GPR_ASSERT(grpc_credentials_has_request_metadata(composite_creds));
+  GPR_ASSERT(!grpc_credentials_has_request_metadata_only(composite_creds));
+  creds_array = grpc_composite_credentials_get_credentials(composite_creds);
+  GPR_ASSERT(creds_array->num_creds == 3);
+  GPR_ASSERT(
+      !strcmp(creds_array->creds_array[0]->type, GRPC_CREDENTIALS_TYPE_SSL));
+  GPR_ASSERT(
+      !strcmp(creds_array->creds_array[1]->type, GRPC_CREDENTIALS_TYPE_OAUTH2));
+  GPR_ASSERT(
+      !strcmp(creds_array->creds_array[2]->type, GRPC_CREDENTIALS_TYPE_IAM));
+  grpc_credentials_get_request_metadata(composite_creds,
+                                        check_ssl_oauth2_iam_composite_metadata,
+                                        composite_creds);
+}
+
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   test_compute_engine_creds_parsing_ok();
@@ -163,5 +294,8 @@
   test_compute_engine_creds_parsing_missing_token();
   test_compute_engine_creds_parsing_missing_token_type();
   test_compute_engine_creds_parsing_missing_token_lifetime();
+  test_iam_creds();
+  test_ssl_oauth2_composite_creds();
+  test_ssl_oauth2_iam_composite_creds();
   return 0;
 }
diff --git a/test/core/transport/chttp2/alpn_test.c b/test/core/transport/chttp2/alpn_test.c
new file mode 100644
index 0000000..65b7af8
--- /dev/null
+++ b/test/core/transport/chttp2/alpn_test.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 "src/core/transport/chttp2/alpn.h"
+
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+static void test_alpn_success(void) {
+  const char *version = GRPC_CHTTP2_ALPN_VERSION;
+  GPR_ASSERT(grpc_chttp2_is_alpn_version_supported(version, strlen(version)));
+  GPR_ASSERT(grpc_chttp2_is_alpn_version_supported("h2-15", 5));
+  GPR_ASSERT(grpc_chttp2_is_alpn_version_supported("h2-14", 5));
+}
+
+static void test_alpn_failure(void) {
+  GPR_ASSERT(!grpc_chttp2_is_alpn_version_supported("h2-155", 6));
+  GPR_ASSERT(!grpc_chttp2_is_alpn_version_supported("h1-15", 5));
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_alpn_success();
+  test_alpn_failure();
+  return 0;
+}
diff --git a/test/core/transport/chttp2/bin_encoder_test.c b/test/core/transport/chttp2/bin_encoder_test.c
new file mode 100644
index 0000000..d2400e7
--- /dev/null
+++ b/test/core/transport/chttp2/bin_encoder_test.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * 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/transport/chttp2/bin_encoder.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+
+static int all_ok = 1;
+
+static void expect_slice_eq(gpr_slice expected, gpr_slice slice, char *debug,
+                            int line) {
+  if (0 != gpr_slice_cmp(slice, expected)) {
+    char *hs = gpr_hexdump((const char *)GPR_SLICE_START_PTR(slice),
+                           GPR_SLICE_LENGTH(slice), GPR_HEXDUMP_PLAINTEXT);
+    char *he = gpr_hexdump((const char *)GPR_SLICE_START_PTR(expected),
+                           GPR_SLICE_LENGTH(expected), GPR_HEXDUMP_PLAINTEXT);
+    gpr_log(GPR_ERROR, "FAILED:%d: %s\ngot:  %s\nwant: %s", line, debug, hs,
+            he);
+    gpr_free(hs);
+    gpr_free(he);
+    all_ok = 0;
+  }
+  gpr_slice_unref(expected);
+  gpr_slice_unref(slice);
+}
+
+static gpr_slice B64(const char *s) {
+  gpr_slice ss = gpr_slice_from_copied_string(s);
+  gpr_slice out = grpc_chttp2_base64_encode(ss);
+  gpr_slice_unref(ss);
+  return out;
+}
+
+static gpr_slice HUFF(const char *s) {
+  gpr_slice ss = gpr_slice_from_copied_string(s);
+  gpr_slice out = grpc_chttp2_huffman_compress(ss);
+  gpr_slice_unref(ss);
+  return out;
+}
+
+#define EXPECT_SLICE_EQ(expected, slice)                                   \
+  expect_slice_eq(                                                         \
+      gpr_slice_from_copied_buffer(expected, sizeof(expected) - 1), slice, \
+      #slice, __LINE__);
+
+static void expect_combined_equiv(const char *s, size_t len, int line) {
+  gpr_slice input = gpr_slice_from_copied_buffer(s, len);
+  gpr_slice base64 = grpc_chttp2_base64_encode(input);
+  gpr_slice expect = grpc_chttp2_huffman_compress(base64);
+  gpr_slice got = grpc_chttp2_base64_encode_and_huffman_compress(input);
+  if (0 != gpr_slice_cmp(expect, got)) {
+    char *t = gpr_hexdump((const char *)GPR_SLICE_START_PTR(input),
+                          GPR_SLICE_LENGTH(input), GPR_HEXDUMP_PLAINTEXT);
+    char *e = gpr_hexdump((const char *)GPR_SLICE_START_PTR(expect),
+                          GPR_SLICE_LENGTH(expect), GPR_HEXDUMP_PLAINTEXT);
+    char *g = gpr_hexdump((const char *)GPR_SLICE_START_PTR(got),
+                          GPR_SLICE_LENGTH(got), GPR_HEXDUMP_PLAINTEXT);
+    gpr_log(GPR_ERROR, "FAILED:%d:\ntest: %s\ngot:  %s\nwant: %s", t, g, e);
+    gpr_free(t);
+    gpr_free(e);
+    gpr_free(g);
+  }
+  gpr_slice_unref(input);
+  gpr_slice_unref(base64);
+  gpr_slice_unref(expect);
+  gpr_slice_unref(got);
+}
+
+#define EXPECT_COMBINED_EQUIV(x) \
+  expect_combined_equiv(x, sizeof(x) - 1, __LINE__)
+
+int main(int argc, char **argv) {
+  /* Base64 test vectors from RFC 4648, with padding removed */
+  /* BASE64("") = "" */
+  EXPECT_SLICE_EQ("", B64(""));
+  /* BASE64("f") = "Zg" */
+  EXPECT_SLICE_EQ("Zg", B64("f"));
+  /* BASE64("fo") = "Zm8" */
+  EXPECT_SLICE_EQ("Zm8", B64("fo"));
+  /* BASE64("foo") = "Zm9v" */
+  EXPECT_SLICE_EQ("Zm9v", B64("foo"));
+  /* BASE64("foob") = "Zm9vYg" */
+  EXPECT_SLICE_EQ("Zm9vYg", B64("foob"));
+  /* BASE64("fooba") = "Zm9vYmE" */
+  EXPECT_SLICE_EQ("Zm9vYmE", B64("fooba"));
+  /* BASE64("foobar") = "Zm9vYmFy" */
+  EXPECT_SLICE_EQ("Zm9vYmFy", B64("foobar"));
+
+  /* Huffman encoding tests */
+  EXPECT_SLICE_EQ("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff",
+                  HUFF("www.example.com"));
+  EXPECT_SLICE_EQ("\xa8\xeb\x10\x64\x9c\xbf", HUFF("no-cache"));
+  EXPECT_SLICE_EQ("\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f", HUFF("custom-key"));
+  EXPECT_SLICE_EQ("\x25\xa8\x49\xe9\x5b\xb8\xe8\xb4\xbf", HUFF("custom-value"));
+  EXPECT_SLICE_EQ("\xae\xc3\x77\x1a\x4b", HUFF("private"));
+  EXPECT_SLICE_EQ(
+      "\xd0\x7a\xbe\x94\x10\x54\xd4\x44\xa8\x20\x05\x95\x04\x0b\x81\x66\xe0\x82"
+      "\xa6\x2d\x1b\xff",
+      HUFF("Mon, 21 Oct 2013 20:13:21 GMT"));
+  EXPECT_SLICE_EQ(
+      "\x9d\x29\xad\x17\x18\x63\xc7\x8f\x0b\x97\xc8\xe9\xae\x82\xae\x43\xd3",
+      HUFF("https://www.example.com"));
+
+  /* Various test vectors for combined encoding */
+  EXPECT_COMBINED_EQUIV("");
+  EXPECT_COMBINED_EQUIV("f");
+  EXPECT_COMBINED_EQUIV("fo");
+  EXPECT_COMBINED_EQUIV("foo");
+  EXPECT_COMBINED_EQUIV("foob");
+  EXPECT_COMBINED_EQUIV("fooba");
+  EXPECT_COMBINED_EQUIV("foobar");
+  EXPECT_COMBINED_EQUIV("www.example.com");
+  EXPECT_COMBINED_EQUIV("no-cache");
+  EXPECT_COMBINED_EQUIV("custom-key");
+  EXPECT_COMBINED_EQUIV("custom-value");
+  EXPECT_COMBINED_EQUIV("private");
+  EXPECT_COMBINED_EQUIV("Mon, 21 Oct 2013 20:13:21 GMT");
+  EXPECT_COMBINED_EQUIV("https://www.example.com");
+  EXPECT_COMBINED_EQUIV(
+      "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+      "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+      "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
+      "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
+      "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
+      "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
+      "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+      "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+      "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+      "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+      "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+      "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+      "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+      "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+      "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff");
+
+  return all_ok ? 0 : 1;
+}
diff --git a/test/core/transport/transport_end2end_tests.c b/test/core/transport/transport_end2end_tests.c
index ce6fbcf..49ce645 100644
--- a/test/core/transport/transport_end2end_tests.c
+++ b/test/core/transport/transport_end2end_tests.c
@@ -466,8 +466,13 @@
 
 static void close_transport(void *user_data, grpc_transport *transport) {}
 
+static void recv_goaway(void *user_data, grpc_transport *transport,
+                        grpc_status_code status, gpr_slice debug) {
+  gpr_slice_unref(debug);
+}
+
 static grpc_transport_callbacks transport_callbacks = {
-    alloc_recv_buffer, create_stream, recv_batch, close_transport};
+    alloc_recv_buffer, create_stream, recv_batch, recv_goaway, close_transport};
 
 /* Helper for tests to create a stream.
    Arguments:
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 255e29e..ea21099 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -41,6 +41,7 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/status.h>
+#include <grpc++/stream.h>
 #include <gtest/gtest.h>
 
 #include <grpc/grpc.h>
@@ -58,6 +59,44 @@
     response->set_message(request->message());
     return Status::OK;
   }
+
+  // Unimplemented is left unimplemented to test the returned error.
+
+  Status RequestStream(ServerReader<EchoRequest>* reader,
+                       EchoResponse* response) {
+    EchoRequest request;
+    response->set_message("");
+    while (reader->Read(&request)) {
+      response->mutable_message()->append(request.message());
+    }
+    return Status::OK;
+  }
+
+  // Return 3 messages.
+  // TODO(yangg) make it generic by adding a parameter into EchoRequest
+  Status ResponseStream(const EchoRequest* request,
+                        ServerWriter<EchoResponse>* writer) {
+    EchoResponse response;
+    response.set_message(request->message() + "0");
+    writer->Write(response);
+    response.set_message(request->message() + "1");
+    writer->Write(response);
+    response.set_message(request->message() + "2");
+    writer->Write(response);
+
+    return Status::OK;
+  }
+
+  Status BidiStream(ServerReaderWriter<EchoResponse, EchoRequest>* stream) {
+    EchoRequest request;
+    EchoResponse response;
+    while (stream->Read(&request)) {
+      gpr_log(GPR_INFO, "recv msg %s", request.message().c_str());
+      response.set_message(request.message());
+      stream->Write(response);
+    }
+    return Status::OK;
+  }
 };
 
 class End2endTest : public ::testing::Test {
@@ -68,7 +107,7 @@
     // Setup server
     ServerBuilder builder;
     builder.AddPort(server_address_.str());
-    builder.RegisterService(service.service());
+    builder.RegisterService(service_.service());
     server_ = builder.BuildAndStart();
   }
 
@@ -78,7 +117,7 @@
 
   std::unique_ptr<Server> server_;
   std::ostringstream server_address_;
-  TestServiceImpl service;
+  TestServiceImpl service_;
 };
 
 static void SendRpc(const grpc::string& server_address, int num_rpcs) {
@@ -114,6 +153,127 @@
   }
 }
 
+TEST_F(End2endTest, UnimplementedRpc) {
+  std::shared_ptr<ChannelInterface> channel =
+      CreateChannel(server_address_.str());
+  TestService::Stub* stub = TestService::NewStub(channel);
+  EchoRequest request;
+  EchoResponse response;
+  request.set_message("Hello");
+
+  ClientContext context;
+  Status s = stub->Unimplemented(&context, request, &response);
+  EXPECT_FALSE(s.IsOk());
+  EXPECT_EQ(s.code(), grpc::StatusCode::UNIMPLEMENTED);
+  EXPECT_EQ(s.details(), "");
+  EXPECT_EQ(response.message(), "");
+
+  delete stub;
+}
+
+TEST_F(End2endTest, RequestStreamOneRequest) {
+  std::shared_ptr<ChannelInterface> channel =
+      CreateChannel(server_address_.str());
+  TestService::Stub* stub = TestService::NewStub(channel);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+
+  ClientWriter<EchoRequest>* stream = stub->RequestStream(&context, &response);
+  request.set_message("hello");
+  EXPECT_TRUE(stream->Write(request));
+  stream->WritesDone();
+  Status s = stream->Wait();
+  EXPECT_EQ(response.message(), request.message());
+  EXPECT_TRUE(s.IsOk());
+
+  delete stream;
+  delete stub;
+}
+
+TEST_F(End2endTest, RequestStreamTwoRequests) {
+  std::shared_ptr<ChannelInterface> channel =
+      CreateChannel(server_address_.str());
+  TestService::Stub* stub = TestService::NewStub(channel);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+
+  ClientWriter<EchoRequest>* stream = stub->RequestStream(&context, &response);
+  request.set_message("hello");
+  EXPECT_TRUE(stream->Write(request));
+  EXPECT_TRUE(stream->Write(request));
+  stream->WritesDone();
+  Status s = stream->Wait();
+  EXPECT_EQ(response.message(), "hellohello");
+  EXPECT_TRUE(s.IsOk());
+
+  delete stream;
+  delete stub;
+}
+
+TEST_F(End2endTest, ResponseStream) {
+  std::shared_ptr<ChannelInterface> channel =
+      CreateChannel(server_address_.str());
+  TestService::Stub* stub = TestService::NewStub(channel);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  request.set_message("hello");
+
+  ClientReader<EchoResponse>* stream = stub->ResponseStream(&context, &request);
+  EXPECT_TRUE(stream->Read(&response));
+  EXPECT_EQ(response.message(), request.message() + "0");
+  EXPECT_TRUE(stream->Read(&response));
+  EXPECT_EQ(response.message(), request.message() + "1");
+  EXPECT_TRUE(stream->Read(&response));
+  EXPECT_EQ(response.message(), request.message() + "2");
+  EXPECT_FALSE(stream->Read(&response));
+
+  Status s = stream->Wait();
+  EXPECT_TRUE(s.IsOk());
+
+  delete stream;
+  delete stub;
+}
+
+TEST_F(End2endTest, BidiStream) {
+  std::shared_ptr<ChannelInterface> channel =
+      CreateChannel(server_address_.str());
+  TestService::Stub* stub = TestService::NewStub(channel);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  grpc::string msg("hello");
+
+  ClientReaderWriter<EchoRequest, EchoResponse>* stream =
+      stub->BidiStream(&context);
+
+  request.set_message(msg + "0");
+  EXPECT_TRUE(stream->Write(request));
+  EXPECT_TRUE(stream->Read(&response));
+  EXPECT_EQ(response.message(), request.message());
+
+  request.set_message(msg + "1");
+  EXPECT_TRUE(stream->Write(request));
+  EXPECT_TRUE(stream->Read(&response));
+  EXPECT_EQ(response.message(), request.message());
+
+  request.set_message(msg + "2");
+  EXPECT_TRUE(stream->Write(request));
+  EXPECT_TRUE(stream->Read(&response));
+  EXPECT_EQ(response.message(), request.message());
+
+  stream->WritesDone();
+  EXPECT_FALSE(stream->Read(&response));
+
+  Status s = stream->Wait();
+  EXPECT_TRUE(s.IsOk());
+
+  delete stream;
+  delete stub;
+}
+
 }  // namespace grpc
 
 int main(int argc, char** argv) {
diff --git a/test/cpp/util/echo.proto b/test/cpp/util/echo.proto
index 37ae6b9..abce7ad 100644
--- a/test/cpp/util/echo.proto
+++ b/test/cpp/util/echo.proto
@@ -11,5 +11,10 @@
 }
 
 service TestService {
-  rpc Echo(EchoRequest) returns (EchoResponse) {}
+  rpc Echo(EchoRequest) returns (EchoResponse);
+  rpc RequestStream(stream EchoRequest) returns (EchoResponse);
+  rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
+  rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
+  rpc Unimplemented(EchoRequest) returns (EchoResponse) {
+  }
 }
diff --git a/test/cpp/util/test_ssl_channel.cc b/test/cpp/util/test_ssl_channel.cc
index bf4a2d6..ca2c563 100644
--- a/test/cpp/util/test_ssl_channel.cc
+++ b/test/cpp/util/test_ssl_channel.cc
@@ -34,10 +34,8 @@
 #include "test/cpp/util/test_ssl_channel.h"
 
 #include <grpc/support/log.h>
-extern "C" {
-#include "src/core/channel/channel_args.h"
+
 #include <grpc/grpc_security.h>
-}
 
 #include "test/core/end2end/data/ssl_test_data.h"
 
@@ -57,3 +55,4 @@
 }
 
 }  // namespace grpc
+